From a3b012b560f8871e9c24abb24e28cabc6d3c92ea Mon Sep 17 00:00:00 2001 From: Robert Haas Date: Sun, 25 Jul 2010 23:21:22 +0000 Subject: [PATCH] CREATE TABLE IF NOT EXISTS. Reviewed by Bernd Helmle. --- doc/src/sgml/ref/create_table.sgml | 18 +++++++++-- src/backend/bootstrap/bootparse.y | 5 ++-- src/backend/catalog/heap.c | 25 ++++++++++++++-- src/backend/catalog/toasting.c | 6 ++-- src/backend/commands/cluster.c | 6 ++-- src/backend/commands/sequence.c | 4 ++- src/backend/commands/tablecmds.c | 14 +++++++-- src/backend/commands/typecmds.c | 8 +++-- src/backend/commands/view.c | 9 ++++-- src/backend/executor/execMain.c | 6 ++-- src/backend/nodes/copyfuncs.c | 3 +- src/backend/nodes/equalfuncs.c | 3 +- src/backend/nodes/outfuncs.c | 3 +- src/backend/parser/gram.y | 35 +++++++++++++++++++++- src/backend/tcop/utility.c | 9 +++++- src/include/catalog/heap.h | 5 ++-- src/include/nodes/parsenodes.h | 3 +- src/test/regress/expected/create_table.out | 6 +++- src/test/regress/expected/typed_table.out | 2 ++ src/test/regress/sql/create_table.sql | 5 +++- src/test/regress/sql/typed_table.sql | 1 + 21 files changed, 145 insertions(+), 31 deletions(-) diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml index 0c02d0cb91..a31bd3ddc5 100644 --- a/doc/src/sgml/ref/create_table.sgml +++ b/doc/src/sgml/ref/create_table.sgml @@ -1,5 +1,5 @@ @@ -21,7 +21,7 @@ PostgreSQL documentation -CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE table_name ( [ +CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE [ IF NOT EXISTS ] table_name ( [ { column_name data_type [ DEFAULT default_expr ] [ column_constraint [ ... ] ] | table_constraint | LIKE parent_table [ like_option ... ] } @@ -32,7 +32,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE tablespace ] -CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE table_name +CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE [ IF NOT EXISTS ] table_name OF type_name [ ( { column_name WITH OPTIONS [ DEFAULT default_expr ] [ column_constraint [ ... ] ] | table_constraint } @@ -163,6 +163,18 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y index 9cc68501ff..70cad32915 100644 --- a/src/backend/bootstrap/bootparse.y +++ b/src/backend/bootstrap/bootparse.y @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.105 2010/02/07 20:48:09 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.106 2010/07/25 23:21:21 rhaas Exp $ * *------------------------------------------------------------------------- */ @@ -245,7 +245,8 @@ Boot_CreateStmt: ONCOMMIT_NOOP, (Datum) 0, false, - true); + true, + false); elog(DEBUG4, "relation created with oid %u", id); } do_end(); diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 47e0c9b9cf..a0268f7177 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.373 2010/04/05 01:09:52 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.374 2010/07/25 23:21:21 rhaas Exp $ * * * INTERFACE ROUTINES @@ -903,11 +903,13 @@ heap_create_with_catalog(const char *relname, OnCommitAction oncommit, Datum reloptions, bool use_user_acl, - bool allow_system_table_mods) + bool allow_system_table_mods, + bool if_not_exists) { Relation pg_class_desc; Relation new_rel_desc; Acl *relacl; + Oid existing_relid; Oid old_type_oid; Oid new_type_oid; Oid new_array_oid = InvalidOid; @@ -921,10 +923,27 @@ heap_create_with_catalog(const char *relname, CheckAttributeNamesTypes(tupdesc, relkind, allow_system_table_mods); - if (get_relname_relid(relname, relnamespace)) + /* + * If the relation already exists, it's an error, unless the user specifies + * "IF NOT EXISTS". In that case, we just print a notice and do nothing + * further. + */ + existing_relid = get_relname_relid(relname, relnamespace); + if (existing_relid != InvalidOid) + { + if (if_not_exists) + { + ereport(NOTICE, + (errcode(ERRCODE_DUPLICATE_TABLE), + errmsg("relation \"%s\" already exists, skipping", + relname))); + heap_close(pg_class_desc, RowExclusiveLock); + return InvalidOid; + } ereport(ERROR, (errcode(ERRCODE_DUPLICATE_TABLE), errmsg("relation \"%s\" already exists", relname))); + } /* * Since we are going to create a rowtype as well, also check for diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 86e7daa1a0..6f658321b4 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/toasting.c,v 1.32 2010/02/26 02:00:37 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/toasting.c,v 1.33 2010/07/25 23:21:21 rhaas Exp $ * *------------------------------------------------------------------------- */ @@ -223,7 +223,9 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio ONCOMMIT_NOOP, reloptions, false, - true); + true, + false); + Assert(toast_relid != InvalidOid); /* make the toast relation visible, else index creation will fail */ CommandCounterIncrement(); diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index 7a1b8e885b..ce99b55a63 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.203 2010/04/28 16:10:41 heikki Exp $ + * $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.204 2010/07/25 23:21:21 rhaas Exp $ * *------------------------------------------------------------------------- */ @@ -687,7 +687,9 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace) ONCOMMIT_NOOP, reloptions, false, - true); + true, + false); + Assert(OIDNewHeap != InvalidOid); ReleaseSysCache(tuple); diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index fc2446997a..0f06bba803 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.168 2010/02/20 21:24:02 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.169 2010/07/25 23:21:21 rhaas Exp $ * *------------------------------------------------------------------------- */ @@ -203,8 +203,10 @@ DefineSequence(CreateSeqStmt *seq) stmt->options = list_make1(defWithOids(false)); stmt->oncommit = ONCOMMIT_NOOP; stmt->tablespacename = NULL; + stmt->if_not_exists = false; seqoid = DefineRelation(stmt, RELKIND_SEQUENCE); + Assert(seqoid != InvalidOid); rel = heap_open(seqoid, AccessExclusiveLock); tupDesc = RelationGetDescr(rel); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index c5981d4e5b..2fdce989c0 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.333 2010/07/23 20:04:18 petere Exp $ + * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.334 2010/07/25 23:21:21 rhaas Exp $ * *------------------------------------------------------------------------- */ @@ -548,8 +548,18 @@ DefineRelation(CreateStmt *stmt, char relkind) stmt->oncommit, reloptions, true, - allowSystemTableMods); + allowSystemTableMods, + stmt->if_not_exists); + /* + * If heap_create_with_catalog returns InvalidOid, it means that the user + * specified "IF NOT EXISTS" and the relation already exists. In that + * case we do nothing further. + */ + if (relationId == InvalidOid) + return InvalidOid; + + /* Store inheritance information for new rel. */ StoreCatalogInheritance(relationId, inheritOids); /* diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 8a85e79ea6..5d94445910 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.148 2010/02/26 02:00:40 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.149 2010/07/25 23:21:21 rhaas Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -1506,6 +1506,7 @@ DefineCompositeType(const RangeVar *typevar, List *coldeflist) CreateStmt *createStmt = makeNode(CreateStmt); Oid old_type_oid; Oid typeNamespace; + Oid relid; if (coldeflist == NIL) ereport(ERROR, @@ -1523,6 +1524,7 @@ DefineCompositeType(const RangeVar *typevar, List *coldeflist) createStmt->options = list_make1(defWithOids(false)); createStmt->oncommit = ONCOMMIT_NOOP; createStmt->tablespacename = NULL; + createStmt->if_not_exists = false; /* * Check for collision with an existing type name. If there is one and @@ -1546,7 +1548,9 @@ DefineCompositeType(const RangeVar *typevar, List *coldeflist) /* * Finally create the relation. This also creates the type. */ - return DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE); + relid = DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE); + Assert(relid != InvalidOid); + return relid; } /* diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index 900def0148..1acf1b802d 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/view.c,v 1.120 2010/01/02 16:57:40 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/view.c,v 1.121 2010/07/25 23:21:21 rhaas Exp $ * *------------------------------------------------------------------------- */ @@ -222,6 +222,8 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace) } else { + Oid relid; + /* * now set the parameters for keys/inheritance etc. All of these are * uninteresting for views... @@ -233,13 +235,16 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace) createStmt->options = list_make1(defWithOids(false)); createStmt->oncommit = ONCOMMIT_NOOP; createStmt->tablespacename = NULL; + createStmt->if_not_exists = false; /* * finally create the relation (this will error out if there's an * existing view, so we don't need more code to complain if "replace" * is false). */ - return DefineRelation(createStmt, RELKIND_VIEW); + relid = DefineRelation(createStmt, RELKIND_VIEW); + Assert(relid != InvalidOid); + return relid; } } diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 87ca2a1b4b..b21dbf4762 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -26,7 +26,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.352 2010/07/22 00:47:52 rhaas Exp $ + * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.353 2010/07/25 23:21:21 rhaas Exp $ * *------------------------------------------------------------------------- */ @@ -2190,7 +2190,9 @@ OpenIntoRel(QueryDesc *queryDesc) into->onCommit, reloptions, true, - allowSystemTableMods); + allowSystemTableMods, + false); + Assert(intoRelationId != InvalidOid); FreeTupleDesc(tupdesc); diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 8e47403a48..a5862771a9 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.465 2010/07/12 17:01:05 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.466 2010/07/25 23:21:21 rhaas Exp $ * *------------------------------------------------------------------------- */ @@ -2537,6 +2537,7 @@ _copyCreateStmt(CreateStmt *from) COPY_NODE_FIELD(options); COPY_SCALAR_FIELD(oncommit); COPY_STRING_FIELD(tablespacename); + COPY_SCALAR_FIELD(if_not_exists); return newnode; } diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index e97a3ea9da..7056287c93 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -22,7 +22,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.385 2010/02/26 02:00:43 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.386 2010/07/25 23:21:21 rhaas Exp $ * *------------------------------------------------------------------------- */ @@ -1104,6 +1104,7 @@ _equalCreateStmt(CreateStmt *a, CreateStmt *b) COMPARE_NODE_FIELD(options); COMPARE_SCALAR_FIELD(oncommit); COMPARE_STRING_FIELD(tablespacename); + COMPARE_SCALAR_FIELD(if_not_exists); return true; } diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index ff4a9aaeef..6089ea3e3a 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.386 2010/07/12 17:01:05 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.387 2010/07/25 23:21:21 rhaas Exp $ * * NOTES * Every node type that can appear in stored rules' parsetrees *must* @@ -1795,6 +1795,7 @@ _outCreateStmt(StringInfo str, CreateStmt *node) WRITE_NODE_FIELD(options); WRITE_ENUM_FIELD(oncommit, OnCommitAction); WRITE_STRING_FIELD(tablespacename); + WRITE_BOOL_FIELD(if_not_exists); } static void diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 122213a134..07ee2a8349 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.713 2010/06/13 17:43:12 rhaas Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.714 2010/07/25 23:21:21 rhaas Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -2212,6 +2212,23 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' n->options = $9; n->oncommit = $10; n->tablespacename = $11; + n->if_not_exists = false; + $$ = (Node *)n; + } + | CREATE OptTemp TABLE IF_P NOT EXISTS qualified_name '(' + OptTableElementList ')' OptInherit OptWith OnCommitOption + OptTableSpace + { + CreateStmt *n = makeNode(CreateStmt); + $7->istemp = $2; + n->relation = $7; + n->tableElts = $9; + n->inhRelations = $11; + n->constraints = NIL; + n->options = $12; + n->oncommit = $13; + n->tablespacename = $14; + n->if_not_exists = true; $$ = (Node *)n; } | CREATE OptTemp TABLE qualified_name OF any_name @@ -2227,6 +2244,22 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' n->options = $8; n->oncommit = $9; n->tablespacename = $10; + n->if_not_exists = false; + $$ = (Node *)n; + } + | CREATE OptTemp TABLE IF_P NOT EXISTS qualified_name OF any_name + OptTypedTableElementList OptWith OnCommitOption OptTableSpace + { + CreateStmt *n = makeNode(CreateStmt); + n->relation = $7; + n->tableElts = $10; + n->ofTypename = makeTypeNameFromNameList($9); + n->ofTypename->location = @9; + n->constraints = NIL; + n->options = $11; + n->oncommit = $12; + n->tablespacename = $13; + n->if_not_exists = true; $$ = (Node *)n; } ; diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 33b1aca72d..db5c6e9c08 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.335 2010/02/26 02:01:04 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.336 2010/07/25 23:21:22 rhaas Exp $ * *------------------------------------------------------------------------- */ @@ -512,6 +512,13 @@ standard_ProcessUtility(Node *parsetree, relOid = DefineRelation((CreateStmt *) stmt, RELKIND_RELATION); + /* + * If "IF NOT EXISTS" was specified and the relation + * already exists, do nothing further. + */ + if (relOid == InvalidOid) + continue; + /* * Let AlterTableCreateToastTable decide if this one * needs a secondary relation too. diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h index 557c311bc2..681239a5c9 100644 --- a/src/include/catalog/heap.h +++ b/src/include/catalog/heap.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/heap.h,v 1.98 2010/02/26 02:01:21 momjian Exp $ + * $PostgreSQL: pgsql/src/include/catalog/heap.h,v 1.99 2010/07/25 23:21:22 rhaas Exp $ * *------------------------------------------------------------------------- */ @@ -61,7 +61,8 @@ extern Oid heap_create_with_catalog(const char *relname, OnCommitAction oncommit, Datum reloptions, bool use_user_acl, - bool allow_system_table_mods); + bool allow_system_table_mods, + bool if_not_exists); extern void heap_drop_with_catalog(Oid relid); diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 5325f7e924..1708225366 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -13,7 +13,7 @@ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.432 2010/02/26 02:01:25 momjian Exp $ + * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.433 2010/07/25 23:21:22 rhaas Exp $ * *------------------------------------------------------------------------- */ @@ -1375,6 +1375,7 @@ typedef struct CreateStmt List *options; /* options from WITH clause */ OnCommitAction oncommit; /* what do we do at COMMIT? */ char *tablespacename; /* table space to use, or NULL */ + bool if_not_exists; /* just do nothing if it already exists? */ } CreateStmt; /* ---------- diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index daecabb5b7..6f65885c82 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -196,7 +196,11 @@ CREATE TABLE array_index_op_test ( i int4[], t text[] ); -CREATE TABLE test_tsvector( +CREATE TABLE IF NOT EXISTS test_tsvector( t text, a tsvector ); +CREATE TABLE IF NOT EXISTS test_tsvector( + t text +); +NOTICE: relation "test_tsvector" already exists, skipping diff --git a/src/test/regress/expected/typed_table.out b/src/test/regress/expected/typed_table.out index 9b933fdadc..8bb9a1dd6a 100644 --- a/src/test/regress/expected/typed_table.out +++ b/src/test/regress/expected/typed_table.out @@ -2,6 +2,8 @@ CREATE TABLE ttable1 OF nothing; ERROR: type "nothing" does not exist CREATE TYPE person_type AS (id int, name text); CREATE TABLE persons OF person_type; +CREATE TABLE IF NOT EXISTS persons OF person_type; +NOTICE: relation "persons" already exists, skipping SELECT * FROM persons; id | name ----+------ diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index e43371eed9..f491e8c142 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -232,8 +232,11 @@ CREATE TABLE array_index_op_test ( t text[] ); -CREATE TABLE test_tsvector( +CREATE TABLE IF NOT EXISTS test_tsvector( t text, a tsvector ); +CREATE TABLE IF NOT EXISTS test_tsvector( + t text +); diff --git a/src/test/regress/sql/typed_table.sql b/src/test/regress/sql/typed_table.sql index 60cb6d6857..1afede14b1 100644 --- a/src/test/regress/sql/typed_table.sql +++ b/src/test/regress/sql/typed_table.sql @@ -2,6 +2,7 @@ CREATE TABLE ttable1 OF nothing; CREATE TYPE person_type AS (id int, name text); CREATE TABLE persons OF person_type; +CREATE TABLE IF NOT EXISTS persons OF person_type; SELECT * FROM persons; \d persons