Skip to content

Commit

Permalink
Merge pull request ClickHouse#70593 from jsc0218/InsertWith
Browse files Browse the repository at this point in the history
With Insert Support
  • Loading branch information
alexey-milovidov authored Oct 21, 2024
2 parents 6771c27 + 691aff2 commit 4c9e469
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 1 deletion.
8 changes: 8 additions & 0 deletions docs/en/sql-reference/statements/insert-into.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,14 @@ However, you can delete old data using `ALTER TABLE ... DROP PARTITION`.

To insert a default value instead of `NULL` into a column with not nullable data type, enable [insert_null_as_default](../../operations/settings/settings.md#insert_null_as_default) setting.

`INSERT` also supports CTE(common table expression). For example, the following two statements are equivalent:

``` sql
INSERT INTO x WITH y AS (SELECT * FROM numbers(10)) SELECT * FROM y;
WITH y AS (SELECT * FROM numbers(10)) INSERT INTO x SELECT * FROM y;
```


## Inserting Data from a File

**Syntax**
Expand Down
27 changes: 26 additions & 1 deletion src/Parsers/ParserInsertQuery.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
#include <Parsers/ASTIdentifier_fwd.h>
#include <Parsers/ASTInsertQuery.h>
#include <Parsers/ASTSelectQuery.h>
#include <Parsers/ASTSelectWithUnionQuery.h>

#include <Parsers/CommonParsers.h>
#include <Parsers/ExpressionElementParsers.h>
#include <Parsers/ExpressionListParsers.h>
#include <Parsers/ParserSelectWithUnionQuery.h>
#include <Parsers/ParserWatchQuery.h>
#include <Parsers/ParserWithElement.h>
#include <Parsers/ParserInsertQuery.h>
#include <Parsers/ParserSetQuery.h>
#include <Parsers/InsertQuerySettingsPushDownVisitor.h>
Expand Down Expand Up @@ -58,10 +60,20 @@ bool ParserInsertQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
ASTPtr settings_ast;
ASTPtr partition_by_expr;
ASTPtr compression;
ASTPtr with_expression_list;

/// Insertion data
const char * data = nullptr;

if (s_with.ignore(pos, expected))
{
if (!ParserList(std::make_unique<ParserWithElement>(), std::make_unique<ParserToken>(TokenType::Comma))
.parse(pos, with_expression_list, expected))
return false;
if (with_expression_list->children.empty())
return false;
}

/// Check for key words `INSERT INTO`. If it isn't found, the query can't be parsed as insert query.
if (!s_insert_into.ignore(pos, expected))
return false;
Expand Down Expand Up @@ -162,14 +174,27 @@ bool ParserInsertQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)

tryGetIdentifierNameInto(format, format_str);
}
else if (s_select.ignore(pos, expected) || s_with.ignore(pos,expected))
else if (s_select.ignore(pos, expected) || s_with.ignore(pos, expected))
{
/// If SELECT is defined, return to position before select and parse
/// rest of query as SELECT query.
pos = before_values;
ParserSelectWithUnionQuery select_p;
select_p.parse(pos, select, expected);

if (with_expression_list)
{
const auto & children = select->as<ASTSelectWithUnionQuery>()->list_of_selects->children;
for (const auto & child : children)
{
if (child->as<ASTSelectQuery>()->getExpression(ASTSelectQuery::Expression::WITH, false))
throw Exception(ErrorCodes::SYNTAX_ERROR,
"Only one WITH should be presented, either before INSERT or SELECT.");
child->as<ASTSelectQuery>()->setExpression(ASTSelectQuery::Expression::WITH,
std::move(with_expression_list));
}
}

/// FORMAT section is expected if we have input() in SELECT part
if (s_format.ignore(pos, expected) && !name_p.parse(pos, format, expected))
return false;
Expand Down
20 changes: 20 additions & 0 deletions tests/queries/0_stateless/03248_with_insert.reference
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
0
1
2
3
4
0
1
2
3
4
2025-01-01
2026-01-01
2027-01-01
2028-01-01
2029-01-01
2030-01-01
2031-01-01
2032-01-01
2033-01-01
2034-01-01
49 changes: 49 additions & 0 deletions tests/queries/0_stateless/03248_with_insert.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
DROP TABLE IF EXISTS x;

CREATE TABLE x ENGINE = Log AS SELECT * FROM numbers(0);

SYSTEM STOP MERGES x;

WITH y AS
(
SELECT *
FROM numbers(10)
)
INSERT INTO x
SELECT *
FROM y
INTERSECT
SELECT *
FROM numbers(5);

WITH y AS
(
SELECT *
FROM numbers(10)
)
INSERT INTO x
SELECT *
FROM numbers(5)
INTERSECT
SELECT *
FROM y;

SELECT * FROM x;

DROP TABLE x;

CREATE TABLE x (d date) ENGINE = Log;

WITH y AS
(
SELECT
number,
date_add(YEAR, number, toDate('2025-01-01')) AS new_date
FROM numbers(10)
)
INSERT INTO x
SELECT y.new_date FROM y;

SELECT * FROM x;

DROP TABLE x;
1 change: 1 addition & 0 deletions tests/queries/0_stateless/03248_with_insert_with.reference
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Only one WITH should be presented, either before INSERT or SELECT
10 changes: 10 additions & 0 deletions tests/queries/0_stateless/03248_with_insert_with.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env bash

CUR_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
# shellcheck source=../shell_config.sh
. "$CUR_DIR"/../shell_config.sh

$CLICKHOUSE_CLIENT -q 'DROP TABLE IF EXISTS x'
$CLICKHOUSE_CLIENT -q 'CREATE TABLE x ENGINE = Log AS SELECT * FROM numbers(0)'
$CLICKHOUSE_CLIENT -q 'WITH y AS ( SELECT * FROM numbers(10) ) INSERT INTO x WITH y2 AS ( SELECT * FROM numbers(10) ) SELECT * FROM y;' |& grep --max-count 1 -F --only-matching "Only one WITH should be presented, either before INSERT or SELECT"
$CLICKHOUSE_CLIENT -q 'DROP TABLE x'

0 comments on commit 4c9e469

Please sign in to comment.