diff options
Diffstat (limited to 'src/backend/catalog/pg_collation.c')
-rw-r--r-- | src/backend/catalog/pg_collation.c | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/src/backend/catalog/pg_collation.c b/src/backend/catalog/pg_collation.c new file mode 100644 index 0000000..6081bf5 --- /dev/null +++ b/src/backend/catalog/pg_collation.c @@ -0,0 +1,232 @@ +/*------------------------------------------------------------------------- + * + * pg_collation.c + * routines to support manipulation of the pg_collation relation + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/catalog/pg_collation.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/htup_details.h" +#include "access/sysattr.h" +#include "access/table.h" +#include "catalog/catalog.h" +#include "catalog/dependency.h" +#include "catalog/indexing.h" +#include "catalog/objectaccess.h" +#include "catalog/pg_collation.h" +#include "catalog/pg_namespace.h" +#include "mb/pg_wchar.h" +#include "utils/builtins.h" +#include "utils/fmgroids.h" +#include "utils/pg_locale.h" +#include "utils/rel.h" +#include "utils/syscache.h" + + +/* + * CollationCreate + * + * Add a new tuple to pg_collation. + * + * if_not_exists: if true, don't fail on duplicate name, just print a notice + * and return InvalidOid. + * quiet: if true, don't fail on duplicate name, just silently return + * InvalidOid (overrides if_not_exists). + */ +Oid +CollationCreate(const char *collname, Oid collnamespace, + Oid collowner, + char collprovider, + bool collisdeterministic, + int32 collencoding, + const char *collcollate, const char *collctype, + const char *colliculocale, + const char *collversion, + bool if_not_exists, + bool quiet) +{ + Relation rel; + TupleDesc tupDesc; + HeapTuple tup; + Datum values[Natts_pg_collation]; + bool nulls[Natts_pg_collation]; + NameData name_name; + Oid oid; + ObjectAddress myself, + referenced; + + AssertArg(collname); + AssertArg(collnamespace); + AssertArg(collowner); + AssertArg((collcollate && collctype) || colliculocale); + + /* + * Make sure there is no existing collation of same name & encoding. + * + * This would be caught by the unique index anyway; we're just giving a + * friendlier error message. The unique index provides a backstop against + * race conditions. + */ + oid = GetSysCacheOid3(COLLNAMEENCNSP, + Anum_pg_collation_oid, + PointerGetDatum(collname), + Int32GetDatum(collencoding), + ObjectIdGetDatum(collnamespace)); + if (OidIsValid(oid)) + { + if (quiet) + return InvalidOid; + else if (if_not_exists) + { + /* + * If we are in an extension script, insist that the pre-existing + * object be a member of the extension, to avoid security risks. + */ + ObjectAddressSet(myself, CollationRelationId, oid); + checkMembershipInCurrentExtension(&myself); + + /* OK to skip */ + ereport(NOTICE, + (errcode(ERRCODE_DUPLICATE_OBJECT), + collencoding == -1 + ? errmsg("collation \"%s\" already exists, skipping", + collname) + : errmsg("collation \"%s\" for encoding \"%s\" already exists, skipping", + collname, pg_encoding_to_char(collencoding)))); + return InvalidOid; + } + else + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + collencoding == -1 + ? errmsg("collation \"%s\" already exists", + collname) + : errmsg("collation \"%s\" for encoding \"%s\" already exists", + collname, pg_encoding_to_char(collencoding)))); + } + + /* open pg_collation; see below about the lock level */ + rel = table_open(CollationRelationId, ShareRowExclusiveLock); + + /* + * Also forbid a specific-encoding collation shadowing an any-encoding + * collation, or an any-encoding collation being shadowed (see + * get_collation_name()). This test is not backed up by the unique index, + * so we take a ShareRowExclusiveLock earlier, to protect against + * concurrent changes fooling this check. + */ + if (collencoding == -1) + oid = GetSysCacheOid3(COLLNAMEENCNSP, + Anum_pg_collation_oid, + PointerGetDatum(collname), + Int32GetDatum(GetDatabaseEncoding()), + ObjectIdGetDatum(collnamespace)); + else + oid = GetSysCacheOid3(COLLNAMEENCNSP, + Anum_pg_collation_oid, + PointerGetDatum(collname), + Int32GetDatum(-1), + ObjectIdGetDatum(collnamespace)); + if (OidIsValid(oid)) + { + if (quiet) + { + table_close(rel, NoLock); + return InvalidOid; + } + else if (if_not_exists) + { + /* + * If we are in an extension script, insist that the pre-existing + * object be a member of the extension, to avoid security risks. + */ + ObjectAddressSet(myself, CollationRelationId, oid); + checkMembershipInCurrentExtension(&myself); + + /* OK to skip */ + table_close(rel, NoLock); + ereport(NOTICE, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("collation \"%s\" already exists, skipping", + collname))); + return InvalidOid; + } + else + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("collation \"%s\" already exists", + collname))); + } + + tupDesc = RelationGetDescr(rel); + + /* form a tuple */ + memset(nulls, 0, sizeof(nulls)); + + namestrcpy(&name_name, collname); + oid = GetNewOidWithIndex(rel, CollationOidIndexId, + Anum_pg_collation_oid); + values[Anum_pg_collation_oid - 1] = ObjectIdGetDatum(oid); + values[Anum_pg_collation_collname - 1] = NameGetDatum(&name_name); + values[Anum_pg_collation_collnamespace - 1] = ObjectIdGetDatum(collnamespace); + values[Anum_pg_collation_collowner - 1] = ObjectIdGetDatum(collowner); + values[Anum_pg_collation_collprovider - 1] = CharGetDatum(collprovider); + values[Anum_pg_collation_collisdeterministic - 1] = BoolGetDatum(collisdeterministic); + values[Anum_pg_collation_collencoding - 1] = Int32GetDatum(collencoding); + if (collcollate) + values[Anum_pg_collation_collcollate - 1] = CStringGetTextDatum(collcollate); + else + nulls[Anum_pg_collation_collcollate - 1] = true; + if (collctype) + values[Anum_pg_collation_collctype - 1] = CStringGetTextDatum(collctype); + else + nulls[Anum_pg_collation_collctype - 1] = true; + if (colliculocale) + values[Anum_pg_collation_colliculocale - 1] = CStringGetTextDatum(colliculocale); + else + nulls[Anum_pg_collation_colliculocale - 1] = true; + if (collversion) + values[Anum_pg_collation_collversion - 1] = CStringGetTextDatum(collversion); + else + nulls[Anum_pg_collation_collversion - 1] = true; + + tup = heap_form_tuple(tupDesc, values, nulls); + + /* insert a new tuple */ + CatalogTupleInsert(rel, tup); + Assert(OidIsValid(oid)); + + /* set up dependencies for the new collation */ + myself.classId = CollationRelationId; + myself.objectId = oid; + myself.objectSubId = 0; + + /* create dependency on namespace */ + referenced.classId = NamespaceRelationId; + referenced.objectId = collnamespace; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + + /* create dependency on owner */ + recordDependencyOnOwner(CollationRelationId, oid, collowner); + + /* dependency on extension */ + recordDependencyOnCurrentExtension(&myself, false); + + /* Post creation hook for new collation */ + InvokeObjectPostCreateHook(CollationRelationId, oid, 0); + + heap_freetuple(tup); + table_close(rel, NoLock); + + return oid; +} |