1 | /*------------------------------------------------------------------------- |
2 | * |
3 | * pg_conversion.c |
4 | * routines to support manipulation of the pg_conversion 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_conversion.c |
12 | * |
13 | *------------------------------------------------------------------------- |
14 | */ |
15 | #include "postgres.h" |
16 | |
17 | #include "access/heapam.h" |
18 | #include "access/htup_details.h" |
19 | #include "access/sysattr.h" |
20 | #include "access/tableam.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_conversion.h" |
26 | #include "catalog/pg_namespace.h" |
27 | #include "catalog/pg_proc.h" |
28 | #include "mb/pg_wchar.h" |
29 | #include "utils/builtins.h" |
30 | #include "utils/catcache.h" |
31 | #include "utils/fmgroids.h" |
32 | #include "utils/rel.h" |
33 | #include "utils/syscache.h" |
34 | |
35 | /* |
36 | * ConversionCreate |
37 | * |
38 | * Add a new tuple to pg_conversion. |
39 | */ |
40 | ObjectAddress |
41 | ConversionCreate(const char *conname, Oid connamespace, |
42 | Oid conowner, |
43 | int32 conforencoding, int32 contoencoding, |
44 | Oid conproc, bool def) |
45 | { |
46 | int i; |
47 | Relation rel; |
48 | TupleDesc tupDesc; |
49 | HeapTuple tup; |
50 | Oid oid; |
51 | bool nulls[Natts_pg_conversion]; |
52 | Datum values[Natts_pg_conversion]; |
53 | NameData cname; |
54 | ObjectAddress myself, |
55 | referenced; |
56 | |
57 | /* sanity checks */ |
58 | if (!conname) |
59 | elog(ERROR, "no conversion name supplied" ); |
60 | |
61 | /* make sure there is no existing conversion of same name */ |
62 | if (SearchSysCacheExists2(CONNAMENSP, |
63 | PointerGetDatum(conname), |
64 | ObjectIdGetDatum(connamespace))) |
65 | ereport(ERROR, |
66 | (errcode(ERRCODE_DUPLICATE_OBJECT), |
67 | errmsg("conversion \"%s\" already exists" , conname))); |
68 | |
69 | if (def) |
70 | { |
71 | /* |
72 | * make sure there is no existing default <for encoding><to encoding> |
73 | * pair in this name space |
74 | */ |
75 | if (FindDefaultConversion(connamespace, |
76 | conforencoding, |
77 | contoencoding)) |
78 | ereport(ERROR, |
79 | (errcode(ERRCODE_DUPLICATE_OBJECT), |
80 | errmsg("default conversion for %s to %s already exists" , |
81 | pg_encoding_to_char(conforencoding), |
82 | pg_encoding_to_char(contoencoding)))); |
83 | } |
84 | |
85 | /* open pg_conversion */ |
86 | rel = table_open(ConversionRelationId, RowExclusiveLock); |
87 | tupDesc = rel->rd_att; |
88 | |
89 | /* initialize nulls and values */ |
90 | for (i = 0; i < Natts_pg_conversion; i++) |
91 | { |
92 | nulls[i] = false; |
93 | values[i] = (Datum) NULL; |
94 | } |
95 | |
96 | /* form a tuple */ |
97 | namestrcpy(&cname, conname); |
98 | oid = GetNewOidWithIndex(rel, ConversionOidIndexId, |
99 | Anum_pg_conversion_oid); |
100 | values[Anum_pg_conversion_oid - 1] = ObjectIdGetDatum(oid); |
101 | values[Anum_pg_conversion_conname - 1] = NameGetDatum(&cname); |
102 | values[Anum_pg_conversion_connamespace - 1] = ObjectIdGetDatum(connamespace); |
103 | values[Anum_pg_conversion_conowner - 1] = ObjectIdGetDatum(conowner); |
104 | values[Anum_pg_conversion_conforencoding - 1] = Int32GetDatum(conforencoding); |
105 | values[Anum_pg_conversion_contoencoding - 1] = Int32GetDatum(contoencoding); |
106 | values[Anum_pg_conversion_conproc - 1] = ObjectIdGetDatum(conproc); |
107 | values[Anum_pg_conversion_condefault - 1] = BoolGetDatum(def); |
108 | |
109 | tup = heap_form_tuple(tupDesc, values, nulls); |
110 | |
111 | /* insert a new tuple */ |
112 | CatalogTupleInsert(rel, tup); |
113 | |
114 | myself.classId = ConversionRelationId; |
115 | myself.objectId = oid; |
116 | myself.objectSubId = 0; |
117 | |
118 | /* create dependency on conversion procedure */ |
119 | referenced.classId = ProcedureRelationId; |
120 | referenced.objectId = conproc; |
121 | referenced.objectSubId = 0; |
122 | recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); |
123 | |
124 | /* create dependency on namespace */ |
125 | referenced.classId = NamespaceRelationId; |
126 | referenced.objectId = connamespace; |
127 | referenced.objectSubId = 0; |
128 | recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); |
129 | |
130 | /* create dependency on owner */ |
131 | recordDependencyOnOwner(ConversionRelationId, oid, conowner); |
132 | |
133 | /* dependency on extension */ |
134 | recordDependencyOnCurrentExtension(&myself, false); |
135 | |
136 | /* Post creation hook for new conversion */ |
137 | InvokeObjectPostCreateHook(ConversionRelationId, oid, 0); |
138 | |
139 | heap_freetuple(tup); |
140 | table_close(rel, RowExclusiveLock); |
141 | |
142 | return myself; |
143 | } |
144 | |
145 | /* |
146 | * RemoveConversionById |
147 | * |
148 | * Remove a tuple from pg_conversion by Oid. This function is solely |
149 | * called inside catalog/dependency.c |
150 | */ |
151 | void |
152 | RemoveConversionById(Oid conversionOid) |
153 | { |
154 | Relation rel; |
155 | HeapTuple tuple; |
156 | TableScanDesc scan; |
157 | ScanKeyData scanKeyData; |
158 | |
159 | ScanKeyInit(&scanKeyData, |
160 | Anum_pg_conversion_oid, |
161 | BTEqualStrategyNumber, F_OIDEQ, |
162 | ObjectIdGetDatum(conversionOid)); |
163 | |
164 | /* open pg_conversion */ |
165 | rel = table_open(ConversionRelationId, RowExclusiveLock); |
166 | |
167 | scan = table_beginscan_catalog(rel, 1, &scanKeyData); |
168 | |
169 | /* search for the target tuple */ |
170 | if (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection))) |
171 | CatalogTupleDelete(rel, &tuple->t_self); |
172 | else |
173 | elog(ERROR, "could not find tuple for conversion %u" , conversionOid); |
174 | table_endscan(scan); |
175 | table_close(rel, RowExclusiveLock); |
176 | } |
177 | |
178 | /* |
179 | * FindDefaultConversion |
180 | * |
181 | * Find "default" conversion proc by for_encoding and to_encoding in the |
182 | * given namespace. |
183 | * |
184 | * If found, returns the procedure's oid, otherwise InvalidOid. Note that |
185 | * you get the procedure's OID not the conversion's OID! |
186 | */ |
187 | Oid |
188 | FindDefaultConversion(Oid name_space, int32 for_encoding, int32 to_encoding) |
189 | { |
190 | CatCList *catlist; |
191 | HeapTuple tuple; |
192 | Form_pg_conversion body; |
193 | Oid proc = InvalidOid; |
194 | int i; |
195 | |
196 | catlist = SearchSysCacheList3(CONDEFAULT, |
197 | ObjectIdGetDatum(name_space), |
198 | Int32GetDatum(for_encoding), |
199 | Int32GetDatum(to_encoding)); |
200 | |
201 | for (i = 0; i < catlist->n_members; i++) |
202 | { |
203 | tuple = &catlist->members[i]->tuple; |
204 | body = (Form_pg_conversion) GETSTRUCT(tuple); |
205 | if (body->condefault) |
206 | { |
207 | proc = body->conproc; |
208 | break; |
209 | } |
210 | } |
211 | ReleaseSysCacheList(catlist); |
212 | return proc; |
213 | } |
214 | |