Commit e39b6f95 authored by Andrew Dunstan's avatar Andrew Dunstan

Add CINE option for CREATE TABLE AS and CREATE MATERIALIZED VIEW

Fabrízio de Royes Mello reviewed by Rushabh Lathia.
parent e311cd6d
...@@ -21,7 +21,7 @@ PostgreSQL documentation ...@@ -21,7 +21,7 @@ PostgreSQL documentation
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
CREATE MATERIALIZED VIEW <replaceable>table_name</replaceable> CREATE MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_name</replaceable>
[ (<replaceable>column_name</replaceable> [, ...] ) ] [ (<replaceable>column_name</replaceable> [, ...] ) ]
[ WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) ] [ WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="PARAMETER">tablespace_name</replaceable> ] [ TABLESPACE <replaceable class="PARAMETER">tablespace_name</replaceable> ]
...@@ -53,6 +53,18 @@ CREATE MATERIALIZED VIEW <replaceable>table_name</replaceable> ...@@ -53,6 +53,18 @@ CREATE MATERIALIZED VIEW <replaceable>table_name</replaceable>
<refsect1> <refsect1>
<title>Parameters</title> <title>Parameters</title>
<varlistentry>
<term><literal>IF NOT EXISTS</></term>
<listitem>
<para>
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.
</para>
</listitem>
</varlistentry>
<variablelist> <variablelist>
<varlistentry> <varlistentry>
<term><replaceable>table_name</replaceable></term> <term><replaceable>table_name</replaceable></term>
......
...@@ -21,7 +21,7 @@ PostgreSQL documentation ...@@ -21,7 +21,7 @@ PostgreSQL documentation
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE <replaceable>table_name</replaceable> CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] <replaceable>table_name</replaceable>
[ (<replaceable>column_name</replaceable> [, ...] ) ] [ (<replaceable>column_name</replaceable> [, ...] ) ]
[ WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) | WITH OIDS | WITHOUT OIDS ] [ WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) | WITH OIDS | WITHOUT OIDS ]
[ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ] [ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
...@@ -90,6 +90,17 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE <replaceable ...@@ -90,6 +90,17 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE <replaceable
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><literal>IF NOT EXISTS</></term>
<listitem>
<para>
Do not throw an error if a relation with the same name already exists.
A notice is issued in this case. Refer to <xref linkend="sql-createtable">
for details.
</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><replaceable>table_name</replaceable></term> <term><replaceable>table_name</replaceable></term>
<listitem> <listitem>
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "access/sysattr.h" #include "access/sysattr.h"
#include "access/xact.h" #include "access/xact.h"
#include "access/xlog.h" #include "access/xlog.h"
#include "catalog/namespace.h"
#include "catalog/toasting.h" #include "catalog/toasting.h"
#include "commands/createas.h" #include "commands/createas.h"
#include "commands/matview.h" #include "commands/matview.h"
...@@ -86,6 +87,22 @@ ExecCreateTableAs(CreateTableAsStmt *stmt, const char *queryString, ...@@ -86,6 +87,22 @@ ExecCreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
QueryDesc *queryDesc; QueryDesc *queryDesc;
ScanDirection dir; 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 * Create the tuple receiver object and insert info it will need
*/ */
......
...@@ -3317,6 +3317,7 @@ _copyCreateTableAsStmt(const CreateTableAsStmt *from) ...@@ -3317,6 +3317,7 @@ _copyCreateTableAsStmt(const CreateTableAsStmt *from)
COPY_NODE_FIELD(into); COPY_NODE_FIELD(into);
COPY_SCALAR_FIELD(relkind); COPY_SCALAR_FIELD(relkind);
COPY_SCALAR_FIELD(is_select_into); COPY_SCALAR_FIELD(is_select_into);
COPY_SCALAR_FIELD(if_not_exists);
return newnode; return newnode;
} }
......
...@@ -1530,6 +1530,7 @@ _equalCreateTableAsStmt(const CreateTableAsStmt *a, const CreateTableAsStmt *b) ...@@ -1530,6 +1530,7 @@ _equalCreateTableAsStmt(const CreateTableAsStmt *a, const CreateTableAsStmt *b)
COMPARE_NODE_FIELD(into); COMPARE_NODE_FIELD(into);
COMPARE_SCALAR_FIELD(relkind); COMPARE_SCALAR_FIELD(relkind);
COMPARE_SCALAR_FIELD(is_select_into); COMPARE_SCALAR_FIELD(is_select_into);
COMPARE_SCALAR_FIELD(if_not_exists);
return true; return true;
} }
......
...@@ -3401,11 +3401,25 @@ CreateAsStmt: ...@@ -3401,11 +3401,25 @@ CreateAsStmt:
ctas->into = $4; ctas->into = $4;
ctas->relkind = OBJECT_TABLE; ctas->relkind = OBJECT_TABLE;
ctas->is_select_into = false; ctas->is_select_into = false;
ctas->if_not_exists = false;
/* cram additional flags into the IntoClause */ /* cram additional flags into the IntoClause */
$4->rel->relpersistence = $2; $4->rel->relpersistence = $2;
$4->skipData = !($7); $4->skipData = !($7);
$$ = (Node *) ctas; $$ = (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: create_as_target:
...@@ -3444,11 +3458,25 @@ CreateMatViewStmt: ...@@ -3444,11 +3458,25 @@ CreateMatViewStmt:
ctas->into = $5; ctas->into = $5;
ctas->relkind = OBJECT_MATVIEW; ctas->relkind = OBJECT_MATVIEW;
ctas->is_select_into = false; ctas->is_select_into = false;
ctas->if_not_exists = false;
/* cram additional flags into the IntoClause */ /* cram additional flags into the IntoClause */
$5->rel->relpersistence = $2; $5->rel->relpersistence = $2;
$5->skipData = !($8); $5->skipData = !($8);
$$ = (Node *) ctas; $$ = (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: create_mv_target:
......
...@@ -2652,6 +2652,7 @@ typedef struct CreateTableAsStmt ...@@ -2652,6 +2652,7 @@ typedef struct CreateTableAsStmt
IntoClause *into; /* destination table */ IntoClause *into; /* destination table */
ObjectType relkind; /* OBJECT_TABLE or OBJECT_MATVIEW */ ObjectType relkind; /* OBJECT_TABLE or OBJECT_MATVIEW */
bool is_select_into; /* it was written as SELECT INTO */ bool is_select_into; /* it was written as SELECT INTO */
bool if_not_exists; /* just do nothing if it already exists? */
} CreateTableAsStmt; } CreateTableAsStmt;
/* ---------------------- /* ----------------------
......
...@@ -218,3 +218,9 @@ CREATE TEMP TABLE pg_temp.doubly_temp (a int primary key); -- also OK ...@@ -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 CREATE TEMP TABLE public.temp_to_perm (a int primary key); -- not OK
ERROR: cannot create temporary relation in non-temporary schema ERROR: cannot create temporary relation in non-temporary schema
DROP TABLE unlogged1, public.unlogged2; 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;
...@@ -508,6 +508,10 @@ SET ROLE user_dw; ...@@ -508,6 +508,10 @@ SET ROLE user_dw;
CREATE TABLE foo_data AS SELECT i, md5(random()::text) CREATE TABLE foo_data AS SELECT i, md5(random()::text)
FROM generate_series(1, 10) i; 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 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); CREATE UNIQUE INDEX ON mv_foo (i);
RESET ROLE; RESET ROLE;
REFRESH MATERIALIZED VIEW mv_foo; REFRESH MATERIALIZED VIEW mv_foo;
......
...@@ -254,3 +254,8 @@ CREATE TEMP TABLE explicitly_temp (a int primary key); -- also OK ...@@ -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 pg_temp.doubly_temp (a int primary key); -- also OK
CREATE TEMP TABLE public.temp_to_perm (a int primary key); -- not OK CREATE TEMP TABLE public.temp_to_perm (a int primary key); -- not OK
DROP TABLE unlogged1, public.unlogged2; 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;
...@@ -201,6 +201,8 @@ SET ROLE user_dw; ...@@ -201,6 +201,8 @@ SET ROLE user_dw;
CREATE TABLE foo_data AS SELECT i, md5(random()::text) CREATE TABLE foo_data AS SELECT i, md5(random()::text)
FROM generate_series(1, 10) i; 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 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); CREATE UNIQUE INDEX ON mv_foo (i);
RESET ROLE; RESET ROLE;
REFRESH MATERIALIZED VIEW mv_foo; REFRESH MATERIALIZED VIEW mv_foo;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment