diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index 2c73852ae3..8a0fb4d3ca 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -21,7 +21,7 @@ PostgreSQL documentation -CREATE MATERIALIZED VIEW table_name +CREATE MATERIALIZED VIEW [ IF NOT EXISTS ] table_name [ (column_name [, ...] ) ] [ WITH ( storage_parameter [= value] [, ... ] ) ] [ TABLESPACE tablespace_name ] @@ -53,6 +53,18 @@ CREATE MATERIALIZED VIEW table_name Parameters + + IF NOT EXISTS + + + Do not throw an error if a materialized view with the same name already + exists. A notice is issued in this case. Note that there is no guarantee + that the existing materialized view is anything like the one that would + have been created. + + + + table_name diff --git a/doc/src/sgml/ref/create_table_as.sgml b/doc/src/sgml/ref/create_table_as.sgml index 60300ff21e..8e4ada794d 100644 --- a/doc/src/sgml/ref/create_table_as.sgml +++ b/doc/src/sgml/ref/create_table_as.sgml @@ -21,7 +21,7 @@ PostgreSQL documentation -CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE table_name +CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] table_name [ (column_name [, ...] ) ] [ WITH ( storage_parameter [= value] [, ... ] ) | WITH OIDS | WITHOUT OIDS ] [ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ] @@ -90,6 +90,17 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE + + IF NOT EXISTS + + + Do not throw an error if a relation with the same name already exists. + A notice is issued in this case. Refer to + for details. + + + + table_name diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c index 5e0ac58560..72315c2f3f 100644 --- a/src/backend/commands/createas.c +++ b/src/backend/commands/createas.c @@ -28,6 +28,7 @@ #include "access/sysattr.h" #include "access/xact.h" #include "access/xlog.h" +#include "catalog/namespace.h" #include "catalog/toasting.h" #include "commands/createas.h" #include "commands/matview.h" @@ -86,6 +87,22 @@ ExecCreateTableAs(CreateTableAsStmt *stmt, const char *queryString, QueryDesc *queryDesc; ScanDirection dir; + if (stmt->if_not_exists) + { + Oid nspid; + + nspid = RangeVarGetCreationNamespace(stmt->into->rel); + + if (get_relname_relid(stmt->into->rel->relname, nspid)) + { + ereport(NOTICE, + (errcode(ERRCODE_DUPLICATE_TABLE), + errmsg("relation \"%s\" already exists, skipping", + stmt->into->rel->relname))); + return InvalidOid; + } + } + /* * Create the tuple receiver object and insert info it will need */ diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 6b1bf7be19..491e4db9d6 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -3317,6 +3317,7 @@ _copyCreateTableAsStmt(const CreateTableAsStmt *from) COPY_NODE_FIELD(into); COPY_SCALAR_FIELD(relkind); COPY_SCALAR_FIELD(is_select_into); + COPY_SCALAR_FIELD(if_not_exists); return newnode; } diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index d5db71d3a8..08036743d5 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -1530,6 +1530,7 @@ _equalCreateTableAsStmt(const CreateTableAsStmt *a, const CreateTableAsStmt *b) COMPARE_NODE_FIELD(into); COMPARE_SCALAR_FIELD(relkind); COMPARE_SCALAR_FIELD(is_select_into); + COMPARE_SCALAR_FIELD(if_not_exists); return true; } diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 4b5009b636..1f4fe9d494 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3401,11 +3401,25 @@ CreateAsStmt: ctas->into = $4; ctas->relkind = OBJECT_TABLE; ctas->is_select_into = false; + ctas->if_not_exists = false; /* cram additional flags into the IntoClause */ $4->rel->relpersistence = $2; $4->skipData = !($7); $$ = (Node *) ctas; } + | CREATE OptTemp TABLE IF_P NOT EXISTS create_as_target AS SelectStmt opt_with_data + { + CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt); + ctas->query = $9; + ctas->into = $7; + ctas->relkind = OBJECT_TABLE; + ctas->is_select_into = false; + ctas->if_not_exists = true; + /* cram additional flags into the IntoClause */ + $7->rel->relpersistence = $2; + $7->skipData = !($10); + $$ = (Node *) ctas; + } ; create_as_target: @@ -3444,11 +3458,25 @@ CreateMatViewStmt: ctas->into = $5; ctas->relkind = OBJECT_MATVIEW; ctas->is_select_into = false; + ctas->if_not_exists = false; /* cram additional flags into the IntoClause */ $5->rel->relpersistence = $2; $5->skipData = !($8); $$ = (Node *) ctas; } + | CREATE OptNoLog MATERIALIZED VIEW IF_P NOT EXISTS create_mv_target AS SelectStmt opt_with_data + { + CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt); + ctas->query = $10; + ctas->into = $8; + ctas->relkind = OBJECT_MATVIEW; + ctas->is_select_into = false; + ctas->if_not_exists = true; + /* cram additional flags into the IntoClause */ + $8->rel->relpersistence = $2; + $8->skipData = !($11); + $$ = (Node *) ctas; + } ; create_mv_target: diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 5eaa435127..458eeb0b9e 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -2652,6 +2652,7 @@ typedef struct CreateTableAsStmt IntoClause *into; /* destination table */ ObjectType relkind; /* OBJECT_TABLE or OBJECT_MATVIEW */ bool is_select_into; /* it was written as SELECT INTO */ + bool if_not_exists; /* just do nothing if it already exists? */ } CreateTableAsStmt; /* ---------------------- diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 167d02d039..35451d5af2 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -218,3 +218,9 @@ CREATE TEMP TABLE pg_temp.doubly_temp (a int primary key); -- also OK CREATE TEMP TABLE public.temp_to_perm (a int primary key); -- not OK ERROR: cannot create temporary relation in non-temporary schema DROP TABLE unlogged1, public.unlogged2; +CREATE TABLE as_select1 AS SELECT * FROM pg_class WHERE relkind = 'r'; +CREATE TABLE as_select1 AS SELECT * FROM pg_class WHERE relkind = 'r'; +ERROR: relation "as_select1" already exists +CREATE TABLE IF NOT EXISTS as_select1 AS SELECT * FROM pg_class WHERE relkind = 'r'; +NOTICE: relation "as_select1" already exists, skipping +DROP TABLE as_select1; diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out index b04510c599..eb13ea75fb 100644 --- a/src/test/regress/expected/matview.out +++ b/src/test/regress/expected/matview.out @@ -508,6 +508,10 @@ SET ROLE user_dw; CREATE TABLE foo_data AS SELECT i, md5(random()::text) FROM generate_series(1, 10) i; CREATE MATERIALIZED VIEW mv_foo AS SELECT * FROM foo_data; +CREATE MATERIALIZED VIEW mv_foo AS SELECT * FROM foo_data; +ERROR: relation "mv_foo" already exists +CREATE MATERIALIZED VIEW IF NOT EXISTS mv_foo AS SELECT * FROM foo_data; +NOTICE: relation "mv_foo" already exists, skipping CREATE UNIQUE INDEX ON mv_foo (i); RESET ROLE; REFRESH MATERIALIZED VIEW mv_foo; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 8eb246b817..08029a99b0 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -254,3 +254,8 @@ CREATE TEMP TABLE explicitly_temp (a int primary key); -- also OK CREATE TEMP TABLE pg_temp.doubly_temp (a int primary key); -- also OK CREATE TEMP TABLE public.temp_to_perm (a int primary key); -- not OK DROP TABLE unlogged1, public.unlogged2; + +CREATE TABLE as_select1 AS SELECT * FROM pg_class WHERE relkind = 'r'; +CREATE TABLE as_select1 AS SELECT * FROM pg_class WHERE relkind = 'r'; +CREATE TABLE IF NOT EXISTS as_select1 AS SELECT * FROM pg_class WHERE relkind = 'r'; +DROP TABLE as_select1; diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql index fee1ddc842..70e4492c1b 100644 --- a/src/test/regress/sql/matview.sql +++ b/src/test/regress/sql/matview.sql @@ -201,6 +201,8 @@ SET ROLE user_dw; CREATE TABLE foo_data AS SELECT i, md5(random()::text) FROM generate_series(1, 10) i; CREATE MATERIALIZED VIEW mv_foo AS SELECT * FROM foo_data; +CREATE MATERIALIZED VIEW mv_foo AS SELECT * FROM foo_data; +CREATE MATERIALIZED VIEW IF NOT EXISTS mv_foo AS SELECT * FROM foo_data; CREATE UNIQUE INDEX ON mv_foo (i); RESET ROLE; REFRESH MATERIALIZED VIEW mv_foo;