diff --git a/src/Client/ClientBase.cpp b/src/Client/ClientBase.cpp index 3df30e98843..d923ff0ba03 100644 --- a/src/Client/ClientBase.cpp +++ b/src/Client/ClientBase.cpp @@ -1058,7 +1058,13 @@ void ClientBase::processInsertQuery(const String & query_to_execute, ASTPtr pars /// Process the query that requires transferring data blocks to the server. const auto parsed_insert_query = parsed_query->as(); if ((!parsed_insert_query.data && !parsed_insert_query.infile) && (is_interactive || (!stdin_is_a_tty && std_in.eof()))) - throw Exception("No data to insert", ErrorCodes::NO_DATA_TO_INSERT); + { + const auto & settings = global_context->getSettingsRef(); + if (settings.throw_if_no_data_to_insert) + throw Exception("No data to insert", ErrorCodes::NO_DATA_TO_INSERT); + else + return; + } connection->sendQuery( connection_parameters.timeouts, diff --git a/src/Core/Settings.h b/src/Core/Settings.h index a2efbf7a92a..27212aa0498 100644 --- a/src/Core/Settings.h +++ b/src/Core/Settings.h @@ -582,6 +582,7 @@ class IColumn; M(Bool, allow_experimental_object_type, false, "Allow Object and JSON data types", 0) \ M(String, insert_deduplication_token, "", "If not empty, used for duplicate detection instead of data digest", 0) \ M(Bool, throw_on_unsupported_query_inside_transaction, true, "Throw exception if unsupported query is used inside transaction", 0) \ + M(Bool, throw_if_no_data_to_insert, true, "Enables or disables empty INSERTs, disable by default", 0) \ // End of COMMON_SETTINGS // Please add settings related to formats into the FORMAT_FACTORY_SETTINGS and move obsolete settings to OBSOLETE_SETTINGS. diff --git a/src/Parsers/ParserInsertQuery.cpp b/src/Parsers/ParserInsertQuery.cpp index b0ca361155f..7f8a8d59fd0 100644 --- a/src/Parsers/ParserInsertQuery.cpp +++ b/src/Parsers/ParserInsertQuery.cpp @@ -41,6 +41,7 @@ bool ParserInsertQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected) ParserKeyword s_with("WITH"); ParserToken s_lparen(TokenType::OpeningRoundBracket); ParserToken s_rparen(TokenType::ClosingRoundBracket); + ParserToken s_semicolon(TokenType::Semicolon); ParserIdentifier name_p(true); ParserList columns_p(std::make_unique(), std::make_unique(TokenType::Comma), false); ParserFunction table_function_p{false}; @@ -146,8 +147,10 @@ bool ParserInsertQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected) /// After FROM INFILE we expect FORMAT, SELECT, WITH or nothing. if (!infile && s_values.ignore(pos, expected)) { - /// If VALUES is defined in query, everything except setting will be parsed as data - data = pos->begin; + /// If VALUES is defined in query, everything except setting will be parsed as data, + /// and if values followed by semicolon, the data should be null. + if (!s_semicolon.checkWithoutMoving(pos, expected)) + data = pos->begin; format_str = "Values"; } else if (s_format.ignore(pos, expected)) diff --git a/src/Server/GRPCServer.cpp b/src/Server/GRPCServer.cpp index 7578f8afc1d..68d73a6be2a 100644 --- a/src/Server/GRPCServer.cpp +++ b/src/Server/GRPCServer.cpp @@ -956,7 +956,13 @@ namespace if (!insert_query) throw Exception("Query requires data to insert, but it is not an INSERT query", ErrorCodes::NO_DATA_TO_INSERT); else - throw Exception("No data to insert", ErrorCodes::NO_DATA_TO_INSERT); + { + const auto & settings = query_context->getSettingsRef(); + if (settings.throw_if_no_data_to_insert) + throw Exception("No data to insert", ErrorCodes::NO_DATA_TO_INSERT); + else + return; + } } /// This is significant, because parallel parsing may be used. diff --git a/tests/queries/0_stateless/02267_insert_empty_data.reference b/tests/queries/0_stateless/02267_insert_empty_data.reference new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/queries/0_stateless/02267_insert_empty_data.sql b/tests/queries/0_stateless/02267_insert_empty_data.sql new file mode 100644 index 00000000000..9c92fc2a3f7 --- /dev/null +++ b/tests/queries/0_stateless/02267_insert_empty_data.sql @@ -0,0 +1,11 @@ +DROP TABLE IF EXISTS t; + +CREATE TABLE t (n UInt32) ENGINE=Memory; + +INSERT INTO t VALUES; -- { clientError 108 } + +set throw_if_no_data_to_insert = 0; + +INSERT INTO t VALUES; + +DROP TABLE t;