00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014 #include "postgres.h"
00015
00016 #include "access/genam.h"
00017 #include "access/heapam.h"
00018 #include "access/htup_details.h"
00019 #include "access/xact.h"
00020 #include "catalog/catalog.h"
00021 #include "catalog/indexing.h"
00022 #include "catalog/pg_enum.h"
00023 #include "catalog/pg_type.h"
00024 #include "storage/lmgr.h"
00025 #include "miscadmin.h"
00026 #include "utils/builtins.h"
00027 #include "utils/catcache.h"
00028 #include "utils/fmgroids.h"
00029 #include "utils/syscache.h"
00030 #include "utils/tqual.h"
00031
00032
00033
00034 Oid binary_upgrade_next_pg_enum_oid = InvalidOid;
00035
00036 static void RenumberEnumType(Relation pg_enum, HeapTuple *existing, int nelems);
00037 static int oid_cmp(const void *p1, const void *p2);
00038 static int sort_order_cmp(const void *p1, const void *p2);
00039
00040
00041
00042
00043
00044
00045
00046
00047 void
00048 EnumValuesCreate(Oid enumTypeOid, List *vals)
00049 {
00050 Relation pg_enum;
00051 NameData enumlabel;
00052 Oid *oids;
00053 int elemno,
00054 num_elems;
00055 Datum values[Natts_pg_enum];
00056 bool nulls[Natts_pg_enum];
00057 ListCell *lc;
00058 HeapTuple tup;
00059
00060 num_elems = list_length(vals);
00061
00062
00063
00064
00065
00066
00067
00068 pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078 oids = (Oid *) palloc(num_elems * sizeof(Oid));
00079
00080 for (elemno = 0; elemno < num_elems; elemno++)
00081 {
00082
00083
00084
00085
00086
00087 Oid new_oid;
00088
00089 do
00090 {
00091 new_oid = GetNewOid(pg_enum);
00092 } while (new_oid & 1);
00093 oids[elemno] = new_oid;
00094 }
00095
00096
00097 qsort(oids, num_elems, sizeof(Oid), oid_cmp);
00098
00099
00100 memset(nulls, false, sizeof(nulls));
00101
00102 elemno = 0;
00103 foreach(lc, vals)
00104 {
00105 char *lab = strVal(lfirst(lc));
00106
00107
00108
00109
00110
00111 if (strlen(lab) > (NAMEDATALEN - 1))
00112 ereport(ERROR,
00113 (errcode(ERRCODE_INVALID_NAME),
00114 errmsg("invalid enum label \"%s\"", lab),
00115 errdetail("Labels must be %d characters or less.",
00116 NAMEDATALEN - 1)));
00117
00118 values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid);
00119 values[Anum_pg_enum_enumsortorder - 1] = Float4GetDatum(elemno + 1);
00120 namestrcpy(&enumlabel, lab);
00121 values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel);
00122
00123 tup = heap_form_tuple(RelationGetDescr(pg_enum), values, nulls);
00124 HeapTupleSetOid(tup, oids[elemno]);
00125
00126 simple_heap_insert(pg_enum, tup);
00127 CatalogUpdateIndexes(pg_enum, tup);
00128 heap_freetuple(tup);
00129
00130 elemno++;
00131 }
00132
00133
00134 pfree(oids);
00135 heap_close(pg_enum, RowExclusiveLock);
00136 }
00137
00138
00139
00140
00141
00142
00143 void
00144 EnumValuesDelete(Oid enumTypeOid)
00145 {
00146 Relation pg_enum;
00147 ScanKeyData key[1];
00148 SysScanDesc scan;
00149 HeapTuple tup;
00150
00151 pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
00152
00153 ScanKeyInit(&key[0],
00154 Anum_pg_enum_enumtypid,
00155 BTEqualStrategyNumber, F_OIDEQ,
00156 ObjectIdGetDatum(enumTypeOid));
00157
00158 scan = systable_beginscan(pg_enum, EnumTypIdLabelIndexId, true,
00159 SnapshotNow, 1, key);
00160
00161 while (HeapTupleIsValid(tup = systable_getnext(scan)))
00162 {
00163 simple_heap_delete(pg_enum, &tup->t_self);
00164 }
00165
00166 systable_endscan(scan);
00167
00168 heap_close(pg_enum, RowExclusiveLock);
00169 }
00170
00171
00172
00173
00174
00175
00176
00177
00178 void
00179 AddEnumLabel(Oid enumTypeOid,
00180 const char *newVal,
00181 const char *neighbor,
00182 bool newValIsAfter,
00183 bool skipIfExists)
00184 {
00185 Relation pg_enum;
00186 Oid newOid;
00187 Datum values[Natts_pg_enum];
00188 bool nulls[Natts_pg_enum];
00189 NameData enumlabel;
00190 HeapTuple enum_tup;
00191 float4 newelemorder;
00192 HeapTuple *existing;
00193 CatCList *list;
00194 int nelems;
00195 int i;
00196
00197
00198 if (strlen(newVal) > (NAMEDATALEN - 1))
00199 ereport(ERROR,
00200 (errcode(ERRCODE_INVALID_NAME),
00201 errmsg("invalid enum label \"%s\"", newVal),
00202 errdetail("Labels must be %d characters or less.",
00203 NAMEDATALEN - 1)));
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213 LockDatabaseObject(TypeRelationId, enumTypeOid, 0, ExclusiveLock);
00214
00215
00216
00217
00218
00219
00220 enum_tup = SearchSysCache2(ENUMTYPOIDNAME,
00221 ObjectIdGetDatum(enumTypeOid),
00222 CStringGetDatum(newVal));
00223 if (HeapTupleIsValid(enum_tup))
00224 {
00225 ReleaseSysCache(enum_tup);
00226 if (skipIfExists)
00227 {
00228 ereport(NOTICE,
00229 (errcode(ERRCODE_DUPLICATE_OBJECT),
00230 errmsg("enum label \"%s\" already exists, skipping",
00231 newVal)));
00232 return;
00233 }
00234 else
00235 ereport(ERROR,
00236 (errcode(ERRCODE_DUPLICATE_OBJECT),
00237 errmsg("enum label \"%s\" already exists",
00238 newVal)));
00239 }
00240
00241 pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
00242
00243
00244 restart:
00245
00246
00247 list = SearchSysCacheList1(ENUMTYPOIDNAME,
00248 ObjectIdGetDatum(enumTypeOid));
00249 nelems = list->n_members;
00250
00251
00252 existing = (HeapTuple *) palloc(nelems * sizeof(HeapTuple));
00253 for (i = 0; i < nelems; i++)
00254 existing[i] = &(list->members[i]->tuple);
00255
00256 qsort(existing, nelems, sizeof(HeapTuple), sort_order_cmp);
00257
00258 if (neighbor == NULL)
00259 {
00260
00261
00262
00263
00264 if (nelems > 0)
00265 {
00266 Form_pg_enum en = (Form_pg_enum) GETSTRUCT(existing[nelems - 1]);
00267
00268 newelemorder = en->enumsortorder + 1;
00269 }
00270 else
00271 newelemorder = 1;
00272 }
00273 else
00274 {
00275
00276 int nbr_index;
00277 int other_nbr_index;
00278 Form_pg_enum nbr_en;
00279 Form_pg_enum other_nbr_en;
00280
00281
00282 for (nbr_index = 0; nbr_index < nelems; nbr_index++)
00283 {
00284 Form_pg_enum en = (Form_pg_enum) GETSTRUCT(existing[nbr_index]);
00285
00286 if (strcmp(NameStr(en->enumlabel), neighbor) == 0)
00287 break;
00288 }
00289 if (nbr_index >= nelems)
00290 ereport(ERROR,
00291 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00292 errmsg("\"%s\" is not an existing enum label",
00293 neighbor)));
00294 nbr_en = (Form_pg_enum) GETSTRUCT(existing[nbr_index]);
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306 if (newValIsAfter)
00307 other_nbr_index = nbr_index + 1;
00308 else
00309 other_nbr_index = nbr_index - 1;
00310
00311 if (other_nbr_index < 0)
00312 newelemorder = nbr_en->enumsortorder - 1;
00313 else if (other_nbr_index >= nelems)
00314 newelemorder = nbr_en->enumsortorder + 1;
00315 else
00316 {
00317 other_nbr_en = (Form_pg_enum) GETSTRUCT(existing[other_nbr_index]);
00318 newelemorder = (nbr_en->enumsortorder +
00319 other_nbr_en->enumsortorder) / 2;
00320
00321
00322
00323
00324
00325
00326
00327
00328 newelemorder = DatumGetFloat4(Float4GetDatum(newelemorder));
00329
00330 if (newelemorder == nbr_en->enumsortorder ||
00331 newelemorder == other_nbr_en->enumsortorder)
00332 {
00333 RenumberEnumType(pg_enum, existing, nelems);
00334
00335 pfree(existing);
00336 ReleaseCatCacheList(list);
00337 goto restart;
00338 }
00339 }
00340 }
00341
00342
00343 if (IsBinaryUpgrade && OidIsValid(binary_upgrade_next_pg_enum_oid))
00344 {
00345
00346
00347
00348
00349
00350 if (neighbor != NULL)
00351 ereport(ERROR,
00352 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00353 errmsg("ALTER TYPE ADD BEFORE/AFTER is incompatible with binary upgrade")));
00354
00355 newOid = binary_upgrade_next_pg_enum_oid;
00356 binary_upgrade_next_pg_enum_oid = InvalidOid;
00357 }
00358 else
00359 {
00360
00361
00362
00363
00364
00365
00366
00367 for (;;)
00368 {
00369 bool sorts_ok;
00370
00371
00372 newOid = GetNewOid(pg_enum);
00373
00374
00375
00376
00377
00378
00379
00380 sorts_ok = true;
00381 for (i = 0; i < nelems; i++)
00382 {
00383 HeapTuple exists_tup = existing[i];
00384 Form_pg_enum exists_en = (Form_pg_enum) GETSTRUCT(exists_tup);
00385 Oid exists_oid = HeapTupleGetOid(exists_tup);
00386
00387 if (exists_oid & 1)
00388 continue;
00389
00390 if (exists_en->enumsortorder < newelemorder)
00391 {
00392
00393 if (exists_oid >= newOid)
00394 {
00395 sorts_ok = false;
00396 break;
00397 }
00398 }
00399 else
00400 {
00401
00402 if (exists_oid <= newOid)
00403 {
00404 sorts_ok = false;
00405 break;
00406 }
00407 }
00408 }
00409
00410 if (sorts_ok)
00411 {
00412
00413 if ((newOid & 1) == 0)
00414 break;
00415
00416
00417
00418
00419
00420
00421 }
00422 else
00423 {
00424
00425
00426
00427
00428
00429 if (newOid & 1)
00430 break;
00431
00432
00433
00434
00435
00436 }
00437 }
00438 }
00439
00440
00441 pfree(existing);
00442 ReleaseCatCacheList(list);
00443
00444
00445 memset(nulls, false, sizeof(nulls));
00446 values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid);
00447 values[Anum_pg_enum_enumsortorder - 1] = Float4GetDatum(newelemorder);
00448 namestrcpy(&enumlabel, newVal);
00449 values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel);
00450 enum_tup = heap_form_tuple(RelationGetDescr(pg_enum), values, nulls);
00451 HeapTupleSetOid(enum_tup, newOid);
00452 simple_heap_insert(pg_enum, enum_tup);
00453 CatalogUpdateIndexes(pg_enum, enum_tup);
00454 heap_freetuple(enum_tup);
00455
00456 heap_close(pg_enum, RowExclusiveLock);
00457 }
00458
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485
00486
00487 static void
00488 RenumberEnumType(Relation pg_enum, HeapTuple *existing, int nelems)
00489 {
00490 int i;
00491
00492
00493
00494
00495
00496
00497 for (i = nelems - 1; i >= 0; i--)
00498 {
00499 HeapTuple newtup;
00500 Form_pg_enum en;
00501 float4 newsortorder;
00502
00503 newtup = heap_copytuple(existing[i]);
00504 en = (Form_pg_enum) GETSTRUCT(newtup);
00505
00506 newsortorder = i + 1;
00507 if (en->enumsortorder != newsortorder)
00508 {
00509 en->enumsortorder = newsortorder;
00510
00511 simple_heap_update(pg_enum, &newtup->t_self, newtup);
00512
00513 CatalogUpdateIndexes(pg_enum, newtup);
00514 }
00515
00516 heap_freetuple(newtup);
00517 }
00518
00519
00520 CommandCounterIncrement();
00521 }
00522
00523
00524
00525 static int
00526 oid_cmp(const void *p1, const void *p2)
00527 {
00528 Oid v1 = *((const Oid *) p1);
00529 Oid v2 = *((const Oid *) p2);
00530
00531 if (v1 < v2)
00532 return -1;
00533 if (v1 > v2)
00534 return 1;
00535 return 0;
00536 }
00537
00538
00539 static int
00540 sort_order_cmp(const void *p1, const void *p2)
00541 {
00542 HeapTuple v1 = *((const HeapTuple *) p1);
00543 HeapTuple v2 = *((const HeapTuple *) p2);
00544 Form_pg_enum en1 = (Form_pg_enum) GETSTRUCT(v1);
00545 Form_pg_enum en2 = (Form_pg_enum) GETSTRUCT(v2);
00546
00547 if (en1->enumsortorder < en2->enumsortorder)
00548 return -1;
00549 else if (en1->enumsortorder > en2->enumsortorder)
00550 return 1;
00551 else
00552 return 0;
00553 }