diff --git a/doc/src/sgml/ref/insert.sgml b/doc/src/sgml/ref/insert.sgml index f995a7637f..341616d8b5 100644 --- a/doc/src/sgml/ref/insert.sgml +++ b/doc/src/sgml/ref/insert.sgml @@ -24,7 +24,12 @@ PostgreSQL documentation [ WITH [ RECURSIVE ] with_query [, ...] ] INSERT INTO table_name [ AS alias ] [ ( column_name [, ...] ) ] [ OVERRIDING { SYSTEM | USER} VALUE ] - { DEFAULT VALUES | VALUES ( { expression | DEFAULT } [, ...] ) [, ...] | query } + { DEFAULT VALUES | VALUES ( { expression | DEFAULT } [, ...] ) [, ...] | + SET { column_name = { expression | DEFAULT } | + ( column_name [, ...] ) = [ ROW ] ( { expression | DEFAULT } [, ...] ) | + ( column_name [, ...] ) = ( sub-SELECT ) + } [, ...] [ FROM from_list ] } | + query } [ ON CONFLICT [ conflict_target ] conflict_action ] [ RETURNING * | output_expression [ [ AS ] output_name ] [, ...] ] @@ -254,6 +259,16 @@ INSERT INTO table_name [ AS + + from_list + + + A list of table expressions, allowing columns from other tables + to be used as values in the expression. + + + + DEFAULT @@ -675,6 +690,13 @@ WITH upd AS ( RETURNING * ) INSERT INTO employees_log SELECT *, current_timestamp FROM upd; + + + + Insert a single row into table distributors using a +SET clause to specify the columns and values: + +INSERT INTO distributors SET did = 4, dname = 'Hammers Unlimited, LLC'; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index c97bb367f8..3015a2925a 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -463,7 +463,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type OptSeqOptList SeqOptList OptParenthesizedSeqOptList %type SeqOptElem -%type insert_rest +%type insert_rest insert_set_clause %type opt_conf_expr %type opt_on_conflict @@ -10892,6 +10892,15 @@ insert_rest: $$->override = $5; $$->selectStmt = $7; } + | insert_set_clause + { + $$ = $1; + } + | OVERRIDING override_kind VALUE_P insert_set_clause + { + $$ = $4; + $$->override = $2; + } | DEFAULT VALUES { $$ = makeNode(InsertStmt); @@ -10923,6 +10932,35 @@ insert_column_item: } ; +insert_set_clause: + SET set_clause_list from_clause + { + SelectStmt *n = makeNode(SelectStmt); + + if ($3 != NULL) + { + n->targetList = $2; + n->fromClause = $3; + } + else + { + List *values = NIL; + ListCell *col_cell; + + foreach(col_cell, $2) + { + ResTarget *res_col = (ResTarget *) lfirst(col_cell); + + values = lappend(values, res_col->val); + } + n->valuesLists = list_make1(values); + } + $$ = makeNode(InsertStmt); + $$->cols = $2; + $$->selectStmt = (Node *) n; + } + ; + opt_on_conflict: ON CONFLICT opt_conf_expr DO UPDATE SET set_clause_list where_clause { diff --git a/src/test/regress/expected/insert.out b/src/test/regress/expected/insert.out index 75e25cdf48..c15260ea76 100644 --- a/src/test/regress/expected/insert.out +++ b/src/test/regress/expected/insert.out @@ -80,6 +80,22 @@ select col1, col2, char_length(col3) from inserttest; 30 | 50 | 10000 (8 rows) +drop table inserttest; +-- +-- SET test +-- +create table inserttest (col1 int4, col2 text default 'bar'); +insert into inserttest set col1 = 1, col2 = 'foo'; +insert into inserttest set col1 = 2, col2 = DEFAULT; +insert into inserttest set (col1, col2) = (3, 'baz'); +select * from inserttest; + col1 | col2 +------+------ + 1 | foo + 2 | bar + 3 | baz +(3 rows) + drop table inserttest; -- -- check indirection (field/array assignment), cf bug #14265 diff --git a/src/test/regress/expected/insert_conflict.out b/src/test/regress/expected/insert_conflict.out index 1338b2b23e..4d6538b1fb 100644 --- a/src/test/regress/expected/insert_conflict.out +++ b/src/test/regress/expected/insert_conflict.out @@ -236,6 +236,8 @@ insert into insertconflicttest values (2, 'Orange') on conflict (key, key, key) insert into insertconflicttest values (1, 'Apple'), (2, 'Orange') on conflict (key) do update set (fruit, key) = (excluded.fruit, excluded.key); +-- Using insert set syntax +insert into insertconflicttest set key = 1, fruit = 'Banana' on conflict (key) do update set fruit = excluded.fruit; -- Give good diagnostic message when EXCLUDED.* spuriously referenced from -- RETURNING: insert into insertconflicttest values (1, 'Apple') on conflict (key) do update set fruit = excluded.fruit RETURNING excluded.fruit; diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out index 2a2085556b..b92f9f8a13 100644 --- a/src/test/regress/expected/with.out +++ b/src/test/regress/expected/with.out @@ -952,6 +952,27 @@ SELECT * FROM y; 10 (10 rows) +DROP TABLE y; +CREATE TEMPORARY TABLE y (a INTEGER); +WITH t AS ( + SELECT generate_series(1, 10) AS a +) +INSERT INTO y SET a = t.a+20 FROM t; +SELECT * FROM y; + a +---- + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 +(10 rows) + DROP TABLE y; -- -- error cases diff --git a/src/test/regress/sql/insert.sql b/src/test/regress/sql/insert.sql index 23885f638c..00747e0939 100644 --- a/src/test/regress/sql/insert.sql +++ b/src/test/regress/sql/insert.sql @@ -37,6 +37,18 @@ select col1, col2, char_length(col3) from inserttest; drop table inserttest; +-- +-- SET test +-- +create table inserttest (col1 int4, col2 text default 'bar'); +insert into inserttest set col1 = 1, col2 = 'foo'; +insert into inserttest set col1 = 2, col2 = DEFAULT; +insert into inserttest set (col1, col2) = (3, 'baz'); + +select * from inserttest; + +drop table inserttest; + -- -- check indirection (field/array assignment), cf bug #14265 -- diff --git a/src/test/regress/sql/insert_conflict.sql b/src/test/regress/sql/insert_conflict.sql index 43691cd335..e0eb2df0c8 100644 --- a/src/test/regress/sql/insert_conflict.sql +++ b/src/test/regress/sql/insert_conflict.sql @@ -97,6 +97,9 @@ insert into insertconflicttest values (1, 'Apple'), (2, 'Orange') on conflict (key) do update set (fruit, key) = (excluded.fruit, excluded.key); +-- Using insert set syntax +insert into insertconflicttest set key = 1, fruit = 'Banana' on conflict (key) do update set fruit = excluded.fruit; + -- Give good diagnostic message when EXCLUDED.* spuriously referenced from -- RETURNING: insert into insertconflicttest values (1, 'Apple') on conflict (key) do update set fruit = excluded.fruit RETURNING excluded.fruit; diff --git a/src/test/regress/sql/with.sql b/src/test/regress/sql/with.sql index f85645efde..e6d2e13714 100644 --- a/src/test/regress/sql/with.sql +++ b/src/test/regress/sql/with.sql @@ -406,6 +406,16 @@ SELECT * FROM y; DROP TABLE y; +CREATE TEMPORARY TABLE y (a INTEGER); +WITH t AS ( + SELECT generate_series(1, 10) AS a +) +INSERT INTO y SET a = t.a+20 FROM t; + +SELECT * FROM y; + +DROP TABLE y; + -- -- error cases --