1 | /*------------------------------------------------------------------------- |
2 | * |
3 | * pg_collation.c |
4 | * routines to support manipulation of the pg_collation relation |
5 | * |
6 | * Portions Copyright (c) 1996-2019, 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 | 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 *collversion, |
53 | bool if_not_exists, |
54 | bool quiet) |
55 | { |
56 | Relation rel; |
57 | TupleDesc tupDesc; |
58 | HeapTuple tup; |
59 | Datum values[Natts_pg_collation]; |
60 | bool nulls[Natts_pg_collation]; |
61 | NameData name_name, |
62 | name_collate, |
63 | name_ctype; |
64 | Oid oid; |
65 | ObjectAddress myself, |
66 | referenced; |
67 | |
68 | AssertArg(collname); |
69 | AssertArg(collnamespace); |
70 | AssertArg(collowner); |
71 | AssertArg(collcollate); |
72 | AssertArg(collctype); |
73 | |
74 | /* |
75 | * Make sure there is no existing collation of same name & encoding. |
76 | * |
77 | * This would be caught by the unique index anyway; we're just giving a |
78 | * friendlier error message. The unique index provides a backstop against |
79 | * race conditions. |
80 | */ |
81 | if (SearchSysCacheExists3(COLLNAMEENCNSP, |
82 | PointerGetDatum(collname), |
83 | Int32GetDatum(collencoding), |
84 | ObjectIdGetDatum(collnamespace))) |
85 | { |
86 | if (quiet) |
87 | return InvalidOid; |
88 | else if (if_not_exists) |
89 | { |
90 | ereport(NOTICE, |
91 | (errcode(ERRCODE_DUPLICATE_OBJECT), |
92 | collencoding == -1 |
93 | ? errmsg("collation \"%s\" already exists, skipping" , |
94 | collname) |
95 | : errmsg("collation \"%s\" for encoding \"%s\" already exists, skipping" , |
96 | collname, pg_encoding_to_char(collencoding)))); |
97 | return InvalidOid; |
98 | } |
99 | else |
100 | ereport(ERROR, |
101 | (errcode(ERRCODE_DUPLICATE_OBJECT), |
102 | collencoding == -1 |
103 | ? errmsg("collation \"%s\" already exists" , |
104 | collname) |
105 | : errmsg("collation \"%s\" for encoding \"%s\" already exists" , |
106 | collname, pg_encoding_to_char(collencoding)))); |
107 | } |
108 | |
109 | /* open pg_collation; see below about the lock level */ |
110 | rel = table_open(CollationRelationId, ShareRowExclusiveLock); |
111 | |
112 | /* |
113 | * Also forbid a specific-encoding collation shadowing an any-encoding |
114 | * collation, or an any-encoding collation being shadowed (see |
115 | * get_collation_name()). This test is not backed up by the unique index, |
116 | * so we take a ShareRowExclusiveLock earlier, to protect against |
117 | * concurrent changes fooling this check. |
118 | */ |
119 | if ((collencoding == -1 && |
120 | SearchSysCacheExists3(COLLNAMEENCNSP, |
121 | PointerGetDatum(collname), |
122 | Int32GetDatum(GetDatabaseEncoding()), |
123 | ObjectIdGetDatum(collnamespace))) || |
124 | (collencoding != -1 && |
125 | SearchSysCacheExists3(COLLNAMEENCNSP, |
126 | PointerGetDatum(collname), |
127 | Int32GetDatum(-1), |
128 | ObjectIdGetDatum(collnamespace)))) |
129 | { |
130 | if (quiet) |
131 | { |
132 | table_close(rel, NoLock); |
133 | return InvalidOid; |
134 | } |
135 | else if (if_not_exists) |
136 | { |
137 | table_close(rel, NoLock); |
138 | ereport(NOTICE, |
139 | (errcode(ERRCODE_DUPLICATE_OBJECT), |
140 | errmsg("collation \"%s\" already exists, skipping" , |
141 | collname))); |
142 | return InvalidOid; |
143 | } |
144 | else |
145 | ereport(ERROR, |
146 | (errcode(ERRCODE_DUPLICATE_OBJECT), |
147 | errmsg("collation \"%s\" already exists" , |
148 | collname))); |
149 | } |
150 | |
151 | tupDesc = RelationGetDescr(rel); |
152 | |
153 | /* form a tuple */ |
154 | memset(nulls, 0, sizeof(nulls)); |
155 | |
156 | namestrcpy(&name_name, collname); |
157 | oid = GetNewOidWithIndex(rel, CollationOidIndexId, |
158 | Anum_pg_collation_oid); |
159 | values[Anum_pg_collation_oid - 1] = ObjectIdGetDatum(oid); |
160 | values[Anum_pg_collation_collname - 1] = NameGetDatum(&name_name); |
161 | values[Anum_pg_collation_collnamespace - 1] = ObjectIdGetDatum(collnamespace); |
162 | values[Anum_pg_collation_collowner - 1] = ObjectIdGetDatum(collowner); |
163 | values[Anum_pg_collation_collprovider - 1] = CharGetDatum(collprovider); |
164 | values[Anum_pg_collation_collisdeterministic - 1] = BoolGetDatum(collisdeterministic); |
165 | values[Anum_pg_collation_collencoding - 1] = Int32GetDatum(collencoding); |
166 | namestrcpy(&name_collate, collcollate); |
167 | values[Anum_pg_collation_collcollate - 1] = NameGetDatum(&name_collate); |
168 | namestrcpy(&name_ctype, collctype); |
169 | values[Anum_pg_collation_collctype - 1] = NameGetDatum(&name_ctype); |
170 | if (collversion) |
171 | values[Anum_pg_collation_collversion - 1] = CStringGetTextDatum(collversion); |
172 | else |
173 | nulls[Anum_pg_collation_collversion - 1] = true; |
174 | |
175 | tup = heap_form_tuple(tupDesc, values, nulls); |
176 | |
177 | /* insert a new tuple */ |
178 | CatalogTupleInsert(rel, tup); |
179 | Assert(OidIsValid(oid)); |
180 | |
181 | /* set up dependencies for the new collation */ |
182 | myself.classId = CollationRelationId; |
183 | myself.objectId = oid; |
184 | myself.objectSubId = 0; |
185 | |
186 | /* create dependency on namespace */ |
187 | referenced.classId = NamespaceRelationId; |
188 | referenced.objectId = collnamespace; |
189 | referenced.objectSubId = 0; |
190 | recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); |
191 | |
192 | /* create dependency on owner */ |
193 | recordDependencyOnOwner(CollationRelationId, oid, collowner); |
194 | |
195 | /* dependency on extension */ |
196 | recordDependencyOnCurrentExtension(&myself, false); |
197 | |
198 | /* Post creation hook for new collation */ |
199 | InvokeObjectPostCreateHook(CollationRelationId, oid, 0); |
200 | |
201 | heap_freetuple(tup); |
202 | table_close(rel, NoLock); |
203 | |
204 | return oid; |
205 | } |
206 | |
207 | /* |
208 | * RemoveCollationById |
209 | * |
210 | * Remove a tuple from pg_collation by Oid. This function is solely |
211 | * called inside catalog/dependency.c |
212 | */ |
213 | void |
214 | RemoveCollationById(Oid collationOid) |
215 | { |
216 | Relation rel; |
217 | ScanKeyData scanKeyData; |
218 | SysScanDesc scandesc; |
219 | HeapTuple tuple; |
220 | |
221 | rel = table_open(CollationRelationId, RowExclusiveLock); |
222 | |
223 | ScanKeyInit(&scanKeyData, |
224 | Anum_pg_collation_oid, |
225 | BTEqualStrategyNumber, F_OIDEQ, |
226 | ObjectIdGetDatum(collationOid)); |
227 | |
228 | scandesc = systable_beginscan(rel, CollationOidIndexId, true, |
229 | NULL, 1, &scanKeyData); |
230 | |
231 | tuple = systable_getnext(scandesc); |
232 | |
233 | if (HeapTupleIsValid(tuple)) |
234 | CatalogTupleDelete(rel, &tuple->t_self); |
235 | else |
236 | elog(ERROR, "could not find tuple for collation %u" , collationOid); |
237 | |
238 | systable_endscan(scandesc); |
239 | |
240 | table_close(rel, RowExclusiveLock); |
241 | } |
242 | |