diff --git a/doc/src/sgml/ref/create_aggregate.sgml b/doc/src/sgml/ref/create_aggregate.sgml index d5e4e27..dcd809a 100644 --- a/doc/src/sgml/ref/create_aggregate.sgml +++ b/doc/src/sgml/ref/create_aggregate.sgml @@ -21,7 +21,7 @@ PostgreSQL documentation -CREATE AGGREGATE name ( input_data_type [ , ... ] ) ( +CREATE AGGREGATE [ IF NOT EXISTS ] name ( input_data_type [ , ... ] ) ( SFUNC = sfunc, STYPE = state_data_type [ , FINALFUNC = ffunc ] @@ -31,7 +31,7 @@ CREATE AGGREGATE name ( or the old syntax -CREATE AGGREGATE name ( +CREATE AGGREGATE [ IF NOT EXISTS ] name ( BASETYPE = base_type, SFUNC = sfunc, STYPE = state_data_type @@ -177,6 +177,16 @@ SELECT col FROM tab ORDER BY col USING sortop LIMIT 1; + IF NOT EXISTS + + + Do nothing (except issuing a notice) if an aggregate function with + the same argument types already exists. + + + + + name diff --git a/doc/src/sgml/ref/create_cast.sgml b/doc/src/sgml/ref/create_cast.sgml index 29ea298..83a803b 100644 --- a/doc/src/sgml/ref/create_cast.sgml +++ b/doc/src/sgml/ref/create_cast.sgml @@ -18,15 +18,15 @@ -CREATE CAST (source_type AS target_type) +CREATE CAST [ IF NOT EXISTS ] (source_type AS target_type) WITH FUNCTION function_name (argument_type [, ...]) [ AS ASSIGNMENT | AS IMPLICIT ] -CREATE CAST (source_type AS target_type) +CREATE CAST [ IF NOT EXISTS ] (source_type AS target_type) WITHOUT FUNCTION [ AS ASSIGNMENT | AS IMPLICIT ] -CREATE CAST (source_type AS target_type) +CREATE CAST [ IF NOT EXISTS ] (source_type AS target_type) WITH INOUT [ AS ASSIGNMENT | AS IMPLICIT ] @@ -171,6 +171,16 @@ SELECT CAST ( 2 AS numeric ) + 4.0; Parameters + + IF NOT EXISTS + + + Do nothing (except issuing a notice) if a cast with the same + from and to type already exists. + + + + source_type diff --git a/doc/src/sgml/ref/create_collation.sgml b/doc/src/sgml/ref/create_collation.sgml index c853576..f93d87e 100644 --- a/doc/src/sgml/ref/create_collation.sgml +++ b/doc/src/sgml/ref/create_collation.sgml @@ -18,12 +18,12 @@ -CREATE COLLATION name ( +CREATE COLLATION [ IF NOT EXISTS ] name ( [ LOCALE = locale, ] [ LC_COLLATE = lc_collate, ] [ LC_CTYPE = lc_ctype ] ) -CREATE COLLATION name FROM existing_collation +CREATE COLLATION [ IF NOT EXISTS ] name FROM existing_collation @@ -47,6 +47,16 @@ CREATE COLLATION name FROM existing_coll Parameters + + IF NOT EXISTS + + + Do nothing (except issuing a notice) if an collation with the same + name already exists. + + + + name diff --git a/doc/src/sgml/ref/create_operator.sgml b/doc/src/sgml/ref/create_operator.sgml index dd33f06..80a6bf6 100644 --- a/doc/src/sgml/ref/create_operator.sgml +++ b/doc/src/sgml/ref/create_operator.sgml @@ -21,7 +21,7 @@ PostgreSQL documentation -CREATE OPERATOR name ( +CREATE OPERATOR [ IF NOT EXISTS ] name ( PROCEDURE = function_name [, LEFTARG = left_type ] [, RIGHTARG = right_type ] [, COMMUTATOR = com_op ] [, NEGATOR = neg_op ] @@ -117,6 +117,16 @@ CREATE OPERATOR name ( + IF NOT EXISTS + + + Do nothing (except issuing a notice) if an operator with the same + name already exists. + + + + + name diff --git a/doc/src/sgml/ref/create_tsconfig.sgml b/doc/src/sgml/ref/create_tsconfig.sgml index c34d1c0..86f8720 100644 --- a/doc/src/sgml/ref/create_tsconfig.sgml +++ b/doc/src/sgml/ref/create_tsconfig.sgml @@ -21,7 +21,7 @@ PostgreSQL documentation -CREATE TEXT SEARCH CONFIGURATION name ( +CREATE TEXT SEARCH CONFIGURATION [ IF NOT EXISTS ] name ( PARSER = parser_name | COPY = source_config ) @@ -66,6 +66,16 @@ CREATE TEXT SEARCH CONFIGURATION name + IF NOT EXISTS + + + Do nothing (except issuing a notice) if a text search configuration + with the same name already exists. + + + + + name diff --git a/doc/src/sgml/ref/create_tsdictionary.sgml b/doc/src/sgml/ref/create_tsdictionary.sgml index 2673bc5..2df11ab 100644 --- a/doc/src/sgml/ref/create_tsdictionary.sgml +++ b/doc/src/sgml/ref/create_tsdictionary.sgml @@ -21,7 +21,7 @@ PostgreSQL documentation -CREATE TEXT SEARCH DICTIONARY name ( +CREATE TEXT SEARCH DICTIONARY [ IF NOT EXISTS ] name ( TEMPLATE = template [, option = value [, ... ]] ) @@ -59,6 +59,16 @@ CREATE TEXT SEARCH DICTIONARY name + IF NOT EXISTS + + + Do nothing (except issuing a notice) if a text search dictionary + with the same name already exists. + + + + + name diff --git a/doc/src/sgml/ref/create_tsparser.sgml b/doc/src/sgml/ref/create_tsparser.sgml index 7643f08..177278c 100644 --- a/doc/src/sgml/ref/create_tsparser.sgml +++ b/doc/src/sgml/ref/create_tsparser.sgml @@ -21,7 +21,7 @@ PostgreSQL documentation -CREATE TEXT SEARCH PARSER name ( +CREATE TEXT SEARCH PARSER [ IF NOT EXISTS ] name ( START = start_function , GETTOKEN = gettoken_function , END = end_function , @@ -64,6 +64,16 @@ CREATE TEXT SEARCH PARSER name ( + IF NOT EXISTS + + + Do nothing (except issuing a notice) if a text search parser + with the same name already exists. + + + + + name diff --git a/doc/src/sgml/ref/create_tstemplate.sgml b/doc/src/sgml/ref/create_tstemplate.sgml index 532419c..b60243e 100644 --- a/doc/src/sgml/ref/create_tstemplate.sgml +++ b/doc/src/sgml/ref/create_tstemplate.sgml @@ -21,7 +21,7 @@ PostgreSQL documentation -CREATE TEXT SEARCH TEMPLATE name ( +CREATE TEXT SEARCH TEMPLATE [ IF NOT EXISTS ] name ( [ INIT = init_function , ] LEXIZE = lexize_function ) @@ -65,6 +65,16 @@ CREATE TEXT SEARCH TEMPLATE name ( + IF NOT EXISTS + + + Do nothing (except issuing a notice) if a text search template with + the same name already exists. + + + + + name diff --git a/doc/src/sgml/ref/create_type.sgml b/doc/src/sgml/ref/create_type.sgml index 606efee..0919da7 100644 --- a/doc/src/sgml/ref/create_type.sgml +++ b/doc/src/sgml/ref/create_type.sgml @@ -21,13 +21,13 @@ PostgreSQL documentation -CREATE TYPE name AS +CREATE TYPE [ IF NOT EXISTS ] name AS ( [ attribute_name data_type [ COLLATE collation ] [, ... ] ] ) -CREATE TYPE name AS ENUM +CREATE TYPE [ IF NOT EXISTS ] name AS ENUM ( [ 'label' [, ... ] ] ) -CREATE TYPE name AS RANGE ( +CREATE TYPE [ IF NOT EXISTS ] name AS RANGE ( SUBTYPE = subtype [ , SUBTYPE_OPCLASS = subtype_operator_class ] [ , COLLATION = collation ] @@ -35,7 +35,7 @@ CREATE TYPE name AS RANGE ( [ , SUBTYPE_DIFF = subtype_diff_function ] ) -CREATE TYPE name ( +CREATE TYPE [ IF NOT EXISTS ] name ( INPUT = input_function, OUTPUT = output_function [ , RECEIVE = receive_function ] @@ -56,7 +56,7 @@ CREATE TYPE name ( [ , COLLATABLE = collatable ] ) -CREATE TYPE name +CREATE TYPE [ IF NOT EXISTS ] name @@ -484,6 +484,16 @@ CREATE TYPE name + IF NOT EXISTS + + + Do nothing (except issuing a notice) if a type with the same name + already exists. + + + + + name diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 45a84e4..e13b450 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -964,7 +964,8 @@ AddNewRelationType(const char *typeName, -1, /* typmod */ 0, /* array dimensions for typBaseType */ false, /* Type NOT NULL */ - InvalidOid); /* rowtypes never have a collation */ + InvalidOid, /* rowtypes never have a collation */ + false); /* if not exists flag */ } /* -------------------------------- @@ -1221,7 +1222,8 @@ heap_create_with_catalog(const char *relname, -1, /* typmod */ 0, /* array dimensions for typBaseType */ false, /* Type NOT NULL */ - InvalidOid); /* rowtypes never have a collation */ + InvalidOid, /* rowtypes never have a collation */ + false); pfree(relarrayname); } diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c index e80b600..4452ba3 100644 --- a/src/backend/catalog/pg_aggregate.c +++ b/src/backend/catalog/pg_aggregate.c @@ -51,7 +51,8 @@ AggregateCreate(const char *aggName, List *aggfinalfnName, List *aggsortopName, Oid aggTransType, - const char *agginitval) + const char *agginitval, + bool aggIfNotExists) { Relation aggdesc; HeapTuple tup; @@ -228,7 +229,7 @@ AggregateCreate(const char *aggName, procOid = ProcedureCreate(aggName, aggNamespace, - false, /* no replacement */ + false, /* replacement */ false, /* doesn't return a set */ finaltype, /* returnType */ GetUserId(), /* proowner */ @@ -252,7 +253,11 @@ AggregateCreate(const char *aggName, NIL, /* parameterDefaults */ PointerGetDatum(NULL), /* proconfig */ 1, /* procost */ - 0); /* prorows */ + 0, /* prorows */ + aggIfNotExists); /* if not exists */ + + if (!OidIsValid(procOid)) + return InvalidOid; /* * Okay to create the pg_aggregate entry. diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c index 3c4fedb..7466e76 100644 --- a/src/backend/catalog/pg_operator.c +++ b/src/backend/catalog/pg_operator.c @@ -336,7 +336,8 @@ OperatorCreate(const char *operatorName, Oid restrictionId, Oid joinId, bool canMerge, - bool canHash) + bool canHash, + bool if_not_exists) { Relation pg_operator_desc; HeapTuple tup; @@ -416,11 +417,18 @@ OperatorCreate(const char *operatorName, rightTypeId, &operatorAlreadyDefined); - if (operatorAlreadyDefined) + if (operatorAlreadyDefined && !if_not_exists) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_FUNCTION), errmsg("operator %s already exists", operatorName))); + if (operatorAlreadyDefined && if_not_exists) { + ereport(NOTICE, + (errcode(ERRCODE_DUPLICATE_FUNCTION), + errmsg("operator %s already exists, skipping", + operatorName))); + return InvalidOid; + } /* * At this point, if operatorObjectId is not InvalidOid then we are diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index 2a98ca9..ef03cfd 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -88,7 +88,8 @@ ProcedureCreate(const char *procedureName, List *parameterDefaults, Datum proconfig, float4 procost, - float4 prorows) + float4 prorows, + bool ifNotExists) { Oid retval; int parameterCount; @@ -388,10 +389,23 @@ ProcedureCreate(const char *procedureName, bool isnull; if (!replace) - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_FUNCTION), - errmsg("function \"%s\" already exists with same argument types", - procedureName))); + { + if (!ifNotExists) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_FUNCTION), + errmsg("function \"%s\" already exists with same argument types", + procedureName))); + else + { + ereport(NOTICE, + (errcode(ERRCODE_DUPLICATE_FUNCTION), + errmsg("function \"%s\" already exists with same argument types, skipping", + procedureName))); + ReleaseSysCache(oldtup); + heap_close(rel, RowExclusiveLock); + return InvalidOid; + } + } if (!pg_proc_ownercheck(HeapTupleGetOid(oldtup), proowner)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, procedureName); diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c index 23ac3dd..3d55360 100644 --- a/src/backend/catalog/pg_type.c +++ b/src/backend/catalog/pg_type.c @@ -215,7 +215,8 @@ TypeCreate(Oid newTypeOid, int32 typeMod, int32 typNDims, /* Array dimensions for baseType */ bool typeNotNull, - Oid typeCollation) + Oid typeCollation, + bool ifNotExists) { Relation pg_type_desc; Oid typeObjectId; @@ -397,9 +398,20 @@ TypeCreate(Oid newTypeOid, * shell type, however. */ if (((Form_pg_type) GETSTRUCT(tup))->typisdefined) - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_OBJECT), - errmsg("type \"%s\" already exists", typeName))); + { + if (!ifNotExists) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("type \"%s\" already exists", typeName))); + else + { + ereport(NOTICE, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("type \"%s\" already exists, skipping", typeName))); + heap_close(pg_type_desc, RowExclusiveLock); + return InvalidOid; + } + } /* * shell type must have been created by same owner diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c index 4a03786..9f128bd 100644 --- a/src/backend/commands/aggregatecmds.c +++ b/src/backend/commands/aggregatecmds.c @@ -48,7 +48,7 @@ * "args" defines the input type(s). */ Oid -DefineAggregate(List *name, List *args, bool oldstyle, List *parameters) +DefineAggregate(List *name, List *args, bool oldstyle, List *parameters, bool ifNotExists) { char *aggName; Oid aggNamespace; @@ -224,6 +224,7 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters) transfuncName, /* step function name */ finalfuncName, /* final function name */ sortoperatorName, /* sort operator name */ - transTypeId, /* transition data type */ - initval); /* initial condition */ + transTypeId, /* transition data type */ + initval, /* initial condition */ + ifNotExists); /* if not exists flag */ } diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index 38187a8..490c7c8 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -983,7 +983,8 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString) parameterDefaults, PointerGetDatum(proconfig), procost, - prorows); + prorows, + false); } @@ -1498,8 +1499,6 @@ CreateCast(CreateCastStmt *stmt) break; } - relation = heap_open(CastRelationId, RowExclusiveLock); - /* * Check for duplicate. This is just to give a friendly error message, * the unique index would catch it anyway (so no need to sweat about race @@ -1509,11 +1508,26 @@ CreateCast(CreateCastStmt *stmt) ObjectIdGetDatum(sourcetypeid), ObjectIdGetDatum(targettypeid)); if (HeapTupleIsValid(tuple)) - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_OBJECT), - errmsg("cast from type %s to type %s already exists", - format_type_be(sourcetypeid), - format_type_be(targettypeid)))); + { + if (!stmt->if_not_exists) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("cast from type %s to type %s already exists", + format_type_be(sourcetypeid), + format_type_be(targettypeid)))); + else + { + ereport(NOTICE, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("cast from type %s to type %s already exists, skipping", + format_type_be(sourcetypeid), + format_type_be(targettypeid)))); + ReleaseSysCache(tuple); + return InvalidOid; + } + } + + relation = heap_open(CastRelationId, RowExclusiveLock); /* ready to go */ values[Anum_pg_cast_castsource - 1] = ObjectIdGetDatum(sourcetypeid); diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c index 4692b08..c8d3363 100644 --- a/src/backend/commands/operatorcmds.c +++ b/src/backend/commands/operatorcmds.c @@ -60,7 +60,7 @@ * 'parameters' is a list of DefElem */ Oid -DefineOperator(List *names, List *parameters) +DefineOperator(List *names, List *parameters, bool ifNotExists) { char *oprName; Oid oprNamespace; @@ -306,7 +306,8 @@ DefineOperator(List *names, List *parameters) restrictionOid, /* optional restrict. sel. procedure */ joinOid, /* optional join sel. procedure name */ canMerge, /* operator merges */ - canHash); /* operator hashes */ + canHash, /* operator hashes */ + ifNotExists); /* if not exists flag */ } /* diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c index 6e4c682..e107e1d 100644 --- a/src/backend/commands/proclang.c +++ b/src/backend/commands/proclang.c @@ -141,7 +141,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) NIL, PointerGetDatum(NULL), 1, - 0); + 0, + false); } /* @@ -178,7 +179,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) NIL, PointerGetDatum(NULL), 1, - 0); + 0, + false); } } else @@ -218,7 +220,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) NIL, PointerGetDatum(NULL), 1, - 0); + 0, + false); } } else diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c index 57b69f8..a993df2 100644 --- a/src/backend/commands/tsearchcmds.c +++ b/src/backend/commands/tsearchcmds.c @@ -168,7 +168,7 @@ makeParserDependencies(HeapTuple tuple) * CREATE TEXT SEARCH PARSER */ Oid -DefineTSParser(List *names, List *parameters) +DefineTSParser(List *names, List *parameters, bool ifNotExists) { char *prsname; ListCell *pl; @@ -188,6 +188,30 @@ DefineTSParser(List *names, List *parameters) /* Convert list of names to a name and namespace */ namespaceoid = QualifiedNameGetCreationNamespace(names, &prsname); + /* Check if text search parser already exists */ + prsOid = GetSysCacheOid2(TSPARSERNAMENSP, + CStringGetDatum(prsname), + ObjectIdGetDatum(namespaceoid)); + + if (OidIsValid(prsOid)) + { + if (!ifNotExists) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("text search parser \"%s\".\"%s\" already exists", + get_namespace_name(namespaceoid), + prsname))); + else + { + ereport(NOTICE, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("text search parser \"%s\".\"%s\" already exists, skipping", + get_namespace_name(namespaceoid), + prsname))); + return InvalidOid; + } + } + /* initialize tuple fields with name/namespace */ memset(values, 0, sizeof(values)); memset(nulls, false, sizeof(nulls)); @@ -398,7 +422,7 @@ verify_dictoptions(Oid tmplId, List *dictoptions) * CREATE TEXT SEARCH DICTIONARY */ Oid -DefineTSDictionary(List *names, List *parameters) +DefineTSDictionary(List *names, List *parameters, bool ifNotExists) { ListCell *pl; Relation dictRel; @@ -412,15 +436,43 @@ DefineTSDictionary(List *names, List *parameters) Oid namespaceoid; AclResult aclresult; char *dictname; + char *dictnamespace; /* Convert list of names to a name and namespace */ namespaceoid = QualifiedNameGetCreationNamespace(names, &dictname); + /* Get namespace name */ + dictnamespace = get_namespace_name(namespaceoid); + /* Check we have creation rights in target namespace */ aclresult = pg_namespace_aclcheck(namespaceoid, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceoid)); + dictnamespace); + + /* Check if text search dictionary already exists */ + dictOid = GetSysCacheOid2(TSDICTNAMENSP, + CStringGetDatum(dictname), + ObjectIdGetDatum(namespaceoid)); + + if (OidIsValid(dictOid)) + { + if (!ifNotExists) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("text search dictionary \"%s\".\"%s\" already exists", + dictnamespace, + dictname))); + else + { + ereport(NOTICE, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("text search dictionary \"%s\".\"%s\" already exists, skipping", + dictnamespace, + dictname))); + return InvalidOid; + } + } /* * loop over the definition list and extract the information we need. @@ -716,7 +768,7 @@ makeTSTemplateDependencies(HeapTuple tuple) * CREATE TEXT SEARCH TEMPLATE */ Oid -DefineTSTemplate(List *names, List *parameters) +DefineTSTemplate(List *names, List *parameters, bool ifNotExists) { ListCell *pl; Relation tmplRel; @@ -737,6 +789,30 @@ DefineTSTemplate(List *names, List *parameters) /* Convert list of names to a name and namespace */ namespaceoid = QualifiedNameGetCreationNamespace(names, &tmplname); + /* Check if text search template already exists */ + tmplOid = GetSysCacheOid2(TSTEMPLATENAMENSP, + CStringGetDatum(tmplname), + ObjectIdGetDatum(namespaceoid)); + + if (OidIsValid(tmplOid)) + { + if (!ifNotExists) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("text search template \"%s\".\"%s\" already exists", + get_namespace_name(namespaceoid), + tmplname))); + else + { + ereport(NOTICE, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("text search template \"%s\".\"%s\" already exists, skipping", + get_namespace_name(namespaceoid), + tmplname))); + return InvalidOid; + } + } + for (i = 0; i < Natts_pg_ts_template; i++) { nulls[i] = false; @@ -946,7 +1022,7 @@ makeConfigurationDependencies(HeapTuple tuple, bool removeOld, * CREATE TEXT SEARCH CONFIGURATION */ Oid -DefineTSConfiguration(List *names, List *parameters) +DefineTSConfiguration(List *names, List *parameters, bool ifNotExists) { Relation cfgRel; Relation mapRel = NULL; @@ -956,6 +1032,7 @@ DefineTSConfiguration(List *names, List *parameters) AclResult aclresult; Oid namespaceoid; char *cfgname; + char *cfgnamespace; NameData cname; Oid sourceOid = InvalidOid; Oid prsOid = InvalidOid; @@ -965,11 +1042,39 @@ DefineTSConfiguration(List *names, List *parameters) /* Convert list of names to a name and namespace */ namespaceoid = QualifiedNameGetCreationNamespace(names, &cfgname); + /* Get namespace name */ + cfgnamespace = get_namespace_name(namespaceoid); + /* Check we have creation rights in target namespace */ aclresult = pg_namespace_aclcheck(namespaceoid, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceoid)); + cfgnamespace); + + /* Check if text search configuration already exists */ + cfgOid = GetSysCacheOid2(TSCONFIGNAMENSP, + CStringGetDatum(cfgname), + ObjectIdGetDatum(namespaceoid)); + + if (OidIsValid(cfgOid)) + { + if (!ifNotExists) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("text search configuration \"%s\".\"%s\" already exists", + cfgnamespace, + cfgname))); + else + { + ereport(NOTICE, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("text search configuration \"%s\".\"%s\" already exists, skipping", + cfgnamespace, + cfgname))); + return InvalidOid; + } + } + /* * loop over the definition list and extract the information we need. diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 6bc16f1..325cbe8 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -113,7 +113,7 @@ static char *domainAddConstraint(Oid domainOid, Oid domainNamespace, * Registers a new base type. */ Oid -DefineType(List *names, List *parameters) +DefineType(List *names, List *parameters, bool ifNotExists) { char *typeName; Oid typeNamespace; @@ -232,9 +232,19 @@ DefineType(List *names, List *parameters) { /* Complain if dummy CREATE TYPE and entry already exists */ if (parameters == NIL) - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_OBJECT), - errmsg("type \"%s\" already exists", typeName))); + { + if (!ifNotExists) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("type \"%s\" already exists", typeName))); + else + { + ereport(NOTICE, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("type \"%s\" already exists, skipping", typeName))); + return InvalidOid; + } + } } /* Extract the parameters from the parameter list */ @@ -584,7 +594,11 @@ DefineType(List *names, List *parameters) -1, /* typMod (Domains only) */ 0, /* Array Dimensions of typbasetype */ false, /* Type NOT NULL */ - collation); /* type's collation */ + collation, /* type's collation */ + ifNotExists); /* if not exists flag */ + + if (!OidIsValid(typoid)) + return typoid; /* * Create the array type that goes with it. @@ -620,11 +634,12 @@ DefineType(List *names, List *parameters) NULL, /* binary default isn't sent either */ false, /* never passed by value */ alignment, /* see above */ - 'x', /* ARRAY is always toastable */ - -1, /* typMod (Domains only) */ - 0, /* Array dimensions of typbasetype */ - false, /* Type NOT NULL */ - collation); /* type's collation */ + 'x', /* ARRAY is always toastable */ + -1, /* typMod (Domains only) */ + 0, /* Array dimensions of typbasetype */ + false, /* Type NOT NULL */ + collation, /* type's collation */ + ifNotExists); /* if not exists flag */ pfree(array_type); @@ -1010,7 +1025,8 @@ DefineDomain(CreateDomainStmt *stmt) basetypeMod, /* typeMod value */ typNDims, /* Array dimensions for base type */ typNotNull, /* Type NOT NULL */ - domaincoll); /* type's collation */ + domaincoll, /* type's collation */ + false); /* if not exists flag */ /* * Process constraints which refer to the domain ID returned by TypeCreate @@ -1083,9 +1099,19 @@ DefineEnum(CreateEnumStmt *stmt) if (OidIsValid(old_type_oid)) { if (!moveArrayTypeName(old_type_oid, enumName, enumNamespace)) - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_OBJECT), - errmsg("type \"%s\" already exists", enumName))); + { + if (!stmt->if_not_exists) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("type \"%s\" already exists", enumName))); + else + { + ereport(NOTICE, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("type \"%s\" already exists, skipping", enumName))); + return InvalidOid; + } + } } enumArrayOid = AssignTypeArrayOid(); @@ -1122,7 +1148,11 @@ DefineEnum(CreateEnumStmt *stmt) -1, /* typMod (Domains only) */ 0, /* Array dimensions of typbasetype */ false, /* Type NOT NULL */ - InvalidOid); /* type's collation */ + InvalidOid, /* type's collation */ + stmt->if_not_exists); /* if not exists flag */ + + if (!OidIsValid(enumTypeOid)) + return enumTypeOid; /* Enter the enum's values into pg_enum */ EnumValuesCreate(enumTypeOid, stmt->vals); @@ -1162,7 +1192,8 @@ DefineEnum(CreateEnumStmt *stmt) -1, /* typMod (Domains only) */ 0, /* Array dimensions of typbasetype */ false, /* Type NOT NULL */ - InvalidOid); /* type's collation */ + InvalidOid, /* type's collation */ + stmt->if_not_exists); /* if not exists flag */ pfree(enumArrayName); @@ -1301,9 +1332,19 @@ DefineRange(CreateRangeStmt *stmt) if (moveArrayTypeName(typoid, typeName, typeNamespace)) typoid = InvalidOid; else - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_OBJECT), - errmsg("type \"%s\" already exists", typeName))); + { + if (!stmt->if_not_exists) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("type \"%s\" already exists", typeName))); + else + { + ereport(NOTICE, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("type \"%s\" already exists, skipping", typeName))); + return InvalidOid; + } + } } /* @@ -1456,7 +1497,11 @@ DefineRange(CreateRangeStmt *stmt) -1, /* typMod (Domains only) */ 0, /* Array dimensions of typbasetype */ false, /* Type NOT NULL */ - InvalidOid); /* type's collation (ranges never have one) */ + InvalidOid, /* type's collation (ranges never have one) */ + stmt->if_not_exists); /* if not exists flag */ + + if (!OidIsValid(typoid)) + return typoid; /* Create the entry in pg_range */ RangeCreate(typoid, rangeSubtype, rangeCollation, rangeSubOpclass, @@ -1497,7 +1542,8 @@ DefineRange(CreateRangeStmt *stmt) -1, /* typMod (Domains only) */ 0, /* Array dimensions of typbasetype */ false, /* Type NOT NULL */ - InvalidOid); /* typcollation */ + InvalidOid, /* typcollation */ + stmt->if_not_exists); /* if not exists flag */ pfree(rangeArrayName); @@ -1568,7 +1614,8 @@ makeRangeConstructors(const char *name, Oid namespace, NIL, /* parameterDefaults */ PointerGetDatum(NULL), /* proconfig */ 1.0, /* procost */ - 0.0); /* prorows */ + 0.0, /* prorows */ + false); /* if not exists */ /* * Make the constructors internally-dependent on the range type so @@ -2017,7 +2064,7 @@ AssignTypeArrayOid(void) *------------------------------------------------------------------- */ Oid -DefineCompositeType(RangeVar *typevar, List *coldeflist) +DefineCompositeType(RangeVar *typevar, List *coldeflist, bool ifNotExists) { CreateStmt *createStmt = makeNode(CreateStmt); Oid old_type_oid; @@ -2053,9 +2100,19 @@ DefineCompositeType(RangeVar *typevar, List *coldeflist) if (OidIsValid(old_type_oid)) { if (!moveArrayTypeName(old_type_oid, createStmt->relation->relname, typeNamespace)) - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_OBJECT), - errmsg("type \"%s\" already exists", createStmt->relation->relname))); + { + if (!ifNotExists) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("type \"%s\" already exists", createStmt->relation->relname))); + else + { + ereport(NOTICE, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("type \"%s\" already exists, skipping", createStmt->relation->relname))); + return InvalidOid; + } + } } /* diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index b5b8d63..7d71d7a 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -2763,6 +2763,7 @@ _copyDefineStmt(const DefineStmt *from) COPY_NODE_FIELD(defnames); COPY_NODE_FIELD(args); COPY_NODE_FIELD(definition); + COPY_SCALAR_FIELD(if_not_exists); return newnode; } @@ -3022,6 +3023,7 @@ _copyCompositeTypeStmt(const CompositeTypeStmt *from) COPY_NODE_FIELD(typevar); COPY_NODE_FIELD(coldeflist); + COPY_SCALAR_FIELD(if_not_exists); return newnode; } @@ -3033,6 +3035,7 @@ _copyCreateEnumStmt(const CreateEnumStmt *from) COPY_NODE_FIELD(typeName); COPY_NODE_FIELD(vals); + COPY_SCALAR_FIELD(if_not_exists); return newnode; } @@ -3044,6 +3047,7 @@ _copyCreateRangeStmt(const CreateRangeStmt *from) COPY_NODE_FIELD(typeName); COPY_NODE_FIELD(params); + COPY_SCALAR_FIELD(if_not_exists); return newnode; } @@ -3658,6 +3662,7 @@ _copyCreateCastStmt(const CreateCastStmt *from) COPY_NODE_FIELD(func); COPY_SCALAR_FIELD(context); COPY_SCALAR_FIELD(inout); + COPY_SCALAR_FIELD(if_not_exists); return newnode; } diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 3f96595..75e0c44 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -1117,6 +1117,7 @@ _equalDefineStmt(const DefineStmt *a, const DefineStmt *b) COMPARE_NODE_FIELD(defnames); COMPARE_NODE_FIELD(args); COMPARE_NODE_FIELD(definition); + COMPARE_SCALAR_FIELD(if_not_exists); return true; } @@ -1338,6 +1339,7 @@ _equalCompositeTypeStmt(const CompositeTypeStmt *a, const CompositeTypeStmt *b) { COMPARE_NODE_FIELD(typevar); COMPARE_NODE_FIELD(coldeflist); + COMPARE_SCALAR_FIELD(if_not_exists); return true; } @@ -1347,6 +1349,7 @@ _equalCreateEnumStmt(const CreateEnumStmt *a, const CreateEnumStmt *b) { COMPARE_NODE_FIELD(typeName); COMPARE_NODE_FIELD(vals); + COMPARE_SCALAR_FIELD(if_not_exists); return true; } @@ -1356,6 +1359,7 @@ _equalCreateRangeStmt(const CreateRangeStmt *a, const CreateRangeStmt *b) { COMPARE_NODE_FIELD(typeName); COMPARE_NODE_FIELD(params); + COMPARE_SCALAR_FIELD(if_not_exists); return true; } @@ -1874,6 +1878,7 @@ _equalCreateCastStmt(const CreateCastStmt *a, const CreateCastStmt *b) COMPARE_NODE_FIELD(func); COMPARE_SCALAR_FIELD(context); COMPARE_SCALAR_FIELD(inout); + COMPARE_SCALAR_FIELD(if_not_exists); return true; } diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 5094226..3c32bcb 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -4594,6 +4594,18 @@ DefineStmt: n->defnames = $3; n->args = $4; n->definition = $5; + n->if_not_exists = false; + $$ = (Node *)n; + } + | CREATE AGGREGATE IF_P NOT EXISTS func_name aggr_args definition + { + DefineStmt *n = makeNode(DefineStmt); + n->kind = OBJECT_AGGREGATE; + n->oldstyle = false; + n->defnames = $6; + n->args = $7; + n->definition = $8; + n->if_not_exists = true; $$ = (Node *)n; } | CREATE AGGREGATE func_name old_aggr_definition @@ -4605,6 +4617,19 @@ DefineStmt: n->defnames = $3; n->args = NIL; n->definition = $4; + n->if_not_exists = false; + $$ = (Node *)n; + } + | CREATE AGGREGATE IF_P NOT EXISTS func_name old_aggr_definition + { + /* old-style (pre-8.2) syntax for CREATE AGGREGATE */ + DefineStmt *n = makeNode(DefineStmt); + n->kind = OBJECT_AGGREGATE; + n->oldstyle = true; + n->defnames = $6; + n->args = NIL; + n->definition = $7; + n->if_not_exists = true; $$ = (Node *)n; } | CREATE OPERATOR any_operator definition @@ -4615,6 +4640,18 @@ DefineStmt: n->defnames = $3; n->args = NIL; n->definition = $4; + n->if_not_exists = false; + $$ = (Node *)n; + } + | CREATE OPERATOR IF_P NOT EXISTS any_operator definition + { + DefineStmt *n = makeNode(DefineStmt); + n->kind = OBJECT_OPERATOR; + n->oldstyle = false; + n->defnames = $6; + n->args = NIL; + n->definition = $7; + n->if_not_exists = true; $$ = (Node *)n; } | CREATE TYPE_P any_name definition @@ -4625,6 +4662,18 @@ DefineStmt: n->defnames = $3; n->args = NIL; n->definition = $4; + n->if_not_exists = false; + $$ = (Node *)n; + } + | CREATE TYPE_P IF_P NOT EXISTS any_name definition + { + DefineStmt *n = makeNode(DefineStmt); + n->kind = OBJECT_TYPE; + n->oldstyle = false; + n->defnames = $6; + n->args = NIL; + n->definition = $7; + n->if_not_exists = true; $$ = (Node *)n; } | CREATE TYPE_P any_name @@ -4636,6 +4685,19 @@ DefineStmt: n->defnames = $3; n->args = NIL; n->definition = NIL; + n->if_not_exists = false; + $$ = (Node *)n; + } + | CREATE TYPE_P IF_P NOT EXISTS any_name + { + /* Shell type (identified by lack of definition) */ + DefineStmt *n = makeNode(DefineStmt); + n->kind = OBJECT_TYPE; + n->oldstyle = false; + n->defnames = $6; + n->args = NIL; + n->definition = NIL; + n->if_not_exists = true; $$ = (Node *)n; } | CREATE TYPE_P any_name AS '(' OptTableFuncElementList ')' @@ -4645,6 +4707,17 @@ DefineStmt: /* can't use qualified_name, sigh */ n->typevar = makeRangeVarFromAnyName($3, @3, yyscanner); n->coldeflist = $6; + n->if_not_exists = false; + $$ = (Node *)n; + } + | CREATE TYPE_P IF_P NOT EXISTS any_name AS '(' OptTableFuncElementList ')' + { + CompositeTypeStmt *n = makeNode(CompositeTypeStmt); + + /* can't use qualified_name, sigh */ + n->typevar = makeRangeVarFromAnyName($6, @6, yyscanner); + n->coldeflist = $9; + n->if_not_exists = true; $$ = (Node *)n; } | CREATE TYPE_P any_name AS ENUM_P '(' opt_enum_val_list ')' @@ -4652,6 +4725,15 @@ DefineStmt: CreateEnumStmt *n = makeNode(CreateEnumStmt); n->typeName = $3; n->vals = $7; + n->if_not_exists = false; + $$ = (Node *)n; + } + | CREATE TYPE_P IF_P NOT EXISTS any_name AS ENUM_P '(' opt_enum_val_list ')' + { + CreateEnumStmt *n = makeNode(CreateEnumStmt); + n->typeName = $6; + n->vals = $10; + n->if_not_exists = true; $$ = (Node *)n; } | CREATE TYPE_P any_name AS RANGE definition @@ -4659,6 +4741,15 @@ DefineStmt: CreateRangeStmt *n = makeNode(CreateRangeStmt); n->typeName = $3; n->params = $6; + n->if_not_exists = false; + $$ = (Node *)n; + } + | CREATE TYPE_P IF_P NOT EXISTS any_name AS RANGE definition + { + CreateRangeStmt *n = makeNode(CreateRangeStmt); + n->typeName = $6; + n->params = $9; + n->if_not_exists = true; $$ = (Node *)n; } | CREATE TEXT_P SEARCH PARSER any_name definition @@ -4668,6 +4759,17 @@ DefineStmt: n->args = NIL; n->defnames = $5; n->definition = $6; + n->if_not_exists = false; + $$ = (Node *)n; + } + | CREATE TEXT_P SEARCH PARSER IF_P NOT EXISTS any_name definition + { + DefineStmt *n = makeNode(DefineStmt); + n->kind = OBJECT_TSPARSER; + n->args = NIL; + n->defnames = $8; + n->definition = $9; + n->if_not_exists = true; $$ = (Node *)n; } | CREATE TEXT_P SEARCH DICTIONARY any_name definition @@ -4677,6 +4779,17 @@ DefineStmt: n->args = NIL; n->defnames = $5; n->definition = $6; + n->if_not_exists = false; + $$ = (Node *)n; + } + | CREATE TEXT_P SEARCH DICTIONARY IF_P NOT EXISTS any_name definition + { + DefineStmt *n = makeNode(DefineStmt); + n->kind = OBJECT_TSDICTIONARY; + n->args = NIL; + n->defnames = $8; + n->definition = $9; + n->if_not_exists = true; $$ = (Node *)n; } | CREATE TEXT_P SEARCH TEMPLATE any_name definition @@ -4686,6 +4799,17 @@ DefineStmt: n->args = NIL; n->defnames = $5; n->definition = $6; + n->if_not_exists = false; + $$ = (Node *)n; + } + | CREATE TEXT_P SEARCH TEMPLATE IF_P NOT EXISTS any_name definition + { + DefineStmt *n = makeNode(DefineStmt); + n->kind = OBJECT_TSTEMPLATE; + n->args = NIL; + n->defnames = $8; + n->definition = $9; + n->if_not_exists = true; $$ = (Node *)n; } | CREATE TEXT_P SEARCH CONFIGURATION any_name definition @@ -4695,6 +4819,17 @@ DefineStmt: n->args = NIL; n->defnames = $5; n->definition = $6; + n->if_not_exists = false; + $$ = (Node *)n; + } + | CREATE TEXT_P SEARCH CONFIGURATION IF_P NOT EXISTS any_name definition + { + DefineStmt *n = makeNode(DefineStmt); + n->kind = OBJECT_TSCONFIGURATION; + n->args = NIL; + n->defnames = $8; + n->definition = $9; + n->if_not_exists = true; $$ = (Node *)n; } | CREATE COLLATION any_name definition @@ -4704,6 +4839,17 @@ DefineStmt: n->args = NIL; n->defnames = $3; n->definition = $4; + n->if_not_exists = false; + $$ = (Node *)n; + } + | CREATE COLLATION IF_P NOT EXISTS any_name definition + { + DefineStmt *n = makeNode(DefineStmt); + n->kind = OBJECT_COLLATION; + n->args = NIL; + n->defnames = $6; + n->definition = $7; + n->if_not_exists = true; $$ = (Node *)n; } | CREATE COLLATION any_name FROM any_name @@ -4713,6 +4859,17 @@ DefineStmt: n->args = NIL; n->defnames = $3; n->definition = list_make1(makeDefElem("from", (Node *) $5)); + n->if_not_exists = false; + $$ = (Node *)n; + } + | CREATE COLLATION IF_P NOT EXISTS any_name FROM any_name + { + DefineStmt *n = makeNode(DefineStmt); + n->kind = OBJECT_COLLATION; + n->args = NIL; + n->defnames = $6; + n->definition = list_make1(makeDefElem("from", (Node *) $8)); + n->if_not_exists = true; $$ = (Node *)n; } ; @@ -4814,8 +4971,8 @@ AlterEnumStmt: } ; -opt_if_not_exists: IF_P NOT EXISTS { $$ = true; } - | /* empty */ { $$ = false; } +opt_if_not_exists: IF_P NOT EXISTS { $$ = TRUE; } + | /* empty */ { $$ = FALSE; } ; @@ -6683,37 +6840,40 @@ dostmt_opt_item: * *****************************************************************************/ -CreateCastStmt: CREATE CAST '(' Typename AS Typename ')' +CreateCastStmt: CREATE CAST opt_if_not_exists '(' Typename AS Typename ')' WITH FUNCTION function_with_argtypes cast_context { CreateCastStmt *n = makeNode(CreateCastStmt); - n->sourcetype = $4; - n->targettype = $6; - n->func = $10; - n->context = (CoercionContext) $11; + n->sourcetype = $5; + n->targettype = $7; + n->func = $11; + n->context = (CoercionContext) $12; n->inout = false; + n->if_not_exists = $3; $$ = (Node *)n; } - | CREATE CAST '(' Typename AS Typename ')' + | CREATE CAST opt_if_not_exists '(' Typename AS Typename ')' WITHOUT FUNCTION cast_context { CreateCastStmt *n = makeNode(CreateCastStmt); - n->sourcetype = $4; - n->targettype = $6; + n->sourcetype = $5; + n->targettype = $7; n->func = NULL; - n->context = (CoercionContext) $10; + n->context = (CoercionContext) $11; n->inout = false; + n->if_not_exists = $3; $$ = (Node *)n; } - | CREATE CAST '(' Typename AS Typename ')' + | CREATE CAST opt_if_not_exists '(' Typename AS Typename ')' WITH INOUT cast_context { CreateCastStmt *n = makeNode(CreateCastStmt); - n->sourcetype = $4; - n->targettype = $6; + n->sourcetype = $5; + n->targettype = $7; n->func = NULL; - n->context = (CoercionContext) $10; + n->context = (CoercionContext) $11; n->inout = true; + n->if_not_exists = $3; $$ = (Node *)n; } ; diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index c940897..d33f67e 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -1103,34 +1103,41 @@ ProcessUtilitySlow(Node *parsetree, { case OBJECT_AGGREGATE: DefineAggregate(stmt->defnames, stmt->args, - stmt->oldstyle, stmt->definition); + stmt->oldstyle, stmt->definition, + stmt->if_not_exists); break; case OBJECT_OPERATOR: Assert(stmt->args == NIL); - DefineOperator(stmt->defnames, stmt->definition); + DefineOperator(stmt->defnames, stmt->definition, + stmt->if_not_exists); break; case OBJECT_TYPE: Assert(stmt->args == NIL); - DefineType(stmt->defnames, stmt->definition); + DefineType(stmt->defnames, stmt->definition, + stmt->if_not_exists); break; case OBJECT_TSPARSER: Assert(stmt->args == NIL); - DefineTSParser(stmt->defnames, stmt->definition); + DefineTSParser(stmt->defnames, stmt->definition, + stmt->if_not_exists); break; case OBJECT_TSDICTIONARY: Assert(stmt->args == NIL); DefineTSDictionary(stmt->defnames, - stmt->definition); + stmt->definition, + stmt->if_not_exists); break; case OBJECT_TSTEMPLATE: Assert(stmt->args == NIL); DefineTSTemplate(stmt->defnames, - stmt->definition); + stmt->definition, + stmt->if_not_exists); break; case OBJECT_TSCONFIGURATION: Assert(stmt->args == NIL); DefineTSConfiguration(stmt->defnames, - stmt->definition); + stmt->definition, + stmt->if_not_exists); break; case OBJECT_COLLATION: Assert(stmt->args == NIL); @@ -1211,7 +1218,7 @@ ProcessUtilitySlow(Node *parsetree, { CompositeTypeStmt *stmt = (CompositeTypeStmt *) parsetree; - DefineCompositeType(stmt->typevar, stmt->coldeflist); + DefineCompositeType(stmt->typevar, stmt->coldeflist, stmt->if_not_exists); } break; diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h index 6fb10a9..65c8616 100644 --- a/src/include/catalog/pg_aggregate.h +++ b/src/include/catalog/pg_aggregate.h @@ -246,6 +246,7 @@ extern Oid AggregateCreate(const char *aggName, List *aggfinalfnName, List *aggsortopName, Oid aggTransType, - const char *agginitval); + const char *agginitval, + bool aggIfNotExists); #endif /* PG_AGGREGATE_H */ diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h index 5f28fc3..18f3897 100644 --- a/src/include/catalog/pg_operator.h +++ b/src/include/catalog/pg_operator.h @@ -1752,6 +1752,7 @@ extern Oid OperatorCreate(const char *operatorName, Oid restrictionId, Oid joinId, bool canMerge, - bool canHash); + bool canHash, + bool ifNotExists); #endif /* PG_OPERATOR_H */ diff --git a/src/include/catalog/pg_proc_fn.h b/src/include/catalog/pg_proc_fn.h index 3b04301..d920c0b 100644 --- a/src/include/catalog/pg_proc_fn.h +++ b/src/include/catalog/pg_proc_fn.h @@ -39,7 +39,8 @@ extern Oid ProcedureCreate(const char *procedureName, List *parameterDefaults, Datum proconfig, float4 procost, - float4 prorows); + float4 prorows, + bool ifNotExists); extern bool function_parse_error_transpose(const char *prosrc); diff --git a/src/include/catalog/pg_type_fn.h b/src/include/catalog/pg_type_fn.h index b12d58a..e21a817 100644 --- a/src/include/catalog/pg_type_fn.h +++ b/src/include/catalog/pg_type_fn.h @@ -51,7 +51,8 @@ extern Oid TypeCreate(Oid newTypeOid, int32 typeMod, int32 typNDims, bool typeNotNull, - Oid typeCollation); + Oid typeCollation, + bool ifNotExists); extern void GenerateTypeDependencies(Oid typeNamespace, Oid typeObjectId, diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h index 01d165f..5847c35 100644 --- a/src/include/commands/defrem.h +++ b/src/include/commands/defrem.h @@ -55,12 +55,12 @@ extern void ExecuteDoStmt(DoStmt *stmt); extern Oid get_cast_oid(Oid sourcetypeid, Oid targettypeid, bool missing_ok); /* commands/operatorcmds.c */ -extern Oid DefineOperator(List *names, List *parameters); +extern Oid DefineOperator(List *names, List *parameters, bool ifNotExists); extern void RemoveOperatorById(Oid operOid); /* commands/aggregatecmds.c */ extern Oid DefineAggregate(List *name, List *args, bool oldstyle, - List *parameters); + List *parameters, bool ifNotExists); /* commands/opclasscmds.c */ extern Oid DefineOpClass(CreateOpClassStmt *stmt); @@ -79,17 +79,17 @@ extern Oid get_opclass_oid(Oid amID, List *opclassname, bool missing_ok); extern Oid get_opfamily_oid(Oid amID, List *opfamilyname, bool missing_ok); /* commands/tsearchcmds.c */ -extern Oid DefineTSParser(List *names, List *parameters); +extern Oid DefineTSParser(List *names, List *parameters, bool ifNotExists); extern void RemoveTSParserById(Oid prsId); -extern Oid DefineTSDictionary(List *names, List *parameters); +extern Oid DefineTSDictionary(List *names, List *parameters, bool ifNotExists); extern void RemoveTSDictionaryById(Oid dictId); extern Oid AlterTSDictionary(AlterTSDictionaryStmt *stmt); -extern Oid DefineTSTemplate(List *names, List *parameters); +extern Oid DefineTSTemplate(List *names, List *parameters, bool ifNotExists); extern void RemoveTSTemplateById(Oid tmplId); -extern Oid DefineTSConfiguration(List *names, List *parameters); +extern Oid DefineTSConfiguration(List *names, List *parameters, bool ifNotExists); extern void RemoveTSConfigurationById(Oid cfgId); extern Oid AlterTSConfiguration(AlterTSConfigurationStmt *stmt); diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h index f45fde7..dbd4e32 100644 --- a/src/include/commands/typecmds.h +++ b/src/include/commands/typecmds.h @@ -21,13 +21,13 @@ #define DEFAULT_TYPDELIM ',' -extern Oid DefineType(List *names, List *parameters); +extern Oid DefineType(List *names, List *parameters, bool ifNotExists); extern void RemoveTypeById(Oid typeOid); extern Oid DefineDomain(CreateDomainStmt *stmt); extern Oid DefineEnum(CreateEnumStmt *stmt); extern Oid DefineRange(CreateRangeStmt *stmt); extern Oid AlterEnum(AlterEnumStmt *stmt, bool isTopLevel); -extern Oid DefineCompositeType(RangeVar *typevar, List *coldeflist); +extern Oid DefineCompositeType(RangeVar *typevar, List *coldeflist, bool ifNotExists); extern Oid AssignTypeArrayOid(void); extern Oid AlterDomainDefault(List *names, Node *defaultRaw); diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 6723647..6339538 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -1870,6 +1870,7 @@ typedef struct DefineStmt List *defnames; /* qualified name (list of Value strings) */ List *args; /* a list of TypeName (if needed) */ List *definition; /* a list of DefElem */ + bool if_not_exists; /* just do nothing if {aggregate|operator|type} already exists? */ } DefineStmt; /* ---------------------- @@ -2284,6 +2285,7 @@ typedef struct CompositeTypeStmt NodeTag type; RangeVar *typevar; /* the composite type to be created */ List *coldeflist; /* list of ColumnDef nodes */ + bool if_not_exists; /* just do nothing if type already exists? */ } CompositeTypeStmt; /* ---------------------- @@ -2295,6 +2297,7 @@ typedef struct CreateEnumStmt NodeTag type; List *typeName; /* qualified name (list of Value strings) */ List *vals; /* enum values (list of Value strings) */ + bool if_not_exists; /* just do nothing if type already exists? */ } CreateEnumStmt; /* ---------------------- @@ -2306,6 +2309,7 @@ typedef struct CreateRangeStmt NodeTag type; List *typeName; /* qualified name (list of Value strings) */ List *params; /* range parameters (list of DefElem) */ + bool if_not_exists; /* just do nothing if type already exists? */ } CreateRangeStmt; /* ---------------------- @@ -2566,6 +2570,7 @@ typedef struct CreateCastStmt FuncWithArgs *func; CoercionContext context; bool inout; + bool if_not_exists; /* just do nothing if cast already exists? */ } CreateCastStmt; /* ---------------------- diff --git a/src/test/regress/expected/alter_generic.out b/src/test/regress/expected/alter_generic.out index 1d7e524..9095ddb 100644 --- a/src/test/regress/expected/alter_generic.out +++ b/src/test/regress/expected/alter_generic.out @@ -406,6 +406,10 @@ SELECT nspname, cfgname, rolname -- Text Search Template -- CREATE TEXT SEARCH TEMPLATE alt_ts_temp1 (lexize=dsimple_lexize); +CREATE TEXT SEARCH TEMPLATE alt_ts_temp1 (lexize=dsimple_lexize); +ERROR: text search template "alt_nsp1"."alt_ts_temp1" already exists +CREATE TEXT SEARCH TEMPLATE IF NOT EXISTS alt_ts_temp1 (lexize=dsimple_lexize); +NOTICE: text search template "alt_nsp1"."alt_ts_temp1" already exists, skipping CREATE TEXT SEARCH TEMPLATE alt_ts_temp2 (lexize=dsimple_lexize); ALTER TEXT SEARCH TEMPLATE alt_ts_temp1 RENAME TO alt_ts_temp2; -- failed (name conflict) ERROR: text search template "alt_ts_temp2" already exists in schema "alt_nsp1" @@ -430,6 +434,12 @@ SELECT nspname, tmplname -- CREATE TEXT SEARCH PARSER alt_ts_prs1 (start = prsd_start, gettoken = prsd_nexttoken, end = prsd_end, lextypes = prsd_lextype); +CREATE TEXT SEARCH PARSER alt_ts_prs1 + (start = prsd_start, gettoken = prsd_nexttoken, end = prsd_end, lextypes = prsd_lextype); +ERROR: text search parser "alt_nsp1"."alt_ts_prs1" already exists +CREATE TEXT SEARCH PARSER IF NOT EXISTS alt_ts_prs1 + (start = prsd_start, gettoken = prsd_nexttoken, end = prsd_end, lextypes = prsd_lextype); +NOTICE: text search parser "alt_nsp1"."alt_ts_prs1" already exists, skipping CREATE TEXT SEARCH PARSER alt_ts_prs2 (start = prsd_start, gettoken = prsd_nexttoken, end = prsd_end, lextypes = prsd_lextype); ALTER TEXT SEARCH PARSER alt_ts_prs1 RENAME TO alt_ts_prs2; -- failed (name conflict) diff --git a/src/test/regress/expected/create_aggregate.out b/src/test/regress/expected/create_aggregate.out index ad14594..e294d06 100644 --- a/src/test/regress/expected/create_aggregate.out +++ b/src/test/regress/expected/create_aggregate.out @@ -12,6 +12,19 @@ COMMENT ON AGGREGATE newavg_wrong (int4) IS 'an agg comment'; ERROR: aggregate newavg_wrong(integer) does not exist COMMENT ON AGGREGATE newavg (int4) IS 'an agg comment'; COMMENT ON AGGREGATE newavg (int4) IS NULL; +-- test IF NOT EXISTS +CREATE AGGREGATE newavg ( + sfunc = int4_avg_accum, basetype = int4, stype = _int8, + finalfunc = int8_avg, + initcond1 = '{0,0}' +); +ERROR: function "newavg" already exists with same argument types +CREATE AGGREGATE IF NOT EXISTS newavg ( + sfunc = int4_avg_accum, basetype = int4, stype = _int8, + finalfunc = int8_avg, + initcond1 = '{0,0}' +); +NOTICE: function "newavg" already exists with same argument types, skipping -- without finalfunc; test obsolete spellings 'sfunc1' etc CREATE AGGREGATE newsum ( sfunc1 = int4pl, basetype = int4, stype1 = int4, diff --git a/src/test/regress/expected/create_cast.out b/src/test/regress/expected/create_cast.out index 56cd86e..1c3e6f0 100644 --- a/src/test/regress/expected/create_cast.out +++ b/src/test/regress/expected/create_cast.out @@ -29,6 +29,10 @@ LINE 1: SELECT casttestfunc('foo'::text); HINT: No function matches the given name and argument types. You might need to add explicit type casts. -- Try binary coercion cast CREATE CAST (text AS casttesttype) WITHOUT FUNCTION; +CREATE CAST (text AS casttesttype) WITHOUT FUNCTION; +ERROR: cast from type text to type casttesttype already exists +CREATE CAST IF NOT EXISTS (text AS casttesttype) WITHOUT FUNCTION; +NOTICE: cast from type text to type casttesttype already exists, skipping SELECT casttestfunc('foo'::text); -- doesn't work, as the cast is explicit ERROR: function casttestfunc(text) does not exist LINE 1: SELECT casttestfunc('foo'::text); @@ -43,6 +47,10 @@ SELECT casttestfunc('foo'::text::casttesttype); -- should work DROP CAST (text AS casttesttype); -- cleanup -- Try IMPLICIT binary coercion cast CREATE CAST (text AS casttesttype) WITHOUT FUNCTION AS IMPLICIT; +CREATE CAST (text AS casttesttype) WITHOUT FUNCTION AS IMPLICIT; +ERROR: cast from type text to type casttesttype already exists +CREATE CAST IF NOT EXISTS (text AS casttesttype) WITHOUT FUNCTION AS IMPLICIT; +NOTICE: cast from type text to type casttesttype already exists, skipping SELECT casttestfunc('foo'::text); -- Should work now casttestfunc -------------- @@ -55,6 +63,10 @@ ERROR: cannot cast type integer to casttesttype LINE 1: SELECT 1234::int4::casttesttype; ^ CREATE CAST (int4 AS casttesttype) WITH INOUT; +CREATE CAST (int4 AS casttesttype) WITH INOUT; +ERROR: cast from type integer to type casttesttype already exists +CREATE CAST IF NOT EXISTS (int4 AS casttesttype) WITH INOUT; +NOTICE: cast from type integer to type casttesttype already exists, skipping SELECT 1234::int4::casttesttype; -- Should work now casttesttype -------------- @@ -66,6 +78,10 @@ DROP CAST (int4 AS casttesttype); CREATE FUNCTION int4_casttesttype(int4) RETURNS casttesttype LANGUAGE SQL AS $$ SELECT ('foo'::text || $1::text)::casttesttype; $$; CREATE CAST (int4 AS casttesttype) WITH FUNCTION int4_casttesttype(int4) AS IMPLICIT; +CREATE CAST (int4 AS casttesttype) WITH FUNCTION int4_casttesttype(int4) AS IMPLICIT; +ERROR: cast from type integer to type casttesttype already exists +CREATE CAST IF NOT EXISTS (int4 AS casttesttype) WITH FUNCTION int4_casttesttype(int4) AS IMPLICIT; +NOTICE: cast from type integer to type casttesttype already exists, skipping SELECT 1234::int4::casttesttype; -- Should work now casttesttype -------------- diff --git a/src/test/regress/expected/create_operator.out b/src/test/regress/expected/create_operator.out index 8656864..f59243e 100644 --- a/src/test/regress/expected/create_operator.out +++ b/src/test/regress/expected/create_operator.out @@ -7,6 +7,20 @@ CREATE OPERATOR ## ( procedure = path_inter, commutator = ## ); +CREATE OPERATOR ## ( + leftarg = path, + rightarg = path, + procedure = path_inter, + commutator = ## +); +ERROR: operator ## already exists +CREATE OPERATOR IF NOT EXISTS ## ( + leftarg = path, + rightarg = path, + procedure = path_inter, + commutator = ## +); +NOTICE: operator ## already exists, skipping CREATE OPERATOR <% ( leftarg = point, rightarg = widget, diff --git a/src/test/regress/expected/create_type.out b/src/test/regress/expected/create_type.out index 6dfe916..666a7c1 100644 --- a/src/test/regress/expected/create_type.out +++ b/src/test/regress/expected/create_type.out @@ -14,6 +14,24 @@ CREATE TYPE widget ( typmod_out = numerictypmodout, alignment = double ); +CREATE TYPE widget ( + internallength = 24, + input = widget_in, + output = widget_out, + typmod_in = numerictypmodin, + typmod_out = numerictypmodout, + alignment = double +); +ERROR: type "widget" already exists +CREATE TYPE IF NOT EXISTS widget ( + internallength = 24, + input = widget_in, + output = widget_out, + typmod_in = numerictypmodin, + typmod_out = numerictypmodout, + alignment = double +); +NOTICE: type "widget" already exists, skipping CREATE TYPE city_budget ( internallength = 16, input = int44in, @@ -26,6 +44,8 @@ CREATE TYPE city_budget ( CREATE TYPE shell; CREATE TYPE shell; -- fail, type already present ERROR: type "shell" already exists +CREATE TYPE IF NOT EXISTS shell; -- do not fail, just skip +NOTICE: type "shell" already exists, skipping DROP TYPE shell; DROP TYPE shell; -- fail, type not exist ERROR: type "shell" does not exist @@ -83,6 +103,10 @@ SELECT * FROM default_test; -- Test stand-alone composite type CREATE TYPE default_test_row AS (f1 text_w_default, f2 int42); +CREATE TYPE default_test_row AS (f1 text_w_default, f2 int42); +ERROR: type "default_test_row" already exists +CREATE TYPE IF NOT EXISTS default_test_row AS (f1 text_w_default, f2 int42); +NOTICE: type "default_test_row" already exists, skipping CREATE FUNCTION get_default_test() RETURNS SETOF default_test_row AS ' SELECT * FROM default_test; ' LANGUAGE SQL; diff --git a/src/test/regress/expected/enum.out b/src/test/regress/expected/enum.out index 3682642..b95e6a5 100644 --- a/src/test/regress/expected/enum.out +++ b/src/test/regress/expected/enum.out @@ -2,6 +2,10 @@ -- Enum tests -- CREATE TYPE rainbow AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'purple'); +CREATE TYPE rainbow AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'purple'); +ERROR: type "rainbow" already exists +CREATE TYPE IF NOT EXISTS rainbow AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'purple'); +NOTICE: type "rainbow" already exists, skipping -- -- Did it create the right number of rows? -- diff --git a/src/test/regress/expected/rangetypes.out b/src/test/regress/expected/rangetypes.out index 39db992..7397498 100644 --- a/src/test/regress/expected/rangetypes.out +++ b/src/test/regress/expected/rangetypes.out @@ -1,5 +1,9 @@ -- Tests for range data types. create type textrange as range (subtype=text, collation="C"); +create type textrange as range (subtype=text, collation="C"); +ERROR: type "textrange" already exists +create type if not exists textrange as range (subtype=text, collation="C"); +NOTICE: type "textrange" already exists, skipping -- -- test input parser -- diff --git a/src/test/regress/expected/tsdicts.out b/src/test/regress/expected/tsdicts.out index 9df1434..3214609 100644 --- a/src/test/regress/expected/tsdicts.out +++ b/src/test/regress/expected/tsdicts.out @@ -5,6 +5,18 @@ CREATE TEXT SEARCH DICTIONARY ispell ( DictFile=ispell_sample, AffFile=ispell_sample ); +CREATE TEXT SEARCH DICTIONARY ispell ( + Template=ispell, + DictFile=ispell_sample, + AffFile=ispell_sample +); +ERROR: text search dictionary "public"."ispell" already exists +CREATE TEXT SEARCH DICTIONARY IF NOT EXISTS ispell ( + Template=ispell, + DictFile=ispell_sample, + AffFile=ispell_sample +); +NOTICE: text search dictionary "public"."ispell" already exists, skipping SELECT ts_lexize('ispell', 'skies'); ts_lexize ----------- @@ -232,6 +244,14 @@ SELECT ts_lexize('thesaurus', 'one'); CREATE TEXT SEARCH CONFIGURATION ispell_tst ( COPY=english ); +CREATE TEXT SEARCH CONFIGURATION ispell_tst ( + COPY=english +); +ERROR: text search configuration "public"."ispell_tst" already exists +CREATE TEXT SEARCH CONFIGURATION IF NOT EXISTS ispell_tst ( + COPY=english +); +NOTICE: text search configuration "public"."ispell_tst" already exists, skipping ALTER TEXT SEARCH CONFIGURATION ispell_tst ALTER MAPPING FOR word, numword, asciiword, hword, numhword, asciihword, hword_part, hword_numpart, hword_asciipart WITH ispell, english_stem; diff --git a/src/test/regress/sql/alter_generic.sql b/src/test/regress/sql/alter_generic.sql index 04c5cc1..2ee71f9 100644 --- a/src/test/regress/sql/alter_generic.sql +++ b/src/test/regress/sql/alter_generic.sql @@ -334,6 +334,8 @@ SELECT nspname, cfgname, rolname -- Text Search Template -- CREATE TEXT SEARCH TEMPLATE alt_ts_temp1 (lexize=dsimple_lexize); +CREATE TEXT SEARCH TEMPLATE alt_ts_temp1 (lexize=dsimple_lexize); +CREATE TEXT SEARCH TEMPLATE IF NOT EXISTS alt_ts_temp1 (lexize=dsimple_lexize); CREATE TEXT SEARCH TEMPLATE alt_ts_temp2 (lexize=dsimple_lexize); ALTER TEXT SEARCH TEMPLATE alt_ts_temp1 RENAME TO alt_ts_temp2; -- failed (name conflict) @@ -354,6 +356,10 @@ SELECT nspname, tmplname CREATE TEXT SEARCH PARSER alt_ts_prs1 (start = prsd_start, gettoken = prsd_nexttoken, end = prsd_end, lextypes = prsd_lextype); +CREATE TEXT SEARCH PARSER alt_ts_prs1 + (start = prsd_start, gettoken = prsd_nexttoken, end = prsd_end, lextypes = prsd_lextype); +CREATE TEXT SEARCH PARSER IF NOT EXISTS alt_ts_prs1 + (start = prsd_start, gettoken = prsd_nexttoken, end = prsd_end, lextypes = prsd_lextype); CREATE TEXT SEARCH PARSER alt_ts_prs2 (start = prsd_start, gettoken = prsd_nexttoken, end = prsd_end, lextypes = prsd_lextype); diff --git a/src/test/regress/sql/create_aggregate.sql b/src/test/regress/sql/create_aggregate.sql index 84f9a4f..2d58c85 100644 --- a/src/test/regress/sql/create_aggregate.sql +++ b/src/test/regress/sql/create_aggregate.sql @@ -14,6 +14,18 @@ COMMENT ON AGGREGATE newavg_wrong (int4) IS 'an agg comment'; COMMENT ON AGGREGATE newavg (int4) IS 'an agg comment'; COMMENT ON AGGREGATE newavg (int4) IS NULL; +-- test IF NOT EXISTS +CREATE AGGREGATE newavg ( + sfunc = int4_avg_accum, basetype = int4, stype = _int8, + finalfunc = int8_avg, + initcond1 = '{0,0}' +); +CREATE AGGREGATE IF NOT EXISTS newavg ( + sfunc = int4_avg_accum, basetype = int4, stype = _int8, + finalfunc = int8_avg, + initcond1 = '{0,0}' +); + -- without finalfunc; test obsolete spellings 'sfunc1' etc CREATE AGGREGATE newsum ( sfunc1 = int4pl, basetype = int4, stype1 = int4, diff --git a/src/test/regress/sql/create_cast.sql b/src/test/regress/sql/create_cast.sql index ad348da..ec9e266 100644 --- a/src/test/regress/sql/create_cast.sql +++ b/src/test/regress/sql/create_cast.sql @@ -29,18 +29,24 @@ SELECT casttestfunc('foo'::text); -- fails, as there's no cast -- Try binary coercion cast CREATE CAST (text AS casttesttype) WITHOUT FUNCTION; +CREATE CAST (text AS casttesttype) WITHOUT FUNCTION; +CREATE CAST IF NOT EXISTS (text AS casttesttype) WITHOUT FUNCTION; SELECT casttestfunc('foo'::text); -- doesn't work, as the cast is explicit SELECT casttestfunc('foo'::text::casttesttype); -- should work DROP CAST (text AS casttesttype); -- cleanup -- Try IMPLICIT binary coercion cast CREATE CAST (text AS casttesttype) WITHOUT FUNCTION AS IMPLICIT; +CREATE CAST (text AS casttesttype) WITHOUT FUNCTION AS IMPLICIT; +CREATE CAST IF NOT EXISTS (text AS casttesttype) WITHOUT FUNCTION AS IMPLICIT; SELECT casttestfunc('foo'::text); -- Should work now -- Try I/O conversion cast. SELECT 1234::int4::casttesttype; -- No cast yet, should fail CREATE CAST (int4 AS casttesttype) WITH INOUT; +CREATE CAST (int4 AS casttesttype) WITH INOUT; +CREATE CAST IF NOT EXISTS (int4 AS casttesttype) WITH INOUT; SELECT 1234::int4::casttesttype; -- Should work now DROP CAST (int4 AS casttesttype); @@ -51,4 +57,6 @@ CREATE FUNCTION int4_casttesttype(int4) RETURNS casttesttype LANGUAGE SQL AS $$ SELECT ('foo'::text || $1::text)::casttesttype; $$; CREATE CAST (int4 AS casttesttype) WITH FUNCTION int4_casttesttype(int4) AS IMPLICIT; +CREATE CAST (int4 AS casttesttype) WITH FUNCTION int4_casttesttype(int4) AS IMPLICIT; +CREATE CAST IF NOT EXISTS (int4 AS casttesttype) WITH FUNCTION int4_casttesttype(int4) AS IMPLICIT; SELECT 1234::int4::casttesttype; -- Should work now diff --git a/src/test/regress/sql/create_operator.sql b/src/test/regress/sql/create_operator.sql index dcad804..002474b 100644 --- a/src/test/regress/sql/create_operator.sql +++ b/src/test/regress/sql/create_operator.sql @@ -1,14 +1,24 @@ -- -- CREATE_OPERATOR -- - CREATE OPERATOR ## ( leftarg = path, rightarg = path, procedure = path_inter, commutator = ## ); - +CREATE OPERATOR ## ( + leftarg = path, + rightarg = path, + procedure = path_inter, + commutator = ## +); +CREATE OPERATOR IF NOT EXISTS ## ( + leftarg = path, + rightarg = path, + procedure = path_inter, + commutator = ## +); CREATE OPERATOR <% ( leftarg = point, rightarg = widget, @@ -16,21 +26,17 @@ CREATE OPERATOR <% ( commutator = >% , negator = >=% ); - CREATE OPERATOR @#@ ( rightarg = int8, -- left unary procedure = numeric_fac ); - CREATE OPERATOR #@# ( leftarg = int8, -- right unary procedure = numeric_fac ); - CREATE OPERATOR #%# ( leftarg = int8, -- right unary procedure = numeric_fac ); - -- Test comments COMMENT ON OPERATOR ###### (int4, NONE) IS 'bad right unary'; diff --git a/src/test/regress/sql/create_type.sql b/src/test/regress/sql/create_type.sql index a4906b6..79e0181 100644 --- a/src/test/regress/sql/create_type.sql +++ b/src/test/regress/sql/create_type.sql @@ -16,6 +16,24 @@ CREATE TYPE widget ( alignment = double ); +CREATE TYPE widget ( + internallength = 24, + input = widget_in, + output = widget_out, + typmod_in = numerictypmodin, + typmod_out = numerictypmodout, + alignment = double +); + +CREATE TYPE IF NOT EXISTS widget ( + internallength = 24, + input = widget_in, + output = widget_out, + typmod_in = numerictypmodin, + typmod_out = numerictypmodout, + alignment = double +); + CREATE TYPE city_budget ( internallength = 16, input = int44in, @@ -28,6 +46,7 @@ CREATE TYPE city_budget ( -- Test creation and destruction of shell types CREATE TYPE shell; CREATE TYPE shell; -- fail, type already present +CREATE TYPE IF NOT EXISTS shell; -- do not fail, just skip DROP TYPE shell; DROP TYPE shell; -- fail, type not exist @@ -85,6 +104,10 @@ SELECT * FROM default_test; CREATE TYPE default_test_row AS (f1 text_w_default, f2 int42); +CREATE TYPE default_test_row AS (f1 text_w_default, f2 int42); + +CREATE TYPE IF NOT EXISTS default_test_row AS (f1 text_w_default, f2 int42); + CREATE FUNCTION get_default_test() RETURNS SETOF default_test_row AS ' SELECT * FROM default_test; ' LANGUAGE SQL; diff --git a/src/test/regress/sql/enum.sql b/src/test/regress/sql/enum.sql index 88a835e..4f9ebb7 100644 --- a/src/test/regress/sql/enum.sql +++ b/src/test/regress/sql/enum.sql @@ -4,6 +4,10 @@ CREATE TYPE rainbow AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'purple'); +CREATE TYPE rainbow AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'purple'); + +CREATE TYPE IF NOT EXISTS rainbow AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'purple'); + -- -- Did it create the right number of rows? -- diff --git a/src/test/regress/sql/rangetypes.sql b/src/test/regress/sql/rangetypes.sql index fad843a..32d5b95 100644 --- a/src/test/regress/sql/rangetypes.sql +++ b/src/test/regress/sql/rangetypes.sql @@ -1,6 +1,8 @@ -- Tests for range data types. create type textrange as range (subtype=text, collation="C"); +create type textrange as range (subtype=text, collation="C"); +create type if not exists textrange as range (subtype=text, collation="C"); -- -- test input parser diff --git a/src/test/regress/sql/tsdicts.sql b/src/test/regress/sql/tsdicts.sql index 55afcec..2f66006 100644 --- a/src/test/regress/sql/tsdicts.sql +++ b/src/test/regress/sql/tsdicts.sql @@ -6,6 +6,16 @@ CREATE TEXT SEARCH DICTIONARY ispell ( DictFile=ispell_sample, AffFile=ispell_sample ); +CREATE TEXT SEARCH DICTIONARY ispell ( + Template=ispell, + DictFile=ispell_sample, + AffFile=ispell_sample +); +CREATE TEXT SEARCH DICTIONARY IF NOT EXISTS ispell ( + Template=ispell, + DictFile=ispell_sample, + AffFile=ispell_sample +); SELECT ts_lexize('ispell', 'skies'); SELECT ts_lexize('ispell', 'bookings'); @@ -73,6 +83,12 @@ SELECT ts_lexize('thesaurus', 'one'); CREATE TEXT SEARCH CONFIGURATION ispell_tst ( COPY=english ); +CREATE TEXT SEARCH CONFIGURATION ispell_tst ( + COPY=english +); +CREATE TEXT SEARCH CONFIGURATION IF NOT EXISTS ispell_tst ( + COPY=english +); ALTER TEXT SEARCH CONFIGURATION ispell_tst ALTER MAPPING FOR word, numword, asciiword, hword, numhword, asciihword, hword_part, hword_numpart, hword_asciipart