| 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 | |