Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * pg_collation.c
4 : * routines to support manipulation of the pg_collation relation
5 : *
6 : * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : *
10 : * IDENTIFICATION
11 : * src/backend/catalog/pg_collation.c
12 : *
13 : *-------------------------------------------------------------------------
14 : */
15 : #include "postgres.h"
16 :
17 : #include "access/genam.h"
18 : #include "access/htup_details.h"
19 : #include "access/sysattr.h"
20 : #include "access/table.h"
21 : #include "catalog/catalog.h"
22 : #include "catalog/dependency.h"
23 : #include "catalog/indexing.h"
24 : #include "catalog/objectaccess.h"
25 : #include "catalog/pg_collation.h"
26 : #include "catalog/pg_namespace.h"
27 : #include "mb/pg_wchar.h"
28 : #include "utils/builtins.h"
29 : #include "utils/fmgroids.h"
30 : #include "utils/pg_locale.h"
31 : #include "utils/rel.h"
32 : #include "utils/syscache.h"
33 :
34 :
35 : /*
36 : * CollationCreate
37 : *
38 : * Add a new tuple to pg_collation.
39 : *
40 : * if_not_exists: if true, don't fail on duplicate name, just print a notice
41 : * and return InvalidOid.
42 : * quiet: if true, don't fail on duplicate name, just silently return
43 : * InvalidOid (overrides if_not_exists).
44 : */
45 : Oid
46 47318 : CollationCreate(const char *collname, Oid collnamespace,
47 : Oid collowner,
48 : char collprovider,
49 : bool collisdeterministic,
50 : int32 collencoding,
51 : const char *collcollate, const char *collctype,
52 : const char *colliculocale,
53 : const char *collicurules,
54 : const char *collversion,
55 : bool if_not_exists,
56 : bool quiet)
57 : {
58 : Relation rel;
59 : TupleDesc tupDesc;
60 : HeapTuple tup;
61 : Datum values[Natts_pg_collation];
62 : bool nulls[Natts_pg_collation];
63 : NameData name_name;
64 : Oid oid;
65 : ObjectAddress myself,
66 : referenced;
67 :
68 : Assert(collname);
69 : Assert(collnamespace);
70 : Assert(collowner);
71 : Assert((collcollate && collctype) || colliculocale);
72 :
73 : /*
74 : * Make sure there is no existing collation of same name & encoding.
75 : *
76 : * This would be caught by the unique index anyway; we're just giving a
77 : * friendlier error message. The unique index provides a backstop against
78 : * race conditions.
79 : */
80 47318 : oid = GetSysCacheOid3(COLLNAMEENCNSP,
81 : Anum_pg_collation_oid,
82 : PointerGetDatum(collname),
83 : Int32GetDatum(collencoding),
84 : ObjectIdGetDatum(collnamespace));
85 47318 : if (OidIsValid(oid))
86 : {
87 6 : if (quiet)
88 0 : return InvalidOid;
89 6 : else if (if_not_exists)
90 : {
91 : /*
92 : * If we are in an extension script, insist that the pre-existing
93 : * object be a member of the extension, to avoid security risks.
94 : */
95 4 : ObjectAddressSet(myself, CollationRelationId, oid);
96 4 : checkMembershipInCurrentExtension(&myself);
97 :
98 : /* OK to skip */
99 2 : ereport(NOTICE,
100 : (errcode(ERRCODE_DUPLICATE_OBJECT),
101 : collencoding == -1
102 : ? errmsg("collation \"%s\" already exists, skipping",
103 : collname)
104 : : errmsg("collation \"%s\" for encoding \"%s\" already exists, skipping",
105 : collname, pg_encoding_to_char(collencoding))));
106 2 : return InvalidOid;
107 : }
108 : else
109 2 : ereport(ERROR,
110 : (errcode(ERRCODE_DUPLICATE_OBJECT),
111 : collencoding == -1
112 : ? errmsg("collation \"%s\" already exists",
113 : collname)
114 : : errmsg("collation \"%s\" for encoding \"%s\" already exists",
115 : collname, pg_encoding_to_char(collencoding))));
116 : }
117 :
118 : /* open pg_collation; see below about the lock level */
119 47312 : rel = table_open(CollationRelationId, ShareRowExclusiveLock);
120 :
121 : /*
122 : * Also forbid a specific-encoding collation shadowing an any-encoding
123 : * collation, or an any-encoding collation being shadowed (see
124 : * get_collation_name()). This test is not backed up by the unique index,
125 : * so we take a ShareRowExclusiveLock earlier, to protect against
126 : * concurrent changes fooling this check.
127 : */
128 47312 : if (collencoding == -1)
129 47048 : oid = GetSysCacheOid3(COLLNAMEENCNSP,
130 : Anum_pg_collation_oid,
131 : PointerGetDatum(collname),
132 : Int32GetDatum(GetDatabaseEncoding()),
133 : ObjectIdGetDatum(collnamespace));
134 : else
135 264 : oid = GetSysCacheOid3(COLLNAMEENCNSP,
136 : Anum_pg_collation_oid,
137 : PointerGetDatum(collname),
138 : Int32GetDatum(-1),
139 : ObjectIdGetDatum(collnamespace));
140 47312 : if (OidIsValid(oid))
141 : {
142 60 : if (quiet)
143 : {
144 60 : table_close(rel, NoLock);
145 60 : return InvalidOid;
146 : }
147 0 : else if (if_not_exists)
148 : {
149 : /*
150 : * If we are in an extension script, insist that the pre-existing
151 : * object be a member of the extension, to avoid security risks.
152 : */
153 0 : ObjectAddressSet(myself, CollationRelationId, oid);
154 0 : checkMembershipInCurrentExtension(&myself);
155 :
156 : /* OK to skip */
157 0 : table_close(rel, NoLock);
158 0 : ereport(NOTICE,
159 : (errcode(ERRCODE_DUPLICATE_OBJECT),
160 : errmsg("collation \"%s\" already exists, skipping",
161 : collname)));
162 0 : return InvalidOid;
163 : }
164 : else
165 0 : ereport(ERROR,
166 : (errcode(ERRCODE_DUPLICATE_OBJECT),
167 : errmsg("collation \"%s\" already exists",
168 : collname)));
169 : }
170 :
171 47252 : tupDesc = RelationGetDescr(rel);
172 :
173 : /* form a tuple */
174 47252 : memset(nulls, 0, sizeof(nulls));
175 :
176 47252 : namestrcpy(&name_name, collname);
177 47252 : oid = GetNewOidWithIndex(rel, CollationOidIndexId,
178 : Anum_pg_collation_oid);
179 47252 : values[Anum_pg_collation_oid - 1] = ObjectIdGetDatum(oid);
180 47252 : values[Anum_pg_collation_collname - 1] = NameGetDatum(&name_name);
181 47252 : values[Anum_pg_collation_collnamespace - 1] = ObjectIdGetDatum(collnamespace);
182 47252 : values[Anum_pg_collation_collowner - 1] = ObjectIdGetDatum(collowner);
183 47252 : values[Anum_pg_collation_collprovider - 1] = CharGetDatum(collprovider);
184 47252 : values[Anum_pg_collation_collisdeterministic - 1] = BoolGetDatum(collisdeterministic);
185 47252 : values[Anum_pg_collation_collencoding - 1] = Int32GetDatum(collencoding);
186 47252 : if (collcollate)
187 222 : values[Anum_pg_collation_collcollate - 1] = CStringGetTextDatum(collcollate);
188 : else
189 47030 : nulls[Anum_pg_collation_collcollate - 1] = true;
190 47252 : if (collctype)
191 222 : values[Anum_pg_collation_collctype - 1] = CStringGetTextDatum(collctype);
192 : else
193 47030 : nulls[Anum_pg_collation_collctype - 1] = true;
194 47252 : if (colliculocale)
195 47030 : values[Anum_pg_collation_colliculocale - 1] = CStringGetTextDatum(colliculocale);
196 : else
197 222 : nulls[Anum_pg_collation_colliculocale - 1] = true;
198 47252 : if (collicurules)
199 4 : values[Anum_pg_collation_collicurules - 1] = CStringGetTextDatum(collicurules);
200 : else
201 47248 : nulls[Anum_pg_collation_collicurules - 1] = true;
202 47252 : if (collversion)
203 47150 : values[Anum_pg_collation_collversion - 1] = CStringGetTextDatum(collversion);
204 : else
205 102 : nulls[Anum_pg_collation_collversion - 1] = true;
206 :
207 47252 : tup = heap_form_tuple(tupDesc, values, nulls);
208 :
209 : /* insert a new tuple */
210 47252 : CatalogTupleInsert(rel, tup);
211 : Assert(OidIsValid(oid));
212 :
213 : /* set up dependencies for the new collation */
214 47252 : myself.classId = CollationRelationId;
215 47252 : myself.objectId = oid;
216 47252 : myself.objectSubId = 0;
217 :
218 : /* create dependency on namespace */
219 47252 : referenced.classId = NamespaceRelationId;
220 47252 : referenced.objectId = collnamespace;
221 47252 : referenced.objectSubId = 0;
222 47252 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
223 :
224 : /* create dependency on owner */
225 47252 : recordDependencyOnOwner(CollationRelationId, oid, collowner);
226 :
227 : /* dependency on extension */
228 47252 : recordDependencyOnCurrentExtension(&myself, false);
229 :
230 : /* Post creation hook for new collation */
231 47252 : InvokeObjectPostCreateHook(CollationRelationId, oid, 0);
232 :
233 47252 : heap_freetuple(tup);
234 47252 : table_close(rel, NoLock);
235 :
236 47252 : return oid;
237 : }
|