WIP: extensible enums
Attached is a WIP patch that allows enums to be extended with additional
labels arbitrarily. As previously discussed, it works by adding an
explicit sort order column to pg_enum. It keeps track of whether the
labels are correctly sorted by oid value, and if so uses that for
comparison, so the possible performance impact on existing uses, and on
almost all cases where a label is added at the end of the list, should
be negligible.
Open items include
* some additional error checking required
* missing documentation
* pg_upgrade considerations
To add a label at the end of the list, do:
ALTER TYPE myenum ADD 'newlabel';
To add a label somewhere else, do:
ALTER TYPE myenum ADD 'newlabel' BEFORE 'existinglabel';
or
ALTER TYPE myenum ADD 'newlabel' AFTER 'existinglabel';
I'm not wedded to the syntax. Let the bikeshedding begin.
cheers
andrew
Attachments:
venum.patchtext/x-patch; name=venum.patchDownload
*** a/src/backend/catalog/heap.c
--- b/src/backend/catalog/heap.c
***************
*** 854,860 **** AddNewRelationType(const char *typeName,
'x', /* fully TOASTable */
-1, /* typmod */
0, /* array dimensions for typBaseType */
! false); /* Type NOT NULL */
}
/* --------------------------------
--- 854,862 ----
'x', /* fully TOASTable */
-1, /* typmod */
0, /* array dimensions for typBaseType */
! false, /* Type NOT NULL */
! -1, /* no enum labels */
! false); /* type is not an enum, so not sorted */
}
/* --------------------------------
***************
*** 1086,1092 **** heap_create_with_catalog(const char *relname,
'x', /* fully TOASTable */
-1, /* typmod */
0, /* array dimensions for typBaseType */
! false); /* Type NOT NULL */
pfree(relarrayname);
}
--- 1088,1096 ----
'x', /* fully TOASTable */
-1, /* typmod */
0, /* array dimensions for typBaseType */
! false, /* Type NOT NULL */
! -1, /* no enum labels */
! false); /* type is not an enum, so not sorted */
pfree(relarrayname);
}
*** a/src/backend/catalog/pg_enum.c
--- b/src/backend/catalog/pg_enum.c
***************
*** 18,29 ****
--- 18,202 ----
#include "catalog/catalog.h"
#include "catalog/indexing.h"
#include "catalog/pg_enum.h"
+ #include "catalog/pg_type.h"
+ #include "utils/lsyscache.h"
+ #include "utils/syscache.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/rel.h"
#include "utils/tqual.h"
static int oid_cmp(const void *p1, const void *p2);
+ static int sort_order_cmp(const void *p1, const void *p2);
+
+ /*
+ * AddEnumLabel
+ * Add a new label to the enum set. By default it goes at
+ * the end, but the user can choose to place it before or
+ * after any existing set member.
+ *
+ * Returns true iff the labels are sorted by oid after the addition.
+ */
+
+ bool
+ AddEnumLabel(Oid enumTypeOid,
+ char *newVal,
+ char *neighbour,
+ bool newValIsAfter,
+ int nelems,
+ bool elems_are_sorted)
+ {
+ Oid newOid;
+ Relation pg_enum;
+ TupleDesc tupDesc;
+ Datum values[Natts_pg_enum];
+ bool nulls[Natts_pg_enum];
+ NameData enumlabel;
+ HeapTuple enum_tup;
+ bool result = elems_are_sorted;
+ int newelemorder;
+
+ /* check length of new label is ok */
+ if (strlen(newVal) > (NAMEDATALEN - 1))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_NAME),
+ errmsg("invalid enum label \"%s\"", newVal),
+ errdetail("Labels must be %d characters or less.",
+ NAMEDATALEN - 1)));
+
+ /* get a new OID for the label */
+ pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
+ newOid = GetNewOid(pg_enum);
+
+ if (neighbour == NULL)
+ {
+ /*
+ * Put the new label at the end of the list.
+ * No change to existing tuples is required.
+ */
+ newelemorder = nelems + 1;
+ /* are the elements still sorted? */
+ if (elems_are_sorted)
+ {
+ CatCList *list;
+ int i;
+ bool still_sorted = true;
+
+ list = SearchSysCacheList1(ENUMTYPOIDNAME,
+ ObjectIdGetDatum(enumTypeOid));
+ for (i = 0; i < nelems; i++)
+ {
+ HeapTuple tup = &(list->members[i]->tuple);
+ Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
+
+ if (en->enumsortorder == nelems)
+ {
+ if (HeapTupleGetOid(tup) > newOid)
+ still_sorted = false;
+ break;
+ }
+ }
+ ReleaseCatCacheList(list);
+ if (! still_sorted)
+ result = false;
+ }
+ }
+ else
+ {
+ /* BEFORE or AFTER specified */
+ CatCList *list;
+ int i;
+ HeapTuple *existing;
+ HeapTuple nbr = NULL;
+ Form_pg_enum nbr_en;
+
+ /* get a list of the existing elements and sort them by enumsortorder */
+ list = SearchSysCacheList1(ENUMTYPOIDNAME,
+ ObjectIdGetDatum(enumTypeOid));
+ existing = palloc(nelems * sizeof(HeapTuple));
+
+ for (i = 0; i < nelems; i++)
+ existing[i] = &(list->members[i]->tuple);
+
+ qsort(existing, nelems, sizeof(HeapTuple), sort_order_cmp);
+
+ /* locate the neighbour element */
+ for (i = 0; i < nelems; i++)
+ {
+ Form_pg_enum exists_en;
+ exists_en = (Form_pg_enum) GETSTRUCT(existing[i]);
+ if (strcmp(NameStr(exists_en->enumlabel),neighbour) == 0)
+ nbr = existing[i];
+
+ }
+
+ if (nbr == NULL)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("\"%s\" is not an existing label.", neighbour)));
+ }
+
+ nbr_en = (Form_pg_enum) GETSTRUCT(nbr);
+
+ /*
+ * If BEFORE was spcified, the new label goes in the neighbour's
+ * position. Otherwise, it goes in the position after that.
+ */
+ newelemorder = nbr_en->enumsortorder;
+ if (newValIsAfter)
+ newelemorder++;
+
+ /*
+ * Add 1 to the sortorder of all the labels after where the
+ * new label goes. Do it from the end back so we don't get
+ * uniqueness violations.
+ */
+ for (i = nelems - 1; i>= 0; i--)
+ {
+ HeapTuple newtup;
+ Form_pg_enum exst_en = (Form_pg_enum) GETSTRUCT(existing[i]);
+ if (exst_en->enumsortorder < newelemorder)
+ break;
+
+ newtup = heap_copytuple(existing[i]);
+ exst_en = (Form_pg_enum) GETSTRUCT(newtup);
+ exst_en->enumsortorder ++;
+
+ simple_heap_update(pg_enum, &newtup->t_self, newtup);
+
+ CatalogUpdateIndexes(pg_enum, newtup);
+
+ }
+
+ ReleaseCatCacheList(list);
+
+ /* are the labels sorted by OID? */
+ if (result && newelemorder > 1)
+ result = newOid > HeapTupleGetOid(existing[newelemorder-2]);
+ if (result && newelemorder < nelems + 1)
+ result = newOid < HeapTupleGetOid(existing[newelemorder-1]);
+
+ }
+
+ /* set up the new entry */
+ tupDesc = pg_enum->rd_att;
+ memset(nulls, false, sizeof(nulls));
+ values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid);
+ namestrcpy(&enumlabel, newVal);
+ values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel);
+ values[Anum_pg_enum_enumsortorder -1] = Int32GetDatum(newelemorder);
+ enum_tup = heap_form_tuple(tupDesc, values, nulls);
+ HeapTupleSetOid(enum_tup, newOid);
+ simple_heap_insert(pg_enum, enum_tup);
+ CatalogUpdateIndexes(pg_enum, enum_tup);
+ heap_freetuple(enum_tup);
+
+ heap_close(pg_enum, RowExclusiveLock);
+
+ return result;
+
+ }
/*
***************
*** 114,119 **** EnumValuesCreate(Oid enumTypeOid, List *vals,
--- 287,293 ----
values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid);
namestrcpy(&enumlabel, lab);
values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel);
+ values[Anum_pg_enum_enumsortorder -1] = Int32GetDatum(elemno+1);
tup = heap_form_tuple(tupDesc, values, nulls);
HeapTupleSetOid(tup, oids[elemno]);
***************
*** 177,179 **** oid_cmp(const void *p1, const void *p2)
--- 351,366 ----
return 1;
return 0;
}
+
+ /* qsort comparison function for tuples by sort order */
+ static int
+ sort_order_cmp(const void *p1, const void *p2)
+ {
+ HeapTuple v1 = *((const HeapTuple *) p1);
+ HeapTuple v2 = *((const HeapTuple *) p2);
+ Form_pg_enum en1 = (Form_pg_enum) GETSTRUCT(v1);
+ Form_pg_enum en2 = (Form_pg_enum) GETSTRUCT(v2);
+
+ return en1->enumsortorder - en2->enumsortorder;
+ }
+
*** a/src/backend/catalog/pg_type.c
--- b/src/backend/catalog/pg_type.c
***************
*** 112,117 **** TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
--- 112,119 ----
values[i++] = ObjectIdGetDatum(InvalidOid); /* typbasetype */
values[i++] = Int32GetDatum(-1); /* typtypmod */
values[i++] = Int32GetDatum(0); /* typndims */
+ values[i++] = Int32GetDatum(-1); /* typnlabels */
+ values[i++] = BoolGetDatum(false); /* typsorted */
nulls[i++] = true; /* typdefaultbin */
nulls[i++] = true; /* typdefault */
***************
*** 204,210 **** TypeCreate(Oid newTypeOid,
char storage,
int32 typeMod,
int32 typNDims, /* Array dimensions for baseType */
! bool typeNotNull)
{
Relation pg_type_desc;
Oid typeObjectId;
--- 206,214 ----
char storage,
int32 typeMod,
int32 typNDims, /* Array dimensions for baseType */
! bool typeNotNull,
! int32 typeNLabels,
! bool typeSorted)
{
Relation pg_type_desc;
Oid typeObjectId;
***************
*** 342,347 **** TypeCreate(Oid newTypeOid,
--- 346,353 ----
values[i++] = ObjectIdGetDatum(baseType); /* typbasetype */
values[i++] = Int32GetDatum(typeMod); /* typtypmod */
values[i++] = Int32GetDatum(typNDims); /* typndims */
+ values[i++] = Int32GetDatum(typeNLabels); /* typnlabels */
+ values[i++] = BoolGetDatum(typeSorted); /* typsorted */
/*
* initialize the default binary value for this type. Check for nulls of
*** a/src/backend/commands/typecmds.c
--- b/src/backend/commands/typecmds.c
***************
*** 85,90 **** static Oid findTypeTypmodoutFunction(List *procname);
--- 85,91 ----
static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid);
static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
static void checkDomainOwner(HeapTuple tup, TypeName *typename);
+ static void checkEnumOwner(HeapTuple tup, TypeName *typename);
static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
Oid baseTypeOid,
int typMod, Constraint *constr,
***************
*** 562,569 **** DefineType(List *names, List *parameters)
storage, /* TOAST strategy */
-1, /* typMod (Domains only) */
0, /* Array Dimensions of typbasetype */
! false); /* Type NOT NULL */
!
/*
* Create the array type that goes with it.
*/
--- 563,571 ----
storage, /* TOAST strategy */
-1, /* typMod (Domains only) */
0, /* Array Dimensions of typbasetype */
! false, /* Type NOT NULL */
! -1, /* no enum labels */
! false); /* type is not an enum, so not sorted */
/*
* Create the array type that goes with it.
*/
***************
*** 601,607 **** DefineType(List *names, List *parameters)
'x', /* ARRAY is always toastable */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
! false); /* Type NOT NULL */
pfree(array_type);
}
--- 603,611 ----
'x', /* ARRAY is always toastable */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
! false, /* Type NOT NULL */
! -1, /* no enum labels */
! false); /* type is not an enum, so not sorted */
pfree(array_type);
}
***************
*** 1044,1050 **** DefineDomain(CreateDomainStmt *stmt)
storage, /* TOAST strategy */
basetypeMod, /* typeMod value */
typNDims, /* Array dimensions for base type */
! typNotNull); /* Type NOT NULL */
/*
* Process constraints which refer to the domain ID returned by TypeCreate
--- 1048,1056 ----
storage, /* TOAST strategy */
basetypeMod, /* typeMod value */
typNDims, /* Array dimensions for base type */
! typNotNull, /* Type NOT NULL */
! -1, /* no enum labels */
! false); /* type is not an enum, so not sorted */
/*
* Process constraints which refer to the domain ID returned by TypeCreate
***************
*** 1094,1099 **** DefineEnum(CreateEnumStmt *stmt)
--- 1100,1108 ----
AclResult aclresult;
Oid old_type_oid;
Oid enumArrayOid;
+ int num_labels;
+
+ num_labels = list_length(stmt->vals);
/* Convert list of names to a name and namespace */
enumNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
***************
*** 1153,1159 **** DefineEnum(CreateEnumStmt *stmt)
'p', /* TOAST strategy always plain */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
! false); /* Type NOT NULL */
/* Enter the enum's values into pg_enum */
EnumValuesCreate(enumTypeOid, stmt->vals, InvalidOid);
--- 1162,1170 ----
'p', /* TOAST strategy always plain */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
! false, /* Type NOT NULL */
! num_labels, /* count enum labels */
! true); /* enums always start sorted */
/* Enter the enum's values into pg_enum */
EnumValuesCreate(enumTypeOid, stmt->vals, InvalidOid);
***************
*** 1192,1202 **** DefineEnum(CreateEnumStmt *stmt)
'x', /* ARRAY is always toastable */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
! false); /* Type NOT NULL */
pfree(enumArrayName);
}
/*
* Find suitable I/O functions for a type.
--- 1203,1290 ----
'x', /* ARRAY is always toastable */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
! false, /* Type NOT NULL */
! -1, /* no enum labels */
! false); /* type is not an enum, so not sorted */
pfree(enumArrayName);
}
+ /*
+ * AlterEnum
+ * Registers a new label for an existing enum.
+ */
+ void
+ AlterEnum (AlterEnumStmt *stmt)
+ {
+ Oid enum_type_oid;
+ TypeName *typename;
+ bool sorted;
+ HeapTuple tup, newtup;
+ Relation rel;
+ Form_pg_type typTup;
+
+ /* Make a TypeName so we can use standard type lookup machinery */
+ typename = makeTypeNameFromNameList(stmt->typeName);
+ enum_type_oid = typenameTypeId(NULL, typename, NULL);
+
+ /* Look up the row in the type table */
+ rel = heap_open(TypeRelationId, RowExclusiveLock);
+
+ tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(enum_type_oid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for type %u", enum_type_oid);
+
+ /* Copy the syscache entry so we can scribble on it below */
+ newtup = heap_copytuple(tup);
+ ReleaseSysCache(tup);
+ tup = newtup;
+ typTup = (Form_pg_type) GETSTRUCT(tup);
+
+ /* Check it's an enum and check user has permission to ALTER the enum */
+ checkEnumOwner(tup, typename);
+
+ /* Add the new label */
+ sorted = AddEnumLabel (enum_type_oid, stmt->newVal,
+ stmt->newValNeighbour, stmt->newValIsAfter,
+ typTup->typnlabels, typTup->typsorted);
+
+ /* Update the row in pg_type */
+ typTup->typnlabels += 1;
+ typTup->typsorted = sorted;
+
+ simple_heap_update(rel, &tup->t_self, tup);
+
+ CatalogUpdateIndexes(rel, tup);
+
+ /* Clean up */
+ heap_close(rel, RowExclusiveLock);
+ }
+
+
+ /*
+ * checkEnumOwner
+ *
+ * Check that the type is actually an enum and that the current user
+ * has permission to do ALTER TYPE on it. Throw an error if not.
+ */
+ static void
+ checkEnumOwner(HeapTuple tup, TypeName *typename)
+ {
+ Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup);
+
+ /* Check that this is actually a domain */
+ if (typTup->typtype != TYPTYPE_ENUM)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not an enum",
+ TypeNameToString(typename))));
+
+ /* Permission check: must own type */
+ if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
+ format_type_be(HeapTupleGetOid(tup)));
+ }
/*
* Find suitable I/O functions for a type.
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 2836,2841 **** _copyCreateEnumStmt(CreateEnumStmt *from)
--- 2836,2854 ----
return newnode;
}
+ static AlterEnumStmt *
+ _copyAlterEnumStmt(AlterEnumStmt *from)
+ {
+ AlterEnumStmt *newnode = makeNode(AlterEnumStmt);
+
+ COPY_NODE_FIELD(typeName);
+ COPY_STRING_FIELD(newVal);
+ COPY_STRING_FIELD(newValNeighbour);
+ COPY_SCALAR_FIELD(newValIsAfter);
+
+ return newnode;
+ }
+
static ViewStmt *
_copyViewStmt(ViewStmt *from)
{
***************
*** 3989,3994 **** copyObject(void *from)
--- 4002,4010 ----
case T_CreateEnumStmt:
retval = _copyCreateEnumStmt(from);
break;
+ case T_AlterEnumStmt:
+ retval = _copyAlterEnumStmt(from);
+ break;
case T_ViewStmt:
retval = _copyViewStmt(from);
break;
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
***************
*** 1375,1380 **** _equalCreateEnumStmt(CreateEnumStmt *a, CreateEnumStmt *b)
--- 1375,1391 ----
}
static bool
+ _equalAlterEnumStmt(AlterEnumStmt *a, AlterEnumStmt *b)
+ {
+ COMPARE_NODE_FIELD(typeName);
+ COMPARE_STRING_FIELD(newVal);
+ COMPARE_STRING_FIELD(newValNeighbour);
+ COMPARE_SCALAR_FIELD(newValIsAfter);
+
+ return true;
+ }
+
+ static bool
_equalViewStmt(ViewStmt *a, ViewStmt *b)
{
COMPARE_NODE_FIELD(view);
***************
*** 2678,2683 **** equal(void *a, void *b)
--- 2689,2697 ----
case T_CreateEnumStmt:
retval = _equalCreateEnumStmt(a, b);
break;
+ case T_AlterEnumStmt:
+ retval = _equalAlterEnumStmt(a, b);
+ break;
case T_ViewStmt:
retval = _equalViewStmt(a, b);
break;
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 181,188 **** static TypeName *TableFuncTypeName(List *columns);
}
%type <node> stmt schema_stmt
! AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterFdwStmt
! AlterForeignServerStmt AlterGroupStmt
AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
AlterRoleStmt AlterRoleSetStmt
--- 181,188 ----
}
%type <node> stmt schema_stmt
! AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt
! AlterFdwStmt AlterForeignServerStmt AlterGroupStmt
AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
AlterRoleStmt AlterRoleSetStmt
***************
*** 648,653 **** stmt :
--- 648,654 ----
| AlterDatabaseSetStmt
| AlterDefaultPrivilegesStmt
| AlterDomainStmt
+ | AlterEnumStmt
| AlterFdwStmt
| AlterForeignServerStmt
| AlterFunctionStmt
***************
*** 3781,3786 **** enum_val_list: Sconst
--- 3782,3827 ----
{ $$ = lappend($1, makeString($3)); }
;
+ /*****************************************************************************
+ *
+ * ALTER TYPE enumtype ADD ...
+ *
+ *****************************************************************************/
+
+ AlterEnumStmt:
+ ALTER TYPE_P any_name ADD_P Sconst
+ {
+ AlterEnumStmt *n = makeNode(AlterEnumStmt);
+ n->typeName = $3;
+ n->newVal = $5;
+ n->newValNeighbour = NULL;
+ n->newValIsAfter = true;
+
+ $$ = (Node *) n;
+ }
+ | ALTER TYPE_P any_name ADD_P Sconst BEFORE Sconst
+ {
+ AlterEnumStmt *n = makeNode(AlterEnumStmt);
+ n->typeName = $3;
+ n->newVal = $5;
+ n->newValNeighbour = $7;
+ n->newValIsAfter = false;
+
+ $$ = (Node *) n;
+ }
+ | ALTER TYPE_P any_name ADD_P Sconst AFTER Sconst
+ {
+ AlterEnumStmt *n = makeNode(AlterEnumStmt);
+ n->typeName = $3;
+ n->newVal = $5;
+ n->newValNeighbour = $7;
+ n->newValIsAfter = true;
+
+ $$ = (Node *) n;
+ }
+ ;
+
+
/*****************************************************************************
*
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
***************
*** 189,194 **** check_xact_readonly(Node *parsetree)
--- 189,195 ----
case T_CreateTrigStmt:
case T_CompositeTypeStmt:
case T_CreateEnumStmt:
+ case T_AlterEnumStmt:
case T_ViewStmt:
case T_DropCastStmt:
case T_DropStmt:
***************
*** 846,851 **** standard_ProcessUtility(Node *parsetree,
--- 847,856 ----
DefineEnum((CreateEnumStmt *) parsetree);
break;
+ case T_AlterEnumStmt: /* ALTER TYPE (enum) */
+ AlterEnum((AlterEnumStmt *) parsetree);
+ break;
+
case T_ViewStmt: /* CREATE VIEW */
DefineView((ViewStmt *) parsetree, queryString);
break;
***************
*** 1846,1851 **** CreateCommandTag(Node *parsetree)
--- 1851,1860 ----
tag = "CREATE TYPE";
break;
+ case T_AlterEnumStmt:
+ tag = "ALTER TYPE";
+ break;
+
case T_ViewStmt:
tag = "CREATE VIEW";
break;
***************
*** 2384,2389 **** GetCommandLogLevel(Node *parsetree)
--- 2393,2402 ----
lev = LOGSTMT_DDL;
break;
+ case T_AlterEnumStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
case T_ViewStmt:
lev = LOGSTMT_DDL;
break;
*** a/src/backend/utils/adt/enum.c
--- b/src/backend/utils/adt/enum.c
***************
*** 14,19 ****
--- 14,20 ----
#include "postgres.h"
#include "catalog/pg_enum.h"
+ #include "catalog/pg_type.h"
#include "fmgr.h"
#include "utils/array.h"
#include "utils/builtins.h"
***************
*** 22,30 ****
#include "libpq/pqformat.h"
#include "miscadmin.h"
static ArrayType *enum_range_internal(Oid enumtypoid, Oid lower, Oid upper);
! static int enum_elem_cmp(const void *left, const void *right);
/* Basic I/O support */
--- 23,48 ----
#include "libpq/pqformat.h"
#include "miscadmin.h"
+ typedef struct
+ {
+ Oid enum_oid;
+ int32 sort_order;
+ } enum_sort;
+
+ typedef struct
+ {
+ Oid enumtypoid;
+ bool oids_are_sorted;
+ int sort_list_length;
+ int label_count;
+ enum_sort sort_order_list[1];
+ } enum_sort_cache;
+
+
static ArrayType *enum_range_internal(Oid enumtypoid, Oid lower, Oid upper);
! static int enum_sort_cmp(const void *left, const void *right);
! static int enum_oid_cmp(const void *left, const void *right);
/* Basic I/O support */
***************
*** 155,167 **** enum_send(PG_FUNCTION_ARGS)
/* Comparison functions and related */
Datum
enum_lt(PG_FUNCTION_ARGS)
{
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(a < b);
}
Datum
--- 173,314 ----
/* Comparison functions and related */
+ static inline int
+ enum_ccmp(Oid arg1, Oid arg2, FunctionCallInfo fcinfo)
+ {
+
+ enum_sort_cache * mycache;
+ enum_sort *es1, *es2, srch;
+ int sort1, sort2;
+ bool added = false;
+ HeapTuple enum_tup, type_tup;
+ Form_pg_enum en;
+ Oid typeoid;
+ Form_pg_type typ;
+
+ if (arg1 == arg2)
+ return 0;
+
+ mycache = (enum_sort_cache *) fcinfo->flinfo->fn_extra;
+ if (mycache == NULL )
+ {
+ enum_tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(arg1));
+ en = (Form_pg_enum) GETSTRUCT(enum_tup);
+ typeoid = en->enumtypid;
+ type_tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeoid));
+ typ = (Form_pg_type) GETSTRUCT(type_tup);
+ if (typ->typtype != 'e')
+ elog(ERROR,"wrong type for oid %u",typeoid);
+ fcinfo->flinfo->fn_extra =
+ MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+ sizeof(enum_sort_cache) +
+ (typ->typnlabels * sizeof(enum_sort)));
+ mycache = (enum_sort_cache *) fcinfo->flinfo->fn_extra;
+ mycache->enumtypoid = typeoid;
+ mycache->sort_list_length = 1;
+ mycache->label_count = typ->typnlabels;
+ mycache->oids_are_sorted = typ->typsorted;
+ mycache->sort_order_list[0].enum_oid = arg1;
+ mycache->sort_order_list[0].sort_order = en->enumsortorder;
+ ReleaseSysCache(type_tup);
+ ReleaseSysCache(enum_tup);
+ }
+
+ if (mycache->oids_are_sorted)
+ return arg1 - arg2;
+
+ srch.enum_oid = arg1;
+ es1 = bsearch(&srch,
+ mycache->sort_order_list,
+ mycache->sort_list_length,
+ sizeof(enum_sort),
+ enum_oid_cmp);
+ srch.enum_oid = arg2;
+ es2 = bsearch(&srch,
+ mycache->sort_order_list,
+ mycache->sort_list_length,
+ sizeof(enum_sort),
+ enum_oid_cmp);
+
+ if (es1 == NULL)
+ {
+
+ enum_tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(arg1));
+ en = (Form_pg_enum) GETSTRUCT(enum_tup);
+ mycache->sort_order_list[mycache->sort_list_length].enum_oid = arg1;
+ sort1 = en->enumsortorder;
+ mycache->sort_order_list[mycache->sort_list_length].sort_order =
+ sort1;
+ ReleaseSysCache(enum_tup);
+ mycache->sort_list_length ++;
+ added = true;
+ }
+ else
+ {
+ /* already in cache */
+ sort1 = es1->sort_order;
+ }
+
+ if (es2 == NULL)
+ {
+
+ enum_tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(arg2));
+ en = (Form_pg_enum) GETSTRUCT(enum_tup);
+ sort2 = en->enumsortorder;
+ mycache->sort_order_list[mycache->sort_list_length].enum_oid = arg2;
+ mycache->sort_order_list[mycache->sort_list_length].sort_order =
+ sort2;
+ ReleaseSysCache(enum_tup);
+ mycache->sort_list_length ++;
+ added = true;
+ }
+ else
+ {
+ /* already in cache */
+ sort2 = es2->sort_order;
+ }
+
+ if (added)
+ {
+ /*
+ * If we have more than a handful, just fetch them all, so we limit
+ * the number of sort operations required.
+ */
+ if (mycache->sort_list_length > 10 &&
+ mycache->sort_list_length < mycache->label_count)
+ {
+ CatCList *nlist;
+ int num, i;
+
+ nlist = SearchSysCacheList1(ENUMTYPOIDNAME,
+ ObjectIdGetDatum(mycache->enumtypoid));
+ num = nlist->n_members;
+ for (i = 0; i < num; i++)
+ {
+ HeapTuple tup = &(nlist->members[i]->tuple);
+ Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
+
+ mycache->sort_order_list[i].enum_oid = HeapTupleGetOid(tup);
+ mycache->sort_order_list[i].sort_order = en->enumsortorder;
+ }
+
+ ReleaseCatCacheList(nlist);
+ }
+
+ qsort(mycache->sort_order_list,mycache->sort_list_length,
+ sizeof(enum_sort),enum_oid_cmp);
+ }
+
+ return sort1 - sort2;
+ }
+
Datum
enum_lt(PG_FUNCTION_ARGS)
{
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(enum_ccmp(a,b,fcinfo) < 0);
}
Datum
***************
*** 170,176 **** enum_le(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(a <= b);
}
Datum
--- 317,323 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(enum_ccmp(a,b,fcinfo) <= 0);
}
Datum
***************
*** 197,203 **** enum_ge(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(a >= b);
}
Datum
--- 344,350 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(enum_ccmp(a,b,fcinfo) >= 0);
}
Datum
***************
*** 206,212 **** enum_gt(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(a > b);
}
Datum
--- 353,359 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(enum_ccmp(a,b,fcinfo) > 0);
}
Datum
***************
*** 215,221 **** enum_smaller(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_OID(a <= b ? a : b);
}
Datum
--- 362,368 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_OID(enum_ccmp(a,b,fcinfo) < 0 ? a : b);
}
Datum
***************
*** 224,230 **** enum_larger(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_OID(a >= b ? a : b);
}
Datum
--- 371,377 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_OID(enum_ccmp(a,b,fcinfo) > 0 ? a : b);
}
Datum
***************
*** 233,242 **** enum_cmp(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! if (a > b)
! PG_RETURN_INT32(1);
! else if (a == b)
PG_RETURN_INT32(0);
else
PG_RETURN_INT32(-1);
}
--- 380,389 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! if (a == b)
PG_RETURN_INT32(0);
+ else if (enum_ccmp(a,b,fcinfo) > 0)
+ PG_RETURN_INT32(1);
else
PG_RETURN_INT32(-1);
}
***************
*** 248,253 **** enum_first(PG_FUNCTION_ARGS)
--- 395,401 ----
{
Oid enumtypoid;
Oid min = InvalidOid;
+ int min_sort = -1; /* value will never in fact be used */
CatCList *list;
int num,
i;
***************
*** 267,276 **** enum_first(PG_FUNCTION_ARGS)
num = list->n_members;
for (i = 0; i < num; i++)
{
! Oid valoid = HeapTupleHeaderGetOid(list->members[i]->tuple.t_data);
!
! if (!OidIsValid(min) || valoid < min)
! min = valoid;
}
ReleaseCatCacheList(list);
--- 415,428 ----
num = list->n_members;
for (i = 0; i < num; i++)
{
! HeapTuple tup = &(list->members[i]->tuple);
! Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
!
! if (!OidIsValid(min) || en->enumsortorder < min_sort)
! {
! min = HeapTupleGetOid(tup);
! min_sort = en->enumsortorder;
! }
}
ReleaseCatCacheList(list);
***************
*** 287,292 **** enum_last(PG_FUNCTION_ARGS)
--- 439,445 ----
{
Oid enumtypoid;
Oid max = InvalidOid;
+ int max_sort = -1; /* value will never in fact be used */
CatCList *list;
int num,
i;
***************
*** 306,315 **** enum_last(PG_FUNCTION_ARGS)
num = list->n_members;
for (i = 0; i < num; i++)
{
! Oid valoid = HeapTupleHeaderGetOid(list->members[i]->tuple.t_data);
!
! if (!OidIsValid(max) || valoid > max)
! max = valoid;
}
ReleaseCatCacheList(list);
--- 459,472 ----
num = list->n_members;
for (i = 0; i < num; i++)
{
! HeapTuple tup = &(list->members[i]->tuple);
! Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
!
! if (!OidIsValid(max) || en->enumsortorder > max_sort)
! {
! max = HeapTupleGetOid(tup);
! max_sort = en->enumsortorder;
! }
}
ReleaseCatCacheList(list);
***************
*** 382,427 **** enum_range_internal(Oid enumtypoid, Oid lower, Oid upper)
i,
j;
Datum *elems;
list = SearchSysCacheList1(ENUMTYPOIDNAME, ObjectIdGetDatum(enumtypoid));
total = list->n_members;
elems = (Datum *) palloc(total * sizeof(Datum));
- j = 0;
for (i = 0; i < total; i++)
{
! Oid val = HeapTupleGetOid(&(list->members[i]->tuple));
- if ((!OidIsValid(lower) || lower <= val) &&
- (!OidIsValid(upper) || val <= upper))
- elems[j++] = ObjectIdGetDatum(val);
}
/* shouldn't need the cache anymore */
ReleaseCatCacheList(list);
! /* sort results into OID order */
! qsort(elems, j, sizeof(Datum), enum_elem_cmp);
/* note this hardwires some details about the representation of Oid */
result = construct_array(elems, j, enumtypoid, sizeof(Oid), true, 'i');
pfree(elems);
return result;
}
! /* qsort comparison function for Datums that are OIDs */
static int
! enum_elem_cmp(const void *left, const void *right)
{
! Oid l = DatumGetObjectId(*((const Datum *) left));
! Oid r = DatumGetObjectId(*((const Datum *) right));
!
! if (l < r)
! return -1;
! if (l > r)
! return 1;
! return 0;
}
--- 539,614 ----
i,
j;
Datum *elems;
+ enum_sort *sort_items;
+ bool left_found;
list = SearchSysCacheList1(ENUMTYPOIDNAME, ObjectIdGetDatum(enumtypoid));
total = list->n_members;
elems = (Datum *) palloc(total * sizeof(Datum));
+ sort_items = (enum_sort *) palloc(total * sizeof(enum_sort));
for (i = 0; i < total; i++)
{
! HeapTuple tup = &(list->members[i]->tuple);
! Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
!
! sort_items[i].enum_oid = HeapTupleGetOid(tup);
! sort_items[i].sort_order = en->enumsortorder;
}
/* shouldn't need the cache anymore */
ReleaseCatCacheList(list);
! /* sort results into sort_order sequence */
! qsort(sort_items, total, sizeof(enum_sort), enum_sort_cmp);
!
! j = 0;
! left_found = !OidIsValid(lower);
! for (i=0; i < total; i++)
! {
! if (! left_found && lower == sort_items[i].enum_oid)
! left_found = true;
!
! if (left_found)
! elems[j++] = ObjectIdGetDatum(sort_items[i].enum_oid);
!
! if (OidIsValid(upper) && upper == sort_items[i].enum_oid)
! break;
! }
/* note this hardwires some details about the representation of Oid */
result = construct_array(elems, j, enumtypoid, sizeof(Oid), true, 'i');
pfree(elems);
+ pfree(sort_items);
return result;
}
! /*
! * qsort comparison using sort order, for range routines
! */
static int
! enum_sort_cmp(const void *left, const void *right)
{
! enum_sort *l = (enum_sort *) left;
! enum_sort *r = (enum_sort *) right;
!
! return l->sort_order - r->sort_order;
}
+
+ /*
+ * qsort comparison using OID order for comparison search cache
+ */
+ static int
+ enum_oid_cmp(const void *es1, const void *es2)
+ {
+ enum_sort *p1, *p2;
+ p1 = (enum_sort *)es1;
+ p2 = (enum_sort *)es2;
+ return p1->enum_oid - p2->enum_oid;
+ }
+
+
*** a/src/include/catalog/indexing.h
--- b/src/include/catalog/indexing.h
***************
*** 147,152 **** DECLARE_UNIQUE_INDEX(pg_enum_oid_index, 3502, on pg_enum using btree(oid oid_ops
--- 147,154 ----
#define EnumOidIndexId 3502
DECLARE_UNIQUE_INDEX(pg_enum_typid_label_index, 3503, on pg_enum using btree(enumtypid oid_ops, enumlabel name_ops));
#define EnumTypIdLabelIndexId 3503
+ DECLARE_UNIQUE_INDEX(pg_enum_typid_sortorder_index, 3539, on pg_enum using btree(enumtypid oid_ops, enumsortorder int4_ops));
+ #define EnumTypIdSortOrderIndexId 3539
/* This following index is not used for a cache and is not unique */
DECLARE_INDEX(pg_index_indrelid_index, 2678, on pg_index using btree(indrelid oid_ops));
*** a/src/include/catalog/pg_class.h
--- b/src/include/catalog/pg_class.h
***************
*** 132,138 **** typedef FormData_pg_class *Form_pg_class;
*/
/* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId */
! DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f f r 28 0 t f f f f f 3 _null_ _null_ ));
DESCR("");
DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f f r 19 0 f f f f f f 3 _null_ _null_ ));
DESCR("");
--- 132,138 ----
*/
/* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId */
! DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f f r 30 0 t f f f f f 3 _null_ _null_ ));
DESCR("");
DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f f r 19 0 f f f f f f 3 _null_ _null_ ));
DESCR("");
*** a/src/include/catalog/pg_enum.h
--- b/src/include/catalog/pg_enum.h
***************
*** 35,40 **** CATALOG(pg_enum,3501)
--- 35,41 ----
{
Oid enumtypid; /* OID of owning enum type */
NameData enumlabel; /* text representation of enum value */
+ int4 enumsortorder; /* sort order for this enum label */
} FormData_pg_enum;
/* ----------------
***************
*** 48,56 **** typedef FormData_pg_enum *Form_pg_enum;
* compiler constants for pg_enum
* ----------------
*/
! #define Natts_pg_enum 2
#define Anum_pg_enum_enumtypid 1
#define Anum_pg_enum_enumlabel 2
/* ----------------
* pg_enum has no initial contents
--- 49,58 ----
* compiler constants for pg_enum
* ----------------
*/
! #define Natts_pg_enum 3
#define Anum_pg_enum_enumtypid 1
#define Anum_pg_enum_enumlabel 2
+ #define Anum_pg_enum_enumsortorder 3
/* ----------------
* pg_enum has no initial contents
***************
*** 63,67 **** typedef FormData_pg_enum *Form_pg_enum;
--- 65,71 ----
extern void EnumValuesCreate(Oid enumTypeOid, List *vals,
Oid binary_upgrade_next_pg_enum_oid);
extern void EnumValuesDelete(Oid enumTypeOid);
+ extern bool AddEnumLabel(Oid enumTypeOid, char *newVal, char *neighbour,
+ bool newValIsAfter, int nelems, bool elems_are_sorted);
#endif /* PG_ENUM_H */
*** a/src/include/catalog/pg_type.h
--- b/src/include/catalog/pg_type.h
***************
*** 194,199 **** CATALOG(pg_type,1247) BKI_BOOTSTRAP BKI_ROWTYPE_OID(71) BKI_SCHEMA_MACRO
--- 194,211 ----
*/
int4 typndims;
+ /*
+ * typnlabels, contains a count on the number of labels an enum type has,
+ * -1 for a non-enum type.
+ */
+ int4 typnlabels;
+
+ /*
+ * typsorted is true if the oids of an enum type reflect the type's sort
+ * order, false otherwise including for a non-enum type.
+ */
+ bool typsorted;
+
/*
* If typdefaultbin is not NULL, it is the nodeToString representation of
* a default expression for the type. Currently this is only used for
***************
*** 224,230 **** typedef FormData_pg_type *Form_pg_type;
* compiler constants for pg_type
* ----------------
*/
! #define Natts_pg_type 28
#define Anum_pg_type_typname 1
#define Anum_pg_type_typnamespace 2
#define Anum_pg_type_typowner 3
--- 236,242 ----
* compiler constants for pg_type
* ----------------
*/
! #define Natts_pg_type 30
#define Anum_pg_type_typname 1
#define Anum_pg_type_typnamespace 2
#define Anum_pg_type_typowner 3
***************
*** 251,258 **** typedef FormData_pg_type *Form_pg_type;
#define Anum_pg_type_typbasetype 24
#define Anum_pg_type_typtypmod 25
#define Anum_pg_type_typndims 26
! #define Anum_pg_type_typdefaultbin 27
! #define Anum_pg_type_typdefault 28
/* ----------------
--- 263,272 ----
#define Anum_pg_type_typbasetype 24
#define Anum_pg_type_typtypmod 25
#define Anum_pg_type_typndims 26
! #define Anum_pg_type_typnlabels 27
! #define Anum_pg_type_typsorted 28
! #define Anum_pg_type_typdefaultbin 29
! #define Anum_pg_type_typdefault 30
/* ----------------
***************
*** 269,351 **** typedef FormData_pg_type *Form_pg_type;
*/
/* OIDS 1 - 99 */
! DATA(insert OID = 16 ( bool PGNSP PGUID 1 t b B t t \054 0 0 1000 boolin boolout boolrecv boolsend - - - c p f 0 -1 0 _null_ _null_ ));
DESCR("boolean, 'true'/'false'");
#define BOOLOID 16
! DATA(insert OID = 17 ( bytea PGNSP PGUID -1 f b U f t \054 0 0 1001 byteain byteaout bytearecv byteasend - - - i x f 0 -1 0 _null_ _null_ ));
DESCR("variable-length string, binary values escaped");
#define BYTEAOID 17
! DATA(insert OID = 18 ( char PGNSP PGUID 1 t b S f t \054 0 0 1002 charin charout charrecv charsend - - - c p f 0 -1 0 _null_ _null_ ));
DESCR("single character");
#define CHAROID 18
! DATA(insert OID = 19 ( name PGNSP PGUID NAMEDATALEN f b S f t \054 0 18 1003 namein nameout namerecv namesend - - - c p f 0 -1 0 _null_ _null_ ));
DESCR("63-character type for storing system identifiers");
#define NAMEOID 19
! DATA(insert OID = 20 ( int8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 1016 int8in int8out int8recv int8send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("~18 digit integer, 8-byte storage");
#define INT8OID 20
! DATA(insert OID = 21 ( int2 PGNSP PGUID 2 t b N f t \054 0 0 1005 int2in int2out int2recv int2send - - - s p f 0 -1 0 _null_ _null_ ));
DESCR("-32 thousand to 32 thousand, 2-byte storage");
#define INT2OID 21
! DATA(insert OID = 22 ( int2vector PGNSP PGUID -1 f b A f t \054 0 21 1006 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("array of int2, used in system tables");
#define INT2VECTOROID 22
! DATA(insert OID = 23 ( int4 PGNSP PGUID 4 t b N f t \054 0 0 1007 int4in int4out int4recv int4send - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("-2 billion to 2 billion integer, 4-byte storage");
#define INT4OID 23
! DATA(insert OID = 24 ( regproc PGNSP PGUID 4 t b N f t \054 0 0 1008 regprocin regprocout regprocrecv regprocsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered procedure");
#define REGPROCOID 24
! DATA(insert OID = 25 ( text PGNSP PGUID -1 f b S t t \054 0 0 1009 textin textout textrecv textsend - - - i x f 0 -1 0 _null_ _null_ ));
DESCR("variable-length string, no limit specified");
#define TEXTOID 25
! DATA(insert OID = 26 ( oid PGNSP PGUID 4 t b N t t \054 0 0 1028 oidin oidout oidrecv oidsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("object identifier(oid), maximum 4 billion");
#define OIDOID 26
! DATA(insert OID = 27 ( tid PGNSP PGUID 6 f b U f t \054 0 0 1010 tidin tidout tidrecv tidsend - - - s p f 0 -1 0 _null_ _null_ ));
DESCR("(block, offset), physical location of tuple");
#define TIDOID 27
! DATA(insert OID = 28 ( xid PGNSP PGUID 4 t b U f t \054 0 0 1011 xidin xidout xidrecv xidsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("transaction id");
#define XIDOID 28
! DATA(insert OID = 29 ( cid PGNSP PGUID 4 t b U f t \054 0 0 1012 cidin cidout cidrecv cidsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("command identifier type, sequence in transaction id");
#define CIDOID 29
! DATA(insert OID = 30 ( oidvector PGNSP PGUID -1 f b A f t \054 0 26 1013 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("array of oids, used in system tables");
#define OIDVECTOROID 30
/* hand-built rowtype entries for bootstrapped catalogs */
/* NB: OIDs assigned here must match the BKI_ROWTYPE_OID declarations */
! DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c C f t \054 1247 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c C f t \054 1249 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c C f t \054 1255 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c C f t \054 1259 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
/* OIDS 100 - 199 */
! DATA(insert OID = 142 ( xml PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 _null_ _null_ ));
DESCR("XML content");
#define XMLOID 142
! DATA(insert OID = 143 ( _xml PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
/* OIDS 200 - 299 */
! DATA(insert OID = 210 ( smgr PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f 0 -1 0 _null_ _null_ ));
DESCR("storage manager");
/* OIDS 300 - 399 */
--- 283,365 ----
*/
/* OIDS 1 - 99 */
! DATA(insert OID = 16 ( bool PGNSP PGUID 1 t b B t t \054 0 0 1000 boolin boolout boolrecv boolsend - - - c p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("boolean, 'true'/'false'");
#define BOOLOID 16
! DATA(insert OID = 17 ( bytea PGNSP PGUID -1 f b U f t \054 0 0 1001 byteain byteaout bytearecv byteasend - - - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("variable-length string, binary values escaped");
#define BYTEAOID 17
! DATA(insert OID = 18 ( char PGNSP PGUID 1 t b S f t \054 0 0 1002 charin charout charrecv charsend - - - c p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("single character");
#define CHAROID 18
! DATA(insert OID = 19 ( name PGNSP PGUID NAMEDATALEN f b S f t \054 0 18 1003 namein nameout namerecv namesend - - - c p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("63-character type for storing system identifiers");
#define NAMEOID 19
! DATA(insert OID = 20 ( int8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 1016 int8in int8out int8recv int8send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("~18 digit integer, 8-byte storage");
#define INT8OID 20
! DATA(insert OID = 21 ( int2 PGNSP PGUID 2 t b N f t \054 0 0 1005 int2in int2out int2recv int2send - - - s p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("-32 thousand to 32 thousand, 2-byte storage");
#define INT2OID 21
! DATA(insert OID = 22 ( int2vector PGNSP PGUID -1 f b A f t \054 0 21 1006 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("array of int2, used in system tables");
#define INT2VECTOROID 22
! DATA(insert OID = 23 ( int4 PGNSP PGUID 4 t b N f t \054 0 0 1007 int4in int4out int4recv int4send - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("-2 billion to 2 billion integer, 4-byte storage");
#define INT4OID 23
! DATA(insert OID = 24 ( regproc PGNSP PGUID 4 t b N f t \054 0 0 1008 regprocin regprocout regprocrecv regprocsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered procedure");
#define REGPROCOID 24
! DATA(insert OID = 25 ( text PGNSP PGUID -1 f b S t t \054 0 0 1009 textin textout textrecv textsend - - - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("variable-length string, no limit specified");
#define TEXTOID 25
! DATA(insert OID = 26 ( oid PGNSP PGUID 4 t b N t t \054 0 0 1028 oidin oidout oidrecv oidsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("object identifier(oid), maximum 4 billion");
#define OIDOID 26
! DATA(insert OID = 27 ( tid PGNSP PGUID 6 f b U f t \054 0 0 1010 tidin tidout tidrecv tidsend - - - s p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("(block, offset), physical location of tuple");
#define TIDOID 27
! DATA(insert OID = 28 ( xid PGNSP PGUID 4 t b U f t \054 0 0 1011 xidin xidout xidrecv xidsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("transaction id");
#define XIDOID 28
! DATA(insert OID = 29 ( cid PGNSP PGUID 4 t b U f t \054 0 0 1012 cidin cidout cidrecv cidsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("command identifier type, sequence in transaction id");
#define CIDOID 29
! DATA(insert OID = 30 ( oidvector PGNSP PGUID -1 f b A f t \054 0 26 1013 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("array of oids, used in system tables");
#define OIDVECTOROID 30
/* hand-built rowtype entries for bootstrapped catalogs */
/* NB: OIDs assigned here must match the BKI_ROWTYPE_OID declarations */
! DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c C f t \054 1247 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c C f t \054 1249 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c C f t \054 1255 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c C f t \054 1259 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
/* OIDS 100 - 199 */
! DATA(insert OID = 142 ( xml PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("XML content");
#define XMLOID 142
! DATA(insert OID = 143 ( _xml PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
/* OIDS 200 - 299 */
! DATA(insert OID = 210 ( smgr PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("storage manager");
/* OIDS 300 - 399 */
***************
*** 355,585 **** DESCR("storage manager");
/* OIDS 500 - 599 */
/* OIDS 600 - 699 */
! DATA(insert OID = 600 ( point PGNSP PGUID 16 f b G f t \054 0 701 1017 point_in point_out point_recv point_send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("geometric point '(x, y)'");
#define POINTOID 600
! DATA(insert OID = 601 ( lseg PGNSP PGUID 32 f b G f t \054 0 600 1018 lseg_in lseg_out lseg_recv lseg_send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("geometric line segment '(pt1,pt2)'");
#define LSEGOID 601
! DATA(insert OID = 602 ( path PGNSP PGUID -1 f b G f t \054 0 0 1019 path_in path_out path_recv path_send - - - d x f 0 -1 0 _null_ _null_ ));
DESCR("geometric path '(pt1,...)'");
#define PATHOID 602
! DATA(insert OID = 603 ( box PGNSP PGUID 32 f b G f t \073 0 600 1020 box_in box_out box_recv box_send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("geometric box '(lower left,upper right)'");
#define BOXOID 603
! DATA(insert OID = 604 ( polygon PGNSP PGUID -1 f b G f t \054 0 0 1027 poly_in poly_out poly_recv poly_send - - - d x f 0 -1 0 _null_ _null_ ));
DESCR("geometric polygon '(pt1,...)'");
#define POLYGONOID 604
! DATA(insert OID = 628 ( line PGNSP PGUID 32 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("geometric line (not implemented)");
#define LINEOID 628
! DATA(insert OID = 629 ( _line PGNSP PGUID -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
DESCR("");
/* OIDS 700 - 799 */
! DATA(insert OID = 700 ( float4 PGNSP PGUID 4 FLOAT4PASSBYVAL b N f t \054 0 0 1021 float4in float4out float4recv float4send - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("single-precision floating point number, 4-byte storage");
#define FLOAT4OID 700
! DATA(insert OID = 701 ( float8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N t t \054 0 0 1022 float8in float8out float8recv float8send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("double-precision floating point number, 8-byte storage");
#define FLOAT8OID 701
! DATA(insert OID = 702 ( abstime PGNSP PGUID 4 t b D f t \054 0 0 1023 abstimein abstimeout abstimerecv abstimesend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("absolute, limited-range date and time (Unix system time)");
#define ABSTIMEOID 702
! DATA(insert OID = 703 ( reltime PGNSP PGUID 4 t b T f t \054 0 0 1024 reltimein reltimeout reltimerecv reltimesend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("relative, limited-range time interval (Unix delta time)");
#define RELTIMEOID 703
! DATA(insert OID = 704 ( tinterval PGNSP PGUID 12 f b T f t \054 0 0 1025 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("(abstime,abstime), time interval");
#define TINTERVALOID 704
! DATA(insert OID = 705 ( unknown PGNSP PGUID -2 f b X f t \054 0 0 0 unknownin unknownout unknownrecv unknownsend - - - c p f 0 -1 0 _null_ _null_ ));
DESCR("");
#define UNKNOWNOID 705
! DATA(insert OID = 718 ( circle PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("geometric circle '(center,radius)'");
#define CIRCLEOID 718
! DATA(insert OID = 719 ( _circle PGNSP PGUID -1 f b A f t \054 0 718 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 790 ( money PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("monetary amounts, $d,ddd.cc");
#define CASHOID 790
! DATA(insert OID = 791 ( _money PGNSP PGUID -1 f b A f t \054 0 790 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
/* OIDS 800 - 899 */
! DATA(insert OID = 829 ( macaddr PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("XX:XX:XX:XX:XX:XX, MAC address");
#define MACADDROID 829
! DATA(insert OID = 869 ( inet PGNSP PGUID -1 f b I t t \054 0 0 1041 inet_in inet_out inet_recv inet_send - - - i m f 0 -1 0 _null_ _null_ ));
DESCR("IP address/netmask, host address, netmask optional");
#define INETOID 869
! DATA(insert OID = 650 ( cidr PGNSP PGUID -1 f b I f t \054 0 0 651 cidr_in cidr_out cidr_recv cidr_send - - - i m f 0 -1 0 _null_ _null_ ));
DESCR("network IP address/netmask, network address");
#define CIDROID 650
/* OIDS 900 - 999 */
/* OIDS 1000 - 1099 */
! DATA(insert OID = 1000 ( _bool PGNSP PGUID -1 f b A f t \054 0 16 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1001 ( _bytea PGNSP PGUID -1 f b A f t \054 0 17 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1002 ( _char PGNSP PGUID -1 f b A f t \054 0 18 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b A f t \054 0 19 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b A f t \054 0 21 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b A f t \054 0 22 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b A f t \054 0 23 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
#define INT4ARRAYOID 1007
! DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b A f t \054 0 24 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b A f t \054 0 25 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
#define TEXTARRAYOID 1009
! DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b A f t \054 0 26 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b A f t \054 0 27 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b A f t \054 0 28 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b A f t \054 0 29 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1013 ( _oidvector PGNSP PGUID -1 f b A f t \054 0 30 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1014 ( _bpchar PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1015 ( _varchar PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1016 ( _int8 PGNSP PGUID -1 f b A f t \054 0 20 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1017 ( _point PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1018 ( _lseg PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1019 ( _path PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1020 ( _box PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1021 ( _float4 PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
#define FLOAT4ARRAYOID 1021
! DATA(insert OID = 1022 ( _float8 PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1033 ( aclitem PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("access control list");
#define ACLITEMOID 1033
! DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1263 ( _cstring PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
#define CSTRINGARRAYOID 1263
! DATA(insert OID = 1042 ( bpchar PGNSP PGUID -1 f b S f t \054 0 0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 _null_ _null_ ));
DESCR("char(length), blank-padded string, fixed storage length");
#define BPCHAROID 1042
! DATA(insert OID = 1043 ( varchar PGNSP PGUID -1 f b S f t \054 0 0 1015 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f 0 -1 0 _null_ _null_ ));
DESCR("varchar(length), non-blank-padded string, variable storage length");
#define VARCHAROID 1043
! DATA(insert OID = 1082 ( date PGNSP PGUID 4 t b D f t \054 0 0 1182 date_in date_out date_recv date_send - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("date");
#define DATEOID 1082
! DATA(insert OID = 1083 ( time PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1183 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f 0 -1 0 _null_ _null_ ));
DESCR("time of day");
#define TIMEOID 1083
/* OIDS 1100 - 1199 */
! DATA(insert OID = 1114 ( timestamp PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 _null_ _null_ ));
DESCR("date and time");
#define TIMESTAMPOID 1114
! DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1182 ( _date PGNSP PGUID -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1183 ( _time PGNSP PGUID -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1184 ( timestamptz PGNSP PGUID 8 FLOAT8PASSBYVAL b D t t \054 0 0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 _null_ _null_ ));
DESCR("date and time with time zone");
#define TIMESTAMPTZOID 1184
! DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0 1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1186 ( interval PGNSP PGUID 16 f b T t t \054 0 0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 _null_ _null_ ));
DESCR("@ <number> <units>, time interval");
#define INTERVALOID 1186
! DATA(insert OID = 1187 ( _interval PGNSP PGUID -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout - d x f 0 -1 0 _null_ _null_ ));
/* OIDS 1200 - 1299 */
! DATA(insert OID = 1231 ( _numeric PGNSP PGUID -1 f b A f t \054 0 1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1266 ( timetz PGNSP PGUID 12 f b D f t \054 0 0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 _null_ _null_ ));
DESCR("time of day with time zone");
#define TIMETZOID 1266
! DATA(insert OID = 1270 ( _timetz PGNSP PGUID -1 f b A f t \054 0 1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout - d x f 0 -1 0 _null_ _null_ ));
/* OIDS 1500 - 1599 */
! DATA(insert OID = 1560 ( bit PGNSP PGUID -1 f b V f t \054 0 0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 _null_ _null_ ));
DESCR("fixed-length bit string");
#define BITOID 1560
! DATA(insert OID = 1561 ( _bit PGNSP PGUID -1 f b A f t \054 0 1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1562 ( varbit PGNSP PGUID -1 f b V t t \054 0 0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 _null_ _null_ ));
DESCR("variable-length bit string");
#define VARBITOID 1562
! DATA(insert OID = 1563 ( _varbit PGNSP PGUID -1 f b A f t \054 0 1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout - i x f 0 -1 0 _null_ _null_ ));
/* OIDS 1600 - 1699 */
/* OIDS 1700 - 1799 */
! DATA(insert OID = 1700 ( numeric PGNSP PGUID -1 f b N f t \054 0 0 1231 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f 0 -1 0 _null_ _null_ ));
DESCR("numeric(precision, decimal), arbitrary precision number");
#define NUMERICOID 1700
! DATA(insert OID = 1790 ( refcursor PGNSP PGUID -1 f b U f t \054 0 0 2201 textin textout textrecv textsend - - - i x f 0 -1 0 _null_ _null_ ));
DESCR("reference to cursor (portal name)");
#define REFCURSOROID 1790
/* OIDS 2200 - 2299 */
! DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 2202 ( regprocedure PGNSP PGUID 4 t b N f t \054 0 0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered procedure (with args)");
#define REGPROCEDUREOID 2202
! DATA(insert OID = 2203 ( regoper PGNSP PGUID 4 t b N f t \054 0 0 2208 regoperin regoperout regoperrecv regopersend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered operator");
#define REGOPEROID 2203
! DATA(insert OID = 2204 ( regoperator PGNSP PGUID 4 t b N f t \054 0 0 2209 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered operator (with args)");
#define REGOPERATOROID 2204
! DATA(insert OID = 2205 ( regclass PGNSP PGUID 4 t b N f t \054 0 0 2210 regclassin regclassout regclassrecv regclasssend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered class");
#define REGCLASSOID 2205
! DATA(insert OID = 2206 ( regtype PGNSP PGUID 4 t b N f t \054 0 0 2211 regtypein regtypeout regtyperecv regtypesend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered type");
#define REGTYPEOID 2206
! DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
#define REGTYPEARRAYOID 2211
/* uuid */
! DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 _null_ _null_ ));
DESCR("UUID datatype");
! DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
/* text search */
! DATA(insert OID = 3614 ( tsvector PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 _null_ _null_ ));
DESCR("text representation for text search");
#define TSVECTOROID 3614
! DATA(insert OID = 3642 ( gtsvector PGNSP PGUID -1 f b U f t \054 0 0 3644 gtsvectorin gtsvectorout - - - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("GiST index internal text representation for text search");
#define GTSVECTOROID 3642
! DATA(insert OID = 3615 ( tsquery PGNSP PGUID -1 f b U f t \054 0 0 3645 tsqueryin tsqueryout tsqueryrecv tsquerysend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("query representation for text search");
#define TSQUERYOID 3615
! DATA(insert OID = 3734 ( regconfig PGNSP PGUID 4 t b N f t \054 0 0 3735 regconfigin regconfigout regconfigrecv regconfigsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered text search configuration");
#define REGCONFIGOID 3734
! DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered text search dictionary");
#define REGDICTIONARYOID 3769
! DATA(insert OID = 3643 ( _tsvector PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 3644 ( _gtsvector PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 _null_ _null_ ));
DESCR("txid snapshot");
! DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
/*
* pseudo-types
--- 369,599 ----
/* OIDS 500 - 599 */
/* OIDS 600 - 699 */
! DATA(insert OID = 600 ( point PGNSP PGUID 16 f b G f t \054 0 701 1017 point_in point_out point_recv point_send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric point '(x, y)'");
#define POINTOID 600
! DATA(insert OID = 601 ( lseg PGNSP PGUID 32 f b G f t \054 0 600 1018 lseg_in lseg_out lseg_recv lseg_send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric line segment '(pt1,pt2)'");
#define LSEGOID 601
! DATA(insert OID = 602 ( path PGNSP PGUID -1 f b G f t \054 0 0 1019 path_in path_out path_recv path_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric path '(pt1,...)'");
#define PATHOID 602
! DATA(insert OID = 603 ( box PGNSP PGUID 32 f b G f t \073 0 600 1020 box_in box_out box_recv box_send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric box '(lower left,upper right)'");
#define BOXOID 603
! DATA(insert OID = 604 ( polygon PGNSP PGUID -1 f b G f t \054 0 0 1027 poly_in poly_out poly_recv poly_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric polygon '(pt1,...)'");
#define POLYGONOID 604
! DATA(insert OID = 628 ( line PGNSP PGUID 32 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric line (not implemented)");
#define LINEOID 628
! DATA(insert OID = 629 ( _line PGNSP PGUID -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("");
/* OIDS 700 - 799 */
! DATA(insert OID = 700 ( float4 PGNSP PGUID 4 FLOAT4PASSBYVAL b N f t \054 0 0 1021 float4in float4out float4recv float4send - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("single-precision floating point number, 4-byte storage");
#define FLOAT4OID 700
! DATA(insert OID = 701 ( float8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N t t \054 0 0 1022 float8in float8out float8recv float8send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("double-precision floating point number, 8-byte storage");
#define FLOAT8OID 701
! DATA(insert OID = 702 ( abstime PGNSP PGUID 4 t b D f t \054 0 0 1023 abstimein abstimeout abstimerecv abstimesend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("absolute, limited-range date and time (Unix system time)");
#define ABSTIMEOID 702
! DATA(insert OID = 703 ( reltime PGNSP PGUID 4 t b T f t \054 0 0 1024 reltimein reltimeout reltimerecv reltimesend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("relative, limited-range time interval (Unix delta time)");
#define RELTIMEOID 703
! DATA(insert OID = 704 ( tinterval PGNSP PGUID 12 f b T f t \054 0 0 1025 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("(abstime,abstime), time interval");
#define TINTERVALOID 704
! DATA(insert OID = 705 ( unknown PGNSP PGUID -2 f b X f t \054 0 0 0 unknownin unknownout unknownrecv unknownsend - - - c p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("");
#define UNKNOWNOID 705
! DATA(insert OID = 718 ( circle PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric circle '(center,radius)'");
#define CIRCLEOID 718
! DATA(insert OID = 719 ( _circle PGNSP PGUID -1 f b A f t \054 0 718 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 790 ( money PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("monetary amounts, $d,ddd.cc");
#define CASHOID 790
! DATA(insert OID = 791 ( _money PGNSP PGUID -1 f b A f t \054 0 790 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
/* OIDS 800 - 899 */
! DATA(insert OID = 829 ( macaddr PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("XX:XX:XX:XX:XX:XX, MAC address");
#define MACADDROID 829
! DATA(insert OID = 869 ( inet PGNSP PGUID -1 f b I t t \054 0 0 1041 inet_in inet_out inet_recv inet_send - - - i m f 0 -1 0 -1 f _null_ _null_ ));
DESCR("IP address/netmask, host address, netmask optional");
#define INETOID 869
! DATA(insert OID = 650 ( cidr PGNSP PGUID -1 f b I f t \054 0 0 651 cidr_in cidr_out cidr_recv cidr_send - - - i m f 0 -1 0 -1 f _null_ _null_ ));
DESCR("network IP address/netmask, network address");
#define CIDROID 650
/* OIDS 900 - 999 */
/* OIDS 1000 - 1099 */
! DATA(insert OID = 1000 ( _bool PGNSP PGUID -1 f b A f t \054 0 16 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1001 ( _bytea PGNSP PGUID -1 f b A f t \054 0 17 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1002 ( _char PGNSP PGUID -1 f b A f t \054 0 18 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b A f t \054 0 19 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b A f t \054 0 21 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b A f t \054 0 22 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b A f t \054 0 23 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
#define INT4ARRAYOID 1007
! DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b A f t \054 0 24 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b A f t \054 0 25 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
#define TEXTARRAYOID 1009
! DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b A f t \054 0 26 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b A f t \054 0 27 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b A f t \054 0 28 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b A f t \054 0 29 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1013 ( _oidvector PGNSP PGUID -1 f b A f t \054 0 30 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1014 ( _bpchar PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1015 ( _varchar PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1016 ( _int8 PGNSP PGUID -1 f b A f t \054 0 20 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1017 ( _point PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1018 ( _lseg PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1019 ( _path PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1020 ( _box PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1021 ( _float4 PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
#define FLOAT4ARRAYOID 1021
! DATA(insert OID = 1022 ( _float8 PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1033 ( aclitem PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("access control list");
#define ACLITEMOID 1033
! DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1263 ( _cstring PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
#define CSTRINGARRAYOID 1263
! DATA(insert OID = 1042 ( bpchar PGNSP PGUID -1 f b S f t \054 0 0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("char(length), blank-padded string, fixed storage length");
#define BPCHAROID 1042
! DATA(insert OID = 1043 ( varchar PGNSP PGUID -1 f b S f t \054 0 0 1015 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("varchar(length), non-blank-padded string, variable storage length");
#define VARCHAROID 1043
! DATA(insert OID = 1082 ( date PGNSP PGUID 4 t b D f t \054 0 0 1182 date_in date_out date_recv date_send - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("date");
#define DATEOID 1082
! DATA(insert OID = 1083 ( time PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1183 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("time of day");
#define TIMEOID 1083
/* OIDS 1100 - 1199 */
! DATA(insert OID = 1114 ( timestamp PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("date and time");
#define TIMESTAMPOID 1114
! DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1182 ( _date PGNSP PGUID -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1183 ( _time PGNSP PGUID -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1184 ( timestamptz PGNSP PGUID 8 FLOAT8PASSBYVAL b D t t \054 0 0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("date and time with time zone");
#define TIMESTAMPTZOID 1184
! DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0 1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1186 ( interval PGNSP PGUID 16 f b T t t \054 0 0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("@ <number> <units>, time interval");
#define INTERVALOID 1186
! DATA(insert OID = 1187 ( _interval PGNSP PGUID -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout - d x f 0 -1 0 -1 f _null_ _null_ ));
/* OIDS 1200 - 1299 */
! DATA(insert OID = 1231 ( _numeric PGNSP PGUID -1 f b A f t \054 0 1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1266 ( timetz PGNSP PGUID 12 f b D f t \054 0 0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("time of day with time zone");
#define TIMETZOID 1266
! DATA(insert OID = 1270 ( _timetz PGNSP PGUID -1 f b A f t \054 0 1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout - d x f 0 -1 0 -1 f _null_ _null_ ));
/* OIDS 1500 - 1599 */
! DATA(insert OID = 1560 ( bit PGNSP PGUID -1 f b V f t \054 0 0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("fixed-length bit string");
#define BITOID 1560
! DATA(insert OID = 1561 ( _bit PGNSP PGUID -1 f b A f t \054 0 1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1562 ( varbit PGNSP PGUID -1 f b V t t \054 0 0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("variable-length bit string");
#define VARBITOID 1562
! DATA(insert OID = 1563 ( _varbit PGNSP PGUID -1 f b A f t \054 0 1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
/* OIDS 1600 - 1699 */
/* OIDS 1700 - 1799 */
! DATA(insert OID = 1700 ( numeric PGNSP PGUID -1 f b N f t \054 0 0 1231 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f 0 -1 0 -1 f _null_ _null_ ));
DESCR("numeric(precision, decimal), arbitrary precision number");
#define NUMERICOID 1700
! DATA(insert OID = 1790 ( refcursor PGNSP PGUID -1 f b U f t \054 0 0 2201 textin textout textrecv textsend - - - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("reference to cursor (portal name)");
#define REFCURSOROID 1790
/* OIDS 2200 - 2299 */
! DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 2202 ( regprocedure PGNSP PGUID 4 t b N f t \054 0 0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered procedure (with args)");
#define REGPROCEDUREOID 2202
! DATA(insert OID = 2203 ( regoper PGNSP PGUID 4 t b N f t \054 0 0 2208 regoperin regoperout regoperrecv regopersend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered operator");
#define REGOPEROID 2203
! DATA(insert OID = 2204 ( regoperator PGNSP PGUID 4 t b N f t \054 0 0 2209 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered operator (with args)");
#define REGOPERATOROID 2204
! DATA(insert OID = 2205 ( regclass PGNSP PGUID 4 t b N f t \054 0 0 2210 regclassin regclassout regclassrecv regclasssend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered class");
#define REGCLASSOID 2205
! DATA(insert OID = 2206 ( regtype PGNSP PGUID 4 t b N f t \054 0 0 2211 regtypein regtypeout regtyperecv regtypesend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered type");
#define REGTYPEOID 2206
! DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
#define REGTYPEARRAYOID 2211
/* uuid */
! DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("UUID datatype");
! DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
/* text search */
! DATA(insert OID = 3614 ( tsvector PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("text representation for text search");
#define TSVECTOROID 3614
! DATA(insert OID = 3642 ( gtsvector PGNSP PGUID -1 f b U f t \054 0 0 3644 gtsvectorin gtsvectorout - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("GiST index internal text representation for text search");
#define GTSVECTOROID 3642
! DATA(insert OID = 3615 ( tsquery PGNSP PGUID -1 f b U f t \054 0 0 3645 tsqueryin tsqueryout tsqueryrecv tsquerysend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("query representation for text search");
#define TSQUERYOID 3615
! DATA(insert OID = 3734 ( regconfig PGNSP PGUID 4 t b N f t \054 0 0 3735 regconfigin regconfigout regconfigrecv regconfigsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered text search configuration");
#define REGCONFIGOID 3734
! DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered text search dictionary");
#define REGDICTIONARYOID 3769
! DATA(insert OID = 3643 ( _tsvector PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 3644 ( _gtsvector PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("txid snapshot");
! DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
/*
* pseudo-types
***************
*** 594,624 **** DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 a
* but there is now support for it in records and arrays. Perhaps we should
* just treat it as a regular base type?
*/
! DATA(insert OID = 2249 ( record PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
#define RECORDOID 2249
! DATA(insert OID = 2287 ( _record PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
#define RECORDARRAYOID 2287
! DATA(insert OID = 2275 ( cstring PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 _null_ _null_ ));
#define CSTRINGOID 2275
! DATA(insert OID = 2276 ( any PGNSP PGUID 4 t p P f t \054 0 0 0 any_in any_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define ANYOID 2276
! DATA(insert OID = 2277 ( anyarray PGNSP PGUID -1 f p P f t \054 0 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f 0 -1 0 _null_ _null_ ));
#define ANYARRAYOID 2277
! DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define VOIDOID 2278
! DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define TRIGGEROID 2279
! DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define LANGUAGE_HANDLEROID 2280
! DATA(insert OID = 2281 ( internal PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 _null_ _null_ ));
#define INTERNALOID 2281
! DATA(insert OID = 2282 ( opaque PGNSP PGUID 4 t p P f t \054 0 0 0 opaque_in opaque_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define OPAQUEOID 2282
! DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p P f t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define ANYELEMENTOID 2283
! DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p P f t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define ANYNONARRAYOID 2776
! DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define ANYENUMOID 3500
--- 608,638 ----
* but there is now support for it in records and arrays. Perhaps we should
* just treat it as a regular base type?
*/
! DATA(insert OID = 2249 ( record PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
#define RECORDOID 2249
! DATA(insert OID = 2287 ( _record PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
#define RECORDARRAYOID 2287
! DATA(insert OID = 2275 ( cstring PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 -1 f _null_ _null_ ));
#define CSTRINGOID 2275
! DATA(insert OID = 2276 ( any PGNSP PGUID 4 t p P f t \054 0 0 0 any_in any_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define ANYOID 2276
! DATA(insert OID = 2277 ( anyarray PGNSP PGUID -1 f p P f t \054 0 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
#define ANYARRAYOID 2277
! DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define VOIDOID 2278
! DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define TRIGGEROID 2279
! DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define LANGUAGE_HANDLEROID 2280
! DATA(insert OID = 2281 ( internal PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 -1 f _null_ _null_ ));
#define INTERNALOID 2281
! DATA(insert OID = 2282 ( opaque PGNSP PGUID 4 t p P f t \054 0 0 0 opaque_in opaque_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define OPAQUEOID 2282
! DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p P f t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define ANYELEMENTOID 2283
! DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p P f t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define ANYNONARRAYOID 2776
! DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define ANYENUMOID 3500
*** a/src/include/catalog/pg_type_fn.h
--- b/src/include/catalog/pg_type_fn.h
***************
*** 50,56 **** extern Oid TypeCreate(Oid newTypeOid,
char storage,
int32 typeMod,
int32 typNDims,
! bool typeNotNull);
extern void GenerateTypeDependencies(Oid typeNamespace,
Oid typeObjectId,
--- 50,58 ----
char storage,
int32 typeMod,
int32 typNDims,
! bool typeNotNull,
! int32 typeNLabels,
! bool typeSorted);
extern void GenerateTypeDependencies(Oid typeNamespace,
Oid typeObjectId,
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
***************
*** 346,351 **** typedef enum NodeTag
--- 346,352 ----
T_AlterUserMappingStmt,
T_DropUserMappingStmt,
T_AlterTableSpaceOptionsStmt,
+ T_AlterEnumStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 2171,2176 **** typedef struct CreateEnumStmt
--- 2171,2190 ----
/* ----------------------
+ * Alter Type Statement, enum types
+ * ----------------------
+ */
+ typedef struct AlterEnumStmt
+ {
+ NodeTag type;
+ List *typeName; /* qualified name (list of Value strings) */
+ char *newVal; /* new enum value */
+ char *newValNeighbour;/* neighbouring enum value */
+ bool newValIsAfter; /* new enum value is after neighbour? */
+ } AlterEnumStmt;
+
+
+ /* ----------------------
* Create View Statement
* ----------------------
*/
*** a/src/test/regress/expected/enum.out
--- b/src/test/regress/expected/enum.out
***************
*** 25,30 **** ERROR: invalid input value for enum rainbow: "mauve"
--- 25,108 ----
LINE 1: SELECT 'mauve'::rainbow;
^
--
+ -- adding new values
+ --
+ CREATE TYPE planets AS ENUM ( 'venus', 'earth', 'mars' );
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+ enumlabel | enumsortorder
+ -----------+---------------
+ venus | 1
+ earth | 2
+ mars | 3
+ (3 rows)
+
+ ALTER TYPE planets ADD 'uranus';
+ SELECT typnlabels, typsorted
+ FROM pg_type
+ WHERE oid = 'planets'::regtype;
+ typnlabels | typsorted
+ ------------+-----------
+ 4 | t
+ (1 row)
+
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+ enumlabel | enumsortorder
+ -----------+---------------
+ venus | 1
+ earth | 2
+ mars | 3
+ uranus | 4
+ (4 rows)
+
+ ALTER TYPE planets ADD 'mercury' BEFORE 'venus';
+ ALTER TYPE planets ADD 'saturn' BEFORE 'uranus';
+ ALTER TYPE planets ADD 'jupiter' AFTER 'mars';
+ ALTER TYPE planets ADD 'neptune' AFTER 'uranus';
+ SELECT typnlabels, typsorted
+ FROM pg_type
+ WHERE oid = 'planets'::regtype;
+ typnlabels | typsorted
+ ------------+-----------
+ 8 | f
+ (1 row)
+
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+ enumlabel | enumsortorder
+ -----------+---------------
+ mercury | 1
+ venus | 2
+ earth | 3
+ mars | 4
+ jupiter | 5
+ saturn | 6
+ uranus | 7
+ neptune | 8
+ (8 rows)
+
+ select 'mars'::planets > 'mercury' as using_sortorder;
+ using_sortorder
+ -----------------
+ t
+ (1 row)
+
+ -- errors for adding labels
+ ALTER TYPE planets ADD
+ 'plutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutopluto';
+ ERROR: invalid enum label "plutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutopluto"
+ DETAIL: Labels must be 63 characters or less.
+ ALTER TYPE planets ADD 'pluto' AFTER 'zeus';
+ ERROR: "zeus" is not an existing label.
+ DROP TYPE planets;
+ --
-- Basic table creation, row selection
--
CREATE TABLE enumtest (col rainbow);
***************
*** 403,409 **** SELECT COUNT(*) FROM pg_type WHERE typname = 'rainbow';
SELECT * FROM pg_enum WHERE NOT EXISTS
(SELECT 1 FROM pg_type WHERE pg_type.oid = enumtypid);
! enumtypid | enumlabel
! -----------+-----------
(0 rows)
--- 481,487 ----
SELECT * FROM pg_enum WHERE NOT EXISTS
(SELECT 1 FROM pg_type WHERE pg_type.oid = enumtypid);
! enumtypid | enumlabel | enumsortorder
! -----------+-----------+---------------
(0 rows)
*** a/src/test/regress/sql/enum.sql
--- b/src/test/regress/sql/enum.sql
***************
*** 16,21 **** SELECT 'red'::rainbow;
--- 16,69 ----
SELECT 'mauve'::rainbow;
--
+ -- adding new values
+ --
+
+ CREATE TYPE planets AS ENUM ( 'venus', 'earth', 'mars' );
+
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+
+ ALTER TYPE planets ADD 'uranus';
+
+ SELECT typnlabels, typsorted
+ FROM pg_type
+ WHERE oid = 'planets'::regtype;
+
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+
+ ALTER TYPE planets ADD 'mercury' BEFORE 'venus';
+
+ ALTER TYPE planets ADD 'saturn' BEFORE 'uranus';
+
+ ALTER TYPE planets ADD 'jupiter' AFTER 'mars';
+
+ ALTER TYPE planets ADD 'neptune' AFTER 'uranus';
+
+ SELECT typnlabels, typsorted
+ FROM pg_type
+ WHERE oid = 'planets'::regtype;
+
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+
+ select 'mars'::planets > 'mercury' as using_sortorder;
+
+ -- errors for adding labels
+ ALTER TYPE planets ADD
+ 'plutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutopluto';
+
+ ALTER TYPE planets ADD 'pluto' AFTER 'zeus';
+
+ DROP TYPE planets;
+ --
-- Basic table creation, row selection
--
CREATE TABLE enumtest (col rainbow);
Excerpts from Andrew Dunstan's message of lun ago 23 05:35:09 -0400 2010:
To add a label at the end of the list, do:
ALTER TYPE myenum ADD 'newlabel';
To add a label somewhere else, do:
ALTER TYPE myenum ADD 'newlabel' BEFORE 'existinglabel';
or
ALTER TYPE myenum ADD 'newlabel' AFTER 'existinglabel';
What do you need AFTER for? Seems to me that BEFORE should be enough.
(You already have the unadorned syntax for adding an item after the last
one, which is the corner case that BEFORE alone doesn't cover).
--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support
On Aug 23, 2010, at 2:35 AM, Andrew Dunstan wrote:
I'm not wedded to the syntax. Let the bikeshedding begin.
Seems pretty good to me as-is.
David
On Mon, August 23, 2010 11:49 am, Alvaro Herrera wrote:
Excerpts from Andrew Dunstan's message of lun ago 23 05:35:09 -0400 2010:
To add a label at the end of the list, do:
ALTER TYPE myenum ADD 'newlabel';
To add a label somewhere else, do:
ALTER TYPE myenum ADD 'newlabel' BEFORE 'existinglabel';
or
ALTER TYPE myenum ADD 'newlabel' AFTER 'existinglabel';
What do you need AFTER for? Seems to me that BEFORE should be enough.
(You already have the unadorned syntax for adding an item after the last
one, which is the corner case that BEFORE alone doesn't cover).
You're right. Strictly speaking we don't need it. But it doesn't hurt much
to provide it for a degree of symmetry.
cheers
andrew
On Mon, August 23, 2010 11:49 am, Alvaro Herrera wrote:
Excerpts from Andrew Dunstan's message of lun ago 23 05:35:09 -0400 2010:
To add a label at the end of the list, do:
ALTER TYPE myenum ADD 'newlabel';
To add a label somewhere else, do:
ALTER TYPE myenum ADD 'newlabel' BEFORE 'existinglabel';
or
ALTER TYPE myenum ADD 'newlabel' AFTER 'existinglabel';
What do you need AFTER for? Seems to me that BEFORE should be enough.
(You already have the unadorned syntax for adding an item after the last
one, which is the corner case that BEFORE alone doesn't cover).
You're right. Strictly speaking we don't need it. But it doesn't hurt much
to provide it for a degree of symmetry.
cheers
andrew
"Andrew Dunstan" <andrew@dunslane.net> writes:
On Mon, August 23, 2010 11:49 am, Alvaro Herrera wrote:
What do you need AFTER for? Seems to me that BEFORE should be enough.
(You already have the unadorned syntax for adding an item after the last
one, which is the corner case that BEFORE alone doesn't cover).
You're right. Strictly speaking we don't need it. But it doesn't hurt much
to provide it for a degree of symmetry.
I'm with Alvaro: drop the AFTER variant. It provides more than one way
to do the same thing, which isn't that exciting, and it's also going to
make it harder to document the performance issues. Without that, you
can just say "ADD BEFORE will make the enum slower, but plain ADD won't"
(ignoring the issue of OID wraparound, which'll confuse matters in any
case).
regards, tom lane
You're right. Strictly speaking we don't need it. But it doesn't hurt much
to provide it for a degree of symmetry.
Swami Josh predicts that if we don't add AFTER now, we'll be adding it
in 2 years when enough people complain about it.
--
-- Josh Berkus
PostgreSQL Experts Inc.
http://www.pgexperts.com
On 23 August 2010 10:35, Andrew Dunstan <andrew@dunslane.net> wrote:
Attached is a WIP patch that allows enums to be extended with additional
labels arbitrarily. As previously discussed, it works by adding an explicit
sort order column to pg_enum. It keeps track of whether the labels are
correctly sorted by oid value, and if so uses that for comparison, so the
possible performance impact on existing uses, and on almost all cases where
a label is added at the end of the list, should be negligible.Open items include
* some additional error checking required
* missing documentation
* pg_upgrade considerationsTo add a label at the end of the list, do:
ALTER TYPE myenum ADD 'newlabel';
To add a label somewhere else, do:
ALTER TYPE myenum ADD 'newlabel' BEFORE 'existinglabel';
or
ALTER TYPE myenum ADD 'newlabel' AFTER 'existinglabel';
I'm not wedded to the syntax. Let the bikeshedding begin.
cheers
andrew
When you write the supporting doc changes, you might want to add a
note in to mention that you cannot remove a label once it has been
added.
Will the modified enums remain intact after a dump/restore?
--
Thom Brown
Registered Linux user: #516935
On Mon, Aug 23, 2010 at 11:49:41AM -0400, Alvaro Herrera wrote:
Excerpts from Andrew Dunstan's message of lun ago 23 05:35:09 -0400 2010:
To add a label at the end of the list, do:
ALTER TYPE myenum ADD 'newlabel';
To add a label somewhere else, do:
ALTER TYPE myenum ADD 'newlabel' BEFORE 'existinglabel';
or
ALTER TYPE myenum ADD 'newlabel' AFTER 'existinglabel';
What do you need AFTER for? Seems to me that BEFORE should be enough.
(You already have the unadorned syntax for adding an item after the last
one, which is the corner case that BEFORE alone doesn't cover).
Making things easier for the users is a good thing all by itself :)
+1 for including both BEFORE and AFTER. Would it be worth it to allow
something like FIRST and LAST?
ALTER TYPE myenum ADD 'newlabel' FIRST;
ALTER TYPE myenum ADD 'newlabel' LAST;
Cheers,
David.
--
David Fetter <david@fetter.org> http://fetter.org/
Phone: +1 415 235 3778 AIM: dfetter666 Yahoo!: dfetter
Skype: davidfetter XMPP: david.fetter@gmail.com
iCal: webcal://www.tripit.com/feed/ical/people/david74/tripit.ics
Remember to vote!
Consider donating to Postgres: http://www.postgresql.org/about/donate
On Mon, Aug 23, 2010 at 01:54:40PM -0400, Tom Lane wrote:
"Andrew Dunstan" <andrew@dunslane.net> writes:
On Mon, August 23, 2010 11:49 am, Alvaro Herrera wrote:
What do you need AFTER for? Seems to me that BEFORE should be
enough. (You already have the unadorned syntax for adding an
item after the last one, which is the corner case that BEFORE
alone doesn't cover).You're right. Strictly speaking we don't need it. But it doesn't
hurt much to provide it for a degree of symmetry.I'm with Alvaro: drop the AFTER variant. It provides more than one
way to do the same thing, which isn't that exciting,
Not to you, maybe, but to users, it's really handy to have intuitive,
rather than strictly orthogonal, ways to do things.
Cheers,
David.
--
David Fetter <david@fetter.org> http://fetter.org/
Phone: +1 415 235 3778 AIM: dfetter666 Yahoo!: dfetter
Skype: davidfetter XMPP: david.fetter@gmail.com
iCal: webcal://www.tripit.com/feed/ical/people/david74/tripit.ics
Remember to vote!
Consider donating to Postgres: http://www.postgresql.org/about/donate
Josh Berkus <josh@agliodbs.com> writes:
Swami Josh predicts that if we don't add AFTER now, we'll be adding it
in 2 years when enough people complain about it.
If it's not there, no one will ever miss it. You might as well argue
that there should be a way of creating a foreign key reference by
ALTER'ing the referenced table instead of the referencing table.
Sure, if the SQL committee was into symmetry, they might have provided
such a thing. But they didn't and no one misses it.
regards, tom lane
On Mon, Aug 23, 2010 at 1:54 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
"Andrew Dunstan" <andrew@dunslane.net> writes:
On Mon, August 23, 2010 11:49 am, Alvaro Herrera wrote:
What do you need AFTER for? Seems to me that BEFORE should be enough.
(You already have the unadorned syntax for adding an item after the last
one, which is the corner case that BEFORE alone doesn't cover).You're right. Strictly speaking we don't need it. But it doesn't hurt much
to provide it for a degree of symmetry.I'm with Alvaro: drop the AFTER variant. It provides more than one way
to do the same thing, which isn't that exciting, and it's also going to
make it harder to document the performance issues. Without that, you
can just say "ADD BEFORE will make the enum slower, but plain ADD won't"
(ignoring the issue of OID wraparound, which'll confuse matters in any
case).
But what if you want to insert an OID at the end? You can't do it if
all you've got is BEFORE:
CREATE TYPE colors AS ENUM ('red', 'green', 'blue');
If I want it to become ('red', 'green', 'blue', 'orange'), what am I to do?
On 23 August 2010 19:25, Joseph Adams <joeyadams3.14159@gmail.com> wrote:
On Mon, Aug 23, 2010 at 1:54 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
"Andrew Dunstan" <andrew@dunslane.net> writes:
On Mon, August 23, 2010 11:49 am, Alvaro Herrera wrote:
What do you need AFTER for? Seems to me that BEFORE should be enough.
(You already have the unadorned syntax for adding an item after the last
one, which is the corner case that BEFORE alone doesn't cover).You're right. Strictly speaking we don't need it. But it doesn't hurt much
to provide it for a degree of symmetry.I'm with Alvaro: drop the AFTER variant. It provides more than one way
to do the same thing, which isn't that exciting, and it's also going to
make it harder to document the performance issues. Without that, you
can just say "ADD BEFORE will make the enum slower, but plain ADD won't"
(ignoring the issue of OID wraparound, which'll confuse matters in any
case).But what if you want to insert an OID at the end? You can't do it if
all you've got is BEFORE:CREATE TYPE colors AS ENUM ('red', 'green', 'blue');
If I want it to become ('red', 'green', 'blue', 'orange'), what am I to do?
ALTER TYPE colors ADD 'orange';
--
Thom Brown
Registered Linux user: #516935
Thom Brown <thom@linux.com> writes:
On 23 August 2010 19:25, Joseph Adams <joeyadams3.14159@gmail.com> wrote:
But what if you want to insert an OID at the end?
ALTER TYPE colors ADD 'orange';
Alternatively, if people are dead set on symmetry, what we should do
to simplify is drop *this* syntax, and just have the BEFORE and AFTER
variants.
regards, tom lane
If it's not there, no one will ever miss it. You might as well argue
that there should be a way of creating a foreign key reference by
ALTER'ing the referenced table instead of the referencing table.
Sure, if the SQL committee was into symmetry, they might have provided
such a thing. But they didn't and no one misses it.
That's a very different situation, since the relationship is not
symmetrical, and it would take far more than a single keyword. Analogy
fail.
And one of the reasons people don't miss it is because far too many
users don't use FKs in the first place. ;-( The only reason why users
wouldn't notice the absence of AFTER (or, more likely, try it and then
ask on IRC for error message diagnosis) is because they're not using the
feature. (In which case it doesn't matter how it operates)
Docs which say "Add new enums BEFORE the enum you want to add them to,
and if you need to add an enum at the end, then add it without the
BEFORE keyword" is unnecessarily confusing to users. Saying "Add new
enum values using the BEFORE or AFTER keyword before or after the
appropriate value" is vastly easier to understand.
I really don't see the value in making a command substantially less
intuitive in order to avoid a single keyword, unless it affects areas of
Postgres outside of this particular command.
--
-- Josh Berkus
PostgreSQL Experts Inc.
http://www.pgexperts.com
On 23/08/10 22:06, Tom Lane wrote:
Thom Brown<thom@linux.com> writes:
On 23 August 2010 19:25, Joseph Adams<joeyadams3.14159@gmail.com> wrote:
But what if you want to insert an OID at the end?
ALTER TYPE colors ADD 'orange';
Alternatively, if people are dead set on symmetry, what we should do
to simplify is drop *this* syntax, and just have the BEFORE and AFTER
variants.
Then you need to know the last existing value to add a new one to the
end. Seems awkward.
--
Heikki Linnakangas
EnterpriseDB http://www.enterprisedb.com
Josh Berkus <josh@agliodbs.com> writes:
I really don't see the value in making a command substantially less
intuitive in order to avoid a single keyword, unless it affects areas of
Postgres outside of this particular command.
It's the three variants to do two things that I find unintuitive.
As I mentioned a minute ago, dropping the "abbreviated" syntax and
just having BEFORE and AFTER would be a good way of achieving
symmetry if you find that important.
regards, tom lane
On Mon, Aug 23, 2010 at 3:06 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Thom Brown <thom@linux.com> writes:
On 23 August 2010 19:25, Joseph Adams <joeyadams3.14159@gmail.com> wrote:
But what if you want to insert an OID at the end?
ALTER TYPE colors ADD 'orange';
Alternatively, if people are dead set on symmetry, what we should do
to simplify is drop *this* syntax, and just have the BEFORE and AFTER
variants.
FWIW, I think Andrew's originally proposed syntax is fine and useful,
and we should just go with it.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise Postgres Company
On Mon, August 23, 2010 3:20 pm, Heikki Linnakangas wrote:
On 23/08/10 22:06, Tom Lane wrote:
Thom Brown<thom@linux.com> writes:
On 23 August 2010 19:25, Joseph Adams<joeyadams3.14159@gmail.com>
wrote:But what if you want to insert an OID at the end?
ALTER TYPE colors ADD 'orange';
Alternatively, if people are dead set on symmetry, what we should do
to simplify is drop *this* syntax, and just have the BEFORE and AFTER
variants.Then you need to know the last existing value to add a new one to the
end. Seems awkward.
I agree. This is a non-starter, I think. The most common case in my
experience is where the user doesn't care at all about the order, and just
wants to add a new label. We should make that as easy as possible,
especially since it's the most efficient.
cheers
andrew
On Aug 23, 2010, at 12:20 PM, Tom Lane wrote:
Josh Berkus <josh@agliodbs.com> writes:
I really don't see the value in making a command substantially less
intuitive in order to avoid a single keyword, unless it affects areas of
Postgres outside of this particular command.It's the three variants to do two things that I find unintuitive.
I strongly suspect that you are in the minority on this one.
Best,
David
On 8/23/10 12:20 PM, Tom Lane wrote:
Josh Berkus <josh@agliodbs.com> writes:
I really don't see the value in making a command substantially less
intuitive in order to avoid a single keyword, unless it affects areas of
Postgres outside of this particular command.It's the three variants to do two things that I find unintuitive.
Actually, it's 3 different things:
1. BEFORE adds a value before the value cited.
2. AFTER adds a value after the value cited.
3. unqualified adds a value at the end.
The fact that AFTER allows you to add a value at the end is
circumstantial overlap. While executing an AFTER, you wouldn't *know*
that you were adding it to the end, necessarily.
The other reason to have AFTER is that, in scripts, the user may not
have the before value handy due to context (i.e. dynamically building an
enum).
Anyway, this'll still be useful with BEFORE only. I'm just convinced
that we'll end up adding AFTER in 9.2 or 9.3 after we get a bunch of
user complaints and questions. So why not add it now?
--
-- Josh Berkus
PostgreSQL Experts Inc.
http://www.pgexperts.com
Josh Berkus wrote:
On 8/23/10 12:20 PM, Tom Lane wrote:
Josh Berkus <josh@agliodbs.com> writes:
I really don't see the value in making a command substantially less
intuitive in order to avoid a single keyword, unless it affects areas of
Postgres outside of this particular command.It's the three variants to do two things that I find unintuitive.
Actually, it's 3 different things:
1. BEFORE adds a value before the value cited.
2. AFTER adds a value after the value cited.
3. unqualified adds a value at the end.The fact that AFTER allows you to add a value at the end is
circumstantial overlap. While executing an AFTER, you wouldn't *know*
that you were adding it to the end, necessarily.The other reason to have AFTER is that, in scripts, the user may not
have the before value handy due to context (i.e. dynamically building an
enum).Anyway, this'll still be useful with BEFORE only. I'm just convinced
that we'll end up adding AFTER in 9.2 or 9.3 after we get a bunch of
user complaints and questions. So why not add it now?
CREATE ENUM in PG 9.0 allows you to create an enum with no columns,
e.g.:
test=> CREATE TYPE etest AS ENUM ();
CREATE TYPE
so I think we have to have the ability add an enum without a
before/after. This ability was added for pg_upgrade.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ It's impossible for everything to be true. +
Andrew Dunstan wrote:
Attached is a WIP patch that allows enums to be extended with additional
labels arbitrarily. As previously discussed, it works by adding an
explicit sort order column to pg_enum. It keeps track of whether the
labels are correctly sorted by oid value, and if so uses that for
comparison, so the possible performance impact on existing uses, and on
almost all cases where a label is added at the end of the list, should
be negligible.Open items include
* some additional error checking required
* missing documentation
* pg_upgrade considerations
I looked at the pg_upgrade ramifications of this change and it seems
some adjustments will have to be made. Right now pg_upgrade creates an
empty enum type:
CREATE TYPE etest AS ENUM ();
and then it calls EnumValuesCreate() to create the enum labels.
EnumValuesCreate() is called from within DefineEnum() where the enum
type is created, and that assumes the enums are always created initially
sorted. That would not be true when pg_upgrade is calling
EnumValuesCreate() directly with oid assignment as part of an upgrade.
I think the cleanest solution would be to modify pg_dump.c so that it
creates an empty ENUM type like before, but uses the new ALTER TYPE
myenum ADD 'newlabel' syntax to add the enum labels (with oid assignment
like we do for CREATE TYPE, etc.) The existing code had to hack to call
EnumValuesCreate() but with this new syntax it will no longer be
necessary. The call to EnumValuesCreate() for enums is the only time
pg_upgrade_support calls into a function rather than just assigning an
oid to a global variable, so it would be great to remove that last
direct function call usage.
Does this code handle the case where CREATE ENUM oid wraps around while
the enum label oids are being assigned? Does our existing code handle
that case?
I also noticed you grab an oid for the new type using the oid counter
without trying to make it in sorted order. Seems that would be possible
for adding enums to the end of the list, and might not be worth it. A
quick hack might be to just try of an oid+1 from the last enum and see
if that causes a conflict with the pg_enum.oid index.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ It's impossible for everything to be true. +
"David E. Wheeler" <david@kineticode.com> writes:
On Aug 23, 2010, at 12:20 PM, Tom Lane wrote:
It's the three variants to do two things that I find unintuitive.
I strongly suspect that you are in the minority on this one.
Yeah, seems like I'm losing the argument. Oh well.
regards, tom lane
Bruce Momjian <bruce@momjian.us> writes:
I also noticed you grab an oid for the new type using the oid counter
without trying to make it in sorted order. Seems that would be possible
for adding enums to the end of the list, and might not be worth it. A
quick hack might be to just try of an oid+1 from the last enum and see
if that causes a conflict with the pg_enum.oid index.
That wouldn't be race-condition-safe.
regards, tom lane
On 08/23/2010 07:12 PM, Bruce Momjian wrote:
Josh Berkus wrote:
On 8/23/10 12:20 PM, Tom Lane wrote:
Josh Berkus<josh@agliodbs.com> writes:
I really don't see the value in making a command substantially less
intuitive in order to avoid a single keyword, unless it affects areas of
Postgres outside of this particular command.It's the three variants to do two things that I find unintuitive.
Actually, it's 3 different things:
1. BEFORE adds a value before the value cited.
2. AFTER adds a value after the value cited.
3. unqualified adds a value at the end.The fact that AFTER allows you to add a value at the end is
circumstantial overlap. While executing an AFTER, you wouldn't *know*
that you were adding it to the end, necessarily.The other reason to have AFTER is that, in scripts, the user may not
have the before value handy due to context (i.e. dynamically building an
enum).Anyway, this'll still be useful with BEFORE only. I'm just convinced
that we'll end up adding AFTER in 9.2 or 9.3 after we get a bunch of
user complaints and questions. So why not add it now?CREATE ENUM in PG 9.0 allows you to create an enum with no columns,
e.g.:test=> CREATE TYPE etest AS ENUM ();
CREATE TYPEso I think we have to have the ability add an enum without a
before/after. This ability was added for pg_upgrade.
No we don't. pg_upgrade calls a C function. There is no support for this
at the SQL level AIUI. And the ability to add labels at arbitrary
positions in the sort order is an essential part of this feature.
cheers
andrew
Andrew Dunstan wrote:
On 08/23/2010 07:12 PM, Bruce Momjian wrote:
Josh Berkus wrote:
On 8/23/10 12:20 PM, Tom Lane wrote:
Josh Berkus<josh@agliodbs.com> writes:
I really don't see the value in making a command substantially less
intuitive in order to avoid a single keyword, unless it affects areas of
Postgres outside of this particular command.It's the three variants to do two things that I find unintuitive.
Actually, it's 3 different things:
1. BEFORE adds a value before the value cited.
2. AFTER adds a value after the value cited.
3. unqualified adds a value at the end.The fact that AFTER allows you to add a value at the end is
circumstantial overlap. While executing an AFTER, you wouldn't *know*
that you were adding it to the end, necessarily.The other reason to have AFTER is that, in scripts, the user may not
have the before value handy due to context (i.e. dynamically building an
enum).Anyway, this'll still be useful with BEFORE only. I'm just convinced
that we'll end up adding AFTER in 9.2 or 9.3 after we get a bunch of
user complaints and questions. So why not add it now?CREATE ENUM in PG 9.0 allows you to create an enum with no columns,
e.g.:test=> CREATE TYPE etest AS ENUM ();
CREATE TYPEso I think we have to have the ability add an enum without a
before/after. This ability was added for pg_upgrade.No we don't. pg_upgrade calls a C function. There is no support for this
at the SQL level AIUI. And the ability to add labels at arbitrary
positions in the sort order is an essential part of this feature.
pg_upgrade calls a C API to add labels, but the ability to create an
enum with no labels is supported at the SQL level, as I showed above. I
am not saying we don't need before/after, but I am saying we need the
ability to add labels without using before/after because there are no
labels in an empty enum.
I am not sure what you are arguing for/against. I thought we were
agreed to allow before/after, and no specification too. I am just
pointing out that we need the "no specification" syntax for logical as
well as practical reasons.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ It's impossible for everything to be true. +
On 08/23/2010 07:34 PM, Bruce Momjian wrote:
I looked at the pg_upgrade ramifications of this change and it seems
some adjustments will have to be made. Right now pg_upgrade creates an
empty enum type:CREATE TYPE etest AS ENUM ();
and then it calls EnumValuesCreate() to create the enum labels.
EnumValuesCreate() is called from within DefineEnum() where the enum
type is created, and that assumes the enums are always created initially
sorted. That would not be true when pg_upgrade is calling
EnumValuesCreate() directly with oid assignment as part of an upgrade.I think the cleanest solution would be to modify pg_dump.c so that it
creates an empty ENUM type like before, but uses the new ALTER TYPE
myenum ADD 'newlabel' syntax to add the enum labels (with oid assignment
like we do for CREATE TYPE, etc.) The existing code had to hack to call
EnumValuesCreate() but with this new syntax it will no longer be
necessary. The call to EnumValuesCreate() for enums is the only time
pg_upgrade_support calls into a function rather than just assigning an
oid to a global variable, so it would be great to remove that last
direct function call usage.
I've just been taking another look at this suggestion. I think it will
work quite cleanly. As long as we add the enums in the correct order it
should just do the Right Thing (tm).
To answer your other question, Oid wraparound will not be a problem.
Will get coding.
cheers
andrew
On 08/25/2010 03:29 AM, Andrew Dunstan wrote:
I've just been taking another look at this suggestion. I think it will
work quite cleanly. As long as we add the enums in the correct order
it should just do the Right Thing (tm).To answer your other question, Oid wraparound will not be a problem.
Will get coding.
Revised patch with pg_dump and pg_upgrade support is attached.
cheers
andrew
Attachments:
patchenum2text/plain; name=patchenum2Download
diff --git a/contrib/pg_upgrade/function.c b/contrib/pg_upgrade/function.c
index d9b54b8..9b3c64c 100644
--- a/contrib/pg_upgrade/function.c
+++ b/contrib/pg_upgrade/function.c
@@ -58,25 +58,25 @@ install_support_functions(migratorContext *ctx)
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
- " binary_upgrade.set_next_heap_relfilenode(OID) "
+ " binary_upgrade.set_next_pg_enum_oid(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
- " binary_upgrade.set_next_toast_relfilenode(OID) "
+ " binary_upgrade.set_next_heap_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
- " binary_upgrade.set_next_index_relfilenode(OID) "
+ " binary_upgrade.set_next_toast_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
- " binary_upgrade.add_pg_enum_label(OID, OID, NAME) "
+ " binary_upgrade.set_next_index_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
diff --git a/contrib/pg_upgrade_support/pg_upgrade_support.c b/contrib/pg_upgrade_support/pg_upgrade_support.c
index 50f9efd..7af7d4d 100644
--- a/contrib/pg_upgrade_support/pg_upgrade_support.c
+++ b/contrib/pg_upgrade_support/pg_upgrade_support.c
@@ -27,6 +27,7 @@ PG_MODULE_MAGIC;
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_array_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_toast_oid;
+extern PGDLLIMPORT Oid binary_upgrade_next_pg_enum_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_heap_relfilenode;
extern PGDLLIMPORT Oid binary_upgrade_next_toast_relfilenode;
extern PGDLLIMPORT Oid binary_upgrade_next_index_relfilenode;
@@ -34,18 +35,18 @@ extern PGDLLIMPORT Oid binary_upgrade_next_index_relfilenode;
Datum set_next_pg_type_oid(PG_FUNCTION_ARGS);
Datum set_next_pg_type_array_oid(PG_FUNCTION_ARGS);
Datum set_next_pg_type_toast_oid(PG_FUNCTION_ARGS);
+Datum set_next_pg_enum_oid(PG_FUNCTION_ARGS);
Datum set_next_heap_relfilenode(PG_FUNCTION_ARGS);
Datum set_next_toast_relfilenode(PG_FUNCTION_ARGS);
Datum set_next_index_relfilenode(PG_FUNCTION_ARGS);
-Datum add_pg_enum_label(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(set_next_pg_type_oid);
PG_FUNCTION_INFO_V1(set_next_pg_type_array_oid);
PG_FUNCTION_INFO_V1(set_next_pg_type_toast_oid);
+PG_FUNCTION_INFO_V1(set_next_pg_enum_oid);
PG_FUNCTION_INFO_V1(set_next_heap_relfilenode);
PG_FUNCTION_INFO_V1(set_next_toast_relfilenode);
PG_FUNCTION_INFO_V1(set_next_index_relfilenode);
-PG_FUNCTION_INFO_V1(add_pg_enum_label);
Datum
set_next_pg_type_oid(PG_FUNCTION_ARGS)
@@ -78,6 +79,16 @@ set_next_pg_type_toast_oid(PG_FUNCTION_ARGS)
}
Datum
+set_next_pg_enum_oid(PG_FUNCTION_ARGS)
+{
+ Oid enumoid = PG_GETARG_OID(0);
+
+ binary_upgrade_next_pg_enum_oid = enumoid;
+
+ PG_RETURN_VOID();
+}
+
+Datum
set_next_heap_relfilenode(PG_FUNCTION_ARGS)
{
Oid relfilenode = PG_GETARG_OID(0);
@@ -106,17 +117,3 @@ set_next_index_relfilenode(PG_FUNCTION_ARGS)
PG_RETURN_VOID();
}
-
-Datum
-add_pg_enum_label(PG_FUNCTION_ARGS)
-{
- Oid enumoid = PG_GETARG_OID(0);
- Oid typoid = PG_GETARG_OID(1);
- Name label = PG_GETARG_NAME(2);
-
- EnumValuesCreate(typoid, list_make1(makeString(NameStr(*label))),
- enumoid);
-
- PG_RETURN_VOID();
-}
-
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index a1d951a..3ef4e2d 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -854,7 +854,9 @@ AddNewRelationType(const char *typeName,
'x', /* fully TOASTable */
-1, /* typmod */
0, /* array dimensions for typBaseType */
- false); /* Type NOT NULL */
+ false, /* Type NOT NULL */
+ -1, /* no enum labels */
+ false); /* type is not an enum, so not sorted */
}
/* --------------------------------
@@ -1086,7 +1088,9 @@ heap_create_with_catalog(const char *relname,
'x', /* fully TOASTable */
-1, /* typmod */
0, /* array dimensions for typBaseType */
- false); /* Type NOT NULL */
+ false, /* Type NOT NULL */
+ -1, /* no enum labels */
+ false); /* type is not an enum, so not sorted */
pfree(relarrayname);
}
diff --git a/src/backend/catalog/pg_enum.c b/src/backend/catalog/pg_enum.c
index 578f8c5..d104d77 100644
--- a/src/backend/catalog/pg_enum.c
+++ b/src/backend/catalog/pg_enum.c
@@ -18,12 +18,202 @@
#include "catalog/catalog.h"
#include "catalog/indexing.h"
#include "catalog/pg_enum.h"
+#include "catalog/pg_type.h"
+#include "utils/lsyscache.h"
+#include "utils/syscache.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/rel.h"
#include "utils/tqual.h"
static int oid_cmp(const void *p1, const void *p2);
+static int sort_order_cmp(const void *p1, const void *p2);
+
+Oid binary_upgrade_next_pg_enum_oid = InvalidOid;
+
+/*
+ * AddEnumLabel
+ * Add a new label to the enum set. By default it goes at
+ * the end, but the user can choose to place it before or
+ * after any existing set member.
+ *
+ * Returns true iff the labels are sorted by oid after the addition.
+ */
+
+bool
+AddEnumLabel(Oid enumTypeOid,
+ char *newVal,
+ char *neighbour,
+ bool newValIsAfter,
+ int nelems,
+ bool elems_are_sorted)
+{
+ Oid newOid;
+ Relation pg_enum;
+ TupleDesc tupDesc;
+ Datum values[Natts_pg_enum];
+ bool nulls[Natts_pg_enum];
+ NameData enumlabel;
+ HeapTuple enum_tup;
+ bool result = elems_are_sorted;
+ int newelemorder;
+
+ /* check length of new label is ok */
+ if (strlen(newVal) > (NAMEDATALEN - 1))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_NAME),
+ errmsg("invalid enum label \"%s\"", newVal),
+ errdetail("Labels must be %d characters or less.",
+ NAMEDATALEN - 1)));
+
+ /* get a new OID for the label */
+ pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
+
+ if (OidIsValid(binary_upgrade_next_pg_enum_oid))
+ {
+ if (neighbour != NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("BEFORE/AFTER incompatible with binary upgrade.")));
+
+ newOid = binary_upgrade_next_pg_enum_oid;
+ binary_upgrade_next_pg_enum_oid = InvalidOid;
+ }
+ else
+ {
+ /* non upgrade case */
+ newOid = GetNewOid(pg_enum);
+ }
+
+ if (neighbour == NULL)
+ {
+ /*
+ * Put the new label at the end of the list.
+ * No change to existing tuples is required.
+ */
+ newelemorder = nelems + 1;
+ /* are the elements still sorted? */
+ if (elems_are_sorted)
+ {
+ CatCList *list;
+ int i;
+ bool still_sorted = true;
+
+ list = SearchSysCacheList1(ENUMTYPOIDNAME,
+ ObjectIdGetDatum(enumTypeOid));
+ for (i = 0; i < nelems; i++)
+ {
+ HeapTuple tup = &(list->members[i]->tuple);
+ Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
+
+ if (en->enumsortorder == nelems)
+ {
+ if (HeapTupleGetOid(tup) > newOid)
+ still_sorted = false;
+ break;
+ }
+ }
+ ReleaseCatCacheList(list);
+ if (! still_sorted)
+ result = false;
+ }
+ }
+ else
+ {
+ /* BEFORE or AFTER specified */
+ CatCList *list;
+ int i;
+ HeapTuple *existing;
+ HeapTuple nbr = NULL;
+ Form_pg_enum nbr_en;
+
+ /* get a list of the existing elements and sort them by enumsortorder */
+ list = SearchSysCacheList1(ENUMTYPOIDNAME,
+ ObjectIdGetDatum(enumTypeOid));
+ existing = palloc(nelems * sizeof(HeapTuple));
+
+ for (i = 0; i < nelems; i++)
+ existing[i] = &(list->members[i]->tuple);
+
+ qsort(existing, nelems, sizeof(HeapTuple), sort_order_cmp);
+
+ /* locate the neighbour element */
+ for (i = 0; i < nelems; i++)
+ {
+ Form_pg_enum exists_en;
+ exists_en = (Form_pg_enum) GETSTRUCT(existing[i]);
+ if (strcmp(NameStr(exists_en->enumlabel),neighbour) == 0)
+ nbr = existing[i];
+
+ }
+
+ if (nbr == NULL)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("\"%s\" is not an existing label.", neighbour)));
+ }
+
+ nbr_en = (Form_pg_enum) GETSTRUCT(nbr);
+
+ /*
+ * If BEFORE was specified, the new label goes in the neighbour's
+ * position. Otherwise, it goes in the position after that.
+ */
+ newelemorder = nbr_en->enumsortorder;
+ if (newValIsAfter)
+ newelemorder++;
+
+ /*
+ * Add 1 to the sortorder of all the labels after where the
+ * new label goes. Do it from the end back so we don't get
+ * uniqueness violations.
+ */
+ for (i = nelems - 1; i>= 0; i--)
+ {
+ HeapTuple newtup;
+ Form_pg_enum exst_en = (Form_pg_enum) GETSTRUCT(existing[i]);
+ if (exst_en->enumsortorder < newelemorder)
+ break;
+
+ newtup = heap_copytuple(existing[i]);
+ exst_en = (Form_pg_enum) GETSTRUCT(newtup);
+ exst_en->enumsortorder ++;
+
+ simple_heap_update(pg_enum, &newtup->t_self, newtup);
+
+ CatalogUpdateIndexes(pg_enum, newtup);
+
+ }
+
+ ReleaseCatCacheList(list);
+
+ /* are the labels sorted by OID? */
+ if (result && newelemorder > 1)
+ result = newOid > HeapTupleGetOid(existing[newelemorder-2]);
+ if (result && newelemorder < nelems + 1)
+ result = newOid < HeapTupleGetOid(existing[newelemorder-1]);
+
+ }
+
+ /* set up the new entry */
+ tupDesc = pg_enum->rd_att;
+ memset(nulls, false, sizeof(nulls));
+ values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid);
+ namestrcpy(&enumlabel, newVal);
+ values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel);
+ values[Anum_pg_enum_enumsortorder -1] = Int32GetDatum(newelemorder);
+ enum_tup = heap_form_tuple(tupDesc, values, nulls);
+ HeapTupleSetOid(enum_tup, newOid);
+ simple_heap_insert(pg_enum, enum_tup);
+ CatalogUpdateIndexes(pg_enum, enum_tup);
+ heap_freetuple(enum_tup);
+
+ heap_close(pg_enum, RowExclusiveLock);
+
+ return result;
+
+}
/*
@@ -33,8 +223,7 @@ static int oid_cmp(const void *p1, const void *p2);
* vals is a list of Value strings.
*/
void
-EnumValuesCreate(Oid enumTypeOid, List *vals,
- Oid binary_upgrade_next_pg_enum_oid)
+EnumValuesCreate(Oid enumTypeOid, List *vals)
{
Relation pg_enum;
TupleDesc tupDesc;
@@ -50,9 +239,9 @@ EnumValuesCreate(Oid enumTypeOid, List *vals,
num_elems = list_length(vals);
/*
- * XXX we do not bother to check the list of values for duplicates --- if
+ * We do not bother to check the list of values for duplicates --- if
* you have any, you'll get a less-than-friendly unique-index violation.
- * Is it worth trying harder?
+ * It is probably not worth trying harder.
*/
pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
@@ -62,35 +251,24 @@ EnumValuesCreate(Oid enumTypeOid, List *vals,
* Allocate oids
*/
oids = (Oid *) palloc(num_elems * sizeof(Oid));
- if (OidIsValid(binary_upgrade_next_pg_enum_oid))
- {
- if (num_elems != 1)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("EnumValuesCreate() can only set a single OID")));
- oids[0] = binary_upgrade_next_pg_enum_oid;
- binary_upgrade_next_pg_enum_oid = InvalidOid;
- }
- else
+
+ /*
+ * While this method does not absolutely guarantee that we generate no
+ * duplicate oids (since we haven't entered each oid into the table
+ * before allocating the next), trouble could only occur if the oid
+ * counter wraps all the way around before we finish. Which seems
+ * unlikely.
+ */
+ for (elemno = 0; elemno < num_elems; elemno++)
{
/*
- * While this method does not absolutely guarantee that we generate no
- * duplicate oids (since we haven't entered each oid into the table
- * before allocating the next), trouble could only occur if the oid
- * counter wraps all the way around before we finish. Which seems
- * unlikely.
+ * The pg_enum.oid is stored in user tables. This oid must be
+ * preserved by binary upgrades.
*/
- for (elemno = 0; elemno < num_elems; elemno++)
- {
- /*
- * The pg_enum.oid is stored in user tables. This oid must be
- * preserved by binary upgrades.
- */
- oids[elemno] = GetNewOid(pg_enum);
- }
- /* sort them, just in case counter wrapped from high to low */
- qsort(oids, num_elems, sizeof(Oid), oid_cmp);
+ oids[elemno] = GetNewOid(pg_enum);
}
+ /* sort them, just in case counter wrapped from high to low */
+ qsort(oids, num_elems, sizeof(Oid), oid_cmp);
/* and make the entries */
memset(nulls, false, sizeof(nulls));
@@ -114,6 +292,7 @@ EnumValuesCreate(Oid enumTypeOid, List *vals,
values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid);
namestrcpy(&enumlabel, lab);
values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel);
+ values[Anum_pg_enum_enumsortorder -1] = Int32GetDatum(elemno+1);
tup = heap_form_tuple(tupDesc, values, nulls);
HeapTupleSetOid(tup, oids[elemno]);
@@ -164,7 +343,7 @@ EnumValuesDelete(Oid enumTypeOid)
}
-/* qsort comparison function */
+/* qsort comparison for oids */
static int
oid_cmp(const void *p1, const void *p2)
{
@@ -177,3 +356,16 @@ oid_cmp(const void *p1, const void *p2)
return 1;
return 0;
}
+
+/* qsort comparison function for tuples by sort order */
+static int
+sort_order_cmp(const void *p1, const void *p2)
+{
+ HeapTuple v1 = *((const HeapTuple *) p1);
+ HeapTuple v2 = *((const HeapTuple *) p2);
+ Form_pg_enum en1 = (Form_pg_enum) GETSTRUCT(v1);
+ Form_pg_enum en2 = (Form_pg_enum) GETSTRUCT(v2);
+
+ return en1->enumsortorder - en2->enumsortorder;
+}
+
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index c7efd6d..109ee0e 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -112,6 +112,8 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
values[i++] = ObjectIdGetDatum(InvalidOid); /* typbasetype */
values[i++] = Int32GetDatum(-1); /* typtypmod */
values[i++] = Int32GetDatum(0); /* typndims */
+ values[i++] = Int32GetDatum(-1); /* typnlabels */
+ values[i++] = BoolGetDatum(false); /* typsorted */
nulls[i++] = true; /* typdefaultbin */
nulls[i++] = true; /* typdefault */
@@ -204,7 +206,9 @@ TypeCreate(Oid newTypeOid,
char storage,
int32 typeMod,
int32 typNDims, /* Array dimensions for baseType */
- bool typeNotNull)
+ bool typeNotNull,
+ int32 typeNLabels,
+ bool typeSorted)
{
Relation pg_type_desc;
Oid typeObjectId;
@@ -342,6 +346,8 @@ TypeCreate(Oid newTypeOid,
values[i++] = ObjectIdGetDatum(baseType); /* typbasetype */
values[i++] = Int32GetDatum(typeMod); /* typtypmod */
values[i++] = Int32GetDatum(typNDims); /* typndims */
+ values[i++] = Int32GetDatum(typeNLabels); /* typnlabels */
+ values[i++] = BoolGetDatum(typeSorted); /* typsorted */
/*
* initialize the default binary value for this type. Check for nulls of
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index fc80175..2ed8278 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -85,12 +85,11 @@ static Oid findTypeTypmodoutFunction(List *procname);
static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid);
static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
static void checkDomainOwner(HeapTuple tup, TypeName *typename);
+static void checkEnumOwner(HeapTuple tup, TypeName *typename);
static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
Oid baseTypeOid,
int typMod, Constraint *constr,
char *domainName);
-
-
/*
* DefineType
* Registers a new base type.
@@ -562,8 +561,9 @@ DefineType(List *names, List *parameters)
storage, /* TOAST strategy */
-1, /* typMod (Domains only) */
0, /* Array Dimensions of typbasetype */
- false); /* Type NOT NULL */
-
+ false, /* Type NOT NULL */
+ -1, /* no enum labels */
+ false); /* type is not an enum, so not sorted */
/*
* Create the array type that goes with it.
*/
@@ -601,7 +601,9 @@ DefineType(List *names, List *parameters)
'x', /* ARRAY is always toastable */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
- false); /* Type NOT NULL */
+ false, /* Type NOT NULL */
+ -1, /* no enum labels */
+ false); /* type is not an enum, so not sorted */
pfree(array_type);
}
@@ -1044,7 +1046,9 @@ DefineDomain(CreateDomainStmt *stmt)
storage, /* TOAST strategy */
basetypeMod, /* typeMod value */
typNDims, /* Array dimensions for base type */
- typNotNull); /* Type NOT NULL */
+ typNotNull, /* Type NOT NULL */
+ -1, /* no enum labels */
+ false); /* type is not an enum, so not sorted */
/*
* Process constraints which refer to the domain ID returned by TypeCreate
@@ -1094,6 +1098,9 @@ DefineEnum(CreateEnumStmt *stmt)
AclResult aclresult;
Oid old_type_oid;
Oid enumArrayOid;
+ int num_labels;
+
+ num_labels = list_length(stmt->vals);
/* Convert list of names to a name and namespace */
enumNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
@@ -1153,10 +1160,12 @@ DefineEnum(CreateEnumStmt *stmt)
'p', /* TOAST strategy always plain */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
- false); /* Type NOT NULL */
+ false, /* Type NOT NULL */
+ num_labels, /* count enum labels */
+ true); /* enums always start sorted */
/* Enter the enum's values into pg_enum */
- EnumValuesCreate(enumTypeOid, stmt->vals, InvalidOid);
+ EnumValuesCreate(enumTypeOid, stmt->vals);
/*
* Create the array type that goes with it.
@@ -1192,11 +1201,88 @@ DefineEnum(CreateEnumStmt *stmt)
'x', /* ARRAY is always toastable */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
- false); /* Type NOT NULL */
+ false, /* Type NOT NULL */
+ -1, /* no enum labels */
+ false); /* type is not an enum, so not sorted */
pfree(enumArrayName);
}
+/*
+ * AlterEnum
+ * Registers a new label for an existing enum.
+ */
+void
+AlterEnum (AlterEnumStmt *stmt)
+{
+ Oid enum_type_oid;
+ TypeName *typename;
+ bool sorted;
+ HeapTuple tup, newtup;
+ Relation rel;
+ Form_pg_type typTup;
+
+ /* Make a TypeName so we can use standard type lookup machinery */
+ typename = makeTypeNameFromNameList(stmt->typeName);
+ enum_type_oid = typenameTypeId(NULL, typename, NULL);
+
+ /* Look up the row in the type table */
+ rel = heap_open(TypeRelationId, RowExclusiveLock);
+
+ tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(enum_type_oid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for type %u", enum_type_oid);
+
+ /* Copy the syscache entry so we can scribble on it below */
+ newtup = heap_copytuple(tup);
+ ReleaseSysCache(tup);
+ tup = newtup;
+ typTup = (Form_pg_type) GETSTRUCT(tup);
+
+ /* Check it's an enum and check user has permission to ALTER the enum */
+ checkEnumOwner(tup, typename);
+
+ /* Add the new label */
+ sorted = AddEnumLabel (enum_type_oid, stmt->newVal,
+ stmt->newValNeighbour, stmt->newValIsAfter,
+ typTup->typnlabels, typTup->typsorted);
+
+ /* Update the row in pg_type */
+ typTup->typnlabels += 1;
+ typTup->typsorted = sorted;
+
+ simple_heap_update(rel, &tup->t_self, tup);
+
+ CatalogUpdateIndexes(rel, tup);
+
+ /* Clean up */
+ heap_close(rel, RowExclusiveLock);
+}
+
+
+/*
+ * checkEnumOwner
+ *
+ * Check that the type is actually an enum and that the current user
+ * has permission to do ALTER TYPE on it. Throw an error if not.
+ */
+static void
+checkEnumOwner(HeapTuple tup, TypeName *typename)
+{
+ Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup);
+
+ /* Check that this is actually a domain */
+ if (typTup->typtype != TYPTYPE_ENUM)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not an enum",
+ TypeNameToString(typename))));
+
+ /* Permission check: must own type */
+ if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
+ format_type_be(HeapTupleGetOid(tup)));
+}
/*
* Find suitable I/O functions for a type.
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index b3e907a..59cfe96 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2836,6 +2836,19 @@ _copyCreateEnumStmt(CreateEnumStmt *from)
return newnode;
}
+static AlterEnumStmt *
+_copyAlterEnumStmt(AlterEnumStmt *from)
+{
+ AlterEnumStmt *newnode = makeNode(AlterEnumStmt);
+
+ COPY_NODE_FIELD(typeName);
+ COPY_STRING_FIELD(newVal);
+ COPY_STRING_FIELD(newValNeighbour);
+ COPY_SCALAR_FIELD(newValIsAfter);
+
+ return newnode;
+}
+
static ViewStmt *
_copyViewStmt(ViewStmt *from)
{
@@ -3989,6 +4002,9 @@ copyObject(void *from)
case T_CreateEnumStmt:
retval = _copyCreateEnumStmt(from);
break;
+ case T_AlterEnumStmt:
+ retval = _copyAlterEnumStmt(from);
+ break;
case T_ViewStmt:
retval = _copyViewStmt(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 7ec0923..900bf37 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1375,6 +1375,17 @@ _equalCreateEnumStmt(CreateEnumStmt *a, CreateEnumStmt *b)
}
static bool
+_equalAlterEnumStmt(AlterEnumStmt *a, AlterEnumStmt *b)
+{
+ COMPARE_NODE_FIELD(typeName);
+ COMPARE_STRING_FIELD(newVal);
+ COMPARE_STRING_FIELD(newValNeighbour);
+ COMPARE_SCALAR_FIELD(newValIsAfter);
+
+ return true;
+}
+
+static bool
_equalViewStmt(ViewStmt *a, ViewStmt *b)
{
COMPARE_NODE_FIELD(view);
@@ -2678,6 +2689,9 @@ equal(void *a, void *b)
case T_CreateEnumStmt:
retval = _equalCreateEnumStmt(a, b);
break;
+ case T_AlterEnumStmt:
+ retval = _equalAlterEnumStmt(a, b);
+ break;
case T_ViewStmt:
retval = _equalViewStmt(a, b);
break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index d5a0f29..9ef84aa 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -181,8 +181,8 @@ static TypeName *TableFuncTypeName(List *columns);
}
%type <node> stmt schema_stmt
- AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterFdwStmt
- AlterForeignServerStmt AlterGroupStmt
+ AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt
+ AlterFdwStmt AlterForeignServerStmt AlterGroupStmt
AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
AlterRoleStmt AlterRoleSetStmt
@@ -648,6 +648,7 @@ stmt :
| AlterDatabaseSetStmt
| AlterDefaultPrivilegesStmt
| AlterDomainStmt
+ | AlterEnumStmt
| AlterFdwStmt
| AlterForeignServerStmt
| AlterFunctionStmt
@@ -3781,6 +3782,46 @@ enum_val_list: Sconst
{ $$ = lappend($1, makeString($3)); }
;
+/*****************************************************************************
+ *
+ * ALTER TYPE enumtype ADD ...
+ *
+ *****************************************************************************/
+
+AlterEnumStmt:
+ ALTER TYPE_P any_name ADD_P Sconst
+ {
+ AlterEnumStmt *n = makeNode(AlterEnumStmt);
+ n->typeName = $3;
+ n->newVal = $5;
+ n->newValNeighbour = NULL;
+ n->newValIsAfter = true;
+
+ $$ = (Node *) n;
+ }
+ | ALTER TYPE_P any_name ADD_P Sconst BEFORE Sconst
+ {
+ AlterEnumStmt *n = makeNode(AlterEnumStmt);
+ n->typeName = $3;
+ n->newVal = $5;
+ n->newValNeighbour = $7;
+ n->newValIsAfter = false;
+
+ $$ = (Node *) n;
+ }
+ | ALTER TYPE_P any_name ADD_P Sconst AFTER Sconst
+ {
+ AlterEnumStmt *n = makeNode(AlterEnumStmt);
+ n->typeName = $3;
+ n->newVal = $5;
+ n->newValNeighbour = $7;
+ n->newValIsAfter = true;
+
+ $$ = (Node *) n;
+ }
+ ;
+
+
/*****************************************************************************
*
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 77ee926..39ea03e 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -189,6 +189,7 @@ check_xact_readonly(Node *parsetree)
case T_CreateTrigStmt:
case T_CompositeTypeStmt:
case T_CreateEnumStmt:
+ case T_AlterEnumStmt:
case T_ViewStmt:
case T_DropCastStmt:
case T_DropStmt:
@@ -846,6 +847,10 @@ standard_ProcessUtility(Node *parsetree,
DefineEnum((CreateEnumStmt *) parsetree);
break;
+ case T_AlterEnumStmt: /* ALTER TYPE (enum) */
+ AlterEnum((AlterEnumStmt *) parsetree);
+ break;
+
case T_ViewStmt: /* CREATE VIEW */
DefineView((ViewStmt *) parsetree, queryString);
break;
@@ -1846,6 +1851,10 @@ CreateCommandTag(Node *parsetree)
tag = "CREATE TYPE";
break;
+ case T_AlterEnumStmt:
+ tag = "ALTER TYPE";
+ break;
+
case T_ViewStmt:
tag = "CREATE VIEW";
break;
@@ -2384,6 +2393,10 @@ GetCommandLogLevel(Node *parsetree)
lev = LOGSTMT_DDL;
break;
+ case T_AlterEnumStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
case T_ViewStmt:
lev = LOGSTMT_DDL;
break;
diff --git a/src/backend/utils/adt/enum.c b/src/backend/utils/adt/enum.c
index 74cc0c2..63eab09 100644
--- a/src/backend/utils/adt/enum.c
+++ b/src/backend/utils/adt/enum.c
@@ -14,6 +14,7 @@
#include "postgres.h"
#include "catalog/pg_enum.h"
+#include "catalog/pg_type.h"
#include "fmgr.h"
#include "utils/array.h"
#include "utils/builtins.h"
@@ -22,9 +23,26 @@
#include "libpq/pqformat.h"
#include "miscadmin.h"
+typedef struct
+{
+ Oid enum_oid;
+ int32 sort_order;
+} enum_sort;
+
+typedef struct
+{
+ Oid enumtypoid;
+ bool oids_are_sorted;
+ int sort_list_length;
+ int label_count;
+ enum_sort sort_order_list[1];
+} enum_sort_cache;
+
+
static ArrayType *enum_range_internal(Oid enumtypoid, Oid lower, Oid upper);
-static int enum_elem_cmp(const void *left, const void *right);
+static int enum_sort_cmp(const void *left, const void *right);
+static int enum_oid_cmp(const void *left, const void *right);
/* Basic I/O support */
@@ -155,13 +173,142 @@ enum_send(PG_FUNCTION_ARGS)
/* Comparison functions and related */
+static inline int
+enum_ccmp(Oid arg1, Oid arg2, FunctionCallInfo fcinfo)
+{
+
+ enum_sort_cache * mycache;
+ enum_sort *es1, *es2, srch;
+ int sort1, sort2;
+ bool added = false;
+ HeapTuple enum_tup, type_tup;
+ Form_pg_enum en;
+ Oid typeoid;
+ Form_pg_type typ;
+
+ if (arg1 == arg2)
+ return 0;
+
+ mycache = (enum_sort_cache *) fcinfo->flinfo->fn_extra;
+ if (mycache == NULL )
+ {
+ enum_tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(arg1));
+ en = (Form_pg_enum) GETSTRUCT(enum_tup);
+ typeoid = en->enumtypid;
+ type_tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeoid));
+ typ = (Form_pg_type) GETSTRUCT(type_tup);
+ if (typ->typtype != 'e')
+ elog(ERROR,"wrong type for oid %u",typeoid);
+ fcinfo->flinfo->fn_extra =
+ MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+ sizeof(enum_sort_cache) +
+ (typ->typnlabels * sizeof(enum_sort)));
+ mycache = (enum_sort_cache *) fcinfo->flinfo->fn_extra;
+ mycache->enumtypoid = typeoid;
+ mycache->sort_list_length = 1;
+ mycache->label_count = typ->typnlabels;
+ mycache->oids_are_sorted = typ->typsorted;
+ mycache->sort_order_list[0].enum_oid = arg1;
+ mycache->sort_order_list[0].sort_order = en->enumsortorder;
+ ReleaseSysCache(type_tup);
+ ReleaseSysCache(enum_tup);
+ }
+
+ if (mycache->oids_are_sorted)
+ return arg1 - arg2;
+
+ srch.enum_oid = arg1;
+ es1 = bsearch(&srch,
+ mycache->sort_order_list,
+ mycache->sort_list_length,
+ sizeof(enum_sort),
+ enum_oid_cmp);
+ srch.enum_oid = arg2;
+ es2 = bsearch(&srch,
+ mycache->sort_order_list,
+ mycache->sort_list_length,
+ sizeof(enum_sort),
+ enum_oid_cmp);
+
+ if (es1 == NULL)
+ {
+
+ enum_tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(arg1));
+ en = (Form_pg_enum) GETSTRUCT(enum_tup);
+ mycache->sort_order_list[mycache->sort_list_length].enum_oid = arg1;
+ sort1 = en->enumsortorder;
+ mycache->sort_order_list[mycache->sort_list_length].sort_order =
+ sort1;
+ ReleaseSysCache(enum_tup);
+ mycache->sort_list_length ++;
+ added = true;
+ }
+ else
+ {
+ /* already in cache */
+ sort1 = es1->sort_order;
+ }
+
+ if (es2 == NULL)
+ {
+
+ enum_tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(arg2));
+ en = (Form_pg_enum) GETSTRUCT(enum_tup);
+ sort2 = en->enumsortorder;
+ mycache->sort_order_list[mycache->sort_list_length].enum_oid = arg2;
+ mycache->sort_order_list[mycache->sort_list_length].sort_order =
+ sort2;
+ ReleaseSysCache(enum_tup);
+ mycache->sort_list_length ++;
+ added = true;
+ }
+ else
+ {
+ /* already in cache */
+ sort2 = es2->sort_order;
+ }
+
+ if (added)
+ {
+ /*
+ * If we have more than a handful, just fetch them all, so we limit
+ * the number of sort operations required.
+ */
+ if (mycache->sort_list_length > 10 &&
+ mycache->sort_list_length < mycache->label_count)
+ {
+ CatCList *nlist;
+ int num, i;
+
+ nlist = SearchSysCacheList1(ENUMTYPOIDNAME,
+ ObjectIdGetDatum(mycache->enumtypoid));
+ num = nlist->n_members;
+ for (i = 0; i < num; i++)
+ {
+ HeapTuple tup = &(nlist->members[i]->tuple);
+ Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
+
+ mycache->sort_order_list[i].enum_oid = HeapTupleGetOid(tup);
+ mycache->sort_order_list[i].sort_order = en->enumsortorder;
+ }
+
+ ReleaseCatCacheList(nlist);
+ }
+
+ qsort(mycache->sort_order_list,mycache->sort_list_length,
+ sizeof(enum_sort),enum_oid_cmp);
+ }
+
+ return sort1 - sort2;
+}
+
Datum
enum_lt(PG_FUNCTION_ARGS)
{
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
- PG_RETURN_BOOL(a < b);
+ PG_RETURN_BOOL(enum_ccmp(a,b,fcinfo) < 0);
}
Datum
@@ -170,7 +317,7 @@ enum_le(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
- PG_RETURN_BOOL(a <= b);
+ PG_RETURN_BOOL(enum_ccmp(a,b,fcinfo) <= 0);
}
Datum
@@ -197,7 +344,7 @@ enum_ge(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
- PG_RETURN_BOOL(a >= b);
+ PG_RETURN_BOOL(enum_ccmp(a,b,fcinfo) >= 0);
}
Datum
@@ -206,7 +353,7 @@ enum_gt(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
- PG_RETURN_BOOL(a > b);
+ PG_RETURN_BOOL(enum_ccmp(a,b,fcinfo) > 0);
}
Datum
@@ -215,7 +362,7 @@ enum_smaller(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
- PG_RETURN_OID(a <= b ? a : b);
+ PG_RETURN_OID(enum_ccmp(a,b,fcinfo) < 0 ? a : b);
}
Datum
@@ -224,7 +371,7 @@ enum_larger(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
- PG_RETURN_OID(a >= b ? a : b);
+ PG_RETURN_OID(enum_ccmp(a,b,fcinfo) > 0 ? a : b);
}
Datum
@@ -233,10 +380,10 @@ enum_cmp(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
- if (a > b)
- PG_RETURN_INT32(1);
- else if (a == b)
+ if (a == b)
PG_RETURN_INT32(0);
+ else if (enum_ccmp(a,b,fcinfo) > 0)
+ PG_RETURN_INT32(1);
else
PG_RETURN_INT32(-1);
}
@@ -248,6 +395,7 @@ enum_first(PG_FUNCTION_ARGS)
{
Oid enumtypoid;
Oid min = InvalidOid;
+ int min_sort = -1; /* value will never in fact be used */
CatCList *list;
int num,
i;
@@ -267,10 +415,14 @@ enum_first(PG_FUNCTION_ARGS)
num = list->n_members;
for (i = 0; i < num; i++)
{
- Oid valoid = HeapTupleHeaderGetOid(list->members[i]->tuple.t_data);
-
- if (!OidIsValid(min) || valoid < min)
- min = valoid;
+ HeapTuple tup = &(list->members[i]->tuple);
+ Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
+
+ if (!OidIsValid(min) || en->enumsortorder < min_sort)
+ {
+ min = HeapTupleGetOid(tup);
+ min_sort = en->enumsortorder;
+ }
}
ReleaseCatCacheList(list);
@@ -287,6 +439,7 @@ enum_last(PG_FUNCTION_ARGS)
{
Oid enumtypoid;
Oid max = InvalidOid;
+ int max_sort = -1; /* value will never in fact be used */
CatCList *list;
int num,
i;
@@ -306,10 +459,14 @@ enum_last(PG_FUNCTION_ARGS)
num = list->n_members;
for (i = 0; i < num; i++)
{
- Oid valoid = HeapTupleHeaderGetOid(list->members[i]->tuple.t_data);
-
- if (!OidIsValid(max) || valoid > max)
- max = valoid;
+ HeapTuple tup = &(list->members[i]->tuple);
+ Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
+
+ if (!OidIsValid(max) || en->enumsortorder > max_sort)
+ {
+ max = HeapTupleGetOid(tup);
+ max_sort = en->enumsortorder;
+ }
}
ReleaseCatCacheList(list);
@@ -382,46 +539,76 @@ enum_range_internal(Oid enumtypoid, Oid lower, Oid upper)
i,
j;
Datum *elems;
+ enum_sort *sort_items;
+ bool left_found;
list = SearchSysCacheList1(ENUMTYPOIDNAME, ObjectIdGetDatum(enumtypoid));
total = list->n_members;
elems = (Datum *) palloc(total * sizeof(Datum));
+ sort_items = (enum_sort *) palloc(total * sizeof(enum_sort));
- j = 0;
for (i = 0; i < total; i++)
{
- Oid val = HeapTupleGetOid(&(list->members[i]->tuple));
+ HeapTuple tup = &(list->members[i]->tuple);
+ Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
+
+ sort_items[i].enum_oid = HeapTupleGetOid(tup);
+ sort_items[i].sort_order = en->enumsortorder;
- if ((!OidIsValid(lower) || lower <= val) &&
- (!OidIsValid(upper) || val <= upper))
- elems[j++] = ObjectIdGetDatum(val);
}
/* shouldn't need the cache anymore */
ReleaseCatCacheList(list);
- /* sort results into OID order */
- qsort(elems, j, sizeof(Datum), enum_elem_cmp);
+ /* sort results into sort_order sequence */
+ qsort(sort_items, total, sizeof(enum_sort), enum_sort_cmp);
+
+ j = 0;
+ left_found = !OidIsValid(lower);
+ for (i=0; i < total; i++)
+ {
+ if (! left_found && lower == sort_items[i].enum_oid)
+ left_found = true;
+
+ if (left_found)
+ elems[j++] = ObjectIdGetDatum(sort_items[i].enum_oid);
+
+ if (OidIsValid(upper) && upper == sort_items[i].enum_oid)
+ break;
+ }
/* note this hardwires some details about the representation of Oid */
result = construct_array(elems, j, enumtypoid, sizeof(Oid), true, 'i');
pfree(elems);
+ pfree(sort_items);
return result;
}
-/* qsort comparison function for Datums that are OIDs */
+/*
+ * qsort comparison using sort order, for range routines
+ */
static int
-enum_elem_cmp(const void *left, const void *right)
+enum_sort_cmp(const void *left, const void *right)
{
- Oid l = DatumGetObjectId(*((const Datum *) left));
- Oid r = DatumGetObjectId(*((const Datum *) right));
-
- if (l < r)
- return -1;
- if (l > r)
- return 1;
- return 0;
+ enum_sort *l = (enum_sort *) left;
+ enum_sort *r = (enum_sort *) right;
+
+ return l->sort_order - r->sort_order;
}
+
+/*
+ * qsort comparison using OID order for comparison search cache
+ */
+static int
+enum_oid_cmp(const void *es1, const void *es2)
+{
+ enum_sort *p1, *p2;
+ p1 = (enum_sort *)es1;
+ p2 = (enum_sort *)es2;
+ return p1->enum_oid - p2->enum_oid;
+}
+
+
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 01e1a48..94ae650 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -6605,17 +6605,24 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo)
PQExpBuffer query = createPQExpBuffer();
PGresult *res;
int num,
- i;
+ i;
Oid enum_oid;
char *label;
/* Set proper schema search path so regproc references list correctly */
selectSourceSchema(tyinfo->dobj.namespace->dobj.name);
- appendPQExpBuffer(query, "SELECT oid, enumlabel "
- "FROM pg_catalog.pg_enum "
- "WHERE enumtypid = '%u'"
- "ORDER BY oid",
+ if (fout->remoteVersion > 90000)
+ appendPQExpBuffer(query, "SELECT oid, enumlabel "
+ "FROM pg_catalog.pg_enum "
+ "WHERE enumtypid = '%u'"
+ "ORDER BY enumsortorder",
+ tyinfo->dobj.catId.oid);
+ else
+ appendPQExpBuffer(query, "SELECT oid, enumlabel "
+ "FROM pg_catalog.pg_enum "
+ "WHERE enumtypid = '%u'"
+ "ORDER BY oid",
tyinfo->dobj.catId.oid);
res = PQexec(g_conn, query->data);
@@ -6661,17 +6668,19 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo)
{
enum_oid = atooid(PQgetvalue(res, i, PQfnumber(res, "oid")));
label = PQgetvalue(res, i, PQfnumber(res, "enumlabel"));
-
+
if (i == 0)
appendPQExpBuffer(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
appendPQExpBuffer(q,
- "SELECT binary_upgrade.add_pg_enum_label('%u'::pg_catalog.oid, "
- "'%u'::pg_catalog.oid, ",
- enum_oid, tyinfo->dobj.catId.oid);
- appendStringLiteralAH(q, label, fout);
- appendPQExpBuffer(q, ");\n");
+ "SELECT binary_upgrade.set_next_pg_enum_oid('%u'::pg_catalog.oid);\n",
+ enum_oid);
+ appendPQExpBuffer(q, "ALTER TYPE %s.",
+ fmtId(tyinfo->dobj.namespace->dobj.name));
+ appendPQExpBuffer(q, "%s ADD ",
+ fmtId(tyinfo->dobj.name));
+ appendStringLiteralAH(q, label, fout);
+ appendPQExpBuffer(q, ";\n\n");
}
- appendPQExpBuffer(q, "\n");
}
ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index e56e15f..8db3800 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -147,6 +147,8 @@ DECLARE_UNIQUE_INDEX(pg_enum_oid_index, 3502, on pg_enum using btree(oid oid_ops
#define EnumOidIndexId 3502
DECLARE_UNIQUE_INDEX(pg_enum_typid_label_index, 3503, on pg_enum using btree(enumtypid oid_ops, enumlabel name_ops));
#define EnumTypIdLabelIndexId 3503
+DECLARE_UNIQUE_INDEX(pg_enum_typid_sortorder_index, 3539, on pg_enum using btree(enumtypid oid_ops, enumsortorder int4_ops));
+#define EnumTypIdSortOrderIndexId 3539
/* This following index is not used for a cache and is not unique */
DECLARE_INDEX(pg_index_indrelid_index, 2678, on pg_index using btree(indrelid oid_ops));
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index de34576..184cfb3 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -132,7 +132,7 @@ typedef FormData_pg_class *Form_pg_class;
*/
/* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId */
-DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f f r 28 0 t f f f f f 3 _null_ _null_ ));
+DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f f r 30 0 t f f f f f 3 _null_ _null_ ));
DESCR("");
DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f f r 19 0 f f f f f f 3 _null_ _null_ ));
DESCR("");
diff --git a/src/include/catalog/pg_enum.h b/src/include/catalog/pg_enum.h
index 3a1df77..b85f9cf 100644
--- a/src/include/catalog/pg_enum.h
+++ b/src/include/catalog/pg_enum.h
@@ -35,6 +35,7 @@ CATALOG(pg_enum,3501)
{
Oid enumtypid; /* OID of owning enum type */
NameData enumlabel; /* text representation of enum value */
+ int4 enumsortorder; /* sort order for this enum label */
} FormData_pg_enum;
/* ----------------
@@ -48,9 +49,10 @@ typedef FormData_pg_enum *Form_pg_enum;
* compiler constants for pg_enum
* ----------------
*/
-#define Natts_pg_enum 2
+#define Natts_pg_enum 3
#define Anum_pg_enum_enumtypid 1
#define Anum_pg_enum_enumlabel 2
+#define Anum_pg_enum_enumsortorder 3
/* ----------------
* pg_enum has no initial contents
@@ -60,8 +62,9 @@ typedef FormData_pg_enum *Form_pg_enum;
/*
* prototypes for functions in pg_enum.c
*/
-extern void EnumValuesCreate(Oid enumTypeOid, List *vals,
- Oid binary_upgrade_next_pg_enum_oid);
+extern void EnumValuesCreate(Oid enumTypeOid, List *vals);
extern void EnumValuesDelete(Oid enumTypeOid);
+extern bool AddEnumLabel(Oid enumTypeOid, char *newVal, char *neighbour,
+ bool newValIsAfter, int nelems, bool elems_are_sorted);
#endif /* PG_ENUM_H */
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index b2e14ce..3ea2118 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -194,6 +194,18 @@ CATALOG(pg_type,1247) BKI_BOOTSTRAP BKI_ROWTYPE_OID(71) BKI_SCHEMA_MACRO
*/
int4 typndims;
+ /*
+ * typnlabels, contains a count on the number of labels an enum type has,
+ * -1 for a non-enum type.
+ */
+ int4 typnlabels;
+
+ /*
+ * typsorted is true if the oids of an enum type reflect the type's sort
+ * order, false otherwise including for a non-enum type.
+ */
+ bool typsorted;
+
/*
* If typdefaultbin is not NULL, it is the nodeToString representation of
* a default expression for the type. Currently this is only used for
@@ -224,7 +236,7 @@ typedef FormData_pg_type *Form_pg_type;
* compiler constants for pg_type
* ----------------
*/
-#define Natts_pg_type 28
+#define Natts_pg_type 30
#define Anum_pg_type_typname 1
#define Anum_pg_type_typnamespace 2
#define Anum_pg_type_typowner 3
@@ -251,8 +263,10 @@ typedef FormData_pg_type *Form_pg_type;
#define Anum_pg_type_typbasetype 24
#define Anum_pg_type_typtypmod 25
#define Anum_pg_type_typndims 26
-#define Anum_pg_type_typdefaultbin 27
-#define Anum_pg_type_typdefault 28
+#define Anum_pg_type_typnlabels 27
+#define Anum_pg_type_typsorted 28
+#define Anum_pg_type_typdefaultbin 29
+#define Anum_pg_type_typdefault 30
/* ----------------
@@ -269,83 +283,83 @@ typedef FormData_pg_type *Form_pg_type;
*/
/* OIDS 1 - 99 */
-DATA(insert OID = 16 ( bool PGNSP PGUID 1 t b B t t \054 0 0 1000 boolin boolout boolrecv boolsend - - - c p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 16 ( bool PGNSP PGUID 1 t b B t t \054 0 0 1000 boolin boolout boolrecv boolsend - - - c p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("boolean, 'true'/'false'");
#define BOOLOID 16
-DATA(insert OID = 17 ( bytea PGNSP PGUID -1 f b U f t \054 0 0 1001 byteain byteaout bytearecv byteasend - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 17 ( bytea PGNSP PGUID -1 f b U f t \054 0 0 1001 byteain byteaout bytearecv byteasend - - - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("variable-length string, binary values escaped");
#define BYTEAOID 17
-DATA(insert OID = 18 ( char PGNSP PGUID 1 t b S f t \054 0 0 1002 charin charout charrecv charsend - - - c p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 18 ( char PGNSP PGUID 1 t b S f t \054 0 0 1002 charin charout charrecv charsend - - - c p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("single character");
#define CHAROID 18
-DATA(insert OID = 19 ( name PGNSP PGUID NAMEDATALEN f b S f t \054 0 18 1003 namein nameout namerecv namesend - - - c p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 19 ( name PGNSP PGUID NAMEDATALEN f b S f t \054 0 18 1003 namein nameout namerecv namesend - - - c p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("63-character type for storing system identifiers");
#define NAMEOID 19
-DATA(insert OID = 20 ( int8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 1016 int8in int8out int8recv int8send - - - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 20 ( int8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 1016 int8in int8out int8recv int8send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("~18 digit integer, 8-byte storage");
#define INT8OID 20
-DATA(insert OID = 21 ( int2 PGNSP PGUID 2 t b N f t \054 0 0 1005 int2in int2out int2recv int2send - - - s p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 21 ( int2 PGNSP PGUID 2 t b N f t \054 0 0 1005 int2in int2out int2recv int2send - - - s p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("-32 thousand to 32 thousand, 2-byte storage");
#define INT2OID 21
-DATA(insert OID = 22 ( int2vector PGNSP PGUID -1 f b A f t \054 0 21 1006 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 22 ( int2vector PGNSP PGUID -1 f b A f t \054 0 21 1006 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("array of int2, used in system tables");
#define INT2VECTOROID 22
-DATA(insert OID = 23 ( int4 PGNSP PGUID 4 t b N f t \054 0 0 1007 int4in int4out int4recv int4send - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 23 ( int4 PGNSP PGUID 4 t b N f t \054 0 0 1007 int4in int4out int4recv int4send - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("-2 billion to 2 billion integer, 4-byte storage");
#define INT4OID 23
-DATA(insert OID = 24 ( regproc PGNSP PGUID 4 t b N f t \054 0 0 1008 regprocin regprocout regprocrecv regprocsend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 24 ( regproc PGNSP PGUID 4 t b N f t \054 0 0 1008 regprocin regprocout regprocrecv regprocsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered procedure");
#define REGPROCOID 24
-DATA(insert OID = 25 ( text PGNSP PGUID -1 f b S t t \054 0 0 1009 textin textout textrecv textsend - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 25 ( text PGNSP PGUID -1 f b S t t \054 0 0 1009 textin textout textrecv textsend - - - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("variable-length string, no limit specified");
#define TEXTOID 25
-DATA(insert OID = 26 ( oid PGNSP PGUID 4 t b N t t \054 0 0 1028 oidin oidout oidrecv oidsend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 26 ( oid PGNSP PGUID 4 t b N t t \054 0 0 1028 oidin oidout oidrecv oidsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("object identifier(oid), maximum 4 billion");
#define OIDOID 26
-DATA(insert OID = 27 ( tid PGNSP PGUID 6 f b U f t \054 0 0 1010 tidin tidout tidrecv tidsend - - - s p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 27 ( tid PGNSP PGUID 6 f b U f t \054 0 0 1010 tidin tidout tidrecv tidsend - - - s p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("(block, offset), physical location of tuple");
#define TIDOID 27
-DATA(insert OID = 28 ( xid PGNSP PGUID 4 t b U f t \054 0 0 1011 xidin xidout xidrecv xidsend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 28 ( xid PGNSP PGUID 4 t b U f t \054 0 0 1011 xidin xidout xidrecv xidsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("transaction id");
#define XIDOID 28
-DATA(insert OID = 29 ( cid PGNSP PGUID 4 t b U f t \054 0 0 1012 cidin cidout cidrecv cidsend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 29 ( cid PGNSP PGUID 4 t b U f t \054 0 0 1012 cidin cidout cidrecv cidsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("command identifier type, sequence in transaction id");
#define CIDOID 29
-DATA(insert OID = 30 ( oidvector PGNSP PGUID -1 f b A f t \054 0 26 1013 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 30 ( oidvector PGNSP PGUID -1 f b A f t \054 0 26 1013 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("array of oids, used in system tables");
#define OIDVECTOROID 30
/* hand-built rowtype entries for bootstrapped catalogs */
/* NB: OIDs assigned here must match the BKI_ROWTYPE_OID declarations */
-DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c C f t \054 1247 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c C f t \054 1249 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c C f t \054 1255 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c C f t \054 1259 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c C f t \054 1247 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c C f t \054 1249 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c C f t \054 1255 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c C f t \054 1259 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
/* OIDS 100 - 199 */
-DATA(insert OID = 142 ( xml PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 142 ( xml PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("XML content");
#define XMLOID 142
-DATA(insert OID = 143 ( _xml PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 143 ( _xml PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
/* OIDS 200 - 299 */
-DATA(insert OID = 210 ( smgr PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 210 ( smgr PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("storage manager");
/* OIDS 300 - 399 */
@@ -355,231 +369,231 @@ DESCR("storage manager");
/* OIDS 500 - 599 */
/* OIDS 600 - 699 */
-DATA(insert OID = 600 ( point PGNSP PGUID 16 f b G f t \054 0 701 1017 point_in point_out point_recv point_send - - - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 600 ( point PGNSP PGUID 16 f b G f t \054 0 701 1017 point_in point_out point_recv point_send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric point '(x, y)'");
#define POINTOID 600
-DATA(insert OID = 601 ( lseg PGNSP PGUID 32 f b G f t \054 0 600 1018 lseg_in lseg_out lseg_recv lseg_send - - - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 601 ( lseg PGNSP PGUID 32 f b G f t \054 0 600 1018 lseg_in lseg_out lseg_recv lseg_send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric line segment '(pt1,pt2)'");
#define LSEGOID 601
-DATA(insert OID = 602 ( path PGNSP PGUID -1 f b G f t \054 0 0 1019 path_in path_out path_recv path_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 602 ( path PGNSP PGUID -1 f b G f t \054 0 0 1019 path_in path_out path_recv path_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric path '(pt1,...)'");
#define PATHOID 602
-DATA(insert OID = 603 ( box PGNSP PGUID 32 f b G f t \073 0 600 1020 box_in box_out box_recv box_send - - - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 603 ( box PGNSP PGUID 32 f b G f t \073 0 600 1020 box_in box_out box_recv box_send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric box '(lower left,upper right)'");
#define BOXOID 603
-DATA(insert OID = 604 ( polygon PGNSP PGUID -1 f b G f t \054 0 0 1027 poly_in poly_out poly_recv poly_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 604 ( polygon PGNSP PGUID -1 f b G f t \054 0 0 1027 poly_in poly_out poly_recv poly_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric polygon '(pt1,...)'");
#define POLYGONOID 604
-DATA(insert OID = 628 ( line PGNSP PGUID 32 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 628 ( line PGNSP PGUID 32 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric line (not implemented)");
#define LINEOID 628
-DATA(insert OID = 629 ( _line PGNSP PGUID -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 629 ( _line PGNSP PGUID -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("");
/* OIDS 700 - 799 */
-DATA(insert OID = 700 ( float4 PGNSP PGUID 4 FLOAT4PASSBYVAL b N f t \054 0 0 1021 float4in float4out float4recv float4send - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 700 ( float4 PGNSP PGUID 4 FLOAT4PASSBYVAL b N f t \054 0 0 1021 float4in float4out float4recv float4send - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("single-precision floating point number, 4-byte storage");
#define FLOAT4OID 700
-DATA(insert OID = 701 ( float8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N t t \054 0 0 1022 float8in float8out float8recv float8send - - - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 701 ( float8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N t t \054 0 0 1022 float8in float8out float8recv float8send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("double-precision floating point number, 8-byte storage");
#define FLOAT8OID 701
-DATA(insert OID = 702 ( abstime PGNSP PGUID 4 t b D f t \054 0 0 1023 abstimein abstimeout abstimerecv abstimesend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 702 ( abstime PGNSP PGUID 4 t b D f t \054 0 0 1023 abstimein abstimeout abstimerecv abstimesend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("absolute, limited-range date and time (Unix system time)");
#define ABSTIMEOID 702
-DATA(insert OID = 703 ( reltime PGNSP PGUID 4 t b T f t \054 0 0 1024 reltimein reltimeout reltimerecv reltimesend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 703 ( reltime PGNSP PGUID 4 t b T f t \054 0 0 1024 reltimein reltimeout reltimerecv reltimesend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("relative, limited-range time interval (Unix delta time)");
#define RELTIMEOID 703
-DATA(insert OID = 704 ( tinterval PGNSP PGUID 12 f b T f t \054 0 0 1025 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 704 ( tinterval PGNSP PGUID 12 f b T f t \054 0 0 1025 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("(abstime,abstime), time interval");
#define TINTERVALOID 704
-DATA(insert OID = 705 ( unknown PGNSP PGUID -2 f b X f t \054 0 0 0 unknownin unknownout unknownrecv unknownsend - - - c p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 705 ( unknown PGNSP PGUID -2 f b X f t \054 0 0 0 unknownin unknownout unknownrecv unknownsend - - - c p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("");
#define UNKNOWNOID 705
-DATA(insert OID = 718 ( circle PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 718 ( circle PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric circle '(center,radius)'");
#define CIRCLEOID 718
-DATA(insert OID = 719 ( _circle PGNSP PGUID -1 f b A f t \054 0 718 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 790 ( money PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 719 ( _circle PGNSP PGUID -1 f b A f t \054 0 718 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 790 ( money PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("monetary amounts, $d,ddd.cc");
#define CASHOID 790
-DATA(insert OID = 791 ( _money PGNSP PGUID -1 f b A f t \054 0 790 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 791 ( _money PGNSP PGUID -1 f b A f t \054 0 790 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
/* OIDS 800 - 899 */
-DATA(insert OID = 829 ( macaddr PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 829 ( macaddr PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("XX:XX:XX:XX:XX:XX, MAC address");
#define MACADDROID 829
-DATA(insert OID = 869 ( inet PGNSP PGUID -1 f b I t t \054 0 0 1041 inet_in inet_out inet_recv inet_send - - - i m f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 869 ( inet PGNSP PGUID -1 f b I t t \054 0 0 1041 inet_in inet_out inet_recv inet_send - - - i m f 0 -1 0 -1 f _null_ _null_ ));
DESCR("IP address/netmask, host address, netmask optional");
#define INETOID 869
-DATA(insert OID = 650 ( cidr PGNSP PGUID -1 f b I f t \054 0 0 651 cidr_in cidr_out cidr_recv cidr_send - - - i m f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 650 ( cidr PGNSP PGUID -1 f b I f t \054 0 0 651 cidr_in cidr_out cidr_recv cidr_send - - - i m f 0 -1 0 -1 f _null_ _null_ ));
DESCR("network IP address/netmask, network address");
#define CIDROID 650
/* OIDS 900 - 999 */
/* OIDS 1000 - 1099 */
-DATA(insert OID = 1000 ( _bool PGNSP PGUID -1 f b A f t \054 0 16 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1001 ( _bytea PGNSP PGUID -1 f b A f t \054 0 17 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1002 ( _char PGNSP PGUID -1 f b A f t \054 0 18 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b A f t \054 0 19 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b A f t \054 0 21 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b A f t \054 0 22 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b A f t \054 0 23 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1000 ( _bool PGNSP PGUID -1 f b A f t \054 0 16 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 1001 ( _bytea PGNSP PGUID -1 f b A f t \054 0 17 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 1002 ( _char PGNSP PGUID -1 f b A f t \054 0 18 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b A f t \054 0 19 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b A f t \054 0 21 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b A f t \054 0 22 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b A f t \054 0 23 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
#define INT4ARRAYOID 1007
-DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b A f t \054 0 24 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b A f t \054 0 25 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b A f t \054 0 24 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b A f t \054 0 25 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
#define TEXTARRAYOID 1009
-DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b A f t \054 0 26 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b A f t \054 0 27 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b A f t \054 0 28 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b A f t \054 0 29 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1013 ( _oidvector PGNSP PGUID -1 f b A f t \054 0 30 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1014 ( _bpchar PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1015 ( _varchar PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1016 ( _int8 PGNSP PGUID -1 f b A f t \054 0 20 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1017 ( _point PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1018 ( _lseg PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1019 ( _path PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1020 ( _box PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1021 ( _float4 PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b A f t \054 0 26 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b A f t \054 0 27 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b A f t \054 0 28 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b A f t \054 0 29 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 1013 ( _oidvector PGNSP PGUID -1 f b A f t \054 0 30 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 1014 ( _bpchar PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 1015 ( _varchar PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 1016 ( _int8 PGNSP PGUID -1 f b A f t \054 0 20 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 1017 ( _point PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 1018 ( _lseg PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 1019 ( _path PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 1020 ( _box PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 1021 ( _float4 PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
#define FLOAT4ARRAYOID 1021
-DATA(insert OID = 1022 ( _float8 PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1033 ( aclitem PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1022 ( _float8 PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 1033 ( aclitem PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("access control list");
#define ACLITEMOID 1033
-DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1263 ( _cstring PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 1263 ( _cstring PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
#define CSTRINGARRAYOID 1263
-DATA(insert OID = 1042 ( bpchar PGNSP PGUID -1 f b S f t \054 0 0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1042 ( bpchar PGNSP PGUID -1 f b S f t \054 0 0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("char(length), blank-padded string, fixed storage length");
#define BPCHAROID 1042
-DATA(insert OID = 1043 ( varchar PGNSP PGUID -1 f b S f t \054 0 0 1015 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1043 ( varchar PGNSP PGUID -1 f b S f t \054 0 0 1015 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("varchar(length), non-blank-padded string, variable storage length");
#define VARCHAROID 1043
-DATA(insert OID = 1082 ( date PGNSP PGUID 4 t b D f t \054 0 0 1182 date_in date_out date_recv date_send - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1082 ( date PGNSP PGUID 4 t b D f t \054 0 0 1182 date_in date_out date_recv date_send - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("date");
#define DATEOID 1082
-DATA(insert OID = 1083 ( time PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1183 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1083 ( time PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1183 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("time of day");
#define TIMEOID 1083
/* OIDS 1100 - 1199 */
-DATA(insert OID = 1114 ( timestamp PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1114 ( timestamp PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("date and time");
#define TIMESTAMPOID 1114
-DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1182 ( _date PGNSP PGUID -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1183 ( _time PGNSP PGUID -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1184 ( timestamptz PGNSP PGUID 8 FLOAT8PASSBYVAL b D t t \054 0 0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout - d x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 1182 ( _date PGNSP PGUID -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 1183 ( _time PGNSP PGUID -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout - d x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 1184 ( timestamptz PGNSP PGUID 8 FLOAT8PASSBYVAL b D t t \054 0 0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("date and time with time zone");
#define TIMESTAMPTZOID 1184
-DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0 1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1186 ( interval PGNSP PGUID 16 f b T t t \054 0 0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0 1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout - d x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 1186 ( interval PGNSP PGUID 16 f b T t t \054 0 0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("@ <number> <units>, time interval");
#define INTERVALOID 1186
-DATA(insert OID = 1187 ( _interval PGNSP PGUID -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1187 ( _interval PGNSP PGUID -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout - d x f 0 -1 0 -1 f _null_ _null_ ));
/* OIDS 1200 - 1299 */
-DATA(insert OID = 1231 ( _numeric PGNSP PGUID -1 f b A f t \054 0 1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1266 ( timetz PGNSP PGUID 12 f b D f t \054 0 0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1231 ( _numeric PGNSP PGUID -1 f b A f t \054 0 1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 1266 ( timetz PGNSP PGUID 12 f b D f t \054 0 0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("time of day with time zone");
#define TIMETZOID 1266
-DATA(insert OID = 1270 ( _timetz PGNSP PGUID -1 f b A f t \054 0 1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1270 ( _timetz PGNSP PGUID -1 f b A f t \054 0 1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout - d x f 0 -1 0 -1 f _null_ _null_ ));
/* OIDS 1500 - 1599 */
-DATA(insert OID = 1560 ( bit PGNSP PGUID -1 f b V f t \054 0 0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1560 ( bit PGNSP PGUID -1 f b V f t \054 0 0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("fixed-length bit string");
#define BITOID 1560
-DATA(insert OID = 1561 ( _bit PGNSP PGUID -1 f b A f t \054 0 1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1562 ( varbit PGNSP PGUID -1 f b V t t \054 0 0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1561 ( _bit PGNSP PGUID -1 f b A f t \054 0 1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 1562 ( varbit PGNSP PGUID -1 f b V t t \054 0 0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("variable-length bit string");
#define VARBITOID 1562
-DATA(insert OID = 1563 ( _varbit PGNSP PGUID -1 f b A f t \054 0 1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1563 ( _varbit PGNSP PGUID -1 f b A f t \054 0 1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
/* OIDS 1600 - 1699 */
/* OIDS 1700 - 1799 */
-DATA(insert OID = 1700 ( numeric PGNSP PGUID -1 f b N f t \054 0 0 1231 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1700 ( numeric PGNSP PGUID -1 f b N f t \054 0 0 1231 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f 0 -1 0 -1 f _null_ _null_ ));
DESCR("numeric(precision, decimal), arbitrary precision number");
#define NUMERICOID 1700
-DATA(insert OID = 1790 ( refcursor PGNSP PGUID -1 f b U f t \054 0 0 2201 textin textout textrecv textsend - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1790 ( refcursor PGNSP PGUID -1 f b U f t \054 0 0 2201 textin textout textrecv textsend - - - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("reference to cursor (portal name)");
#define REFCURSOROID 1790
/* OIDS 2200 - 2299 */
-DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
-DATA(insert OID = 2202 ( regprocedure PGNSP PGUID 4 t b N f t \054 0 0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2202 ( regprocedure PGNSP PGUID 4 t b N f t \054 0 0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered procedure (with args)");
#define REGPROCEDUREOID 2202
-DATA(insert OID = 2203 ( regoper PGNSP PGUID 4 t b N f t \054 0 0 2208 regoperin regoperout regoperrecv regopersend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2203 ( regoper PGNSP PGUID 4 t b N f t \054 0 0 2208 regoperin regoperout regoperrecv regopersend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered operator");
#define REGOPEROID 2203
-DATA(insert OID = 2204 ( regoperator PGNSP PGUID 4 t b N f t \054 0 0 2209 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2204 ( regoperator PGNSP PGUID 4 t b N f t \054 0 0 2209 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered operator (with args)");
#define REGOPERATOROID 2204
-DATA(insert OID = 2205 ( regclass PGNSP PGUID 4 t b N f t \054 0 0 2210 regclassin regclassout regclassrecv regclasssend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2205 ( regclass PGNSP PGUID 4 t b N f t \054 0 0 2210 regclassin regclassout regclassrecv regclasssend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered class");
#define REGCLASSOID 2205
-DATA(insert OID = 2206 ( regtype PGNSP PGUID 4 t b N f t \054 0 0 2211 regtypein regtypeout regtyperecv regtypesend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2206 ( regtype PGNSP PGUID 4 t b N f t \054 0 0 2211 regtypein regtypeout regtyperecv regtypesend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered type");
#define REGTYPEOID 2206
-DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
#define REGTYPEARRAYOID 2211
/* uuid */
-DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("UUID datatype");
-DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
/* text search */
-DATA(insert OID = 3614 ( tsvector PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 3614 ( tsvector PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("text representation for text search");
#define TSVECTOROID 3614
-DATA(insert OID = 3642 ( gtsvector PGNSP PGUID -1 f b U f t \054 0 0 3644 gtsvectorin gtsvectorout - - - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 3642 ( gtsvector PGNSP PGUID -1 f b U f t \054 0 0 3644 gtsvectorin gtsvectorout - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("GiST index internal text representation for text search");
#define GTSVECTOROID 3642
-DATA(insert OID = 3615 ( tsquery PGNSP PGUID -1 f b U f t \054 0 0 3645 tsqueryin tsqueryout tsqueryrecv tsquerysend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 3615 ( tsquery PGNSP PGUID -1 f b U f t \054 0 0 3645 tsqueryin tsqueryout tsqueryrecv tsquerysend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("query representation for text search");
#define TSQUERYOID 3615
-DATA(insert OID = 3734 ( regconfig PGNSP PGUID 4 t b N f t \054 0 0 3735 regconfigin regconfigout regconfigrecv regconfigsend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 3734 ( regconfig PGNSP PGUID 4 t b N f t \054 0 0 3735 regconfigin regconfigout regconfigrecv regconfigsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered text search configuration");
#define REGCONFIGOID 3734
-DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered text search dictionary");
#define REGDICTIONARYOID 3769
-DATA(insert OID = 3643 ( _tsvector PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 3644 ( _gtsvector PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 3643 ( _tsvector PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 3644 ( _gtsvector PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
+DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
-DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("txid snapshot");
-DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
/*
* pseudo-types
@@ -594,31 +608,31 @@ DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 a
* but there is now support for it in records and arrays. Perhaps we should
* just treat it as a regular base type?
*/
-DATA(insert OID = 2249 ( record PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2249 ( record PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
#define RECORDOID 2249
-DATA(insert OID = 2287 ( _record PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2287 ( _record PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
#define RECORDARRAYOID 2287
-DATA(insert OID = 2275 ( cstring PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2275 ( cstring PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 -1 f _null_ _null_ ));
#define CSTRINGOID 2275
-DATA(insert OID = 2276 ( any PGNSP PGUID 4 t p P f t \054 0 0 0 any_in any_out - - - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2276 ( any PGNSP PGUID 4 t p P f t \054 0 0 0 any_in any_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define ANYOID 2276
-DATA(insert OID = 2277 ( anyarray PGNSP PGUID -1 f p P f t \054 0 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2277 ( anyarray PGNSP PGUID -1 f p P f t \054 0 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
#define ANYARRAYOID 2277
-DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void_out - - - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define VOIDOID 2278
-DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define TRIGGEROID 2279
-DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define LANGUAGE_HANDLEROID 2280
-DATA(insert OID = 2281 ( internal PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2281 ( internal PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 -1 f _null_ _null_ ));
#define INTERNALOID 2281
-DATA(insert OID = 2282 ( opaque PGNSP PGUID 4 t p P f t \054 0 0 0 opaque_in opaque_out - - - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2282 ( opaque PGNSP PGUID 4 t p P f t \054 0 0 0 opaque_in opaque_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define OPAQUEOID 2282
-DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p P f t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p P f t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define ANYELEMENTOID 2283
-DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p P f t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p P f t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define ANYNONARRAYOID 2776
-DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define ANYENUMOID 3500
diff --git a/src/include/catalog/pg_type_fn.h b/src/include/catalog/pg_type_fn.h
index e5b5a96..1769af1 100644
--- a/src/include/catalog/pg_type_fn.h
+++ b/src/include/catalog/pg_type_fn.h
@@ -50,7 +50,9 @@ extern Oid TypeCreate(Oid newTypeOid,
char storage,
int32 typeMod,
int32 typNDims,
- bool typeNotNull);
+ bool typeNotNull,
+ int32 typeNLabels,
+ bool typeSorted);
extern void GenerateTypeDependencies(Oid typeNamespace,
Oid typeObjectId,
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index 809143c..fb890b8 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -24,6 +24,7 @@ extern void RemoveTypes(DropStmt *drop);
extern void RemoveTypeById(Oid typeOid);
extern void DefineDomain(CreateDomainStmt *stmt);
extern void DefineEnum(CreateEnumStmt *stmt);
+extern void AlterEnum (AlterEnumStmt *stmt);
extern Oid DefineCompositeType(const RangeVar *typevar, List *coldeflist);
extern Oid AssignTypeArrayOid(void);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index ebec0eb..b61938c 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -346,6 +346,7 @@ typedef enum NodeTag
T_AlterUserMappingStmt,
T_DropUserMappingStmt,
T_AlterTableSpaceOptionsStmt,
+ T_AlterEnumStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index ce3da66..d06dfb8 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2171,6 +2171,20 @@ typedef struct CreateEnumStmt
/* ----------------------
+ * Alter Type Statement, enum types
+ * ----------------------
+ */
+typedef struct AlterEnumStmt
+{
+ NodeTag type;
+ List *typeName; /* qualified name (list of Value strings) */
+ char *newVal; /* new enum value */
+ char *newValNeighbour;/* neighbouring enum value */
+ bool newValIsAfter; /* new enum value is after neighbour? */
+} AlterEnumStmt;
+
+
+/* ----------------------
* Create View Statement
* ----------------------
*/
diff --git a/src/test/regress/expected/enum.out b/src/test/regress/expected/enum.out
index 56240c0..ae595af 100644
--- a/src/test/regress/expected/enum.out
+++ b/src/test/regress/expected/enum.out
@@ -25,6 +25,84 @@ ERROR: invalid input value for enum rainbow: "mauve"
LINE 1: SELECT 'mauve'::rainbow;
^
--
+-- adding new values
+--
+CREATE TYPE planets AS ENUM ( 'venus', 'earth', 'mars' );
+SELECT enumlabel, enumsortorder
+FROM pg_enum
+WHERE enumtypid = 'planets'::regtype
+ORDER by 2;
+ enumlabel | enumsortorder
+-----------+---------------
+ venus | 1
+ earth | 2
+ mars | 3
+(3 rows)
+
+ALTER TYPE planets ADD 'uranus';
+SELECT typnlabels, typsorted
+FROM pg_type
+WHERE oid = 'planets'::regtype;
+ typnlabels | typsorted
+------------+-----------
+ 4 | t
+(1 row)
+
+SELECT enumlabel, enumsortorder
+FROM pg_enum
+WHERE enumtypid = 'planets'::regtype
+ORDER by 2;
+ enumlabel | enumsortorder
+-----------+---------------
+ venus | 1
+ earth | 2
+ mars | 3
+ uranus | 4
+(4 rows)
+
+ALTER TYPE planets ADD 'mercury' BEFORE 'venus';
+ALTER TYPE planets ADD 'saturn' BEFORE 'uranus';
+ALTER TYPE planets ADD 'jupiter' AFTER 'mars';
+ALTER TYPE planets ADD 'neptune' AFTER 'uranus';
+SELECT typnlabels, typsorted
+FROM pg_type
+WHERE oid = 'planets'::regtype;
+ typnlabels | typsorted
+------------+-----------
+ 8 | f
+(1 row)
+
+SELECT enumlabel, enumsortorder
+FROM pg_enum
+WHERE enumtypid = 'planets'::regtype
+ORDER by 2;
+ enumlabel | enumsortorder
+-----------+---------------
+ mercury | 1
+ venus | 2
+ earth | 3
+ mars | 4
+ jupiter | 5
+ saturn | 6
+ uranus | 7
+ neptune | 8
+(8 rows)
+
+select 'mars'::planets > 'mercury' as using_sortorder;
+ using_sortorder
+-----------------
+ t
+(1 row)
+
+-- errors for adding labels
+ALTER TYPE planets ADD
+ 'plutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutopluto';
+ERROR: invalid enum label "plutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutopluto"
+DETAIL: Labels must be 63 characters or less.
+ALTER TYPE planets ADD 'pluto' AFTER 'zeus';
+ERROR: "zeus" is not an existing label.
+DROP TYPE planets;
+--
-- Basic table creation, row selection
--
CREATE TABLE enumtest (col rainbow);
@@ -403,7 +481,7 @@ SELECT COUNT(*) FROM pg_type WHERE typname = 'rainbow';
SELECT * FROM pg_enum WHERE NOT EXISTS
(SELECT 1 FROM pg_type WHERE pg_type.oid = enumtypid);
- enumtypid | enumlabel
------------+-----------
+ enumtypid | enumlabel | enumsortorder
+-----------+-----------+---------------
(0 rows)
diff --git a/src/test/regress/sql/enum.sql b/src/test/regress/sql/enum.sql
index 387e8e7..0e240b2 100644
--- a/src/test/regress/sql/enum.sql
+++ b/src/test/regress/sql/enum.sql
@@ -16,6 +16,54 @@ SELECT 'red'::rainbow;
SELECT 'mauve'::rainbow;
--
+-- adding new values
+--
+
+CREATE TYPE planets AS ENUM ( 'venus', 'earth', 'mars' );
+
+SELECT enumlabel, enumsortorder
+FROM pg_enum
+WHERE enumtypid = 'planets'::regtype
+ORDER by 2;
+
+ALTER TYPE planets ADD 'uranus';
+
+SELECT typnlabels, typsorted
+FROM pg_type
+WHERE oid = 'planets'::regtype;
+
+SELECT enumlabel, enumsortorder
+FROM pg_enum
+WHERE enumtypid = 'planets'::regtype
+ORDER by 2;
+
+ALTER TYPE planets ADD 'mercury' BEFORE 'venus';
+
+ALTER TYPE planets ADD 'saturn' BEFORE 'uranus';
+
+ALTER TYPE planets ADD 'jupiter' AFTER 'mars';
+
+ALTER TYPE planets ADD 'neptune' AFTER 'uranus';
+
+SELECT typnlabels, typsorted
+FROM pg_type
+WHERE oid = 'planets'::regtype;
+
+SELECT enumlabel, enumsortorder
+FROM pg_enum
+WHERE enumtypid = 'planets'::regtype
+ORDER by 2;
+
+select 'mars'::planets > 'mercury' as using_sortorder;
+
+-- errors for adding labels
+ALTER TYPE planets ADD
+ 'plutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutopluto';
+
+ALTER TYPE planets ADD 'pluto' AFTER 'zeus';
+
+DROP TYPE planets;
+--
-- Basic table creation, row selection
--
CREATE TABLE enumtest (col rainbow);
On 08/26/2010 05:24 AM, Andrew Dunstan wrote:
On 08/25/2010 03:29 AM, Andrew Dunstan wrote:
I've just been taking another look at this suggestion. I think it
will work quite cleanly. As long as we add the enums in the correct
order it should just do the Right Thing (tm).To answer your other question, Oid wraparound will not be a problem.
Will get coding.
Revised patch with pg_dump and pg_upgrade support is attached.
This time in context diff format.
cheers
andrew
Attachments:
venum2.patchtext/x-patch; name=venum2.patchDownload
*** a/contrib/pg_upgrade/function.c
--- b/contrib/pg_upgrade/function.c
***************
*** 58,82 **** install_support_functions(migratorContext *ctx)
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_heap_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_toast_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_index_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.add_pg_enum_label(OID, OID, NAME) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
--- 58,82 ----
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_pg_enum_oid(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_heap_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_toast_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_index_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
*** a/contrib/pg_upgrade_support/pg_upgrade_support.c
--- b/contrib/pg_upgrade_support/pg_upgrade_support.c
***************
*** 27,32 **** PG_MODULE_MAGIC;
--- 27,33 ----
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_array_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_toast_oid;
+ extern PGDLLIMPORT Oid binary_upgrade_next_pg_enum_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_heap_relfilenode;
extern PGDLLIMPORT Oid binary_upgrade_next_toast_relfilenode;
extern PGDLLIMPORT Oid binary_upgrade_next_index_relfilenode;
***************
*** 34,51 **** extern PGDLLIMPORT Oid binary_upgrade_next_index_relfilenode;
Datum set_next_pg_type_oid(PG_FUNCTION_ARGS);
Datum set_next_pg_type_array_oid(PG_FUNCTION_ARGS);
Datum set_next_pg_type_toast_oid(PG_FUNCTION_ARGS);
Datum set_next_heap_relfilenode(PG_FUNCTION_ARGS);
Datum set_next_toast_relfilenode(PG_FUNCTION_ARGS);
Datum set_next_index_relfilenode(PG_FUNCTION_ARGS);
- Datum add_pg_enum_label(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(set_next_pg_type_oid);
PG_FUNCTION_INFO_V1(set_next_pg_type_array_oid);
PG_FUNCTION_INFO_V1(set_next_pg_type_toast_oid);
PG_FUNCTION_INFO_V1(set_next_heap_relfilenode);
PG_FUNCTION_INFO_V1(set_next_toast_relfilenode);
PG_FUNCTION_INFO_V1(set_next_index_relfilenode);
- PG_FUNCTION_INFO_V1(add_pg_enum_label);
Datum
set_next_pg_type_oid(PG_FUNCTION_ARGS)
--- 35,52 ----
Datum set_next_pg_type_oid(PG_FUNCTION_ARGS);
Datum set_next_pg_type_array_oid(PG_FUNCTION_ARGS);
Datum set_next_pg_type_toast_oid(PG_FUNCTION_ARGS);
+ Datum set_next_pg_enum_oid(PG_FUNCTION_ARGS);
Datum set_next_heap_relfilenode(PG_FUNCTION_ARGS);
Datum set_next_toast_relfilenode(PG_FUNCTION_ARGS);
Datum set_next_index_relfilenode(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(set_next_pg_type_oid);
PG_FUNCTION_INFO_V1(set_next_pg_type_array_oid);
PG_FUNCTION_INFO_V1(set_next_pg_type_toast_oid);
+ PG_FUNCTION_INFO_V1(set_next_pg_enum_oid);
PG_FUNCTION_INFO_V1(set_next_heap_relfilenode);
PG_FUNCTION_INFO_V1(set_next_toast_relfilenode);
PG_FUNCTION_INFO_V1(set_next_index_relfilenode);
Datum
set_next_pg_type_oid(PG_FUNCTION_ARGS)
***************
*** 78,83 **** set_next_pg_type_toast_oid(PG_FUNCTION_ARGS)
--- 79,94 ----
}
Datum
+ set_next_pg_enum_oid(PG_FUNCTION_ARGS)
+ {
+ Oid enumoid = PG_GETARG_OID(0);
+
+ binary_upgrade_next_pg_enum_oid = enumoid;
+
+ PG_RETURN_VOID();
+ }
+
+ Datum
set_next_heap_relfilenode(PG_FUNCTION_ARGS)
{
Oid relfilenode = PG_GETARG_OID(0);
***************
*** 106,122 **** set_next_index_relfilenode(PG_FUNCTION_ARGS)
PG_RETURN_VOID();
}
-
- Datum
- add_pg_enum_label(PG_FUNCTION_ARGS)
- {
- Oid enumoid = PG_GETARG_OID(0);
- Oid typoid = PG_GETARG_OID(1);
- Name label = PG_GETARG_NAME(2);
-
- EnumValuesCreate(typoid, list_make1(makeString(NameStr(*label))),
- enumoid);
-
- PG_RETURN_VOID();
- }
-
--- 117,119 ----
*** a/src/backend/catalog/heap.c
--- b/src/backend/catalog/heap.c
***************
*** 854,860 **** AddNewRelationType(const char *typeName,
'x', /* fully TOASTable */
-1, /* typmod */
0, /* array dimensions for typBaseType */
! false); /* Type NOT NULL */
}
/* --------------------------------
--- 854,862 ----
'x', /* fully TOASTable */
-1, /* typmod */
0, /* array dimensions for typBaseType */
! false, /* Type NOT NULL */
! -1, /* no enum labels */
! false); /* type is not an enum, so not sorted */
}
/* --------------------------------
***************
*** 1086,1092 **** heap_create_with_catalog(const char *relname,
'x', /* fully TOASTable */
-1, /* typmod */
0, /* array dimensions for typBaseType */
! false); /* Type NOT NULL */
pfree(relarrayname);
}
--- 1088,1096 ----
'x', /* fully TOASTable */
-1, /* typmod */
0, /* array dimensions for typBaseType */
! false, /* Type NOT NULL */
! -1, /* no enum labels */
! false); /* type is not an enum, so not sorted */
pfree(relarrayname);
}
*** a/src/backend/catalog/pg_enum.c
--- b/src/backend/catalog/pg_enum.c
***************
*** 18,29 ****
--- 18,219 ----
#include "catalog/catalog.h"
#include "catalog/indexing.h"
#include "catalog/pg_enum.h"
+ #include "catalog/pg_type.h"
+ #include "utils/lsyscache.h"
+ #include "utils/syscache.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/rel.h"
#include "utils/tqual.h"
static int oid_cmp(const void *p1, const void *p2);
+ static int sort_order_cmp(const void *p1, const void *p2);
+
+ Oid binary_upgrade_next_pg_enum_oid = InvalidOid;
+
+ /*
+ * AddEnumLabel
+ * Add a new label to the enum set. By default it goes at
+ * the end, but the user can choose to place it before or
+ * after any existing set member.
+ *
+ * Returns true iff the labels are sorted by oid after the addition.
+ */
+
+ bool
+ AddEnumLabel(Oid enumTypeOid,
+ char *newVal,
+ char *neighbour,
+ bool newValIsAfter,
+ int nelems,
+ bool elems_are_sorted)
+ {
+ Oid newOid;
+ Relation pg_enum;
+ TupleDesc tupDesc;
+ Datum values[Natts_pg_enum];
+ bool nulls[Natts_pg_enum];
+ NameData enumlabel;
+ HeapTuple enum_tup;
+ bool result = elems_are_sorted;
+ int newelemorder;
+
+ /* check length of new label is ok */
+ if (strlen(newVal) > (NAMEDATALEN - 1))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_NAME),
+ errmsg("invalid enum label \"%s\"", newVal),
+ errdetail("Labels must be %d characters or less.",
+ NAMEDATALEN - 1)));
+
+ /* get a new OID for the label */
+ pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
+
+ if (OidIsValid(binary_upgrade_next_pg_enum_oid))
+ {
+ if (neighbour != NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("BEFORE/AFTER incompatible with binary upgrade.")));
+
+ newOid = binary_upgrade_next_pg_enum_oid;
+ binary_upgrade_next_pg_enum_oid = InvalidOid;
+ }
+ else
+ {
+ /* non upgrade case */
+ newOid = GetNewOid(pg_enum);
+ }
+
+ if (neighbour == NULL)
+ {
+ /*
+ * Put the new label at the end of the list.
+ * No change to existing tuples is required.
+ */
+ newelemorder = nelems + 1;
+ /* are the elements still sorted? */
+ if (elems_are_sorted)
+ {
+ CatCList *list;
+ int i;
+ bool still_sorted = true;
+
+ list = SearchSysCacheList1(ENUMTYPOIDNAME,
+ ObjectIdGetDatum(enumTypeOid));
+ for (i = 0; i < nelems; i++)
+ {
+ HeapTuple tup = &(list->members[i]->tuple);
+ Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
+
+ if (en->enumsortorder == nelems)
+ {
+ if (HeapTupleGetOid(tup) > newOid)
+ still_sorted = false;
+ break;
+ }
+ }
+ ReleaseCatCacheList(list);
+ if (! still_sorted)
+ result = false;
+ }
+ }
+ else
+ {
+ /* BEFORE or AFTER specified */
+ CatCList *list;
+ int i;
+ HeapTuple *existing;
+ HeapTuple nbr = NULL;
+ Form_pg_enum nbr_en;
+
+ /* get a list of the existing elements and sort them by enumsortorder */
+ list = SearchSysCacheList1(ENUMTYPOIDNAME,
+ ObjectIdGetDatum(enumTypeOid));
+ existing = palloc(nelems * sizeof(HeapTuple));
+
+ for (i = 0; i < nelems; i++)
+ existing[i] = &(list->members[i]->tuple);
+
+ qsort(existing, nelems, sizeof(HeapTuple), sort_order_cmp);
+
+ /* locate the neighbour element */
+ for (i = 0; i < nelems; i++)
+ {
+ Form_pg_enum exists_en;
+ exists_en = (Form_pg_enum) GETSTRUCT(existing[i]);
+ if (strcmp(NameStr(exists_en->enumlabel),neighbour) == 0)
+ nbr = existing[i];
+
+ }
+
+ if (nbr == NULL)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("\"%s\" is not an existing label.", neighbour)));
+ }
+
+ nbr_en = (Form_pg_enum) GETSTRUCT(nbr);
+
+ /*
+ * If BEFORE was specified, the new label goes in the neighbour's
+ * position. Otherwise, it goes in the position after that.
+ */
+ newelemorder = nbr_en->enumsortorder;
+ if (newValIsAfter)
+ newelemorder++;
+
+ /*
+ * Add 1 to the sortorder of all the labels after where the
+ * new label goes. Do it from the end back so we don't get
+ * uniqueness violations.
+ */
+ for (i = nelems - 1; i>= 0; i--)
+ {
+ HeapTuple newtup;
+ Form_pg_enum exst_en = (Form_pg_enum) GETSTRUCT(existing[i]);
+ if (exst_en->enumsortorder < newelemorder)
+ break;
+
+ newtup = heap_copytuple(existing[i]);
+ exst_en = (Form_pg_enum) GETSTRUCT(newtup);
+ exst_en->enumsortorder ++;
+
+ simple_heap_update(pg_enum, &newtup->t_self, newtup);
+
+ CatalogUpdateIndexes(pg_enum, newtup);
+
+ }
+
+ ReleaseCatCacheList(list);
+
+ /* are the labels sorted by OID? */
+ if (result && newelemorder > 1)
+ result = newOid > HeapTupleGetOid(existing[newelemorder-2]);
+ if (result && newelemorder < nelems + 1)
+ result = newOid < HeapTupleGetOid(existing[newelemorder-1]);
+
+ }
+
+ /* set up the new entry */
+ tupDesc = pg_enum->rd_att;
+ memset(nulls, false, sizeof(nulls));
+ values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid);
+ namestrcpy(&enumlabel, newVal);
+ values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel);
+ values[Anum_pg_enum_enumsortorder -1] = Int32GetDatum(newelemorder);
+ enum_tup = heap_form_tuple(tupDesc, values, nulls);
+ HeapTupleSetOid(enum_tup, newOid);
+ simple_heap_insert(pg_enum, enum_tup);
+ CatalogUpdateIndexes(pg_enum, enum_tup);
+ heap_freetuple(enum_tup);
+
+ heap_close(pg_enum, RowExclusiveLock);
+
+ return result;
+
+ }
/*
***************
*** 33,40 **** static int oid_cmp(const void *p1, const void *p2);
* vals is a list of Value strings.
*/
void
! EnumValuesCreate(Oid enumTypeOid, List *vals,
! Oid binary_upgrade_next_pg_enum_oid)
{
Relation pg_enum;
TupleDesc tupDesc;
--- 223,229 ----
* vals is a list of Value strings.
*/
void
! EnumValuesCreate(Oid enumTypeOid, List *vals)
{
Relation pg_enum;
TupleDesc tupDesc;
***************
*** 50,58 **** EnumValuesCreate(Oid enumTypeOid, List *vals,
num_elems = list_length(vals);
/*
! * XXX we do not bother to check the list of values for duplicates --- if
* you have any, you'll get a less-than-friendly unique-index violation.
! * Is it worth trying harder?
*/
pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
--- 239,247 ----
num_elems = list_length(vals);
/*
! * We do not bother to check the list of values for duplicates --- if
* you have any, you'll get a less-than-friendly unique-index violation.
! * It is probably not worth trying harder.
*/
pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
***************
*** 62,96 **** EnumValuesCreate(Oid enumTypeOid, List *vals,
* Allocate oids
*/
oids = (Oid *) palloc(num_elems * sizeof(Oid));
! if (OidIsValid(binary_upgrade_next_pg_enum_oid))
! {
! if (num_elems != 1)
! ereport(ERROR,
! (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("EnumValuesCreate() can only set a single OID")));
! oids[0] = binary_upgrade_next_pg_enum_oid;
! binary_upgrade_next_pg_enum_oid = InvalidOid;
! }
! else
{
/*
! * While this method does not absolutely guarantee that we generate no
! * duplicate oids (since we haven't entered each oid into the table
! * before allocating the next), trouble could only occur if the oid
! * counter wraps all the way around before we finish. Which seems
! * unlikely.
*/
! for (elemno = 0; elemno < num_elems; elemno++)
! {
! /*
! * The pg_enum.oid is stored in user tables. This oid must be
! * preserved by binary upgrades.
! */
! oids[elemno] = GetNewOid(pg_enum);
! }
! /* sort them, just in case counter wrapped from high to low */
! qsort(oids, num_elems, sizeof(Oid), oid_cmp);
}
/* and make the entries */
memset(nulls, false, sizeof(nulls));
--- 251,274 ----
* Allocate oids
*/
oids = (Oid *) palloc(num_elems * sizeof(Oid));
!
! /*
! * While this method does not absolutely guarantee that we generate no
! * duplicate oids (since we haven't entered each oid into the table
! * before allocating the next), trouble could only occur if the oid
! * counter wraps all the way around before we finish. Which seems
! * unlikely.
! */
! for (elemno = 0; elemno < num_elems; elemno++)
{
/*
! * The pg_enum.oid is stored in user tables. This oid must be
! * preserved by binary upgrades.
*/
! oids[elemno] = GetNewOid(pg_enum);
}
+ /* sort them, just in case counter wrapped from high to low */
+ qsort(oids, num_elems, sizeof(Oid), oid_cmp);
/* and make the entries */
memset(nulls, false, sizeof(nulls));
***************
*** 114,119 **** EnumValuesCreate(Oid enumTypeOid, List *vals,
--- 292,298 ----
values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid);
namestrcpy(&enumlabel, lab);
values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel);
+ values[Anum_pg_enum_enumsortorder -1] = Int32GetDatum(elemno+1);
tup = heap_form_tuple(tupDesc, values, nulls);
HeapTupleSetOid(tup, oids[elemno]);
***************
*** 164,170 **** EnumValuesDelete(Oid enumTypeOid)
}
! /* qsort comparison function */
static int
oid_cmp(const void *p1, const void *p2)
{
--- 343,349 ----
}
! /* qsort comparison for oids */
static int
oid_cmp(const void *p1, const void *p2)
{
***************
*** 177,179 **** oid_cmp(const void *p1, const void *p2)
--- 356,371 ----
return 1;
return 0;
}
+
+ /* qsort comparison function for tuples by sort order */
+ static int
+ sort_order_cmp(const void *p1, const void *p2)
+ {
+ HeapTuple v1 = *((const HeapTuple *) p1);
+ HeapTuple v2 = *((const HeapTuple *) p2);
+ Form_pg_enum en1 = (Form_pg_enum) GETSTRUCT(v1);
+ Form_pg_enum en2 = (Form_pg_enum) GETSTRUCT(v2);
+
+ return en1->enumsortorder - en2->enumsortorder;
+ }
+
*** a/src/backend/catalog/pg_type.c
--- b/src/backend/catalog/pg_type.c
***************
*** 112,117 **** TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
--- 112,119 ----
values[i++] = ObjectIdGetDatum(InvalidOid); /* typbasetype */
values[i++] = Int32GetDatum(-1); /* typtypmod */
values[i++] = Int32GetDatum(0); /* typndims */
+ values[i++] = Int32GetDatum(-1); /* typnlabels */
+ values[i++] = BoolGetDatum(false); /* typsorted */
nulls[i++] = true; /* typdefaultbin */
nulls[i++] = true; /* typdefault */
***************
*** 204,210 **** TypeCreate(Oid newTypeOid,
char storage,
int32 typeMod,
int32 typNDims, /* Array dimensions for baseType */
! bool typeNotNull)
{
Relation pg_type_desc;
Oid typeObjectId;
--- 206,214 ----
char storage,
int32 typeMod,
int32 typNDims, /* Array dimensions for baseType */
! bool typeNotNull,
! int32 typeNLabels,
! bool typeSorted)
{
Relation pg_type_desc;
Oid typeObjectId;
***************
*** 342,347 **** TypeCreate(Oid newTypeOid,
--- 346,353 ----
values[i++] = ObjectIdGetDatum(baseType); /* typbasetype */
values[i++] = Int32GetDatum(typeMod); /* typtypmod */
values[i++] = Int32GetDatum(typNDims); /* typndims */
+ values[i++] = Int32GetDatum(typeNLabels); /* typnlabels */
+ values[i++] = BoolGetDatum(typeSorted); /* typsorted */
/*
* initialize the default binary value for this type. Check for nulls of
*** a/src/backend/commands/typecmds.c
--- b/src/backend/commands/typecmds.c
***************
*** 85,96 **** static Oid findTypeTypmodoutFunction(List *procname);
static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid);
static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
static void checkDomainOwner(HeapTuple tup, TypeName *typename);
static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
Oid baseTypeOid,
int typMod, Constraint *constr,
char *domainName);
-
-
/*
* DefineType
* Registers a new base type.
--- 85,95 ----
static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid);
static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
static void checkDomainOwner(HeapTuple tup, TypeName *typename);
+ static void checkEnumOwner(HeapTuple tup, TypeName *typename);
static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
Oid baseTypeOid,
int typMod, Constraint *constr,
char *domainName);
/*
* DefineType
* Registers a new base type.
***************
*** 562,569 **** DefineType(List *names, List *parameters)
storage, /* TOAST strategy */
-1, /* typMod (Domains only) */
0, /* Array Dimensions of typbasetype */
! false); /* Type NOT NULL */
!
/*
* Create the array type that goes with it.
*/
--- 561,569 ----
storage, /* TOAST strategy */
-1, /* typMod (Domains only) */
0, /* Array Dimensions of typbasetype */
! false, /* Type NOT NULL */
! -1, /* no enum labels */
! false); /* type is not an enum, so not sorted */
/*
* Create the array type that goes with it.
*/
***************
*** 601,607 **** DefineType(List *names, List *parameters)
'x', /* ARRAY is always toastable */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
! false); /* Type NOT NULL */
pfree(array_type);
}
--- 601,609 ----
'x', /* ARRAY is always toastable */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
! false, /* Type NOT NULL */
! -1, /* no enum labels */
! false); /* type is not an enum, so not sorted */
pfree(array_type);
}
***************
*** 1044,1050 **** DefineDomain(CreateDomainStmt *stmt)
storage, /* TOAST strategy */
basetypeMod, /* typeMod value */
typNDims, /* Array dimensions for base type */
! typNotNull); /* Type NOT NULL */
/*
* Process constraints which refer to the domain ID returned by TypeCreate
--- 1046,1054 ----
storage, /* TOAST strategy */
basetypeMod, /* typeMod value */
typNDims, /* Array dimensions for base type */
! typNotNull, /* Type NOT NULL */
! -1, /* no enum labels */
! false); /* type is not an enum, so not sorted */
/*
* Process constraints which refer to the domain ID returned by TypeCreate
***************
*** 1094,1099 **** DefineEnum(CreateEnumStmt *stmt)
--- 1098,1106 ----
AclResult aclresult;
Oid old_type_oid;
Oid enumArrayOid;
+ int num_labels;
+
+ num_labels = list_length(stmt->vals);
/* Convert list of names to a name and namespace */
enumNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
***************
*** 1153,1162 **** DefineEnum(CreateEnumStmt *stmt)
'p', /* TOAST strategy always plain */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
! false); /* Type NOT NULL */
/* Enter the enum's values into pg_enum */
! EnumValuesCreate(enumTypeOid, stmt->vals, InvalidOid);
/*
* Create the array type that goes with it.
--- 1160,1171 ----
'p', /* TOAST strategy always plain */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
! false, /* Type NOT NULL */
! num_labels, /* count enum labels */
! true); /* enums always start sorted */
/* Enter the enum's values into pg_enum */
! EnumValuesCreate(enumTypeOid, stmt->vals);
/*
* Create the array type that goes with it.
***************
*** 1192,1202 **** DefineEnum(CreateEnumStmt *stmt)
'x', /* ARRAY is always toastable */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
! false); /* Type NOT NULL */
pfree(enumArrayName);
}
/*
* Find suitable I/O functions for a type.
--- 1201,1288 ----
'x', /* ARRAY is always toastable */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
! false, /* Type NOT NULL */
! -1, /* no enum labels */
! false); /* type is not an enum, so not sorted */
pfree(enumArrayName);
}
+ /*
+ * AlterEnum
+ * Registers a new label for an existing enum.
+ */
+ void
+ AlterEnum (AlterEnumStmt *stmt)
+ {
+ Oid enum_type_oid;
+ TypeName *typename;
+ bool sorted;
+ HeapTuple tup, newtup;
+ Relation rel;
+ Form_pg_type typTup;
+
+ /* Make a TypeName so we can use standard type lookup machinery */
+ typename = makeTypeNameFromNameList(stmt->typeName);
+ enum_type_oid = typenameTypeId(NULL, typename, NULL);
+
+ /* Look up the row in the type table */
+ rel = heap_open(TypeRelationId, RowExclusiveLock);
+
+ tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(enum_type_oid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for type %u", enum_type_oid);
+
+ /* Copy the syscache entry so we can scribble on it below */
+ newtup = heap_copytuple(tup);
+ ReleaseSysCache(tup);
+ tup = newtup;
+ typTup = (Form_pg_type) GETSTRUCT(tup);
+
+ /* Check it's an enum and check user has permission to ALTER the enum */
+ checkEnumOwner(tup, typename);
+
+ /* Add the new label */
+ sorted = AddEnumLabel (enum_type_oid, stmt->newVal,
+ stmt->newValNeighbour, stmt->newValIsAfter,
+ typTup->typnlabels, typTup->typsorted);
+
+ /* Update the row in pg_type */
+ typTup->typnlabels += 1;
+ typTup->typsorted = sorted;
+
+ simple_heap_update(rel, &tup->t_self, tup);
+
+ CatalogUpdateIndexes(rel, tup);
+
+ /* Clean up */
+ heap_close(rel, RowExclusiveLock);
+ }
+
+
+ /*
+ * checkEnumOwner
+ *
+ * Check that the type is actually an enum and that the current user
+ * has permission to do ALTER TYPE on it. Throw an error if not.
+ */
+ static void
+ checkEnumOwner(HeapTuple tup, TypeName *typename)
+ {
+ Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup);
+
+ /* Check that this is actually a domain */
+ if (typTup->typtype != TYPTYPE_ENUM)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not an enum",
+ TypeNameToString(typename))));
+
+ /* Permission check: must own type */
+ if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
+ format_type_be(HeapTupleGetOid(tup)));
+ }
/*
* Find suitable I/O functions for a type.
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 2836,2841 **** _copyCreateEnumStmt(CreateEnumStmt *from)
--- 2836,2854 ----
return newnode;
}
+ static AlterEnumStmt *
+ _copyAlterEnumStmt(AlterEnumStmt *from)
+ {
+ AlterEnumStmt *newnode = makeNode(AlterEnumStmt);
+
+ COPY_NODE_FIELD(typeName);
+ COPY_STRING_FIELD(newVal);
+ COPY_STRING_FIELD(newValNeighbour);
+ COPY_SCALAR_FIELD(newValIsAfter);
+
+ return newnode;
+ }
+
static ViewStmt *
_copyViewStmt(ViewStmt *from)
{
***************
*** 3989,3994 **** copyObject(void *from)
--- 4002,4010 ----
case T_CreateEnumStmt:
retval = _copyCreateEnumStmt(from);
break;
+ case T_AlterEnumStmt:
+ retval = _copyAlterEnumStmt(from);
+ break;
case T_ViewStmt:
retval = _copyViewStmt(from);
break;
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
***************
*** 1375,1380 **** _equalCreateEnumStmt(CreateEnumStmt *a, CreateEnumStmt *b)
--- 1375,1391 ----
}
static bool
+ _equalAlterEnumStmt(AlterEnumStmt *a, AlterEnumStmt *b)
+ {
+ COMPARE_NODE_FIELD(typeName);
+ COMPARE_STRING_FIELD(newVal);
+ COMPARE_STRING_FIELD(newValNeighbour);
+ COMPARE_SCALAR_FIELD(newValIsAfter);
+
+ return true;
+ }
+
+ static bool
_equalViewStmt(ViewStmt *a, ViewStmt *b)
{
COMPARE_NODE_FIELD(view);
***************
*** 2678,2683 **** equal(void *a, void *b)
--- 2689,2697 ----
case T_CreateEnumStmt:
retval = _equalCreateEnumStmt(a, b);
break;
+ case T_AlterEnumStmt:
+ retval = _equalAlterEnumStmt(a, b);
+ break;
case T_ViewStmt:
retval = _equalViewStmt(a, b);
break;
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 181,188 **** static TypeName *TableFuncTypeName(List *columns);
}
%type <node> stmt schema_stmt
! AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterFdwStmt
! AlterForeignServerStmt AlterGroupStmt
AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
AlterRoleStmt AlterRoleSetStmt
--- 181,188 ----
}
%type <node> stmt schema_stmt
! AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt
! AlterFdwStmt AlterForeignServerStmt AlterGroupStmt
AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
AlterRoleStmt AlterRoleSetStmt
***************
*** 648,653 **** stmt :
--- 648,654 ----
| AlterDatabaseSetStmt
| AlterDefaultPrivilegesStmt
| AlterDomainStmt
+ | AlterEnumStmt
| AlterFdwStmt
| AlterForeignServerStmt
| AlterFunctionStmt
***************
*** 3781,3786 **** enum_val_list: Sconst
--- 3782,3827 ----
{ $$ = lappend($1, makeString($3)); }
;
+ /*****************************************************************************
+ *
+ * ALTER TYPE enumtype ADD ...
+ *
+ *****************************************************************************/
+
+ AlterEnumStmt:
+ ALTER TYPE_P any_name ADD_P Sconst
+ {
+ AlterEnumStmt *n = makeNode(AlterEnumStmt);
+ n->typeName = $3;
+ n->newVal = $5;
+ n->newValNeighbour = NULL;
+ n->newValIsAfter = true;
+
+ $$ = (Node *) n;
+ }
+ | ALTER TYPE_P any_name ADD_P Sconst BEFORE Sconst
+ {
+ AlterEnumStmt *n = makeNode(AlterEnumStmt);
+ n->typeName = $3;
+ n->newVal = $5;
+ n->newValNeighbour = $7;
+ n->newValIsAfter = false;
+
+ $$ = (Node *) n;
+ }
+ | ALTER TYPE_P any_name ADD_P Sconst AFTER Sconst
+ {
+ AlterEnumStmt *n = makeNode(AlterEnumStmt);
+ n->typeName = $3;
+ n->newVal = $5;
+ n->newValNeighbour = $7;
+ n->newValIsAfter = true;
+
+ $$ = (Node *) n;
+ }
+ ;
+
+
/*****************************************************************************
*
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
***************
*** 189,194 **** check_xact_readonly(Node *parsetree)
--- 189,195 ----
case T_CreateTrigStmt:
case T_CompositeTypeStmt:
case T_CreateEnumStmt:
+ case T_AlterEnumStmt:
case T_ViewStmt:
case T_DropCastStmt:
case T_DropStmt:
***************
*** 846,851 **** standard_ProcessUtility(Node *parsetree,
--- 847,856 ----
DefineEnum((CreateEnumStmt *) parsetree);
break;
+ case T_AlterEnumStmt: /* ALTER TYPE (enum) */
+ AlterEnum((AlterEnumStmt *) parsetree);
+ break;
+
case T_ViewStmt: /* CREATE VIEW */
DefineView((ViewStmt *) parsetree, queryString);
break;
***************
*** 1846,1851 **** CreateCommandTag(Node *parsetree)
--- 1851,1860 ----
tag = "CREATE TYPE";
break;
+ case T_AlterEnumStmt:
+ tag = "ALTER TYPE";
+ break;
+
case T_ViewStmt:
tag = "CREATE VIEW";
break;
***************
*** 2384,2389 **** GetCommandLogLevel(Node *parsetree)
--- 2393,2402 ----
lev = LOGSTMT_DDL;
break;
+ case T_AlterEnumStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
case T_ViewStmt:
lev = LOGSTMT_DDL;
break;
*** a/src/backend/utils/adt/enum.c
--- b/src/backend/utils/adt/enum.c
***************
*** 14,19 ****
--- 14,20 ----
#include "postgres.h"
#include "catalog/pg_enum.h"
+ #include "catalog/pg_type.h"
#include "fmgr.h"
#include "utils/array.h"
#include "utils/builtins.h"
***************
*** 22,30 ****
#include "libpq/pqformat.h"
#include "miscadmin.h"
static ArrayType *enum_range_internal(Oid enumtypoid, Oid lower, Oid upper);
! static int enum_elem_cmp(const void *left, const void *right);
/* Basic I/O support */
--- 23,48 ----
#include "libpq/pqformat.h"
#include "miscadmin.h"
+ typedef struct
+ {
+ Oid enum_oid;
+ int32 sort_order;
+ } enum_sort;
+
+ typedef struct
+ {
+ Oid enumtypoid;
+ bool oids_are_sorted;
+ int sort_list_length;
+ int label_count;
+ enum_sort sort_order_list[1];
+ } enum_sort_cache;
+
+
static ArrayType *enum_range_internal(Oid enumtypoid, Oid lower, Oid upper);
! static int enum_sort_cmp(const void *left, const void *right);
! static int enum_oid_cmp(const void *left, const void *right);
/* Basic I/O support */
***************
*** 155,167 **** enum_send(PG_FUNCTION_ARGS)
/* Comparison functions and related */
Datum
enum_lt(PG_FUNCTION_ARGS)
{
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(a < b);
}
Datum
--- 173,314 ----
/* Comparison functions and related */
+ static inline int
+ enum_ccmp(Oid arg1, Oid arg2, FunctionCallInfo fcinfo)
+ {
+
+ enum_sort_cache * mycache;
+ enum_sort *es1, *es2, srch;
+ int sort1, sort2;
+ bool added = false;
+ HeapTuple enum_tup, type_tup;
+ Form_pg_enum en;
+ Oid typeoid;
+ Form_pg_type typ;
+
+ if (arg1 == arg2)
+ return 0;
+
+ mycache = (enum_sort_cache *) fcinfo->flinfo->fn_extra;
+ if (mycache == NULL )
+ {
+ enum_tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(arg1));
+ en = (Form_pg_enum) GETSTRUCT(enum_tup);
+ typeoid = en->enumtypid;
+ type_tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeoid));
+ typ = (Form_pg_type) GETSTRUCT(type_tup);
+ if (typ->typtype != 'e')
+ elog(ERROR,"wrong type for oid %u",typeoid);
+ fcinfo->flinfo->fn_extra =
+ MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+ sizeof(enum_sort_cache) +
+ (typ->typnlabels * sizeof(enum_sort)));
+ mycache = (enum_sort_cache *) fcinfo->flinfo->fn_extra;
+ mycache->enumtypoid = typeoid;
+ mycache->sort_list_length = 1;
+ mycache->label_count = typ->typnlabels;
+ mycache->oids_are_sorted = typ->typsorted;
+ mycache->sort_order_list[0].enum_oid = arg1;
+ mycache->sort_order_list[0].sort_order = en->enumsortorder;
+ ReleaseSysCache(type_tup);
+ ReleaseSysCache(enum_tup);
+ }
+
+ if (mycache->oids_are_sorted)
+ return arg1 - arg2;
+
+ srch.enum_oid = arg1;
+ es1 = bsearch(&srch,
+ mycache->sort_order_list,
+ mycache->sort_list_length,
+ sizeof(enum_sort),
+ enum_oid_cmp);
+ srch.enum_oid = arg2;
+ es2 = bsearch(&srch,
+ mycache->sort_order_list,
+ mycache->sort_list_length,
+ sizeof(enum_sort),
+ enum_oid_cmp);
+
+ if (es1 == NULL)
+ {
+
+ enum_tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(arg1));
+ en = (Form_pg_enum) GETSTRUCT(enum_tup);
+ mycache->sort_order_list[mycache->sort_list_length].enum_oid = arg1;
+ sort1 = en->enumsortorder;
+ mycache->sort_order_list[mycache->sort_list_length].sort_order =
+ sort1;
+ ReleaseSysCache(enum_tup);
+ mycache->sort_list_length ++;
+ added = true;
+ }
+ else
+ {
+ /* already in cache */
+ sort1 = es1->sort_order;
+ }
+
+ if (es2 == NULL)
+ {
+
+ enum_tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(arg2));
+ en = (Form_pg_enum) GETSTRUCT(enum_tup);
+ sort2 = en->enumsortorder;
+ mycache->sort_order_list[mycache->sort_list_length].enum_oid = arg2;
+ mycache->sort_order_list[mycache->sort_list_length].sort_order =
+ sort2;
+ ReleaseSysCache(enum_tup);
+ mycache->sort_list_length ++;
+ added = true;
+ }
+ else
+ {
+ /* already in cache */
+ sort2 = es2->sort_order;
+ }
+
+ if (added)
+ {
+ /*
+ * If we have more than a handful, just fetch them all, so we limit
+ * the number of sort operations required.
+ */
+ if (mycache->sort_list_length > 10 &&
+ mycache->sort_list_length < mycache->label_count)
+ {
+ CatCList *nlist;
+ int num, i;
+
+ nlist = SearchSysCacheList1(ENUMTYPOIDNAME,
+ ObjectIdGetDatum(mycache->enumtypoid));
+ num = nlist->n_members;
+ for (i = 0; i < num; i++)
+ {
+ HeapTuple tup = &(nlist->members[i]->tuple);
+ Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
+
+ mycache->sort_order_list[i].enum_oid = HeapTupleGetOid(tup);
+ mycache->sort_order_list[i].sort_order = en->enumsortorder;
+ }
+
+ ReleaseCatCacheList(nlist);
+ }
+
+ qsort(mycache->sort_order_list,mycache->sort_list_length,
+ sizeof(enum_sort),enum_oid_cmp);
+ }
+
+ return sort1 - sort2;
+ }
+
Datum
enum_lt(PG_FUNCTION_ARGS)
{
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(enum_ccmp(a,b,fcinfo) < 0);
}
Datum
***************
*** 170,176 **** enum_le(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(a <= b);
}
Datum
--- 317,323 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(enum_ccmp(a,b,fcinfo) <= 0);
}
Datum
***************
*** 197,203 **** enum_ge(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(a >= b);
}
Datum
--- 344,350 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(enum_ccmp(a,b,fcinfo) >= 0);
}
Datum
***************
*** 206,212 **** enum_gt(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(a > b);
}
Datum
--- 353,359 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(enum_ccmp(a,b,fcinfo) > 0);
}
Datum
***************
*** 215,221 **** enum_smaller(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_OID(a <= b ? a : b);
}
Datum
--- 362,368 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_OID(enum_ccmp(a,b,fcinfo) < 0 ? a : b);
}
Datum
***************
*** 224,230 **** enum_larger(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_OID(a >= b ? a : b);
}
Datum
--- 371,377 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_OID(enum_ccmp(a,b,fcinfo) > 0 ? a : b);
}
Datum
***************
*** 233,242 **** enum_cmp(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! if (a > b)
! PG_RETURN_INT32(1);
! else if (a == b)
PG_RETURN_INT32(0);
else
PG_RETURN_INT32(-1);
}
--- 380,389 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! if (a == b)
PG_RETURN_INT32(0);
+ else if (enum_ccmp(a,b,fcinfo) > 0)
+ PG_RETURN_INT32(1);
else
PG_RETURN_INT32(-1);
}
***************
*** 248,253 **** enum_first(PG_FUNCTION_ARGS)
--- 395,401 ----
{
Oid enumtypoid;
Oid min = InvalidOid;
+ int min_sort = -1; /* value will never in fact be used */
CatCList *list;
int num,
i;
***************
*** 267,276 **** enum_first(PG_FUNCTION_ARGS)
num = list->n_members;
for (i = 0; i < num; i++)
{
! Oid valoid = HeapTupleHeaderGetOid(list->members[i]->tuple.t_data);
!
! if (!OidIsValid(min) || valoid < min)
! min = valoid;
}
ReleaseCatCacheList(list);
--- 415,428 ----
num = list->n_members;
for (i = 0; i < num; i++)
{
! HeapTuple tup = &(list->members[i]->tuple);
! Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
!
! if (!OidIsValid(min) || en->enumsortorder < min_sort)
! {
! min = HeapTupleGetOid(tup);
! min_sort = en->enumsortorder;
! }
}
ReleaseCatCacheList(list);
***************
*** 287,292 **** enum_last(PG_FUNCTION_ARGS)
--- 439,445 ----
{
Oid enumtypoid;
Oid max = InvalidOid;
+ int max_sort = -1; /* value will never in fact be used */
CatCList *list;
int num,
i;
***************
*** 306,315 **** enum_last(PG_FUNCTION_ARGS)
num = list->n_members;
for (i = 0; i < num; i++)
{
! Oid valoid = HeapTupleHeaderGetOid(list->members[i]->tuple.t_data);
!
! if (!OidIsValid(max) || valoid > max)
! max = valoid;
}
ReleaseCatCacheList(list);
--- 459,472 ----
num = list->n_members;
for (i = 0; i < num; i++)
{
! HeapTuple tup = &(list->members[i]->tuple);
! Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
!
! if (!OidIsValid(max) || en->enumsortorder > max_sort)
! {
! max = HeapTupleGetOid(tup);
! max_sort = en->enumsortorder;
! }
}
ReleaseCatCacheList(list);
***************
*** 382,427 **** enum_range_internal(Oid enumtypoid, Oid lower, Oid upper)
i,
j;
Datum *elems;
list = SearchSysCacheList1(ENUMTYPOIDNAME, ObjectIdGetDatum(enumtypoid));
total = list->n_members;
elems = (Datum *) palloc(total * sizeof(Datum));
- j = 0;
for (i = 0; i < total; i++)
{
! Oid val = HeapTupleGetOid(&(list->members[i]->tuple));
- if ((!OidIsValid(lower) || lower <= val) &&
- (!OidIsValid(upper) || val <= upper))
- elems[j++] = ObjectIdGetDatum(val);
}
/* shouldn't need the cache anymore */
ReleaseCatCacheList(list);
! /* sort results into OID order */
! qsort(elems, j, sizeof(Datum), enum_elem_cmp);
/* note this hardwires some details about the representation of Oid */
result = construct_array(elems, j, enumtypoid, sizeof(Oid), true, 'i');
pfree(elems);
return result;
}
! /* qsort comparison function for Datums that are OIDs */
static int
! enum_elem_cmp(const void *left, const void *right)
{
! Oid l = DatumGetObjectId(*((const Datum *) left));
! Oid r = DatumGetObjectId(*((const Datum *) right));
!
! if (l < r)
! return -1;
! if (l > r)
! return 1;
! return 0;
}
--- 539,614 ----
i,
j;
Datum *elems;
+ enum_sort *sort_items;
+ bool left_found;
list = SearchSysCacheList1(ENUMTYPOIDNAME, ObjectIdGetDatum(enumtypoid));
total = list->n_members;
elems = (Datum *) palloc(total * sizeof(Datum));
+ sort_items = (enum_sort *) palloc(total * sizeof(enum_sort));
for (i = 0; i < total; i++)
{
! HeapTuple tup = &(list->members[i]->tuple);
! Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
!
! sort_items[i].enum_oid = HeapTupleGetOid(tup);
! sort_items[i].sort_order = en->enumsortorder;
}
/* shouldn't need the cache anymore */
ReleaseCatCacheList(list);
! /* sort results into sort_order sequence */
! qsort(sort_items, total, sizeof(enum_sort), enum_sort_cmp);
!
! j = 0;
! left_found = !OidIsValid(lower);
! for (i=0; i < total; i++)
! {
! if (! left_found && lower == sort_items[i].enum_oid)
! left_found = true;
!
! if (left_found)
! elems[j++] = ObjectIdGetDatum(sort_items[i].enum_oid);
!
! if (OidIsValid(upper) && upper == sort_items[i].enum_oid)
! break;
! }
/* note this hardwires some details about the representation of Oid */
result = construct_array(elems, j, enumtypoid, sizeof(Oid), true, 'i');
pfree(elems);
+ pfree(sort_items);
return result;
}
! /*
! * qsort comparison using sort order, for range routines
! */
static int
! enum_sort_cmp(const void *left, const void *right)
{
! enum_sort *l = (enum_sort *) left;
! enum_sort *r = (enum_sort *) right;
!
! return l->sort_order - r->sort_order;
}
+
+ /*
+ * qsort comparison using OID order for comparison search cache
+ */
+ static int
+ enum_oid_cmp(const void *es1, const void *es2)
+ {
+ enum_sort *p1, *p2;
+ p1 = (enum_sort *)es1;
+ p2 = (enum_sort *)es2;
+ return p1->enum_oid - p2->enum_oid;
+ }
+
+
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
***************
*** 6605,6621 **** dumpEnumType(Archive *fout, TypeInfo *tyinfo)
PQExpBuffer query = createPQExpBuffer();
PGresult *res;
int num,
! i;
Oid enum_oid;
char *label;
/* Set proper schema search path so regproc references list correctly */
selectSourceSchema(tyinfo->dobj.namespace->dobj.name);
! appendPQExpBuffer(query, "SELECT oid, enumlabel "
! "FROM pg_catalog.pg_enum "
! "WHERE enumtypid = '%u'"
! "ORDER BY oid",
tyinfo->dobj.catId.oid);
res = PQexec(g_conn, query->data);
--- 6605,6628 ----
PQExpBuffer query = createPQExpBuffer();
PGresult *res;
int num,
! i;
Oid enum_oid;
char *label;
/* Set proper schema search path so regproc references list correctly */
selectSourceSchema(tyinfo->dobj.namespace->dobj.name);
! if (fout->remoteVersion > 90000)
! appendPQExpBuffer(query, "SELECT oid, enumlabel "
! "FROM pg_catalog.pg_enum "
! "WHERE enumtypid = '%u'"
! "ORDER BY enumsortorder",
! tyinfo->dobj.catId.oid);
! else
! appendPQExpBuffer(query, "SELECT oid, enumlabel "
! "FROM pg_catalog.pg_enum "
! "WHERE enumtypid = '%u'"
! "ORDER BY oid",
tyinfo->dobj.catId.oid);
res = PQexec(g_conn, query->data);
***************
*** 6661,6677 **** dumpEnumType(Archive *fout, TypeInfo *tyinfo)
{
enum_oid = atooid(PQgetvalue(res, i, PQfnumber(res, "oid")));
label = PQgetvalue(res, i, PQfnumber(res, "enumlabel"));
!
if (i == 0)
appendPQExpBuffer(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
appendPQExpBuffer(q,
! "SELECT binary_upgrade.add_pg_enum_label('%u'::pg_catalog.oid, "
! "'%u'::pg_catalog.oid, ",
! enum_oid, tyinfo->dobj.catId.oid);
! appendStringLiteralAH(q, label, fout);
! appendPQExpBuffer(q, ");\n");
}
- appendPQExpBuffer(q, "\n");
}
ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
--- 6668,6686 ----
{
enum_oid = atooid(PQgetvalue(res, i, PQfnumber(res, "oid")));
label = PQgetvalue(res, i, PQfnumber(res, "enumlabel"));
!
if (i == 0)
appendPQExpBuffer(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
appendPQExpBuffer(q,
! "SELECT binary_upgrade.set_next_pg_enum_oid('%u'::pg_catalog.oid);\n",
! enum_oid);
! appendPQExpBuffer(q, "ALTER TYPE %s.",
! fmtId(tyinfo->dobj.namespace->dobj.name));
! appendPQExpBuffer(q, "%s ADD ",
! fmtId(tyinfo->dobj.name));
! appendStringLiteralAH(q, label, fout);
! appendPQExpBuffer(q, ";\n\n");
}
}
ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
*** a/src/include/catalog/indexing.h
--- b/src/include/catalog/indexing.h
***************
*** 147,152 **** DECLARE_UNIQUE_INDEX(pg_enum_oid_index, 3502, on pg_enum using btree(oid oid_ops
--- 147,154 ----
#define EnumOidIndexId 3502
DECLARE_UNIQUE_INDEX(pg_enum_typid_label_index, 3503, on pg_enum using btree(enumtypid oid_ops, enumlabel name_ops));
#define EnumTypIdLabelIndexId 3503
+ DECLARE_UNIQUE_INDEX(pg_enum_typid_sortorder_index, 3539, on pg_enum using btree(enumtypid oid_ops, enumsortorder int4_ops));
+ #define EnumTypIdSortOrderIndexId 3539
/* This following index is not used for a cache and is not unique */
DECLARE_INDEX(pg_index_indrelid_index, 2678, on pg_index using btree(indrelid oid_ops));
*** a/src/include/catalog/pg_class.h
--- b/src/include/catalog/pg_class.h
***************
*** 132,138 **** typedef FormData_pg_class *Form_pg_class;
*/
/* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId */
! DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f f r 28 0 t f f f f f 3 _null_ _null_ ));
DESCR("");
DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f f r 19 0 f f f f f f 3 _null_ _null_ ));
DESCR("");
--- 132,138 ----
*/
/* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId */
! DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f f r 30 0 t f f f f f 3 _null_ _null_ ));
DESCR("");
DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f f r 19 0 f f f f f f 3 _null_ _null_ ));
DESCR("");
*** a/src/include/catalog/pg_enum.h
--- b/src/include/catalog/pg_enum.h
***************
*** 35,40 **** CATALOG(pg_enum,3501)
--- 35,41 ----
{
Oid enumtypid; /* OID of owning enum type */
NameData enumlabel; /* text representation of enum value */
+ int4 enumsortorder; /* sort order for this enum label */
} FormData_pg_enum;
/* ----------------
***************
*** 48,56 **** typedef FormData_pg_enum *Form_pg_enum;
* compiler constants for pg_enum
* ----------------
*/
! #define Natts_pg_enum 2
#define Anum_pg_enum_enumtypid 1
#define Anum_pg_enum_enumlabel 2
/* ----------------
* pg_enum has no initial contents
--- 49,58 ----
* compiler constants for pg_enum
* ----------------
*/
! #define Natts_pg_enum 3
#define Anum_pg_enum_enumtypid 1
#define Anum_pg_enum_enumlabel 2
+ #define Anum_pg_enum_enumsortorder 3
/* ----------------
* pg_enum has no initial contents
***************
*** 60,67 **** typedef FormData_pg_enum *Form_pg_enum;
/*
* prototypes for functions in pg_enum.c
*/
! extern void EnumValuesCreate(Oid enumTypeOid, List *vals,
! Oid binary_upgrade_next_pg_enum_oid);
extern void EnumValuesDelete(Oid enumTypeOid);
#endif /* PG_ENUM_H */
--- 62,70 ----
/*
* prototypes for functions in pg_enum.c
*/
! extern void EnumValuesCreate(Oid enumTypeOid, List *vals);
extern void EnumValuesDelete(Oid enumTypeOid);
+ extern bool AddEnumLabel(Oid enumTypeOid, char *newVal, char *neighbour,
+ bool newValIsAfter, int nelems, bool elems_are_sorted);
#endif /* PG_ENUM_H */
*** a/src/include/catalog/pg_type.h
--- b/src/include/catalog/pg_type.h
***************
*** 194,199 **** CATALOG(pg_type,1247) BKI_BOOTSTRAP BKI_ROWTYPE_OID(71) BKI_SCHEMA_MACRO
--- 194,211 ----
*/
int4 typndims;
+ /*
+ * typnlabels, contains a count on the number of labels an enum type has,
+ * -1 for a non-enum type.
+ */
+ int4 typnlabels;
+
+ /*
+ * typsorted is true if the oids of an enum type reflect the type's sort
+ * order, false otherwise including for a non-enum type.
+ */
+ bool typsorted;
+
/*
* If typdefaultbin is not NULL, it is the nodeToString representation of
* a default expression for the type. Currently this is only used for
***************
*** 224,230 **** typedef FormData_pg_type *Form_pg_type;
* compiler constants for pg_type
* ----------------
*/
! #define Natts_pg_type 28
#define Anum_pg_type_typname 1
#define Anum_pg_type_typnamespace 2
#define Anum_pg_type_typowner 3
--- 236,242 ----
* compiler constants for pg_type
* ----------------
*/
! #define Natts_pg_type 30
#define Anum_pg_type_typname 1
#define Anum_pg_type_typnamespace 2
#define Anum_pg_type_typowner 3
***************
*** 251,258 **** typedef FormData_pg_type *Form_pg_type;
#define Anum_pg_type_typbasetype 24
#define Anum_pg_type_typtypmod 25
#define Anum_pg_type_typndims 26
! #define Anum_pg_type_typdefaultbin 27
! #define Anum_pg_type_typdefault 28
/* ----------------
--- 263,272 ----
#define Anum_pg_type_typbasetype 24
#define Anum_pg_type_typtypmod 25
#define Anum_pg_type_typndims 26
! #define Anum_pg_type_typnlabels 27
! #define Anum_pg_type_typsorted 28
! #define Anum_pg_type_typdefaultbin 29
! #define Anum_pg_type_typdefault 30
/* ----------------
***************
*** 269,351 **** typedef FormData_pg_type *Form_pg_type;
*/
/* OIDS 1 - 99 */
! DATA(insert OID = 16 ( bool PGNSP PGUID 1 t b B t t \054 0 0 1000 boolin boolout boolrecv boolsend - - - c p f 0 -1 0 _null_ _null_ ));
DESCR("boolean, 'true'/'false'");
#define BOOLOID 16
! DATA(insert OID = 17 ( bytea PGNSP PGUID -1 f b U f t \054 0 0 1001 byteain byteaout bytearecv byteasend - - - i x f 0 -1 0 _null_ _null_ ));
DESCR("variable-length string, binary values escaped");
#define BYTEAOID 17
! DATA(insert OID = 18 ( char PGNSP PGUID 1 t b S f t \054 0 0 1002 charin charout charrecv charsend - - - c p f 0 -1 0 _null_ _null_ ));
DESCR("single character");
#define CHAROID 18
! DATA(insert OID = 19 ( name PGNSP PGUID NAMEDATALEN f b S f t \054 0 18 1003 namein nameout namerecv namesend - - - c p f 0 -1 0 _null_ _null_ ));
DESCR("63-character type for storing system identifiers");
#define NAMEOID 19
! DATA(insert OID = 20 ( int8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 1016 int8in int8out int8recv int8send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("~18 digit integer, 8-byte storage");
#define INT8OID 20
! DATA(insert OID = 21 ( int2 PGNSP PGUID 2 t b N f t \054 0 0 1005 int2in int2out int2recv int2send - - - s p f 0 -1 0 _null_ _null_ ));
DESCR("-32 thousand to 32 thousand, 2-byte storage");
#define INT2OID 21
! DATA(insert OID = 22 ( int2vector PGNSP PGUID -1 f b A f t \054 0 21 1006 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("array of int2, used in system tables");
#define INT2VECTOROID 22
! DATA(insert OID = 23 ( int4 PGNSP PGUID 4 t b N f t \054 0 0 1007 int4in int4out int4recv int4send - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("-2 billion to 2 billion integer, 4-byte storage");
#define INT4OID 23
! DATA(insert OID = 24 ( regproc PGNSP PGUID 4 t b N f t \054 0 0 1008 regprocin regprocout regprocrecv regprocsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered procedure");
#define REGPROCOID 24
! DATA(insert OID = 25 ( text PGNSP PGUID -1 f b S t t \054 0 0 1009 textin textout textrecv textsend - - - i x f 0 -1 0 _null_ _null_ ));
DESCR("variable-length string, no limit specified");
#define TEXTOID 25
! DATA(insert OID = 26 ( oid PGNSP PGUID 4 t b N t t \054 0 0 1028 oidin oidout oidrecv oidsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("object identifier(oid), maximum 4 billion");
#define OIDOID 26
! DATA(insert OID = 27 ( tid PGNSP PGUID 6 f b U f t \054 0 0 1010 tidin tidout tidrecv tidsend - - - s p f 0 -1 0 _null_ _null_ ));
DESCR("(block, offset), physical location of tuple");
#define TIDOID 27
! DATA(insert OID = 28 ( xid PGNSP PGUID 4 t b U f t \054 0 0 1011 xidin xidout xidrecv xidsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("transaction id");
#define XIDOID 28
! DATA(insert OID = 29 ( cid PGNSP PGUID 4 t b U f t \054 0 0 1012 cidin cidout cidrecv cidsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("command identifier type, sequence in transaction id");
#define CIDOID 29
! DATA(insert OID = 30 ( oidvector PGNSP PGUID -1 f b A f t \054 0 26 1013 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("array of oids, used in system tables");
#define OIDVECTOROID 30
/* hand-built rowtype entries for bootstrapped catalogs */
/* NB: OIDs assigned here must match the BKI_ROWTYPE_OID declarations */
! DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c C f t \054 1247 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c C f t \054 1249 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c C f t \054 1255 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c C f t \054 1259 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
/* OIDS 100 - 199 */
! DATA(insert OID = 142 ( xml PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 _null_ _null_ ));
DESCR("XML content");
#define XMLOID 142
! DATA(insert OID = 143 ( _xml PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
/* OIDS 200 - 299 */
! DATA(insert OID = 210 ( smgr PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f 0 -1 0 _null_ _null_ ));
DESCR("storage manager");
/* OIDS 300 - 399 */
--- 283,365 ----
*/
/* OIDS 1 - 99 */
! DATA(insert OID = 16 ( bool PGNSP PGUID 1 t b B t t \054 0 0 1000 boolin boolout boolrecv boolsend - - - c p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("boolean, 'true'/'false'");
#define BOOLOID 16
! DATA(insert OID = 17 ( bytea PGNSP PGUID -1 f b U f t \054 0 0 1001 byteain byteaout bytearecv byteasend - - - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("variable-length string, binary values escaped");
#define BYTEAOID 17
! DATA(insert OID = 18 ( char PGNSP PGUID 1 t b S f t \054 0 0 1002 charin charout charrecv charsend - - - c p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("single character");
#define CHAROID 18
! DATA(insert OID = 19 ( name PGNSP PGUID NAMEDATALEN f b S f t \054 0 18 1003 namein nameout namerecv namesend - - - c p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("63-character type for storing system identifiers");
#define NAMEOID 19
! DATA(insert OID = 20 ( int8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 1016 int8in int8out int8recv int8send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("~18 digit integer, 8-byte storage");
#define INT8OID 20
! DATA(insert OID = 21 ( int2 PGNSP PGUID 2 t b N f t \054 0 0 1005 int2in int2out int2recv int2send - - - s p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("-32 thousand to 32 thousand, 2-byte storage");
#define INT2OID 21
! DATA(insert OID = 22 ( int2vector PGNSP PGUID -1 f b A f t \054 0 21 1006 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("array of int2, used in system tables");
#define INT2VECTOROID 22
! DATA(insert OID = 23 ( int4 PGNSP PGUID 4 t b N f t \054 0 0 1007 int4in int4out int4recv int4send - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("-2 billion to 2 billion integer, 4-byte storage");
#define INT4OID 23
! DATA(insert OID = 24 ( regproc PGNSP PGUID 4 t b N f t \054 0 0 1008 regprocin regprocout regprocrecv regprocsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered procedure");
#define REGPROCOID 24
! DATA(insert OID = 25 ( text PGNSP PGUID -1 f b S t t \054 0 0 1009 textin textout textrecv textsend - - - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("variable-length string, no limit specified");
#define TEXTOID 25
! DATA(insert OID = 26 ( oid PGNSP PGUID 4 t b N t t \054 0 0 1028 oidin oidout oidrecv oidsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("object identifier(oid), maximum 4 billion");
#define OIDOID 26
! DATA(insert OID = 27 ( tid PGNSP PGUID 6 f b U f t \054 0 0 1010 tidin tidout tidrecv tidsend - - - s p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("(block, offset), physical location of tuple");
#define TIDOID 27
! DATA(insert OID = 28 ( xid PGNSP PGUID 4 t b U f t \054 0 0 1011 xidin xidout xidrecv xidsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("transaction id");
#define XIDOID 28
! DATA(insert OID = 29 ( cid PGNSP PGUID 4 t b U f t \054 0 0 1012 cidin cidout cidrecv cidsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("command identifier type, sequence in transaction id");
#define CIDOID 29
! DATA(insert OID = 30 ( oidvector PGNSP PGUID -1 f b A f t \054 0 26 1013 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("array of oids, used in system tables");
#define OIDVECTOROID 30
/* hand-built rowtype entries for bootstrapped catalogs */
/* NB: OIDs assigned here must match the BKI_ROWTYPE_OID declarations */
! DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c C f t \054 1247 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c C f t \054 1249 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c C f t \054 1255 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c C f t \054 1259 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
/* OIDS 100 - 199 */
! DATA(insert OID = 142 ( xml PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("XML content");
#define XMLOID 142
! DATA(insert OID = 143 ( _xml PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
/* OIDS 200 - 299 */
! DATA(insert OID = 210 ( smgr PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("storage manager");
/* OIDS 300 - 399 */
***************
*** 355,585 **** DESCR("storage manager");
/* OIDS 500 - 599 */
/* OIDS 600 - 699 */
! DATA(insert OID = 600 ( point PGNSP PGUID 16 f b G f t \054 0 701 1017 point_in point_out point_recv point_send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("geometric point '(x, y)'");
#define POINTOID 600
! DATA(insert OID = 601 ( lseg PGNSP PGUID 32 f b G f t \054 0 600 1018 lseg_in lseg_out lseg_recv lseg_send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("geometric line segment '(pt1,pt2)'");
#define LSEGOID 601
! DATA(insert OID = 602 ( path PGNSP PGUID -1 f b G f t \054 0 0 1019 path_in path_out path_recv path_send - - - d x f 0 -1 0 _null_ _null_ ));
DESCR("geometric path '(pt1,...)'");
#define PATHOID 602
! DATA(insert OID = 603 ( box PGNSP PGUID 32 f b G f t \073 0 600 1020 box_in box_out box_recv box_send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("geometric box '(lower left,upper right)'");
#define BOXOID 603
! DATA(insert OID = 604 ( polygon PGNSP PGUID -1 f b G f t \054 0 0 1027 poly_in poly_out poly_recv poly_send - - - d x f 0 -1 0 _null_ _null_ ));
DESCR("geometric polygon '(pt1,...)'");
#define POLYGONOID 604
! DATA(insert OID = 628 ( line PGNSP PGUID 32 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("geometric line (not implemented)");
#define LINEOID 628
! DATA(insert OID = 629 ( _line PGNSP PGUID -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
DESCR("");
/* OIDS 700 - 799 */
! DATA(insert OID = 700 ( float4 PGNSP PGUID 4 FLOAT4PASSBYVAL b N f t \054 0 0 1021 float4in float4out float4recv float4send - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("single-precision floating point number, 4-byte storage");
#define FLOAT4OID 700
! DATA(insert OID = 701 ( float8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N t t \054 0 0 1022 float8in float8out float8recv float8send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("double-precision floating point number, 8-byte storage");
#define FLOAT8OID 701
! DATA(insert OID = 702 ( abstime PGNSP PGUID 4 t b D f t \054 0 0 1023 abstimein abstimeout abstimerecv abstimesend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("absolute, limited-range date and time (Unix system time)");
#define ABSTIMEOID 702
! DATA(insert OID = 703 ( reltime PGNSP PGUID 4 t b T f t \054 0 0 1024 reltimein reltimeout reltimerecv reltimesend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("relative, limited-range time interval (Unix delta time)");
#define RELTIMEOID 703
! DATA(insert OID = 704 ( tinterval PGNSP PGUID 12 f b T f t \054 0 0 1025 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("(abstime,abstime), time interval");
#define TINTERVALOID 704
! DATA(insert OID = 705 ( unknown PGNSP PGUID -2 f b X f t \054 0 0 0 unknownin unknownout unknownrecv unknownsend - - - c p f 0 -1 0 _null_ _null_ ));
DESCR("");
#define UNKNOWNOID 705
! DATA(insert OID = 718 ( circle PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("geometric circle '(center,radius)'");
#define CIRCLEOID 718
! DATA(insert OID = 719 ( _circle PGNSP PGUID -1 f b A f t \054 0 718 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 790 ( money PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("monetary amounts, $d,ddd.cc");
#define CASHOID 790
! DATA(insert OID = 791 ( _money PGNSP PGUID -1 f b A f t \054 0 790 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
/* OIDS 800 - 899 */
! DATA(insert OID = 829 ( macaddr PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("XX:XX:XX:XX:XX:XX, MAC address");
#define MACADDROID 829
! DATA(insert OID = 869 ( inet PGNSP PGUID -1 f b I t t \054 0 0 1041 inet_in inet_out inet_recv inet_send - - - i m f 0 -1 0 _null_ _null_ ));
DESCR("IP address/netmask, host address, netmask optional");
#define INETOID 869
! DATA(insert OID = 650 ( cidr PGNSP PGUID -1 f b I f t \054 0 0 651 cidr_in cidr_out cidr_recv cidr_send - - - i m f 0 -1 0 _null_ _null_ ));
DESCR("network IP address/netmask, network address");
#define CIDROID 650
/* OIDS 900 - 999 */
/* OIDS 1000 - 1099 */
! DATA(insert OID = 1000 ( _bool PGNSP PGUID -1 f b A f t \054 0 16 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1001 ( _bytea PGNSP PGUID -1 f b A f t \054 0 17 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1002 ( _char PGNSP PGUID -1 f b A f t \054 0 18 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b A f t \054 0 19 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b A f t \054 0 21 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b A f t \054 0 22 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b A f t \054 0 23 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
#define INT4ARRAYOID 1007
! DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b A f t \054 0 24 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b A f t \054 0 25 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
#define TEXTARRAYOID 1009
! DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b A f t \054 0 26 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b A f t \054 0 27 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b A f t \054 0 28 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b A f t \054 0 29 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1013 ( _oidvector PGNSP PGUID -1 f b A f t \054 0 30 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1014 ( _bpchar PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1015 ( _varchar PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1016 ( _int8 PGNSP PGUID -1 f b A f t \054 0 20 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1017 ( _point PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1018 ( _lseg PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1019 ( _path PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1020 ( _box PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1021 ( _float4 PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
#define FLOAT4ARRAYOID 1021
! DATA(insert OID = 1022 ( _float8 PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1033 ( aclitem PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("access control list");
#define ACLITEMOID 1033
! DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1263 ( _cstring PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
#define CSTRINGARRAYOID 1263
! DATA(insert OID = 1042 ( bpchar PGNSP PGUID -1 f b S f t \054 0 0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 _null_ _null_ ));
DESCR("char(length), blank-padded string, fixed storage length");
#define BPCHAROID 1042
! DATA(insert OID = 1043 ( varchar PGNSP PGUID -1 f b S f t \054 0 0 1015 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f 0 -1 0 _null_ _null_ ));
DESCR("varchar(length), non-blank-padded string, variable storage length");
#define VARCHAROID 1043
! DATA(insert OID = 1082 ( date PGNSP PGUID 4 t b D f t \054 0 0 1182 date_in date_out date_recv date_send - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("date");
#define DATEOID 1082
! DATA(insert OID = 1083 ( time PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1183 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f 0 -1 0 _null_ _null_ ));
DESCR("time of day");
#define TIMEOID 1083
/* OIDS 1100 - 1199 */
! DATA(insert OID = 1114 ( timestamp PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 _null_ _null_ ));
DESCR("date and time");
#define TIMESTAMPOID 1114
! DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1182 ( _date PGNSP PGUID -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1183 ( _time PGNSP PGUID -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1184 ( timestamptz PGNSP PGUID 8 FLOAT8PASSBYVAL b D t t \054 0 0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 _null_ _null_ ));
DESCR("date and time with time zone");
#define TIMESTAMPTZOID 1184
! DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0 1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1186 ( interval PGNSP PGUID 16 f b T t t \054 0 0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 _null_ _null_ ));
DESCR("@ <number> <units>, time interval");
#define INTERVALOID 1186
! DATA(insert OID = 1187 ( _interval PGNSP PGUID -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout - d x f 0 -1 0 _null_ _null_ ));
/* OIDS 1200 - 1299 */
! DATA(insert OID = 1231 ( _numeric PGNSP PGUID -1 f b A f t \054 0 1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1266 ( timetz PGNSP PGUID 12 f b D f t \054 0 0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 _null_ _null_ ));
DESCR("time of day with time zone");
#define TIMETZOID 1266
! DATA(insert OID = 1270 ( _timetz PGNSP PGUID -1 f b A f t \054 0 1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout - d x f 0 -1 0 _null_ _null_ ));
/* OIDS 1500 - 1599 */
! DATA(insert OID = 1560 ( bit PGNSP PGUID -1 f b V f t \054 0 0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 _null_ _null_ ));
DESCR("fixed-length bit string");
#define BITOID 1560
! DATA(insert OID = 1561 ( _bit PGNSP PGUID -1 f b A f t \054 0 1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1562 ( varbit PGNSP PGUID -1 f b V t t \054 0 0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 _null_ _null_ ));
DESCR("variable-length bit string");
#define VARBITOID 1562
! DATA(insert OID = 1563 ( _varbit PGNSP PGUID -1 f b A f t \054 0 1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout - i x f 0 -1 0 _null_ _null_ ));
/* OIDS 1600 - 1699 */
/* OIDS 1700 - 1799 */
! DATA(insert OID = 1700 ( numeric PGNSP PGUID -1 f b N f t \054 0 0 1231 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f 0 -1 0 _null_ _null_ ));
DESCR("numeric(precision, decimal), arbitrary precision number");
#define NUMERICOID 1700
! DATA(insert OID = 1790 ( refcursor PGNSP PGUID -1 f b U f t \054 0 0 2201 textin textout textrecv textsend - - - i x f 0 -1 0 _null_ _null_ ));
DESCR("reference to cursor (portal name)");
#define REFCURSOROID 1790
/* OIDS 2200 - 2299 */
! DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 2202 ( regprocedure PGNSP PGUID 4 t b N f t \054 0 0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered procedure (with args)");
#define REGPROCEDUREOID 2202
! DATA(insert OID = 2203 ( regoper PGNSP PGUID 4 t b N f t \054 0 0 2208 regoperin regoperout regoperrecv regopersend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered operator");
#define REGOPEROID 2203
! DATA(insert OID = 2204 ( regoperator PGNSP PGUID 4 t b N f t \054 0 0 2209 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered operator (with args)");
#define REGOPERATOROID 2204
! DATA(insert OID = 2205 ( regclass PGNSP PGUID 4 t b N f t \054 0 0 2210 regclassin regclassout regclassrecv regclasssend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered class");
#define REGCLASSOID 2205
! DATA(insert OID = 2206 ( regtype PGNSP PGUID 4 t b N f t \054 0 0 2211 regtypein regtypeout regtyperecv regtypesend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered type");
#define REGTYPEOID 2206
! DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
#define REGTYPEARRAYOID 2211
/* uuid */
! DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 _null_ _null_ ));
DESCR("UUID datatype");
! DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
/* text search */
! DATA(insert OID = 3614 ( tsvector PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 _null_ _null_ ));
DESCR("text representation for text search");
#define TSVECTOROID 3614
! DATA(insert OID = 3642 ( gtsvector PGNSP PGUID -1 f b U f t \054 0 0 3644 gtsvectorin gtsvectorout - - - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("GiST index internal text representation for text search");
#define GTSVECTOROID 3642
! DATA(insert OID = 3615 ( tsquery PGNSP PGUID -1 f b U f t \054 0 0 3645 tsqueryin tsqueryout tsqueryrecv tsquerysend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("query representation for text search");
#define TSQUERYOID 3615
! DATA(insert OID = 3734 ( regconfig PGNSP PGUID 4 t b N f t \054 0 0 3735 regconfigin regconfigout regconfigrecv regconfigsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered text search configuration");
#define REGCONFIGOID 3734
! DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered text search dictionary");
#define REGDICTIONARYOID 3769
! DATA(insert OID = 3643 ( _tsvector PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 3644 ( _gtsvector PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 _null_ _null_ ));
DESCR("txid snapshot");
! DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
/*
* pseudo-types
--- 369,599 ----
/* OIDS 500 - 599 */
/* OIDS 600 - 699 */
! DATA(insert OID = 600 ( point PGNSP PGUID 16 f b G f t \054 0 701 1017 point_in point_out point_recv point_send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric point '(x, y)'");
#define POINTOID 600
! DATA(insert OID = 601 ( lseg PGNSP PGUID 32 f b G f t \054 0 600 1018 lseg_in lseg_out lseg_recv lseg_send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric line segment '(pt1,pt2)'");
#define LSEGOID 601
! DATA(insert OID = 602 ( path PGNSP PGUID -1 f b G f t \054 0 0 1019 path_in path_out path_recv path_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric path '(pt1,...)'");
#define PATHOID 602
! DATA(insert OID = 603 ( box PGNSP PGUID 32 f b G f t \073 0 600 1020 box_in box_out box_recv box_send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric box '(lower left,upper right)'");
#define BOXOID 603
! DATA(insert OID = 604 ( polygon PGNSP PGUID -1 f b G f t \054 0 0 1027 poly_in poly_out poly_recv poly_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric polygon '(pt1,...)'");
#define POLYGONOID 604
! DATA(insert OID = 628 ( line PGNSP PGUID 32 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric line (not implemented)");
#define LINEOID 628
! DATA(insert OID = 629 ( _line PGNSP PGUID -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("");
/* OIDS 700 - 799 */
! DATA(insert OID = 700 ( float4 PGNSP PGUID 4 FLOAT4PASSBYVAL b N f t \054 0 0 1021 float4in float4out float4recv float4send - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("single-precision floating point number, 4-byte storage");
#define FLOAT4OID 700
! DATA(insert OID = 701 ( float8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N t t \054 0 0 1022 float8in float8out float8recv float8send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("double-precision floating point number, 8-byte storage");
#define FLOAT8OID 701
! DATA(insert OID = 702 ( abstime PGNSP PGUID 4 t b D f t \054 0 0 1023 abstimein abstimeout abstimerecv abstimesend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("absolute, limited-range date and time (Unix system time)");
#define ABSTIMEOID 702
! DATA(insert OID = 703 ( reltime PGNSP PGUID 4 t b T f t \054 0 0 1024 reltimein reltimeout reltimerecv reltimesend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("relative, limited-range time interval (Unix delta time)");
#define RELTIMEOID 703
! DATA(insert OID = 704 ( tinterval PGNSP PGUID 12 f b T f t \054 0 0 1025 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("(abstime,abstime), time interval");
#define TINTERVALOID 704
! DATA(insert OID = 705 ( unknown PGNSP PGUID -2 f b X f t \054 0 0 0 unknownin unknownout unknownrecv unknownsend - - - c p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("");
#define UNKNOWNOID 705
! DATA(insert OID = 718 ( circle PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric circle '(center,radius)'");
#define CIRCLEOID 718
! DATA(insert OID = 719 ( _circle PGNSP PGUID -1 f b A f t \054 0 718 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 790 ( money PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("monetary amounts, $d,ddd.cc");
#define CASHOID 790
! DATA(insert OID = 791 ( _money PGNSP PGUID -1 f b A f t \054 0 790 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
/* OIDS 800 - 899 */
! DATA(insert OID = 829 ( macaddr PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("XX:XX:XX:XX:XX:XX, MAC address");
#define MACADDROID 829
! DATA(insert OID = 869 ( inet PGNSP PGUID -1 f b I t t \054 0 0 1041 inet_in inet_out inet_recv inet_send - - - i m f 0 -1 0 -1 f _null_ _null_ ));
DESCR("IP address/netmask, host address, netmask optional");
#define INETOID 869
! DATA(insert OID = 650 ( cidr PGNSP PGUID -1 f b I f t \054 0 0 651 cidr_in cidr_out cidr_recv cidr_send - - - i m f 0 -1 0 -1 f _null_ _null_ ));
DESCR("network IP address/netmask, network address");
#define CIDROID 650
/* OIDS 900 - 999 */
/* OIDS 1000 - 1099 */
! DATA(insert OID = 1000 ( _bool PGNSP PGUID -1 f b A f t \054 0 16 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1001 ( _bytea PGNSP PGUID -1 f b A f t \054 0 17 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1002 ( _char PGNSP PGUID -1 f b A f t \054 0 18 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b A f t \054 0 19 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b A f t \054 0 21 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b A f t \054 0 22 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b A f t \054 0 23 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
#define INT4ARRAYOID 1007
! DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b A f t \054 0 24 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b A f t \054 0 25 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
#define TEXTARRAYOID 1009
! DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b A f t \054 0 26 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b A f t \054 0 27 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b A f t \054 0 28 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b A f t \054 0 29 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1013 ( _oidvector PGNSP PGUID -1 f b A f t \054 0 30 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1014 ( _bpchar PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1015 ( _varchar PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1016 ( _int8 PGNSP PGUID -1 f b A f t \054 0 20 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1017 ( _point PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1018 ( _lseg PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1019 ( _path PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1020 ( _box PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1021 ( _float4 PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
#define FLOAT4ARRAYOID 1021
! DATA(insert OID = 1022 ( _float8 PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1033 ( aclitem PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("access control list");
#define ACLITEMOID 1033
! DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1263 ( _cstring PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
#define CSTRINGARRAYOID 1263
! DATA(insert OID = 1042 ( bpchar PGNSP PGUID -1 f b S f t \054 0 0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("char(length), blank-padded string, fixed storage length");
#define BPCHAROID 1042
! DATA(insert OID = 1043 ( varchar PGNSP PGUID -1 f b S f t \054 0 0 1015 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("varchar(length), non-blank-padded string, variable storage length");
#define VARCHAROID 1043
! DATA(insert OID = 1082 ( date PGNSP PGUID 4 t b D f t \054 0 0 1182 date_in date_out date_recv date_send - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("date");
#define DATEOID 1082
! DATA(insert OID = 1083 ( time PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1183 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("time of day");
#define TIMEOID 1083
/* OIDS 1100 - 1199 */
! DATA(insert OID = 1114 ( timestamp PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("date and time");
#define TIMESTAMPOID 1114
! DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1182 ( _date PGNSP PGUID -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1183 ( _time PGNSP PGUID -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1184 ( timestamptz PGNSP PGUID 8 FLOAT8PASSBYVAL b D t t \054 0 0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("date and time with time zone");
#define TIMESTAMPTZOID 1184
! DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0 1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1186 ( interval PGNSP PGUID 16 f b T t t \054 0 0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("@ <number> <units>, time interval");
#define INTERVALOID 1186
! DATA(insert OID = 1187 ( _interval PGNSP PGUID -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout - d x f 0 -1 0 -1 f _null_ _null_ ));
/* OIDS 1200 - 1299 */
! DATA(insert OID = 1231 ( _numeric PGNSP PGUID -1 f b A f t \054 0 1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1266 ( timetz PGNSP PGUID 12 f b D f t \054 0 0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("time of day with time zone");
#define TIMETZOID 1266
! DATA(insert OID = 1270 ( _timetz PGNSP PGUID -1 f b A f t \054 0 1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout - d x f 0 -1 0 -1 f _null_ _null_ ));
/* OIDS 1500 - 1599 */
! DATA(insert OID = 1560 ( bit PGNSP PGUID -1 f b V f t \054 0 0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("fixed-length bit string");
#define BITOID 1560
! DATA(insert OID = 1561 ( _bit PGNSP PGUID -1 f b A f t \054 0 1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1562 ( varbit PGNSP PGUID -1 f b V t t \054 0 0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("variable-length bit string");
#define VARBITOID 1562
! DATA(insert OID = 1563 ( _varbit PGNSP PGUID -1 f b A f t \054 0 1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
/* OIDS 1600 - 1699 */
/* OIDS 1700 - 1799 */
! DATA(insert OID = 1700 ( numeric PGNSP PGUID -1 f b N f t \054 0 0 1231 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f 0 -1 0 -1 f _null_ _null_ ));
DESCR("numeric(precision, decimal), arbitrary precision number");
#define NUMERICOID 1700
! DATA(insert OID = 1790 ( refcursor PGNSP PGUID -1 f b U f t \054 0 0 2201 textin textout textrecv textsend - - - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("reference to cursor (portal name)");
#define REFCURSOROID 1790
/* OIDS 2200 - 2299 */
! DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 2202 ( regprocedure PGNSP PGUID 4 t b N f t \054 0 0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered procedure (with args)");
#define REGPROCEDUREOID 2202
! DATA(insert OID = 2203 ( regoper PGNSP PGUID 4 t b N f t \054 0 0 2208 regoperin regoperout regoperrecv regopersend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered operator");
#define REGOPEROID 2203
! DATA(insert OID = 2204 ( regoperator PGNSP PGUID 4 t b N f t \054 0 0 2209 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered operator (with args)");
#define REGOPERATOROID 2204
! DATA(insert OID = 2205 ( regclass PGNSP PGUID 4 t b N f t \054 0 0 2210 regclassin regclassout regclassrecv regclasssend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered class");
#define REGCLASSOID 2205
! DATA(insert OID = 2206 ( regtype PGNSP PGUID 4 t b N f t \054 0 0 2211 regtypein regtypeout regtyperecv regtypesend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered type");
#define REGTYPEOID 2206
! DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
#define REGTYPEARRAYOID 2211
/* uuid */
! DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("UUID datatype");
! DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
/* text search */
! DATA(insert OID = 3614 ( tsvector PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("text representation for text search");
#define TSVECTOROID 3614
! DATA(insert OID = 3642 ( gtsvector PGNSP PGUID -1 f b U f t \054 0 0 3644 gtsvectorin gtsvectorout - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("GiST index internal text representation for text search");
#define GTSVECTOROID 3642
! DATA(insert OID = 3615 ( tsquery PGNSP PGUID -1 f b U f t \054 0 0 3645 tsqueryin tsqueryout tsqueryrecv tsquerysend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("query representation for text search");
#define TSQUERYOID 3615
! DATA(insert OID = 3734 ( regconfig PGNSP PGUID 4 t b N f t \054 0 0 3735 regconfigin regconfigout regconfigrecv regconfigsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered text search configuration");
#define REGCONFIGOID 3734
! DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered text search dictionary");
#define REGDICTIONARYOID 3769
! DATA(insert OID = 3643 ( _tsvector PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 3644 ( _gtsvector PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("txid snapshot");
! DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
/*
* pseudo-types
***************
*** 594,624 **** DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 a
* but there is now support for it in records and arrays. Perhaps we should
* just treat it as a regular base type?
*/
! DATA(insert OID = 2249 ( record PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
#define RECORDOID 2249
! DATA(insert OID = 2287 ( _record PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
#define RECORDARRAYOID 2287
! DATA(insert OID = 2275 ( cstring PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 _null_ _null_ ));
#define CSTRINGOID 2275
! DATA(insert OID = 2276 ( any PGNSP PGUID 4 t p P f t \054 0 0 0 any_in any_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define ANYOID 2276
! DATA(insert OID = 2277 ( anyarray PGNSP PGUID -1 f p P f t \054 0 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f 0 -1 0 _null_ _null_ ));
#define ANYARRAYOID 2277
! DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define VOIDOID 2278
! DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define TRIGGEROID 2279
! DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define LANGUAGE_HANDLEROID 2280
! DATA(insert OID = 2281 ( internal PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 _null_ _null_ ));
#define INTERNALOID 2281
! DATA(insert OID = 2282 ( opaque PGNSP PGUID 4 t p P f t \054 0 0 0 opaque_in opaque_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define OPAQUEOID 2282
! DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p P f t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define ANYELEMENTOID 2283
! DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p P f t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define ANYNONARRAYOID 2776
! DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define ANYENUMOID 3500
--- 608,638 ----
* but there is now support for it in records and arrays. Perhaps we should
* just treat it as a regular base type?
*/
! DATA(insert OID = 2249 ( record PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
#define RECORDOID 2249
! DATA(insert OID = 2287 ( _record PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
#define RECORDARRAYOID 2287
! DATA(insert OID = 2275 ( cstring PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 -1 f _null_ _null_ ));
#define CSTRINGOID 2275
! DATA(insert OID = 2276 ( any PGNSP PGUID 4 t p P f t \054 0 0 0 any_in any_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define ANYOID 2276
! DATA(insert OID = 2277 ( anyarray PGNSP PGUID -1 f p P f t \054 0 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
#define ANYARRAYOID 2277
! DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define VOIDOID 2278
! DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define TRIGGEROID 2279
! DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define LANGUAGE_HANDLEROID 2280
! DATA(insert OID = 2281 ( internal PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 -1 f _null_ _null_ ));
#define INTERNALOID 2281
! DATA(insert OID = 2282 ( opaque PGNSP PGUID 4 t p P f t \054 0 0 0 opaque_in opaque_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define OPAQUEOID 2282
! DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p P f t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define ANYELEMENTOID 2283
! DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p P f t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define ANYNONARRAYOID 2776
! DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define ANYENUMOID 3500
*** a/src/include/catalog/pg_type_fn.h
--- b/src/include/catalog/pg_type_fn.h
***************
*** 50,56 **** extern Oid TypeCreate(Oid newTypeOid,
char storage,
int32 typeMod,
int32 typNDims,
! bool typeNotNull);
extern void GenerateTypeDependencies(Oid typeNamespace,
Oid typeObjectId,
--- 50,58 ----
char storage,
int32 typeMod,
int32 typNDims,
! bool typeNotNull,
! int32 typeNLabels,
! bool typeSorted);
extern void GenerateTypeDependencies(Oid typeNamespace,
Oid typeObjectId,
*** a/src/include/commands/typecmds.h
--- b/src/include/commands/typecmds.h
***************
*** 24,29 **** extern void RemoveTypes(DropStmt *drop);
--- 24,30 ----
extern void RemoveTypeById(Oid typeOid);
extern void DefineDomain(CreateDomainStmt *stmt);
extern void DefineEnum(CreateEnumStmt *stmt);
+ extern void AlterEnum (AlterEnumStmt *stmt);
extern Oid DefineCompositeType(const RangeVar *typevar, List *coldeflist);
extern Oid AssignTypeArrayOid(void);
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
***************
*** 346,351 **** typedef enum NodeTag
--- 346,352 ----
T_AlterUserMappingStmt,
T_DropUserMappingStmt,
T_AlterTableSpaceOptionsStmt,
+ T_AlterEnumStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 2171,2176 **** typedef struct CreateEnumStmt
--- 2171,2190 ----
/* ----------------------
+ * Alter Type Statement, enum types
+ * ----------------------
+ */
+ typedef struct AlterEnumStmt
+ {
+ NodeTag type;
+ List *typeName; /* qualified name (list of Value strings) */
+ char *newVal; /* new enum value */
+ char *newValNeighbour;/* neighbouring enum value */
+ bool newValIsAfter; /* new enum value is after neighbour? */
+ } AlterEnumStmt;
+
+
+ /* ----------------------
* Create View Statement
* ----------------------
*/
*** a/src/test/regress/expected/enum.out
--- b/src/test/regress/expected/enum.out
***************
*** 25,30 **** ERROR: invalid input value for enum rainbow: "mauve"
--- 25,108 ----
LINE 1: SELECT 'mauve'::rainbow;
^
--
+ -- adding new values
+ --
+ CREATE TYPE planets AS ENUM ( 'venus', 'earth', 'mars' );
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+ enumlabel | enumsortorder
+ -----------+---------------
+ venus | 1
+ earth | 2
+ mars | 3
+ (3 rows)
+
+ ALTER TYPE planets ADD 'uranus';
+ SELECT typnlabels, typsorted
+ FROM pg_type
+ WHERE oid = 'planets'::regtype;
+ typnlabels | typsorted
+ ------------+-----------
+ 4 | t
+ (1 row)
+
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+ enumlabel | enumsortorder
+ -----------+---------------
+ venus | 1
+ earth | 2
+ mars | 3
+ uranus | 4
+ (4 rows)
+
+ ALTER TYPE planets ADD 'mercury' BEFORE 'venus';
+ ALTER TYPE planets ADD 'saturn' BEFORE 'uranus';
+ ALTER TYPE planets ADD 'jupiter' AFTER 'mars';
+ ALTER TYPE planets ADD 'neptune' AFTER 'uranus';
+ SELECT typnlabels, typsorted
+ FROM pg_type
+ WHERE oid = 'planets'::regtype;
+ typnlabels | typsorted
+ ------------+-----------
+ 8 | f
+ (1 row)
+
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+ enumlabel | enumsortorder
+ -----------+---------------
+ mercury | 1
+ venus | 2
+ earth | 3
+ mars | 4
+ jupiter | 5
+ saturn | 6
+ uranus | 7
+ neptune | 8
+ (8 rows)
+
+ select 'mars'::planets > 'mercury' as using_sortorder;
+ using_sortorder
+ -----------------
+ t
+ (1 row)
+
+ -- errors for adding labels
+ ALTER TYPE planets ADD
+ 'plutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutopluto';
+ ERROR: invalid enum label "plutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutopluto"
+ DETAIL: Labels must be 63 characters or less.
+ ALTER TYPE planets ADD 'pluto' AFTER 'zeus';
+ ERROR: "zeus" is not an existing label.
+ DROP TYPE planets;
+ --
-- Basic table creation, row selection
--
CREATE TABLE enumtest (col rainbow);
***************
*** 403,409 **** SELECT COUNT(*) FROM pg_type WHERE typname = 'rainbow';
SELECT * FROM pg_enum WHERE NOT EXISTS
(SELECT 1 FROM pg_type WHERE pg_type.oid = enumtypid);
! enumtypid | enumlabel
! -----------+-----------
(0 rows)
--- 481,487 ----
SELECT * FROM pg_enum WHERE NOT EXISTS
(SELECT 1 FROM pg_type WHERE pg_type.oid = enumtypid);
! enumtypid | enumlabel | enumsortorder
! -----------+-----------+---------------
(0 rows)
*** a/src/test/regress/sql/enum.sql
--- b/src/test/regress/sql/enum.sql
***************
*** 16,21 **** SELECT 'red'::rainbow;
--- 16,69 ----
SELECT 'mauve'::rainbow;
--
+ -- adding new values
+ --
+
+ CREATE TYPE planets AS ENUM ( 'venus', 'earth', 'mars' );
+
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+
+ ALTER TYPE planets ADD 'uranus';
+
+ SELECT typnlabels, typsorted
+ FROM pg_type
+ WHERE oid = 'planets'::regtype;
+
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+
+ ALTER TYPE planets ADD 'mercury' BEFORE 'venus';
+
+ ALTER TYPE planets ADD 'saturn' BEFORE 'uranus';
+
+ ALTER TYPE planets ADD 'jupiter' AFTER 'mars';
+
+ ALTER TYPE planets ADD 'neptune' AFTER 'uranus';
+
+ SELECT typnlabels, typsorted
+ FROM pg_type
+ WHERE oid = 'planets'::regtype;
+
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+
+ select 'mars'::planets > 'mercury' as using_sortorder;
+
+ -- errors for adding labels
+ ALTER TYPE planets ADD
+ 'plutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutopluto';
+
+ ALTER TYPE planets ADD 'pluto' AFTER 'zeus';
+
+ DROP TYPE planets;
+ --
-- Basic table creation, row selection
--
CREATE TABLE enumtest (col rainbow);
Attached is a a slightly updated version of this with the bitrot fixed.
cheers
andrew
Attachments:
venum3.patchtext/x-patch; name=venum3.patchDownload
*** a/contrib/pg_upgrade/function.c
--- b/contrib/pg_upgrade/function.c
***************
*** 61,85 **** install_support_functions(migratorContext *ctx)
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_heap_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_toast_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_index_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.add_pg_enum_label(OID, OID, NAME) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
--- 61,85 ----
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_pg_enum_oid(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_heap_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_toast_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_index_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
*** a/contrib/pg_upgrade_support/pg_upgrade_support.c
--- b/contrib/pg_upgrade_support/pg_upgrade_support.c
***************
*** 30,35 **** PG_MODULE_MAGIC;
--- 30,36 ----
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_array_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_toast_oid;
+ extern PGDLLIMPORT Oid binary_upgrade_next_pg_enum_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_heap_relfilenode;
extern PGDLLIMPORT Oid binary_upgrade_next_toast_relfilenode;
extern PGDLLIMPORT Oid binary_upgrade_next_index_relfilenode;
***************
*** 37,54 **** extern PGDLLIMPORT Oid binary_upgrade_next_index_relfilenode;
Datum set_next_pg_type_oid(PG_FUNCTION_ARGS);
Datum set_next_pg_type_array_oid(PG_FUNCTION_ARGS);
Datum set_next_pg_type_toast_oid(PG_FUNCTION_ARGS);
Datum set_next_heap_relfilenode(PG_FUNCTION_ARGS);
Datum set_next_toast_relfilenode(PG_FUNCTION_ARGS);
Datum set_next_index_relfilenode(PG_FUNCTION_ARGS);
- Datum add_pg_enum_label(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(set_next_pg_type_oid);
PG_FUNCTION_INFO_V1(set_next_pg_type_array_oid);
PG_FUNCTION_INFO_V1(set_next_pg_type_toast_oid);
PG_FUNCTION_INFO_V1(set_next_heap_relfilenode);
PG_FUNCTION_INFO_V1(set_next_toast_relfilenode);
PG_FUNCTION_INFO_V1(set_next_index_relfilenode);
- PG_FUNCTION_INFO_V1(add_pg_enum_label);
Datum
set_next_pg_type_oid(PG_FUNCTION_ARGS)
--- 38,55 ----
Datum set_next_pg_type_oid(PG_FUNCTION_ARGS);
Datum set_next_pg_type_array_oid(PG_FUNCTION_ARGS);
Datum set_next_pg_type_toast_oid(PG_FUNCTION_ARGS);
+ Datum set_next_pg_enum_oid(PG_FUNCTION_ARGS);
Datum set_next_heap_relfilenode(PG_FUNCTION_ARGS);
Datum set_next_toast_relfilenode(PG_FUNCTION_ARGS);
Datum set_next_index_relfilenode(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(set_next_pg_type_oid);
PG_FUNCTION_INFO_V1(set_next_pg_type_array_oid);
PG_FUNCTION_INFO_V1(set_next_pg_type_toast_oid);
+ PG_FUNCTION_INFO_V1(set_next_pg_enum_oid);
PG_FUNCTION_INFO_V1(set_next_heap_relfilenode);
PG_FUNCTION_INFO_V1(set_next_toast_relfilenode);
PG_FUNCTION_INFO_V1(set_next_index_relfilenode);
Datum
set_next_pg_type_oid(PG_FUNCTION_ARGS)
***************
*** 81,86 **** set_next_pg_type_toast_oid(PG_FUNCTION_ARGS)
--- 82,97 ----
}
Datum
+ set_next_pg_enum_oid(PG_FUNCTION_ARGS)
+ {
+ Oid enumoid = PG_GETARG_OID(0);
+
+ binary_upgrade_next_pg_enum_oid = enumoid;
+
+ PG_RETURN_VOID();
+ }
+
+ Datum
set_next_heap_relfilenode(PG_FUNCTION_ARGS)
{
Oid relfilenode = PG_GETARG_OID(0);
***************
*** 110,124 **** set_next_index_relfilenode(PG_FUNCTION_ARGS)
PG_RETURN_VOID();
}
- Datum
- add_pg_enum_label(PG_FUNCTION_ARGS)
- {
- Oid enumoid = PG_GETARG_OID(0);
- Oid typoid = PG_GETARG_OID(1);
- Name label = PG_GETARG_NAME(2);
-
- EnumValuesCreate(typoid, list_make1(makeString(NameStr(*label))),
- enumoid);
-
- PG_RETURN_VOID();
- }
--- 121,123 ----
*** a/src/backend/catalog/heap.c
--- b/src/backend/catalog/heap.c
***************
*** 855,861 **** AddNewRelationType(const char *typeName,
'x', /* fully TOASTable */
-1, /* typmod */
0, /* array dimensions for typBaseType */
! false); /* Type NOT NULL */
}
/* --------------------------------
--- 855,863 ----
'x', /* fully TOASTable */
-1, /* typmod */
0, /* array dimensions for typBaseType */
! false, /* Type NOT NULL */
! -1, /* no enum labels */
! false); /* type is not an enum, so not sorted */
}
/* --------------------------------
***************
*** 1108,1114 **** heap_create_with_catalog(const char *relname,
'x', /* fully TOASTable */
-1, /* typmod */
0, /* array dimensions for typBaseType */
! false); /* Type NOT NULL */
pfree(relarrayname);
}
--- 1110,1118 ----
'x', /* fully TOASTable */
-1, /* typmod */
0, /* array dimensions for typBaseType */
! false, /* Type NOT NULL */
! -1, /* no enum labels */
! false); /* type is not an enum, so not sorted */
pfree(relarrayname);
}
*** a/src/backend/catalog/pg_enum.c
--- b/src/backend/catalog/pg_enum.c
***************
*** 18,29 ****
--- 18,219 ----
#include "catalog/catalog.h"
#include "catalog/indexing.h"
#include "catalog/pg_enum.h"
+ #include "catalog/pg_type.h"
+ #include "utils/lsyscache.h"
+ #include "utils/syscache.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/rel.h"
#include "utils/tqual.h"
static int oid_cmp(const void *p1, const void *p2);
+ static int sort_order_cmp(const void *p1, const void *p2);
+
+ Oid binary_upgrade_next_pg_enum_oid = InvalidOid;
+
+ /*
+ * AddEnumLabel
+ * Add a new label to the enum set. By default it goes at
+ * the end, but the user can choose to place it before or
+ * after any existing set member.
+ *
+ * Returns true iff the labels are sorted by oid after the addition.
+ */
+
+ bool
+ AddEnumLabel(Oid enumTypeOid,
+ char *newVal,
+ char *neighbour,
+ bool newValIsAfter,
+ int nelems,
+ bool elems_are_sorted)
+ {
+ Oid newOid;
+ Relation pg_enum;
+ TupleDesc tupDesc;
+ Datum values[Natts_pg_enum];
+ bool nulls[Natts_pg_enum];
+ NameData enumlabel;
+ HeapTuple enum_tup;
+ bool result = elems_are_sorted;
+ int newelemorder;
+
+ /* check length of new label is ok */
+ if (strlen(newVal) > (NAMEDATALEN - 1))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_NAME),
+ errmsg("invalid enum label \"%s\"", newVal),
+ errdetail("Labels must be %d characters or less.",
+ NAMEDATALEN - 1)));
+
+ /* get a new OID for the label */
+ pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
+
+ if (OidIsValid(binary_upgrade_next_pg_enum_oid))
+ {
+ if (neighbour != NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("BEFORE/AFTER incompatible with binary upgrade.")));
+
+ newOid = binary_upgrade_next_pg_enum_oid;
+ binary_upgrade_next_pg_enum_oid = InvalidOid;
+ }
+ else
+ {
+ /* non upgrade case */
+ newOid = GetNewOid(pg_enum);
+ }
+
+ if (neighbour == NULL)
+ {
+ /*
+ * Put the new label at the end of the list.
+ * No change to existing tuples is required.
+ */
+ newelemorder = nelems + 1;
+ /* are the elements still sorted? */
+ if (elems_are_sorted)
+ {
+ CatCList *list;
+ int i;
+ bool still_sorted = true;
+
+ list = SearchSysCacheList1(ENUMTYPOIDNAME,
+ ObjectIdGetDatum(enumTypeOid));
+ for (i = 0; i < nelems; i++)
+ {
+ HeapTuple tup = &(list->members[i]->tuple);
+ Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
+
+ if (en->enumsortorder == nelems)
+ {
+ if (HeapTupleGetOid(tup) > newOid)
+ still_sorted = false;
+ break;
+ }
+ }
+ ReleaseCatCacheList(list);
+ if (! still_sorted)
+ result = false;
+ }
+ }
+ else
+ {
+ /* BEFORE or AFTER specified */
+ CatCList *list;
+ int i;
+ HeapTuple *existing;
+ HeapTuple nbr = NULL;
+ Form_pg_enum nbr_en;
+
+ /* get a list of the existing elements and sort them by enumsortorder */
+ list = SearchSysCacheList1(ENUMTYPOIDNAME,
+ ObjectIdGetDatum(enumTypeOid));
+ existing = palloc(nelems * sizeof(HeapTuple));
+
+ for (i = 0; i < nelems; i++)
+ existing[i] = &(list->members[i]->tuple);
+
+ qsort(existing, nelems, sizeof(HeapTuple), sort_order_cmp);
+
+ /* locate the neighbour element */
+ for (i = 0; i < nelems; i++)
+ {
+ Form_pg_enum exists_en;
+ exists_en = (Form_pg_enum) GETSTRUCT(existing[i]);
+ if (strcmp(NameStr(exists_en->enumlabel),neighbour) == 0)
+ nbr = existing[i];
+
+ }
+
+ if (nbr == NULL)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("\"%s\" is not an existing label.", neighbour)));
+ }
+
+ nbr_en = (Form_pg_enum) GETSTRUCT(nbr);
+
+ /*
+ * If BEFORE was specified, the new label goes in the neighbour's
+ * position. Otherwise, it goes in the position after that.
+ */
+ newelemorder = nbr_en->enumsortorder;
+ if (newValIsAfter)
+ newelemorder++;
+
+ /*
+ * Add 1 to the sortorder of all the labels after where the
+ * new label goes. Do it from the end back so we don't get
+ * uniqueness violations.
+ */
+ for (i = nelems - 1; i>= 0; i--)
+ {
+ HeapTuple newtup;
+ Form_pg_enum exst_en = (Form_pg_enum) GETSTRUCT(existing[i]);
+ if (exst_en->enumsortorder < newelemorder)
+ break;
+
+ newtup = heap_copytuple(existing[i]);
+ exst_en = (Form_pg_enum) GETSTRUCT(newtup);
+ exst_en->enumsortorder ++;
+
+ simple_heap_update(pg_enum, &newtup->t_self, newtup);
+
+ CatalogUpdateIndexes(pg_enum, newtup);
+
+ }
+
+ ReleaseCatCacheList(list);
+
+ /* are the labels sorted by OID? */
+ if (result && newelemorder > 1)
+ result = newOid > HeapTupleGetOid(existing[newelemorder-2]);
+ if (result && newelemorder < nelems + 1)
+ result = newOid < HeapTupleGetOid(existing[newelemorder-1]);
+
+ }
+
+ /* set up the new entry */
+ tupDesc = pg_enum->rd_att;
+ memset(nulls, false, sizeof(nulls));
+ values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid);
+ namestrcpy(&enumlabel, newVal);
+ values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel);
+ values[Anum_pg_enum_enumsortorder -1] = Int32GetDatum(newelemorder);
+ enum_tup = heap_form_tuple(tupDesc, values, nulls);
+ HeapTupleSetOid(enum_tup, newOid);
+ simple_heap_insert(pg_enum, enum_tup);
+ CatalogUpdateIndexes(pg_enum, enum_tup);
+ heap_freetuple(enum_tup);
+
+ heap_close(pg_enum, RowExclusiveLock);
+
+ return result;
+
+ }
/*
***************
*** 33,40 **** static int oid_cmp(const void *p1, const void *p2);
* vals is a list of Value strings.
*/
void
! EnumValuesCreate(Oid enumTypeOid, List *vals,
! Oid binary_upgrade_next_pg_enum_oid)
{
Relation pg_enum;
TupleDesc tupDesc;
--- 223,229 ----
* vals is a list of Value strings.
*/
void
! EnumValuesCreate(Oid enumTypeOid, List *vals)
{
Relation pg_enum;
TupleDesc tupDesc;
***************
*** 50,58 **** EnumValuesCreate(Oid enumTypeOid, List *vals,
num_elems = list_length(vals);
/*
! * XXX we do not bother to check the list of values for duplicates --- if
* you have any, you'll get a less-than-friendly unique-index violation.
! * Is it worth trying harder?
*/
pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
--- 239,247 ----
num_elems = list_length(vals);
/*
! * We do not bother to check the list of values for duplicates --- if
* you have any, you'll get a less-than-friendly unique-index violation.
! * It is probably not worth trying harder.
*/
pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
***************
*** 62,96 **** EnumValuesCreate(Oid enumTypeOid, List *vals,
* Allocate oids
*/
oids = (Oid *) palloc(num_elems * sizeof(Oid));
! if (OidIsValid(binary_upgrade_next_pg_enum_oid))
! {
! if (num_elems != 1)
! ereport(ERROR,
! (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("EnumValuesCreate() can only set a single OID")));
! oids[0] = binary_upgrade_next_pg_enum_oid;
! binary_upgrade_next_pg_enum_oid = InvalidOid;
! }
! else
{
/*
! * While this method does not absolutely guarantee that we generate no
! * duplicate oids (since we haven't entered each oid into the table
! * before allocating the next), trouble could only occur if the oid
! * counter wraps all the way around before we finish. Which seems
! * unlikely.
*/
! for (elemno = 0; elemno < num_elems; elemno++)
! {
! /*
! * The pg_enum.oid is stored in user tables. This oid must be
! * preserved by binary upgrades.
! */
! oids[elemno] = GetNewOid(pg_enum);
! }
! /* sort them, just in case counter wrapped from high to low */
! qsort(oids, num_elems, sizeof(Oid), oid_cmp);
}
/* and make the entries */
memset(nulls, false, sizeof(nulls));
--- 251,274 ----
* Allocate oids
*/
oids = (Oid *) palloc(num_elems * sizeof(Oid));
!
! /*
! * While this method does not absolutely guarantee that we generate no
! * duplicate oids (since we haven't entered each oid into the table
! * before allocating the next), trouble could only occur if the oid
! * counter wraps all the way around before we finish. Which seems
! * unlikely.
! */
! for (elemno = 0; elemno < num_elems; elemno++)
{
/*
! * The pg_enum.oid is stored in user tables. This oid must be
! * preserved by binary upgrades.
*/
! oids[elemno] = GetNewOid(pg_enum);
}
+ /* sort them, just in case counter wrapped from high to low */
+ qsort(oids, num_elems, sizeof(Oid), oid_cmp);
/* and make the entries */
memset(nulls, false, sizeof(nulls));
***************
*** 114,119 **** EnumValuesCreate(Oid enumTypeOid, List *vals,
--- 292,298 ----
values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid);
namestrcpy(&enumlabel, lab);
values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel);
+ values[Anum_pg_enum_enumsortorder -1] = Int32GetDatum(elemno+1);
tup = heap_form_tuple(tupDesc, values, nulls);
HeapTupleSetOid(tup, oids[elemno]);
***************
*** 164,170 **** EnumValuesDelete(Oid enumTypeOid)
}
! /* qsort comparison function */
static int
oid_cmp(const void *p1, const void *p2)
{
--- 343,349 ----
}
! /* qsort comparison for oids */
static int
oid_cmp(const void *p1, const void *p2)
{
***************
*** 177,179 **** oid_cmp(const void *p1, const void *p2)
--- 356,371 ----
return 1;
return 0;
}
+
+ /* qsort comparison function for tuples by sort order */
+ static int
+ sort_order_cmp(const void *p1, const void *p2)
+ {
+ HeapTuple v1 = *((const HeapTuple *) p1);
+ HeapTuple v2 = *((const HeapTuple *) p2);
+ Form_pg_enum en1 = (Form_pg_enum) GETSTRUCT(v1);
+ Form_pg_enum en2 = (Form_pg_enum) GETSTRUCT(v2);
+
+ return en1->enumsortorder - en2->enumsortorder;
+ }
+
*** a/src/backend/catalog/pg_type.c
--- b/src/backend/catalog/pg_type.c
***************
*** 112,117 **** TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
--- 112,119 ----
values[i++] = ObjectIdGetDatum(InvalidOid); /* typbasetype */
values[i++] = Int32GetDatum(-1); /* typtypmod */
values[i++] = Int32GetDatum(0); /* typndims */
+ values[i++] = Int32GetDatum(-1); /* typnlabels */
+ values[i++] = BoolGetDatum(false); /* typsorted */
nulls[i++] = true; /* typdefaultbin */
nulls[i++] = true; /* typdefault */
***************
*** 204,210 **** TypeCreate(Oid newTypeOid,
char storage,
int32 typeMod,
int32 typNDims, /* Array dimensions for baseType */
! bool typeNotNull)
{
Relation pg_type_desc;
Oid typeObjectId;
--- 206,214 ----
char storage,
int32 typeMod,
int32 typNDims, /* Array dimensions for baseType */
! bool typeNotNull,
! int32 typeNLabels,
! bool typeSorted)
{
Relation pg_type_desc;
Oid typeObjectId;
***************
*** 342,347 **** TypeCreate(Oid newTypeOid,
--- 346,353 ----
values[i++] = ObjectIdGetDatum(baseType); /* typbasetype */
values[i++] = Int32GetDatum(typeMod); /* typtypmod */
values[i++] = Int32GetDatum(typNDims); /* typndims */
+ values[i++] = Int32GetDatum(typeNLabels); /* typnlabels */
+ values[i++] = BoolGetDatum(typeSorted); /* typsorted */
/*
* initialize the default binary value for this type. Check for nulls of
*** a/src/backend/commands/typecmds.c
--- b/src/backend/commands/typecmds.c
***************
*** 85,96 **** static Oid findTypeTypmodoutFunction(List *procname);
static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid);
static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
static void checkDomainOwner(HeapTuple tup, TypeName *typename);
static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
Oid baseTypeOid,
int typMod, Constraint *constr,
char *domainName);
-
-
/*
* DefineType
* Registers a new base type.
--- 85,95 ----
static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid);
static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
static void checkDomainOwner(HeapTuple tup, TypeName *typename);
+ static void checkEnumOwner(HeapTuple tup, TypeName *typename);
static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
Oid baseTypeOid,
int typMod, Constraint *constr,
char *domainName);
/*
* DefineType
* Registers a new base type.
***************
*** 562,569 **** DefineType(List *names, List *parameters)
storage, /* TOAST strategy */
-1, /* typMod (Domains only) */
0, /* Array Dimensions of typbasetype */
! false); /* Type NOT NULL */
!
/*
* Create the array type that goes with it.
*/
--- 561,569 ----
storage, /* TOAST strategy */
-1, /* typMod (Domains only) */
0, /* Array Dimensions of typbasetype */
! false, /* Type NOT NULL */
! -1, /* no enum labels */
! false); /* type is not an enum, so not sorted */
/*
* Create the array type that goes with it.
*/
***************
*** 601,607 **** DefineType(List *names, List *parameters)
'x', /* ARRAY is always toastable */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
! false); /* Type NOT NULL */
pfree(array_type);
}
--- 601,609 ----
'x', /* ARRAY is always toastable */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
! false, /* Type NOT NULL */
! -1, /* no enum labels */
! false); /* type is not an enum, so not sorted */
pfree(array_type);
}
***************
*** 1044,1050 **** DefineDomain(CreateDomainStmt *stmt)
storage, /* TOAST strategy */
basetypeMod, /* typeMod value */
typNDims, /* Array dimensions for base type */
! typNotNull); /* Type NOT NULL */
/*
* Process constraints which refer to the domain ID returned by TypeCreate
--- 1046,1054 ----
storage, /* TOAST strategy */
basetypeMod, /* typeMod value */
typNDims, /* Array dimensions for base type */
! typNotNull, /* Type NOT NULL */
! -1, /* no enum labels */
! false); /* type is not an enum, so not sorted */
/*
* Process constraints which refer to the domain ID returned by TypeCreate
***************
*** 1094,1099 **** DefineEnum(CreateEnumStmt *stmt)
--- 1098,1106 ----
AclResult aclresult;
Oid old_type_oid;
Oid enumArrayOid;
+ int num_labels;
+
+ num_labels = list_length(stmt->vals);
/* Convert list of names to a name and namespace */
enumNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
***************
*** 1153,1162 **** DefineEnum(CreateEnumStmt *stmt)
'p', /* TOAST strategy always plain */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
! false); /* Type NOT NULL */
/* Enter the enum's values into pg_enum */
! EnumValuesCreate(enumTypeOid, stmt->vals, InvalidOid);
/*
* Create the array type that goes with it.
--- 1160,1171 ----
'p', /* TOAST strategy always plain */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
! false, /* Type NOT NULL */
! num_labels, /* count enum labels */
! true); /* enums always start sorted */
/* Enter the enum's values into pg_enum */
! EnumValuesCreate(enumTypeOid, stmt->vals);
/*
* Create the array type that goes with it.
***************
*** 1192,1202 **** DefineEnum(CreateEnumStmt *stmt)
'x', /* ARRAY is always toastable */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
! false); /* Type NOT NULL */
pfree(enumArrayName);
}
/*
* Find suitable I/O functions for a type.
--- 1201,1288 ----
'x', /* ARRAY is always toastable */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
! false, /* Type NOT NULL */
! -1, /* no enum labels */
! false); /* type is not an enum, so not sorted */
pfree(enumArrayName);
}
+ /*
+ * AlterEnum
+ * Registers a new label for an existing enum.
+ */
+ void
+ AlterEnum (AlterEnumStmt *stmt)
+ {
+ Oid enum_type_oid;
+ TypeName *typename;
+ bool sorted;
+ HeapTuple tup, newtup;
+ Relation rel;
+ Form_pg_type typTup;
+
+ /* Make a TypeName so we can use standard type lookup machinery */
+ typename = makeTypeNameFromNameList(stmt->typeName);
+ enum_type_oid = typenameTypeId(NULL, typename, NULL);
+
+ /* Look up the row in the type table */
+ rel = heap_open(TypeRelationId, RowExclusiveLock);
+
+ tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(enum_type_oid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for type %u", enum_type_oid);
+
+ /* Copy the syscache entry so we can scribble on it below */
+ newtup = heap_copytuple(tup);
+ ReleaseSysCache(tup);
+ tup = newtup;
+ typTup = (Form_pg_type) GETSTRUCT(tup);
+
+ /* Check it's an enum and check user has permission to ALTER the enum */
+ checkEnumOwner(tup, typename);
+
+ /* Add the new label */
+ sorted = AddEnumLabel (enum_type_oid, stmt->newVal,
+ stmt->newValNeighbour, stmt->newValIsAfter,
+ typTup->typnlabels, typTup->typsorted);
+
+ /* Update the row in pg_type */
+ typTup->typnlabels += 1;
+ typTup->typsorted = sorted;
+
+ simple_heap_update(rel, &tup->t_self, tup);
+
+ CatalogUpdateIndexes(rel, tup);
+
+ /* Clean up */
+ heap_close(rel, RowExclusiveLock);
+ }
+
+
+ /*
+ * checkEnumOwner
+ *
+ * Check that the type is actually an enum and that the current user
+ * has permission to do ALTER TYPE on it. Throw an error if not.
+ */
+ static void
+ checkEnumOwner(HeapTuple tup, TypeName *typename)
+ {
+ Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup);
+
+ /* Check that this is actually a domain */
+ if (typTup->typtype != TYPTYPE_ENUM)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not an enum",
+ TypeNameToString(typename))));
+
+ /* Permission check: must own type */
+ if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
+ format_type_be(HeapTupleGetOid(tup)));
+ }
/*
* Find suitable I/O functions for a type.
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 2873,2878 **** _copyCreateEnumStmt(CreateEnumStmt *from)
--- 2873,2891 ----
return newnode;
}
+ static AlterEnumStmt *
+ _copyAlterEnumStmt(AlterEnumStmt *from)
+ {
+ AlterEnumStmt *newnode = makeNode(AlterEnumStmt);
+
+ COPY_NODE_FIELD(typeName);
+ COPY_STRING_FIELD(newVal);
+ COPY_STRING_FIELD(newValNeighbour);
+ COPY_SCALAR_FIELD(newValIsAfter);
+
+ return newnode;
+ }
+
static ViewStmt *
_copyViewStmt(ViewStmt *from)
{
***************
*** 4033,4038 **** copyObject(void *from)
--- 4046,4054 ----
case T_CreateEnumStmt:
retval = _copyCreateEnumStmt(from);
break;
+ case T_AlterEnumStmt:
+ retval = _copyAlterEnumStmt(from);
+ break;
case T_ViewStmt:
retval = _copyViewStmt(from);
break;
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
***************
*** 1390,1395 **** _equalCreateEnumStmt(CreateEnumStmt *a, CreateEnumStmt *b)
--- 1390,1406 ----
}
static bool
+ _equalAlterEnumStmt(AlterEnumStmt *a, AlterEnumStmt *b)
+ {
+ COMPARE_NODE_FIELD(typeName);
+ COMPARE_STRING_FIELD(newVal);
+ COMPARE_STRING_FIELD(newValNeighbour);
+ COMPARE_SCALAR_FIELD(newValIsAfter);
+
+ return true;
+ }
+
+ static bool
_equalViewStmt(ViewStmt *a, ViewStmt *b)
{
COMPARE_NODE_FIELD(view);
***************
*** 2697,2702 **** equal(void *a, void *b)
--- 2708,2716 ----
case T_CreateEnumStmt:
retval = _equalCreateEnumStmt(a, b);
break;
+ case T_AlterEnumStmt:
+ retval = _equalAlterEnumStmt(a, b);
+ break;
case T_ViewStmt:
retval = _equalViewStmt(a, b);
break;
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 182,189 **** static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
}
%type <node> stmt schema_stmt
! AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterFdwStmt
! AlterForeignServerStmt AlterGroupStmt
AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
AlterCompositeTypeStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
AlterRoleStmt AlterRoleSetStmt
--- 182,189 ----
}
%type <node> stmt schema_stmt
! AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt
! AlterFdwStmt AlterForeignServerStmt AlterGroupStmt
AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
AlterCompositeTypeStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
AlterRoleStmt AlterRoleSetStmt
***************
*** 652,657 **** stmt :
--- 652,658 ----
| AlterDatabaseSetStmt
| AlterDefaultPrivilegesStmt
| AlterDomainStmt
+ | AlterEnumStmt
| AlterFdwStmt
| AlterForeignServerStmt
| AlterFunctionStmt
***************
*** 3861,3866 **** enum_val_list: Sconst
--- 3862,3907 ----
{ $$ = lappend($1, makeString($3)); }
;
+ /*****************************************************************************
+ *
+ * ALTER TYPE enumtype ADD ...
+ *
+ *****************************************************************************/
+
+ AlterEnumStmt:
+ ALTER TYPE_P any_name ADD_P Sconst
+ {
+ AlterEnumStmt *n = makeNode(AlterEnumStmt);
+ n->typeName = $3;
+ n->newVal = $5;
+ n->newValNeighbour = NULL;
+ n->newValIsAfter = true;
+
+ $$ = (Node *) n;
+ }
+ | ALTER TYPE_P any_name ADD_P Sconst BEFORE Sconst
+ {
+ AlterEnumStmt *n = makeNode(AlterEnumStmt);
+ n->typeName = $3;
+ n->newVal = $5;
+ n->newValNeighbour = $7;
+ n->newValIsAfter = false;
+
+ $$ = (Node *) n;
+ }
+ | ALTER TYPE_P any_name ADD_P Sconst AFTER Sconst
+ {
+ AlterEnumStmt *n = makeNode(AlterEnumStmt);
+ n->typeName = $3;
+ n->newVal = $5;
+ n->newValNeighbour = $7;
+ n->newValIsAfter = true;
+
+ $$ = (Node *) n;
+ }
+ ;
+
+
/*****************************************************************************
*
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
***************
*** 190,195 **** check_xact_readonly(Node *parsetree)
--- 190,196 ----
case T_CreateTrigStmt:
case T_CompositeTypeStmt:
case T_CreateEnumStmt:
+ case T_AlterEnumStmt:
case T_ViewStmt:
case T_DropCastStmt:
case T_DropStmt:
***************
*** 860,865 **** standard_ProcessUtility(Node *parsetree,
--- 861,870 ----
DefineEnum((CreateEnumStmt *) parsetree);
break;
+ case T_AlterEnumStmt: /* ALTER TYPE (enum) */
+ AlterEnum((AlterEnumStmt *) parsetree);
+ break;
+
case T_ViewStmt: /* CREATE VIEW */
DefineView((ViewStmt *) parsetree, queryString);
break;
***************
*** 1868,1873 **** CreateCommandTag(Node *parsetree)
--- 1873,1882 ----
tag = "CREATE TYPE";
break;
+ case T_AlterEnumStmt:
+ tag = "ALTER TYPE";
+ break;
+
case T_ViewStmt:
tag = "CREATE VIEW";
break;
***************
*** 2410,2415 **** GetCommandLogLevel(Node *parsetree)
--- 2419,2428 ----
lev = LOGSTMT_DDL;
break;
+ case T_AlterEnumStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
case T_ViewStmt:
lev = LOGSTMT_DDL;
break;
*** a/src/backend/utils/adt/enum.c
--- b/src/backend/utils/adt/enum.c
***************
*** 14,19 ****
--- 14,20 ----
#include "postgres.h"
#include "catalog/pg_enum.h"
+ #include "catalog/pg_type.h"
#include "fmgr.h"
#include "utils/array.h"
#include "utils/builtins.h"
***************
*** 22,30 ****
#include "libpq/pqformat.h"
#include "miscadmin.h"
static ArrayType *enum_range_internal(Oid enumtypoid, Oid lower, Oid upper);
! static int enum_elem_cmp(const void *left, const void *right);
/* Basic I/O support */
--- 23,48 ----
#include "libpq/pqformat.h"
#include "miscadmin.h"
+ typedef struct
+ {
+ Oid enum_oid;
+ int32 sort_order;
+ } enum_sort;
+
+ typedef struct
+ {
+ Oid enumtypoid;
+ bool oids_are_sorted;
+ int sort_list_length;
+ int label_count;
+ enum_sort sort_order_list[1];
+ } enum_sort_cache;
+
+
static ArrayType *enum_range_internal(Oid enumtypoid, Oid lower, Oid upper);
! static int enum_sort_cmp(const void *left, const void *right);
! static int enum_oid_cmp(const void *left, const void *right);
/* Basic I/O support */
***************
*** 155,167 **** enum_send(PG_FUNCTION_ARGS)
/* Comparison functions and related */
Datum
enum_lt(PG_FUNCTION_ARGS)
{
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(a < b);
}
Datum
--- 173,314 ----
/* Comparison functions and related */
+ static inline int
+ enum_ccmp(Oid arg1, Oid arg2, FunctionCallInfo fcinfo)
+ {
+
+ enum_sort_cache * mycache;
+ enum_sort *es1, *es2, srch;
+ int sort1, sort2;
+ bool added = false;
+ HeapTuple enum_tup, type_tup;
+ Form_pg_enum en;
+ Oid typeoid;
+ Form_pg_type typ;
+
+ if (arg1 == arg2)
+ return 0;
+
+ mycache = (enum_sort_cache *) fcinfo->flinfo->fn_extra;
+ if (mycache == NULL )
+ {
+ enum_tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(arg1));
+ en = (Form_pg_enum) GETSTRUCT(enum_tup);
+ typeoid = en->enumtypid;
+ type_tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeoid));
+ typ = (Form_pg_type) GETSTRUCT(type_tup);
+ if (typ->typtype != 'e')
+ elog(ERROR,"wrong type for oid %u",typeoid);
+ fcinfo->flinfo->fn_extra =
+ MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+ sizeof(enum_sort_cache) +
+ (typ->typnlabels * sizeof(enum_sort)));
+ mycache = (enum_sort_cache *) fcinfo->flinfo->fn_extra;
+ mycache->enumtypoid = typeoid;
+ mycache->sort_list_length = 1;
+ mycache->label_count = typ->typnlabels;
+ mycache->oids_are_sorted = typ->typsorted;
+ mycache->sort_order_list[0].enum_oid = arg1;
+ mycache->sort_order_list[0].sort_order = en->enumsortorder;
+ ReleaseSysCache(type_tup);
+ ReleaseSysCache(enum_tup);
+ }
+
+ if (mycache->oids_are_sorted)
+ return arg1 - arg2;
+
+ srch.enum_oid = arg1;
+ es1 = bsearch(&srch,
+ mycache->sort_order_list,
+ mycache->sort_list_length,
+ sizeof(enum_sort),
+ enum_oid_cmp);
+ srch.enum_oid = arg2;
+ es2 = bsearch(&srch,
+ mycache->sort_order_list,
+ mycache->sort_list_length,
+ sizeof(enum_sort),
+ enum_oid_cmp);
+
+ if (es1 == NULL)
+ {
+
+ enum_tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(arg1));
+ en = (Form_pg_enum) GETSTRUCT(enum_tup);
+ mycache->sort_order_list[mycache->sort_list_length].enum_oid = arg1;
+ sort1 = en->enumsortorder;
+ mycache->sort_order_list[mycache->sort_list_length].sort_order =
+ sort1;
+ ReleaseSysCache(enum_tup);
+ mycache->sort_list_length ++;
+ added = true;
+ }
+ else
+ {
+ /* already in cache */
+ sort1 = es1->sort_order;
+ }
+
+ if (es2 == NULL)
+ {
+
+ enum_tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(arg2));
+ en = (Form_pg_enum) GETSTRUCT(enum_tup);
+ sort2 = en->enumsortorder;
+ mycache->sort_order_list[mycache->sort_list_length].enum_oid = arg2;
+ mycache->sort_order_list[mycache->sort_list_length].sort_order =
+ sort2;
+ ReleaseSysCache(enum_tup);
+ mycache->sort_list_length ++;
+ added = true;
+ }
+ else
+ {
+ /* already in cache */
+ sort2 = es2->sort_order;
+ }
+
+ if (added)
+ {
+ /*
+ * If we have more than a handful, just fetch them all, so we limit
+ * the number of sort operations required.
+ */
+ if (mycache->sort_list_length > 10 &&
+ mycache->sort_list_length < mycache->label_count)
+ {
+ CatCList *nlist;
+ int num, i;
+
+ nlist = SearchSysCacheList1(ENUMTYPOIDNAME,
+ ObjectIdGetDatum(mycache->enumtypoid));
+ num = nlist->n_members;
+ for (i = 0; i < num; i++)
+ {
+ HeapTuple tup = &(nlist->members[i]->tuple);
+ Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
+
+ mycache->sort_order_list[i].enum_oid = HeapTupleGetOid(tup);
+ mycache->sort_order_list[i].sort_order = en->enumsortorder;
+ }
+
+ ReleaseCatCacheList(nlist);
+ }
+
+ qsort(mycache->sort_order_list,mycache->sort_list_length,
+ sizeof(enum_sort),enum_oid_cmp);
+ }
+
+ return sort1 - sort2;
+ }
+
Datum
enum_lt(PG_FUNCTION_ARGS)
{
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(enum_ccmp(a,b,fcinfo) < 0);
}
Datum
***************
*** 170,176 **** enum_le(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(a <= b);
}
Datum
--- 317,323 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(enum_ccmp(a,b,fcinfo) <= 0);
}
Datum
***************
*** 197,203 **** enum_ge(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(a >= b);
}
Datum
--- 344,350 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(enum_ccmp(a,b,fcinfo) >= 0);
}
Datum
***************
*** 206,212 **** enum_gt(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(a > b);
}
Datum
--- 353,359 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(enum_ccmp(a,b,fcinfo) > 0);
}
Datum
***************
*** 215,221 **** enum_smaller(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_OID(a <= b ? a : b);
}
Datum
--- 362,368 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_OID(enum_ccmp(a,b,fcinfo) < 0 ? a : b);
}
Datum
***************
*** 224,230 **** enum_larger(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_OID(a >= b ? a : b);
}
Datum
--- 371,377 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_OID(enum_ccmp(a,b,fcinfo) > 0 ? a : b);
}
Datum
***************
*** 233,242 **** enum_cmp(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! if (a > b)
! PG_RETURN_INT32(1);
! else if (a == b)
PG_RETURN_INT32(0);
else
PG_RETURN_INT32(-1);
}
--- 380,389 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! if (a == b)
PG_RETURN_INT32(0);
+ else if (enum_ccmp(a,b,fcinfo) > 0)
+ PG_RETURN_INT32(1);
else
PG_RETURN_INT32(-1);
}
***************
*** 248,253 **** enum_first(PG_FUNCTION_ARGS)
--- 395,401 ----
{
Oid enumtypoid;
Oid min = InvalidOid;
+ int min_sort = -1; /* value will never in fact be used */
CatCList *list;
int num,
i;
***************
*** 267,276 **** enum_first(PG_FUNCTION_ARGS)
num = list->n_members;
for (i = 0; i < num; i++)
{
! Oid valoid = HeapTupleHeaderGetOid(list->members[i]->tuple.t_data);
!
! if (!OidIsValid(min) || valoid < min)
! min = valoid;
}
ReleaseCatCacheList(list);
--- 415,428 ----
num = list->n_members;
for (i = 0; i < num; i++)
{
! HeapTuple tup = &(list->members[i]->tuple);
! Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
!
! if (!OidIsValid(min) || en->enumsortorder < min_sort)
! {
! min = HeapTupleGetOid(tup);
! min_sort = en->enumsortorder;
! }
}
ReleaseCatCacheList(list);
***************
*** 287,292 **** enum_last(PG_FUNCTION_ARGS)
--- 439,445 ----
{
Oid enumtypoid;
Oid max = InvalidOid;
+ int max_sort = -1; /* value will never in fact be used */
CatCList *list;
int num,
i;
***************
*** 306,315 **** enum_last(PG_FUNCTION_ARGS)
num = list->n_members;
for (i = 0; i < num; i++)
{
! Oid valoid = HeapTupleHeaderGetOid(list->members[i]->tuple.t_data);
!
! if (!OidIsValid(max) || valoid > max)
! max = valoid;
}
ReleaseCatCacheList(list);
--- 459,472 ----
num = list->n_members;
for (i = 0; i < num; i++)
{
! HeapTuple tup = &(list->members[i]->tuple);
! Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
!
! if (!OidIsValid(max) || en->enumsortorder > max_sort)
! {
! max = HeapTupleGetOid(tup);
! max_sort = en->enumsortorder;
! }
}
ReleaseCatCacheList(list);
***************
*** 382,427 **** enum_range_internal(Oid enumtypoid, Oid lower, Oid upper)
i,
j;
Datum *elems;
list = SearchSysCacheList1(ENUMTYPOIDNAME, ObjectIdGetDatum(enumtypoid));
total = list->n_members;
elems = (Datum *) palloc(total * sizeof(Datum));
- j = 0;
for (i = 0; i < total; i++)
{
! Oid val = HeapTupleGetOid(&(list->members[i]->tuple));
- if ((!OidIsValid(lower) || lower <= val) &&
- (!OidIsValid(upper) || val <= upper))
- elems[j++] = ObjectIdGetDatum(val);
}
/* shouldn't need the cache anymore */
ReleaseCatCacheList(list);
! /* sort results into OID order */
! qsort(elems, j, sizeof(Datum), enum_elem_cmp);
/* note this hardwires some details about the representation of Oid */
result = construct_array(elems, j, enumtypoid, sizeof(Oid), true, 'i');
pfree(elems);
return result;
}
! /* qsort comparison function for Datums that are OIDs */
static int
! enum_elem_cmp(const void *left, const void *right)
{
! Oid l = DatumGetObjectId(*((const Datum *) left));
! Oid r = DatumGetObjectId(*((const Datum *) right));
!
! if (l < r)
! return -1;
! if (l > r)
! return 1;
! return 0;
}
--- 539,614 ----
i,
j;
Datum *elems;
+ enum_sort *sort_items;
+ bool left_found;
list = SearchSysCacheList1(ENUMTYPOIDNAME, ObjectIdGetDatum(enumtypoid));
total = list->n_members;
elems = (Datum *) palloc(total * sizeof(Datum));
+ sort_items = (enum_sort *) palloc(total * sizeof(enum_sort));
for (i = 0; i < total; i++)
{
! HeapTuple tup = &(list->members[i]->tuple);
! Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
!
! sort_items[i].enum_oid = HeapTupleGetOid(tup);
! sort_items[i].sort_order = en->enumsortorder;
}
/* shouldn't need the cache anymore */
ReleaseCatCacheList(list);
! /* sort results into sort_order sequence */
! qsort(sort_items, total, sizeof(enum_sort), enum_sort_cmp);
!
! j = 0;
! left_found = !OidIsValid(lower);
! for (i=0; i < total; i++)
! {
! if (! left_found && lower == sort_items[i].enum_oid)
! left_found = true;
!
! if (left_found)
! elems[j++] = ObjectIdGetDatum(sort_items[i].enum_oid);
!
! if (OidIsValid(upper) && upper == sort_items[i].enum_oid)
! break;
! }
/* note this hardwires some details about the representation of Oid */
result = construct_array(elems, j, enumtypoid, sizeof(Oid), true, 'i');
pfree(elems);
+ pfree(sort_items);
return result;
}
! /*
! * qsort comparison using sort order, for range routines
! */
static int
! enum_sort_cmp(const void *left, const void *right)
{
! enum_sort *l = (enum_sort *) left;
! enum_sort *r = (enum_sort *) right;
!
! return l->sort_order - r->sort_order;
}
+
+ /*
+ * qsort comparison using OID order for comparison search cache
+ */
+ static int
+ enum_oid_cmp(const void *es1, const void *es2)
+ {
+ enum_sort *p1, *p2;
+ p1 = (enum_sort *)es1;
+ p2 = (enum_sort *)es2;
+ return p1->enum_oid - p2->enum_oid;
+ }
+
+
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
***************
*** 6653,6669 **** dumpEnumType(Archive *fout, TypeInfo *tyinfo)
PQExpBuffer query = createPQExpBuffer();
PGresult *res;
int num,
! i;
Oid enum_oid;
char *label;
/* Set proper schema search path so regproc references list correctly */
selectSourceSchema(tyinfo->dobj.namespace->dobj.name);
! appendPQExpBuffer(query, "SELECT oid, enumlabel "
! "FROM pg_catalog.pg_enum "
! "WHERE enumtypid = '%u'"
! "ORDER BY oid",
tyinfo->dobj.catId.oid);
res = PQexec(g_conn, query->data);
--- 6653,6676 ----
PQExpBuffer query = createPQExpBuffer();
PGresult *res;
int num,
! i;
Oid enum_oid;
char *label;
/* Set proper schema search path so regproc references list correctly */
selectSourceSchema(tyinfo->dobj.namespace->dobj.name);
! if (fout->remoteVersion > 90000)
! appendPQExpBuffer(query, "SELECT oid, enumlabel "
! "FROM pg_catalog.pg_enum "
! "WHERE enumtypid = '%u'"
! "ORDER BY enumsortorder",
! tyinfo->dobj.catId.oid);
! else
! appendPQExpBuffer(query, "SELECT oid, enumlabel "
! "FROM pg_catalog.pg_enum "
! "WHERE enumtypid = '%u'"
! "ORDER BY oid",
tyinfo->dobj.catId.oid);
res = PQexec(g_conn, query->data);
***************
*** 6709,6725 **** dumpEnumType(Archive *fout, TypeInfo *tyinfo)
{
enum_oid = atooid(PQgetvalue(res, i, PQfnumber(res, "oid")));
label = PQgetvalue(res, i, PQfnumber(res, "enumlabel"));
!
if (i == 0)
appendPQExpBuffer(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
appendPQExpBuffer(q,
! "SELECT binary_upgrade.add_pg_enum_label('%u'::pg_catalog.oid, "
! "'%u'::pg_catalog.oid, ",
! enum_oid, tyinfo->dobj.catId.oid);
! appendStringLiteralAH(q, label, fout);
! appendPQExpBuffer(q, ");\n");
}
- appendPQExpBuffer(q, "\n");
}
ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
--- 6716,6734 ----
{
enum_oid = atooid(PQgetvalue(res, i, PQfnumber(res, "oid")));
label = PQgetvalue(res, i, PQfnumber(res, "enumlabel"));
!
if (i == 0)
appendPQExpBuffer(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
appendPQExpBuffer(q,
! "SELECT binary_upgrade.set_next_pg_enum_oid('%u'::pg_catalog.oid);\n",
! enum_oid);
! appendPQExpBuffer(q, "ALTER TYPE %s.",
! fmtId(tyinfo->dobj.namespace->dobj.name));
! appendPQExpBuffer(q, "%s ADD ",
! fmtId(tyinfo->dobj.name));
! appendStringLiteralAH(q, label, fout);
! appendPQExpBuffer(q, ";\n\n");
}
}
ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
*** a/src/include/catalog/indexing.h
--- b/src/include/catalog/indexing.h
***************
*** 147,152 **** DECLARE_UNIQUE_INDEX(pg_enum_oid_index, 3502, on pg_enum using btree(oid oid_ops
--- 147,154 ----
#define EnumOidIndexId 3502
DECLARE_UNIQUE_INDEX(pg_enum_typid_label_index, 3503, on pg_enum using btree(enumtypid oid_ops, enumlabel name_ops));
#define EnumTypIdLabelIndexId 3503
+ DECLARE_UNIQUE_INDEX(pg_enum_typid_sortorder_index, 3539, on pg_enum using btree(enumtypid oid_ops, enumsortorder int4_ops));
+ #define EnumTypIdSortOrderIndexId 3539
/* This following index is not used for a cache and is not unique */
DECLARE_INDEX(pg_index_indrelid_index, 2678, on pg_index using btree(indrelid oid_ops));
*** a/src/include/catalog/pg_class.h
--- b/src/include/catalog/pg_class.h
***************
*** 132,138 **** typedef FormData_pg_class *Form_pg_class;
*/
/* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId */
! DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f f r 28 0 t f f f f f 3 _null_ _null_ ));
DESCR("");
DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f f r 19 0 f f f f f f 3 _null_ _null_ ));
DESCR("");
--- 132,138 ----
*/
/* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId */
! DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f f r 30 0 t f f f f f 3 _null_ _null_ ));
DESCR("");
DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f f r 19 0 f f f f f f 3 _null_ _null_ ));
DESCR("");
*** a/src/include/catalog/pg_enum.h
--- b/src/include/catalog/pg_enum.h
***************
*** 35,40 **** CATALOG(pg_enum,3501)
--- 35,41 ----
{
Oid enumtypid; /* OID of owning enum type */
NameData enumlabel; /* text representation of enum value */
+ int4 enumsortorder; /* sort order for this enum label */
} FormData_pg_enum;
/* ----------------
***************
*** 48,56 **** typedef FormData_pg_enum *Form_pg_enum;
* compiler constants for pg_enum
* ----------------
*/
! #define Natts_pg_enum 2
#define Anum_pg_enum_enumtypid 1
#define Anum_pg_enum_enumlabel 2
/* ----------------
* pg_enum has no initial contents
--- 49,58 ----
* compiler constants for pg_enum
* ----------------
*/
! #define Natts_pg_enum 3
#define Anum_pg_enum_enumtypid 1
#define Anum_pg_enum_enumlabel 2
+ #define Anum_pg_enum_enumsortorder 3
/* ----------------
* pg_enum has no initial contents
***************
*** 60,67 **** typedef FormData_pg_enum *Form_pg_enum;
/*
* prototypes for functions in pg_enum.c
*/
! extern void EnumValuesCreate(Oid enumTypeOid, List *vals,
! Oid binary_upgrade_next_pg_enum_oid);
extern void EnumValuesDelete(Oid enumTypeOid);
#endif /* PG_ENUM_H */
--- 62,70 ----
/*
* prototypes for functions in pg_enum.c
*/
! extern void EnumValuesCreate(Oid enumTypeOid, List *vals);
extern void EnumValuesDelete(Oid enumTypeOid);
+ extern bool AddEnumLabel(Oid enumTypeOid, char *newVal, char *neighbour,
+ bool newValIsAfter, int nelems, bool elems_are_sorted);
#endif /* PG_ENUM_H */
*** a/src/include/catalog/pg_type.h
--- b/src/include/catalog/pg_type.h
***************
*** 194,199 **** CATALOG(pg_type,1247) BKI_BOOTSTRAP BKI_ROWTYPE_OID(71) BKI_SCHEMA_MACRO
--- 194,211 ----
*/
int4 typndims;
+ /*
+ * typnlabels, contains a count on the number of labels an enum type has,
+ * -1 for a non-enum type.
+ */
+ int4 typnlabels;
+
+ /*
+ * typsorted is true if the oids of an enum type reflect the type's sort
+ * order, false otherwise including for a non-enum type.
+ */
+ bool typsorted;
+
/*
* If typdefaultbin is not NULL, it is the nodeToString representation of
* a default expression for the type. Currently this is only used for
***************
*** 224,230 **** typedef FormData_pg_type *Form_pg_type;
* compiler constants for pg_type
* ----------------
*/
! #define Natts_pg_type 28
#define Anum_pg_type_typname 1
#define Anum_pg_type_typnamespace 2
#define Anum_pg_type_typowner 3
--- 236,242 ----
* compiler constants for pg_type
* ----------------
*/
! #define Natts_pg_type 30
#define Anum_pg_type_typname 1
#define Anum_pg_type_typnamespace 2
#define Anum_pg_type_typowner 3
***************
*** 251,258 **** typedef FormData_pg_type *Form_pg_type;
#define Anum_pg_type_typbasetype 24
#define Anum_pg_type_typtypmod 25
#define Anum_pg_type_typndims 26
! #define Anum_pg_type_typdefaultbin 27
! #define Anum_pg_type_typdefault 28
/* ----------------
--- 263,272 ----
#define Anum_pg_type_typbasetype 24
#define Anum_pg_type_typtypmod 25
#define Anum_pg_type_typndims 26
! #define Anum_pg_type_typnlabels 27
! #define Anum_pg_type_typsorted 28
! #define Anum_pg_type_typdefaultbin 29
! #define Anum_pg_type_typdefault 30
/* ----------------
***************
*** 269,355 **** typedef FormData_pg_type *Form_pg_type;
*/
/* OIDS 1 - 99 */
! DATA(insert OID = 16 ( bool PGNSP PGUID 1 t b B t t \054 0 0 1000 boolin boolout boolrecv boolsend - - - c p f 0 -1 0 _null_ _null_ ));
DESCR("boolean, 'true'/'false'");
#define BOOLOID 16
! DATA(insert OID = 17 ( bytea PGNSP PGUID -1 f b U f t \054 0 0 1001 byteain byteaout bytearecv byteasend - - - i x f 0 -1 0 _null_ _null_ ));
DESCR("variable-length string, binary values escaped");
#define BYTEAOID 17
! DATA(insert OID = 18 ( char PGNSP PGUID 1 t b S f t \054 0 0 1002 charin charout charrecv charsend - - - c p f 0 -1 0 _null_ _null_ ));
DESCR("single character");
#define CHAROID 18
! DATA(insert OID = 19 ( name PGNSP PGUID NAMEDATALEN f b S f t \054 0 18 1003 namein nameout namerecv namesend - - - c p f 0 -1 0 _null_ _null_ ));
DESCR("63-character type for storing system identifiers");
#define NAMEOID 19
! DATA(insert OID = 20 ( int8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 1016 int8in int8out int8recv int8send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("~18 digit integer, 8-byte storage");
#define INT8OID 20
! DATA(insert OID = 21 ( int2 PGNSP PGUID 2 t b N f t \054 0 0 1005 int2in int2out int2recv int2send - - - s p f 0 -1 0 _null_ _null_ ));
DESCR("-32 thousand to 32 thousand, 2-byte storage");
#define INT2OID 21
! DATA(insert OID = 22 ( int2vector PGNSP PGUID -1 f b A f t \054 0 21 1006 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("array of int2, used in system tables");
#define INT2VECTOROID 22
! DATA(insert OID = 23 ( int4 PGNSP PGUID 4 t b N f t \054 0 0 1007 int4in int4out int4recv int4send - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("-2 billion to 2 billion integer, 4-byte storage");
#define INT4OID 23
! DATA(insert OID = 24 ( regproc PGNSP PGUID 4 t b N f t \054 0 0 1008 regprocin regprocout regprocrecv regprocsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered procedure");
#define REGPROCOID 24
! DATA(insert OID = 25 ( text PGNSP PGUID -1 f b S t t \054 0 0 1009 textin textout textrecv textsend - - - i x f 0 -1 0 _null_ _null_ ));
DESCR("variable-length string, no limit specified");
#define TEXTOID 25
! DATA(insert OID = 26 ( oid PGNSP PGUID 4 t b N t t \054 0 0 1028 oidin oidout oidrecv oidsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("object identifier(oid), maximum 4 billion");
#define OIDOID 26
! DATA(insert OID = 27 ( tid PGNSP PGUID 6 f b U f t \054 0 0 1010 tidin tidout tidrecv tidsend - - - s p f 0 -1 0 _null_ _null_ ));
DESCR("(block, offset), physical location of tuple");
#define TIDOID 27
! DATA(insert OID = 28 ( xid PGNSP PGUID 4 t b U f t \054 0 0 1011 xidin xidout xidrecv xidsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("transaction id");
#define XIDOID 28
! DATA(insert OID = 29 ( cid PGNSP PGUID 4 t b U f t \054 0 0 1012 cidin cidout cidrecv cidsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("command identifier type, sequence in transaction id");
#define CIDOID 29
! DATA(insert OID = 30 ( oidvector PGNSP PGUID -1 f b A f t \054 0 26 1013 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("array of oids, used in system tables");
#define OIDVECTOROID 30
/* hand-built rowtype entries for bootstrapped catalogs */
/* NB: OIDs assigned here must match the BKI_ROWTYPE_OID declarations */
! DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c C f t \054 1247 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c C f t \054 1249 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c C f t \054 1255 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c C f t \054 1259 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
/* OIDS 100 - 199 */
! DATA(insert OID = 142 ( xml PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 _null_ _null_ ));
DESCR("XML content");
#define XMLOID 142
! DATA(insert OID = 143 ( _xml PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 194 ( pg_node_tree PGNSP PGUID -1 f b S f t \054 0 0 0 pg_node_tree_in pg_node_tree_out pg_node_tree_recv pg_node_tree_send - - - i x f 0 -1 0 _null_ _null_ ));
DESCR("string representing an internal node tree");
#define PGNODETREEOID 194
/* OIDS 200 - 299 */
! DATA(insert OID = 210 ( smgr PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f 0 -1 0 _null_ _null_ ));
DESCR("storage manager");
/* OIDS 300 - 399 */
--- 283,369 ----
*/
/* OIDS 1 - 99 */
! DATA(insert OID = 16 ( bool PGNSP PGUID 1 t b B t t \054 0 0 1000 boolin boolout boolrecv boolsend - - - c p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("boolean, 'true'/'false'");
#define BOOLOID 16
! DATA(insert OID = 17 ( bytea PGNSP PGUID -1 f b U f t \054 0 0 1001 byteain byteaout bytearecv byteasend - - - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("variable-length string, binary values escaped");
#define BYTEAOID 17
! DATA(insert OID = 18 ( char PGNSP PGUID 1 t b S f t \054 0 0 1002 charin charout charrecv charsend - - - c p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("single character");
#define CHAROID 18
! DATA(insert OID = 19 ( name PGNSP PGUID NAMEDATALEN f b S f t \054 0 18 1003 namein nameout namerecv namesend - - - c p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("63-character type for storing system identifiers");
#define NAMEOID 19
! DATA(insert OID = 20 ( int8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 1016 int8in int8out int8recv int8send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("~18 digit integer, 8-byte storage");
#define INT8OID 20
! DATA(insert OID = 21 ( int2 PGNSP PGUID 2 t b N f t \054 0 0 1005 int2in int2out int2recv int2send - - - s p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("-32 thousand to 32 thousand, 2-byte storage");
#define INT2OID 21
! DATA(insert OID = 22 ( int2vector PGNSP PGUID -1 f b A f t \054 0 21 1006 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("array of int2, used in system tables");
#define INT2VECTOROID 22
! DATA(insert OID = 23 ( int4 PGNSP PGUID 4 t b N f t \054 0 0 1007 int4in int4out int4recv int4send - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("-2 billion to 2 billion integer, 4-byte storage");
#define INT4OID 23
! DATA(insert OID = 24 ( regproc PGNSP PGUID 4 t b N f t \054 0 0 1008 regprocin regprocout regprocrecv regprocsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered procedure");
#define REGPROCOID 24
! DATA(insert OID = 25 ( text PGNSP PGUID -1 f b S t t \054 0 0 1009 textin textout textrecv textsend - - - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("variable-length string, no limit specified");
#define TEXTOID 25
! DATA(insert OID = 26 ( oid PGNSP PGUID 4 t b N t t \054 0 0 1028 oidin oidout oidrecv oidsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("object identifier(oid), maximum 4 billion");
#define OIDOID 26
! DATA(insert OID = 27 ( tid PGNSP PGUID 6 f b U f t \054 0 0 1010 tidin tidout tidrecv tidsend - - - s p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("(block, offset), physical location of tuple");
#define TIDOID 27
! DATA(insert OID = 28 ( xid PGNSP PGUID 4 t b U f t \054 0 0 1011 xidin xidout xidrecv xidsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("transaction id");
#define XIDOID 28
! DATA(insert OID = 29 ( cid PGNSP PGUID 4 t b U f t \054 0 0 1012 cidin cidout cidrecv cidsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("command identifier type, sequence in transaction id");
#define CIDOID 29
! DATA(insert OID = 30 ( oidvector PGNSP PGUID -1 f b A f t \054 0 26 1013 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("array of oids, used in system tables");
#define OIDVECTOROID 30
/* hand-built rowtype entries for bootstrapped catalogs */
/* NB: OIDs assigned here must match the BKI_ROWTYPE_OID declarations */
! DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c C f t \054 1247 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c C f t \054 1249 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c C f t \054 1255 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c C f t \054 1259 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
/* OIDS 100 - 199 */
! DATA(insert OID = 142 ( xml PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("XML content");
#define XMLOID 142
! DATA(insert OID = 143 ( _xml PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 194 ( pg_node_tree PGNSP PGUID -1 f b S f t \054 0 0 0 pg_node_tree_in pg_node_tree_out pg_node_tree_recv pg_node_tree_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("string representing an internal node tree");
#define PGNODETREEOID 194
/* OIDS 200 - 299 */
! DATA(insert OID = 210 ( smgr PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("storage manager");
/* OIDS 300 - 399 */
***************
*** 359,589 **** DESCR("storage manager");
/* OIDS 500 - 599 */
/* OIDS 600 - 699 */
! DATA(insert OID = 600 ( point PGNSP PGUID 16 f b G f t \054 0 701 1017 point_in point_out point_recv point_send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("geometric point '(x, y)'");
#define POINTOID 600
! DATA(insert OID = 601 ( lseg PGNSP PGUID 32 f b G f t \054 0 600 1018 lseg_in lseg_out lseg_recv lseg_send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("geometric line segment '(pt1,pt2)'");
#define LSEGOID 601
! DATA(insert OID = 602 ( path PGNSP PGUID -1 f b G f t \054 0 0 1019 path_in path_out path_recv path_send - - - d x f 0 -1 0 _null_ _null_ ));
DESCR("geometric path '(pt1,...)'");
#define PATHOID 602
! DATA(insert OID = 603 ( box PGNSP PGUID 32 f b G f t \073 0 600 1020 box_in box_out box_recv box_send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("geometric box '(lower left,upper right)'");
#define BOXOID 603
! DATA(insert OID = 604 ( polygon PGNSP PGUID -1 f b G f t \054 0 0 1027 poly_in poly_out poly_recv poly_send - - - d x f 0 -1 0 _null_ _null_ ));
DESCR("geometric polygon '(pt1,...)'");
#define POLYGONOID 604
! DATA(insert OID = 628 ( line PGNSP PGUID 32 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("geometric line (not implemented)");
#define LINEOID 628
! DATA(insert OID = 629 ( _line PGNSP PGUID -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
DESCR("");
/* OIDS 700 - 799 */
! DATA(insert OID = 700 ( float4 PGNSP PGUID 4 FLOAT4PASSBYVAL b N f t \054 0 0 1021 float4in float4out float4recv float4send - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("single-precision floating point number, 4-byte storage");
#define FLOAT4OID 700
! DATA(insert OID = 701 ( float8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N t t \054 0 0 1022 float8in float8out float8recv float8send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("double-precision floating point number, 8-byte storage");
#define FLOAT8OID 701
! DATA(insert OID = 702 ( abstime PGNSP PGUID 4 t b D f t \054 0 0 1023 abstimein abstimeout abstimerecv abstimesend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("absolute, limited-range date and time (Unix system time)");
#define ABSTIMEOID 702
! DATA(insert OID = 703 ( reltime PGNSP PGUID 4 t b T f t \054 0 0 1024 reltimein reltimeout reltimerecv reltimesend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("relative, limited-range time interval (Unix delta time)");
#define RELTIMEOID 703
! DATA(insert OID = 704 ( tinterval PGNSP PGUID 12 f b T f t \054 0 0 1025 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("(abstime,abstime), time interval");
#define TINTERVALOID 704
! DATA(insert OID = 705 ( unknown PGNSP PGUID -2 f b X f t \054 0 0 0 unknownin unknownout unknownrecv unknownsend - - - c p f 0 -1 0 _null_ _null_ ));
DESCR("");
#define UNKNOWNOID 705
! DATA(insert OID = 718 ( circle PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("geometric circle '(center,radius)'");
#define CIRCLEOID 718
! DATA(insert OID = 719 ( _circle PGNSP PGUID -1 f b A f t \054 0 718 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 790 ( money PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("monetary amounts, $d,ddd.cc");
#define CASHOID 790
! DATA(insert OID = 791 ( _money PGNSP PGUID -1 f b A f t \054 0 790 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
/* OIDS 800 - 899 */
! DATA(insert OID = 829 ( macaddr PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("XX:XX:XX:XX:XX:XX, MAC address");
#define MACADDROID 829
! DATA(insert OID = 869 ( inet PGNSP PGUID -1 f b I t t \054 0 0 1041 inet_in inet_out inet_recv inet_send - - - i m f 0 -1 0 _null_ _null_ ));
DESCR("IP address/netmask, host address, netmask optional");
#define INETOID 869
! DATA(insert OID = 650 ( cidr PGNSP PGUID -1 f b I f t \054 0 0 651 cidr_in cidr_out cidr_recv cidr_send - - - i m f 0 -1 0 _null_ _null_ ));
DESCR("network IP address/netmask, network address");
#define CIDROID 650
/* OIDS 900 - 999 */
/* OIDS 1000 - 1099 */
! DATA(insert OID = 1000 ( _bool PGNSP PGUID -1 f b A f t \054 0 16 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1001 ( _bytea PGNSP PGUID -1 f b A f t \054 0 17 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1002 ( _char PGNSP PGUID -1 f b A f t \054 0 18 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b A f t \054 0 19 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b A f t \054 0 21 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b A f t \054 0 22 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b A f t \054 0 23 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
#define INT4ARRAYOID 1007
! DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b A f t \054 0 24 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b A f t \054 0 25 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
#define TEXTARRAYOID 1009
! DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b A f t \054 0 26 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b A f t \054 0 27 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b A f t \054 0 28 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b A f t \054 0 29 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1013 ( _oidvector PGNSP PGUID -1 f b A f t \054 0 30 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1014 ( _bpchar PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1015 ( _varchar PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1016 ( _int8 PGNSP PGUID -1 f b A f t \054 0 20 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1017 ( _point PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1018 ( _lseg PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1019 ( _path PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1020 ( _box PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1021 ( _float4 PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
#define FLOAT4ARRAYOID 1021
! DATA(insert OID = 1022 ( _float8 PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1033 ( aclitem PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("access control list");
#define ACLITEMOID 1033
! DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1263 ( _cstring PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
#define CSTRINGARRAYOID 1263
! DATA(insert OID = 1042 ( bpchar PGNSP PGUID -1 f b S f t \054 0 0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 _null_ _null_ ));
DESCR("char(length), blank-padded string, fixed storage length");
#define BPCHAROID 1042
! DATA(insert OID = 1043 ( varchar PGNSP PGUID -1 f b S f t \054 0 0 1015 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f 0 -1 0 _null_ _null_ ));
DESCR("varchar(length), non-blank-padded string, variable storage length");
#define VARCHAROID 1043
! DATA(insert OID = 1082 ( date PGNSP PGUID 4 t b D f t \054 0 0 1182 date_in date_out date_recv date_send - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("date");
#define DATEOID 1082
! DATA(insert OID = 1083 ( time PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1183 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f 0 -1 0 _null_ _null_ ));
DESCR("time of day");
#define TIMEOID 1083
/* OIDS 1100 - 1199 */
! DATA(insert OID = 1114 ( timestamp PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 _null_ _null_ ));
DESCR("date and time");
#define TIMESTAMPOID 1114
! DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1182 ( _date PGNSP PGUID -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1183 ( _time PGNSP PGUID -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1184 ( timestamptz PGNSP PGUID 8 FLOAT8PASSBYVAL b D t t \054 0 0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 _null_ _null_ ));
DESCR("date and time with time zone");
#define TIMESTAMPTZOID 1184
! DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0 1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1186 ( interval PGNSP PGUID 16 f b T t t \054 0 0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 _null_ _null_ ));
DESCR("@ <number> <units>, time interval");
#define INTERVALOID 1186
! DATA(insert OID = 1187 ( _interval PGNSP PGUID -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout - d x f 0 -1 0 _null_ _null_ ));
/* OIDS 1200 - 1299 */
! DATA(insert OID = 1231 ( _numeric PGNSP PGUID -1 f b A f t \054 0 1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1266 ( timetz PGNSP PGUID 12 f b D f t \054 0 0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 _null_ _null_ ));
DESCR("time of day with time zone");
#define TIMETZOID 1266
! DATA(insert OID = 1270 ( _timetz PGNSP PGUID -1 f b A f t \054 0 1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout - d x f 0 -1 0 _null_ _null_ ));
/* OIDS 1500 - 1599 */
! DATA(insert OID = 1560 ( bit PGNSP PGUID -1 f b V f t \054 0 0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 _null_ _null_ ));
DESCR("fixed-length bit string");
#define BITOID 1560
! DATA(insert OID = 1561 ( _bit PGNSP PGUID -1 f b A f t \054 0 1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1562 ( varbit PGNSP PGUID -1 f b V t t \054 0 0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 _null_ _null_ ));
DESCR("variable-length bit string");
#define VARBITOID 1562
! DATA(insert OID = 1563 ( _varbit PGNSP PGUID -1 f b A f t \054 0 1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout - i x f 0 -1 0 _null_ _null_ ));
/* OIDS 1600 - 1699 */
/* OIDS 1700 - 1799 */
! DATA(insert OID = 1700 ( numeric PGNSP PGUID -1 f b N f t \054 0 0 1231 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f 0 -1 0 _null_ _null_ ));
DESCR("numeric(precision, decimal), arbitrary precision number");
#define NUMERICOID 1700
! DATA(insert OID = 1790 ( refcursor PGNSP PGUID -1 f b U f t \054 0 0 2201 textin textout textrecv textsend - - - i x f 0 -1 0 _null_ _null_ ));
DESCR("reference to cursor (portal name)");
#define REFCURSOROID 1790
/* OIDS 2200 - 2299 */
! DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 2202 ( regprocedure PGNSP PGUID 4 t b N f t \054 0 0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered procedure (with args)");
#define REGPROCEDUREOID 2202
! DATA(insert OID = 2203 ( regoper PGNSP PGUID 4 t b N f t \054 0 0 2208 regoperin regoperout regoperrecv regopersend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered operator");
#define REGOPEROID 2203
! DATA(insert OID = 2204 ( regoperator PGNSP PGUID 4 t b N f t \054 0 0 2209 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered operator (with args)");
#define REGOPERATOROID 2204
! DATA(insert OID = 2205 ( regclass PGNSP PGUID 4 t b N f t \054 0 0 2210 regclassin regclassout regclassrecv regclasssend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered class");
#define REGCLASSOID 2205
! DATA(insert OID = 2206 ( regtype PGNSP PGUID 4 t b N f t \054 0 0 2211 regtypein regtypeout regtyperecv regtypesend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered type");
#define REGTYPEOID 2206
! DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
#define REGTYPEARRAYOID 2211
/* uuid */
! DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 _null_ _null_ ));
DESCR("UUID datatype");
! DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
/* text search */
! DATA(insert OID = 3614 ( tsvector PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 _null_ _null_ ));
DESCR("text representation for text search");
#define TSVECTOROID 3614
! DATA(insert OID = 3642 ( gtsvector PGNSP PGUID -1 f b U f t \054 0 0 3644 gtsvectorin gtsvectorout - - - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("GiST index internal text representation for text search");
#define GTSVECTOROID 3642
! DATA(insert OID = 3615 ( tsquery PGNSP PGUID -1 f b U f t \054 0 0 3645 tsqueryin tsqueryout tsqueryrecv tsquerysend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("query representation for text search");
#define TSQUERYOID 3615
! DATA(insert OID = 3734 ( regconfig PGNSP PGUID 4 t b N f t \054 0 0 3735 regconfigin regconfigout regconfigrecv regconfigsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered text search configuration");
#define REGCONFIGOID 3734
! DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered text search dictionary");
#define REGDICTIONARYOID 3769
! DATA(insert OID = 3643 ( _tsvector PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 3644 ( _gtsvector PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 _null_ _null_ ));
DESCR("txid snapshot");
! DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
/*
* pseudo-types
--- 373,603 ----
/* OIDS 500 - 599 */
/* OIDS 600 - 699 */
! DATA(insert OID = 600 ( point PGNSP PGUID 16 f b G f t \054 0 701 1017 point_in point_out point_recv point_send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric point '(x, y)'");
#define POINTOID 600
! DATA(insert OID = 601 ( lseg PGNSP PGUID 32 f b G f t \054 0 600 1018 lseg_in lseg_out lseg_recv lseg_send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric line segment '(pt1,pt2)'");
#define LSEGOID 601
! DATA(insert OID = 602 ( path PGNSP PGUID -1 f b G f t \054 0 0 1019 path_in path_out path_recv path_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric path '(pt1,...)'");
#define PATHOID 602
! DATA(insert OID = 603 ( box PGNSP PGUID 32 f b G f t \073 0 600 1020 box_in box_out box_recv box_send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric box '(lower left,upper right)'");
#define BOXOID 603
! DATA(insert OID = 604 ( polygon PGNSP PGUID -1 f b G f t \054 0 0 1027 poly_in poly_out poly_recv poly_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric polygon '(pt1,...)'");
#define POLYGONOID 604
! DATA(insert OID = 628 ( line PGNSP PGUID 32 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric line (not implemented)");
#define LINEOID 628
! DATA(insert OID = 629 ( _line PGNSP PGUID -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("");
/* OIDS 700 - 799 */
! DATA(insert OID = 700 ( float4 PGNSP PGUID 4 FLOAT4PASSBYVAL b N f t \054 0 0 1021 float4in float4out float4recv float4send - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("single-precision floating point number, 4-byte storage");
#define FLOAT4OID 700
! DATA(insert OID = 701 ( float8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N t t \054 0 0 1022 float8in float8out float8recv float8send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("double-precision floating point number, 8-byte storage");
#define FLOAT8OID 701
! DATA(insert OID = 702 ( abstime PGNSP PGUID 4 t b D f t \054 0 0 1023 abstimein abstimeout abstimerecv abstimesend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("absolute, limited-range date and time (Unix system time)");
#define ABSTIMEOID 702
! DATA(insert OID = 703 ( reltime PGNSP PGUID 4 t b T f t \054 0 0 1024 reltimein reltimeout reltimerecv reltimesend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("relative, limited-range time interval (Unix delta time)");
#define RELTIMEOID 703
! DATA(insert OID = 704 ( tinterval PGNSP PGUID 12 f b T f t \054 0 0 1025 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("(abstime,abstime), time interval");
#define TINTERVALOID 704
! DATA(insert OID = 705 ( unknown PGNSP PGUID -2 f b X f t \054 0 0 0 unknownin unknownout unknownrecv unknownsend - - - c p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("");
#define UNKNOWNOID 705
! DATA(insert OID = 718 ( circle PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric circle '(center,radius)'");
#define CIRCLEOID 718
! DATA(insert OID = 719 ( _circle PGNSP PGUID -1 f b A f t \054 0 718 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 790 ( money PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("monetary amounts, $d,ddd.cc");
#define CASHOID 790
! DATA(insert OID = 791 ( _money PGNSP PGUID -1 f b A f t \054 0 790 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
/* OIDS 800 - 899 */
! DATA(insert OID = 829 ( macaddr PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("XX:XX:XX:XX:XX:XX, MAC address");
#define MACADDROID 829
! DATA(insert OID = 869 ( inet PGNSP PGUID -1 f b I t t \054 0 0 1041 inet_in inet_out inet_recv inet_send - - - i m f 0 -1 0 -1 f _null_ _null_ ));
DESCR("IP address/netmask, host address, netmask optional");
#define INETOID 869
! DATA(insert OID = 650 ( cidr PGNSP PGUID -1 f b I f t \054 0 0 651 cidr_in cidr_out cidr_recv cidr_send - - - i m f 0 -1 0 -1 f _null_ _null_ ));
DESCR("network IP address/netmask, network address");
#define CIDROID 650
/* OIDS 900 - 999 */
/* OIDS 1000 - 1099 */
! DATA(insert OID = 1000 ( _bool PGNSP PGUID -1 f b A f t \054 0 16 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1001 ( _bytea PGNSP PGUID -1 f b A f t \054 0 17 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1002 ( _char PGNSP PGUID -1 f b A f t \054 0 18 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b A f t \054 0 19 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b A f t \054 0 21 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b A f t \054 0 22 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b A f t \054 0 23 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
#define INT4ARRAYOID 1007
! DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b A f t \054 0 24 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b A f t \054 0 25 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
#define TEXTARRAYOID 1009
! DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b A f t \054 0 26 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b A f t \054 0 27 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b A f t \054 0 28 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b A f t \054 0 29 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1013 ( _oidvector PGNSP PGUID -1 f b A f t \054 0 30 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1014 ( _bpchar PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1015 ( _varchar PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1016 ( _int8 PGNSP PGUID -1 f b A f t \054 0 20 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1017 ( _point PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1018 ( _lseg PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1019 ( _path PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1020 ( _box PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1021 ( _float4 PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
#define FLOAT4ARRAYOID 1021
! DATA(insert OID = 1022 ( _float8 PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1033 ( aclitem PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("access control list");
#define ACLITEMOID 1033
! DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1263 ( _cstring PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
#define CSTRINGARRAYOID 1263
! DATA(insert OID = 1042 ( bpchar PGNSP PGUID -1 f b S f t \054 0 0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("char(length), blank-padded string, fixed storage length");
#define BPCHAROID 1042
! DATA(insert OID = 1043 ( varchar PGNSP PGUID -1 f b S f t \054 0 0 1015 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("varchar(length), non-blank-padded string, variable storage length");
#define VARCHAROID 1043
! DATA(insert OID = 1082 ( date PGNSP PGUID 4 t b D f t \054 0 0 1182 date_in date_out date_recv date_send - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("date");
#define DATEOID 1082
! DATA(insert OID = 1083 ( time PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1183 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("time of day");
#define TIMEOID 1083
/* OIDS 1100 - 1199 */
! DATA(insert OID = 1114 ( timestamp PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("date and time");
#define TIMESTAMPOID 1114
! DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1182 ( _date PGNSP PGUID -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1183 ( _time PGNSP PGUID -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1184 ( timestamptz PGNSP PGUID 8 FLOAT8PASSBYVAL b D t t \054 0 0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("date and time with time zone");
#define TIMESTAMPTZOID 1184
! DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0 1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1186 ( interval PGNSP PGUID 16 f b T t t \054 0 0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("@ <number> <units>, time interval");
#define INTERVALOID 1186
! DATA(insert OID = 1187 ( _interval PGNSP PGUID -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout - d x f 0 -1 0 -1 f _null_ _null_ ));
/* OIDS 1200 - 1299 */
! DATA(insert OID = 1231 ( _numeric PGNSP PGUID -1 f b A f t \054 0 1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1266 ( timetz PGNSP PGUID 12 f b D f t \054 0 0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("time of day with time zone");
#define TIMETZOID 1266
! DATA(insert OID = 1270 ( _timetz PGNSP PGUID -1 f b A f t \054 0 1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout - d x f 0 -1 0 -1 f _null_ _null_ ));
/* OIDS 1500 - 1599 */
! DATA(insert OID = 1560 ( bit PGNSP PGUID -1 f b V f t \054 0 0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("fixed-length bit string");
#define BITOID 1560
! DATA(insert OID = 1561 ( _bit PGNSP PGUID -1 f b A f t \054 0 1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1562 ( varbit PGNSP PGUID -1 f b V t t \054 0 0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("variable-length bit string");
#define VARBITOID 1562
! DATA(insert OID = 1563 ( _varbit PGNSP PGUID -1 f b A f t \054 0 1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
/* OIDS 1600 - 1699 */
/* OIDS 1700 - 1799 */
! DATA(insert OID = 1700 ( numeric PGNSP PGUID -1 f b N f t \054 0 0 1231 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f 0 -1 0 -1 f _null_ _null_ ));
DESCR("numeric(precision, decimal), arbitrary precision number");
#define NUMERICOID 1700
! DATA(insert OID = 1790 ( refcursor PGNSP PGUID -1 f b U f t \054 0 0 2201 textin textout textrecv textsend - - - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("reference to cursor (portal name)");
#define REFCURSOROID 1790
/* OIDS 2200 - 2299 */
! DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 2202 ( regprocedure PGNSP PGUID 4 t b N f t \054 0 0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered procedure (with args)");
#define REGPROCEDUREOID 2202
! DATA(insert OID = 2203 ( regoper PGNSP PGUID 4 t b N f t \054 0 0 2208 regoperin regoperout regoperrecv regopersend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered operator");
#define REGOPEROID 2203
! DATA(insert OID = 2204 ( regoperator PGNSP PGUID 4 t b N f t \054 0 0 2209 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered operator (with args)");
#define REGOPERATOROID 2204
! DATA(insert OID = 2205 ( regclass PGNSP PGUID 4 t b N f t \054 0 0 2210 regclassin regclassout regclassrecv regclasssend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered class");
#define REGCLASSOID 2205
! DATA(insert OID = 2206 ( regtype PGNSP PGUID 4 t b N f t \054 0 0 2211 regtypein regtypeout regtyperecv regtypesend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered type");
#define REGTYPEOID 2206
! DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
#define REGTYPEARRAYOID 2211
/* uuid */
! DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("UUID datatype");
! DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
/* text search */
! DATA(insert OID = 3614 ( tsvector PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("text representation for text search");
#define TSVECTOROID 3614
! DATA(insert OID = 3642 ( gtsvector PGNSP PGUID -1 f b U f t \054 0 0 3644 gtsvectorin gtsvectorout - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("GiST index internal text representation for text search");
#define GTSVECTOROID 3642
! DATA(insert OID = 3615 ( tsquery PGNSP PGUID -1 f b U f t \054 0 0 3645 tsqueryin tsqueryout tsqueryrecv tsquerysend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("query representation for text search");
#define TSQUERYOID 3615
! DATA(insert OID = 3734 ( regconfig PGNSP PGUID 4 t b N f t \054 0 0 3735 regconfigin regconfigout regconfigrecv regconfigsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered text search configuration");
#define REGCONFIGOID 3734
! DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered text search dictionary");
#define REGDICTIONARYOID 3769
! DATA(insert OID = 3643 ( _tsvector PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 3644 ( _gtsvector PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("txid snapshot");
! DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
/*
* pseudo-types
***************
*** 598,628 **** DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 a
* but there is now support for it in records and arrays. Perhaps we should
* just treat it as a regular base type?
*/
! DATA(insert OID = 2249 ( record PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
#define RECORDOID 2249
! DATA(insert OID = 2287 ( _record PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
#define RECORDARRAYOID 2287
! DATA(insert OID = 2275 ( cstring PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 _null_ _null_ ));
#define CSTRINGOID 2275
! DATA(insert OID = 2276 ( any PGNSP PGUID 4 t p P f t \054 0 0 0 any_in any_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define ANYOID 2276
! DATA(insert OID = 2277 ( anyarray PGNSP PGUID -1 f p P f t \054 0 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f 0 -1 0 _null_ _null_ ));
#define ANYARRAYOID 2277
! DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define VOIDOID 2278
! DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define TRIGGEROID 2279
! DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define LANGUAGE_HANDLEROID 2280
! DATA(insert OID = 2281 ( internal PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 _null_ _null_ ));
#define INTERNALOID 2281
! DATA(insert OID = 2282 ( opaque PGNSP PGUID 4 t p P f t \054 0 0 0 opaque_in opaque_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define OPAQUEOID 2282
! DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p P f t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define ANYELEMENTOID 2283
! DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p P f t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define ANYNONARRAYOID 2776
! DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define ANYENUMOID 3500
--- 612,642 ----
* but there is now support for it in records and arrays. Perhaps we should
* just treat it as a regular base type?
*/
! DATA(insert OID = 2249 ( record PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
#define RECORDOID 2249
! DATA(insert OID = 2287 ( _record PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
#define RECORDARRAYOID 2287
! DATA(insert OID = 2275 ( cstring PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 -1 f _null_ _null_ ));
#define CSTRINGOID 2275
! DATA(insert OID = 2276 ( any PGNSP PGUID 4 t p P f t \054 0 0 0 any_in any_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define ANYOID 2276
! DATA(insert OID = 2277 ( anyarray PGNSP PGUID -1 f p P f t \054 0 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
#define ANYARRAYOID 2277
! DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define VOIDOID 2278
! DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define TRIGGEROID 2279
! DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define LANGUAGE_HANDLEROID 2280
! DATA(insert OID = 2281 ( internal PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 -1 f _null_ _null_ ));
#define INTERNALOID 2281
! DATA(insert OID = 2282 ( opaque PGNSP PGUID 4 t p P f t \054 0 0 0 opaque_in opaque_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define OPAQUEOID 2282
! DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p P f t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define ANYELEMENTOID 2283
! DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p P f t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define ANYNONARRAYOID 2776
! DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define ANYENUMOID 3500
*** a/src/include/catalog/pg_type_fn.h
--- b/src/include/catalog/pg_type_fn.h
***************
*** 50,56 **** extern Oid TypeCreate(Oid newTypeOid,
char storage,
int32 typeMod,
int32 typNDims,
! bool typeNotNull);
extern void GenerateTypeDependencies(Oid typeNamespace,
Oid typeObjectId,
--- 50,58 ----
char storage,
int32 typeMod,
int32 typNDims,
! bool typeNotNull,
! int32 typeNLabels,
! bool typeSorted);
extern void GenerateTypeDependencies(Oid typeNamespace,
Oid typeObjectId,
*** a/src/include/commands/typecmds.h
--- b/src/include/commands/typecmds.h
***************
*** 24,29 **** extern void RemoveTypes(DropStmt *drop);
--- 24,30 ----
extern void RemoveTypeById(Oid typeOid);
extern void DefineDomain(CreateDomainStmt *stmt);
extern void DefineEnum(CreateEnumStmt *stmt);
+ extern void AlterEnum (AlterEnumStmt *stmt);
extern Oid DefineCompositeType(const RangeVar *typevar, List *coldeflist);
extern Oid AssignTypeArrayOid(void);
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
***************
*** 348,354 **** typedef enum NodeTag
T_DropUserMappingStmt,
T_AlterTableSpaceOptionsStmt,
T_SecLabelStmt,
!
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
*/
--- 348,354 ----
T_DropUserMappingStmt,
T_AlterTableSpaceOptionsStmt,
T_SecLabelStmt,
! T_AlterEnumStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
*/
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 2191,2196 **** typedef struct CreateEnumStmt
--- 2191,2210 ----
/* ----------------------
+ * Alter Type Statement, enum types
+ * ----------------------
+ */
+ typedef struct AlterEnumStmt
+ {
+ NodeTag type;
+ List *typeName; /* qualified name (list of Value strings) */
+ char *newVal; /* new enum value */
+ char *newValNeighbour;/* neighbouring enum value */
+ bool newValIsAfter; /* new enum value is after neighbour? */
+ } AlterEnumStmt;
+
+
+ /* ----------------------
* Create View Statement
* ----------------------
*/
*** a/src/test/regress/expected/enum.out
--- b/src/test/regress/expected/enum.out
***************
*** 25,30 **** ERROR: invalid input value for enum rainbow: "mauve"
--- 25,108 ----
LINE 1: SELECT 'mauve'::rainbow;
^
--
+ -- adding new values
+ --
+ CREATE TYPE planets AS ENUM ( 'venus', 'earth', 'mars' );
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+ enumlabel | enumsortorder
+ -----------+---------------
+ venus | 1
+ earth | 2
+ mars | 3
+ (3 rows)
+
+ ALTER TYPE planets ADD 'uranus';
+ SELECT typnlabels, typsorted
+ FROM pg_type
+ WHERE oid = 'planets'::regtype;
+ typnlabels | typsorted
+ ------------+-----------
+ 4 | t
+ (1 row)
+
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+ enumlabel | enumsortorder
+ -----------+---------------
+ venus | 1
+ earth | 2
+ mars | 3
+ uranus | 4
+ (4 rows)
+
+ ALTER TYPE planets ADD 'mercury' BEFORE 'venus';
+ ALTER TYPE planets ADD 'saturn' BEFORE 'uranus';
+ ALTER TYPE planets ADD 'jupiter' AFTER 'mars';
+ ALTER TYPE planets ADD 'neptune' AFTER 'uranus';
+ SELECT typnlabels, typsorted
+ FROM pg_type
+ WHERE oid = 'planets'::regtype;
+ typnlabels | typsorted
+ ------------+-----------
+ 8 | f
+ (1 row)
+
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+ enumlabel | enumsortorder
+ -----------+---------------
+ mercury | 1
+ venus | 2
+ earth | 3
+ mars | 4
+ jupiter | 5
+ saturn | 6
+ uranus | 7
+ neptune | 8
+ (8 rows)
+
+ select 'mars'::planets > 'mercury' as using_sortorder;
+ using_sortorder
+ -----------------
+ t
+ (1 row)
+
+ -- errors for adding labels
+ ALTER TYPE planets ADD
+ 'plutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutopluto';
+ ERROR: invalid enum label "plutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutopluto"
+ DETAIL: Labels must be 63 characters or less.
+ ALTER TYPE planets ADD 'pluto' AFTER 'zeus';
+ ERROR: "zeus" is not an existing label.
+ DROP TYPE planets;
+ --
-- Basic table creation, row selection
--
CREATE TABLE enumtest (col rainbow);
***************
*** 403,409 **** SELECT COUNT(*) FROM pg_type WHERE typname = 'rainbow';
SELECT * FROM pg_enum WHERE NOT EXISTS
(SELECT 1 FROM pg_type WHERE pg_type.oid = enumtypid);
! enumtypid | enumlabel
! -----------+-----------
(0 rows)
--- 481,487 ----
SELECT * FROM pg_enum WHERE NOT EXISTS
(SELECT 1 FROM pg_type WHERE pg_type.oid = enumtypid);
! enumtypid | enumlabel | enumsortorder
! -----------+-----------+---------------
(0 rows)
*** a/src/test/regress/sql/enum.sql
--- b/src/test/regress/sql/enum.sql
***************
*** 16,21 **** SELECT 'red'::rainbow;
--- 16,69 ----
SELECT 'mauve'::rainbow;
--
+ -- adding new values
+ --
+
+ CREATE TYPE planets AS ENUM ( 'venus', 'earth', 'mars' );
+
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+
+ ALTER TYPE planets ADD 'uranus';
+
+ SELECT typnlabels, typsorted
+ FROM pg_type
+ WHERE oid = 'planets'::regtype;
+
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+
+ ALTER TYPE planets ADD 'mercury' BEFORE 'venus';
+
+ ALTER TYPE planets ADD 'saturn' BEFORE 'uranus';
+
+ ALTER TYPE planets ADD 'jupiter' AFTER 'mars';
+
+ ALTER TYPE planets ADD 'neptune' AFTER 'uranus';
+
+ SELECT typnlabels, typsorted
+ FROM pg_type
+ WHERE oid = 'planets'::regtype;
+
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+
+ select 'mars'::planets > 'mercury' as using_sortorder;
+
+ -- errors for adding labels
+ ALTER TYPE planets ADD
+ 'plutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutopluto';
+
+ ALTER TYPE planets ADD 'pluto' AFTER 'zeus';
+
+ DROP TYPE planets;
+ --
-- Basic table creation, row selection
--
CREATE TABLE enumtest (col rainbow);
On 29 September 2010 20:46, Andrew Dunstan <andrew@dunslane.net> wrote:
Attached is a a slightly updated version of this with the bitrot fixed.
cheers
andrew
Hi,
I had a quick look at this last night. I haven't had time to give it a
full review, but I did spot a couple of things:
1). It still has no docs.
2). In enum_ccmp(), when you cache the full list of elements, you're
not updating mycache->sort_list_length, so it will keep fetching the
full list each time. Also, I think that function could use a few more
comments.
3). I think you need to update psql so that \dT+ returns the enum
elements in the right order.
Otherwise I like it, and I definitely prefer the flexibility that this
syntax gives.
Regards,
Dean
On 10/01/2010 04:35 AM, Dean Rasheed wrote:
2). In enum_ccmp(), when you cache the full list of elements, you're
not updating mycache->sort_list_length, so it will keep fetching the
full list each time. Also, I think that function could use a few more
comments.
Good catch. Will fix.
3). I think you need to update psql so that \dT+ returns the enum
elements in the right order.
Yeah. Will do.
I will post a revised patch soon.
cheers
andrew
On Fri, Oct 1, 2010 at 7:12 AM, Andrew Dunstan <andrew@dunslane.net> wrote:
On 10/01/2010 04:35 AM, Dean Rasheed wrote:
2). In enum_ccmp(), when you cache the full list of elements, you're
not updating mycache->sort_list_length, so it will keep fetching the
full list each time. Also, I think that function could use a few more
comments.Good catch. Will fix.
3). I think you need to update psql so that \dT+ returns the enum
elements in the right order.Yeah. Will do.
I will post a revised patch soon.
Should we postpone this to the next CommitFest?
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On 10/13/2010 02:08 AM, Robert Haas wrote:
On Fri, Oct 1, 2010 at 7:12 AM, Andrew Dunstan<andrew@dunslane.net> wrote:
On 10/01/2010 04:35 AM, Dean Rasheed wrote:
2). In enum_ccmp(), when you cache the full list of elements, you're
not updating mycache->sort_list_length, so it will keep fetching the
full list each time. Also, I think that function could use a few more
comments.Good catch. Will fix.
3). I think you need to update psql so that \dT+ returns the enum
elements in the right order.Yeah. Will do.
I will post a revised patch soon.
Should we postpone this to the next CommitFest?
Sorry, got distracted. Here's a new patch that fixes the above and also
contains some documentation.
cheers
andrew
Attachments:
venum4.patchtext/x-patch; name=venum4.patchDownload
*** a/contrib/pg_upgrade/function.c
--- b/contrib/pg_upgrade/function.c
***************
*** 61,85 **** install_support_functions(migratorContext *ctx)
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_heap_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_toast_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_index_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.add_pg_enum_label(OID, OID, NAME) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
--- 61,85 ----
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_pg_enum_oid(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_heap_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_toast_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_index_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
*** a/contrib/pg_upgrade_support/pg_upgrade_support.c
--- b/contrib/pg_upgrade_support/pg_upgrade_support.c
***************
*** 30,35 **** PG_MODULE_MAGIC;
--- 30,36 ----
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_array_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_toast_oid;
+ extern PGDLLIMPORT Oid binary_upgrade_next_pg_enum_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_heap_relfilenode;
extern PGDLLIMPORT Oid binary_upgrade_next_toast_relfilenode;
extern PGDLLIMPORT Oid binary_upgrade_next_index_relfilenode;
***************
*** 37,54 **** extern PGDLLIMPORT Oid binary_upgrade_next_index_relfilenode;
Datum set_next_pg_type_oid(PG_FUNCTION_ARGS);
Datum set_next_pg_type_array_oid(PG_FUNCTION_ARGS);
Datum set_next_pg_type_toast_oid(PG_FUNCTION_ARGS);
Datum set_next_heap_relfilenode(PG_FUNCTION_ARGS);
Datum set_next_toast_relfilenode(PG_FUNCTION_ARGS);
Datum set_next_index_relfilenode(PG_FUNCTION_ARGS);
- Datum add_pg_enum_label(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(set_next_pg_type_oid);
PG_FUNCTION_INFO_V1(set_next_pg_type_array_oid);
PG_FUNCTION_INFO_V1(set_next_pg_type_toast_oid);
PG_FUNCTION_INFO_V1(set_next_heap_relfilenode);
PG_FUNCTION_INFO_V1(set_next_toast_relfilenode);
PG_FUNCTION_INFO_V1(set_next_index_relfilenode);
- PG_FUNCTION_INFO_V1(add_pg_enum_label);
Datum
set_next_pg_type_oid(PG_FUNCTION_ARGS)
--- 38,55 ----
Datum set_next_pg_type_oid(PG_FUNCTION_ARGS);
Datum set_next_pg_type_array_oid(PG_FUNCTION_ARGS);
Datum set_next_pg_type_toast_oid(PG_FUNCTION_ARGS);
+ Datum set_next_pg_enum_oid(PG_FUNCTION_ARGS);
Datum set_next_heap_relfilenode(PG_FUNCTION_ARGS);
Datum set_next_toast_relfilenode(PG_FUNCTION_ARGS);
Datum set_next_index_relfilenode(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(set_next_pg_type_oid);
PG_FUNCTION_INFO_V1(set_next_pg_type_array_oid);
PG_FUNCTION_INFO_V1(set_next_pg_type_toast_oid);
+ PG_FUNCTION_INFO_V1(set_next_pg_enum_oid);
PG_FUNCTION_INFO_V1(set_next_heap_relfilenode);
PG_FUNCTION_INFO_V1(set_next_toast_relfilenode);
PG_FUNCTION_INFO_V1(set_next_index_relfilenode);
Datum
set_next_pg_type_oid(PG_FUNCTION_ARGS)
***************
*** 81,86 **** set_next_pg_type_toast_oid(PG_FUNCTION_ARGS)
--- 82,97 ----
}
Datum
+ set_next_pg_enum_oid(PG_FUNCTION_ARGS)
+ {
+ Oid enumoid = PG_GETARG_OID(0);
+
+ binary_upgrade_next_pg_enum_oid = enumoid;
+
+ PG_RETURN_VOID();
+ }
+
+ Datum
set_next_heap_relfilenode(PG_FUNCTION_ARGS)
{
Oid relfilenode = PG_GETARG_OID(0);
***************
*** 110,124 **** set_next_index_relfilenode(PG_FUNCTION_ARGS)
PG_RETURN_VOID();
}
- Datum
- add_pg_enum_label(PG_FUNCTION_ARGS)
- {
- Oid enumoid = PG_GETARG_OID(0);
- Oid typoid = PG_GETARG_OID(1);
- Name label = PG_GETARG_NAME(2);
-
- EnumValuesCreate(typoid, list_make1(makeString(NameStr(*label))),
- enumoid);
-
- PG_RETURN_VOID();
- }
--- 121,123 ----
*** a/doc/src/sgml/ref/alter_type.sgml
--- b/doc/src/sgml/ref/alter_type.sgml
***************
*** 23,33 **** PostgreSQL documentation
<refsynopsisdiv>
<synopsis>
! ALTER TYPE <replaceable class="PARAMETER">name</replaceable> <replaceable class="PARAMETER">action</replaceable> [, ... ]
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> OWNER TO <replaceable class="PARAMETER">new_owner</replaceable>
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> RENAME ATTRIBUTE <replaceable class="PARAMETER">attribute_name</replaceable> TO <replaceable class="PARAMETER">new_attribute_name</replaceable>
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> RENAME TO <replaceable class="PARAMETER">new_name</replaceable>
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> SET SCHEMA <replaceable class="PARAMETER">new_schema</replaceable>
<phrase>where <replaceable class="PARAMETER">action</replaceable> is one of:</phrase>
--- 23,34 ----
<refsynopsisdiv>
<synopsis>
! ALTER TYPE <replaceable class="PARAMETER">name</replaceable> <replaceable class="PARAMETER">action</replaceable> [, ... ]
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> OWNER TO <replaceable class="PARAMETER">new_owner</replaceable>
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> RENAME ATTRIBUTE <replaceable class="PARAMETER">attribute_name</replaceable> TO <replaceable class="PARAMETER">new_attribute_name</replaceable>
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> RENAME TO <replaceable class="PARAMETER">new_name</replaceable>
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> SET SCHEMA <replaceable class="PARAMETER">new_schema</replaceable>
+ ALTER TYPE <replaceable class="PARAMETER">name</replaceable> ADD <replaceable class="PARAMETER">new_enum_value</replaceable> [ BEFORE | AFTER <replaceable class="PARAMETER">existing_enum_value</replaceable>
<phrase>where <replaceable class="PARAMETER">action</replaceable> is one of:</phrase>
***************
*** 103,108 **** ALTER TYPE <replaceable class="PARAMETER">name</replaceable> SET SCHEMA <replace
--- 104,131 ----
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><literal>ADD [ BEFORE | AFTER ]</literal></term>
+ <listitem>
+ <para>
+ This form adds a new value to an enum type. If the new value's place
+ in the sort order is not set using <literal>BEFORE</literal> or
+ <literal>AFTER</literal>, then the new item is placed at the end of
+ the list of values.
+ </para>
+ <note>
+ <para>
+ Adding a new enum value will in some cases lower the comparison and
+ sorting performance of the enum type. This will only usually occur if
+ <literal>BEFORE</literal> or <literal>AFTER</literal> are used to set
+ the new value's sort order position somewhere other than at the end
+ of the list. Optimal performance will be restored if the database is
+ dumped and reloaded.
+ </para>
+ </note>
+ </listitem>
+ </varlistentry>
</variablelist>
</para>
***************
*** 196,201 **** ALTER TYPE <replaceable class="PARAMETER">name</replaceable> SET SCHEMA <replace
--- 219,254 ----
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><replaceable class="PARAMETER">data_type</replaceable></term>
+ <listitem>
+ <para>
+ The data type of the attribute to add, or the new type of the
+ attribute to alter.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="PARAMETER">new_enum_value</replaceable></term>
+ <listitem>
+ <para>
+ The new value to be added to the num type's list of values. Like all
+ enum literals it needs to be quoted.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="PARAMETER">exisiting_enum_value</replaceable></term>
+ <listitem>
+ <para>
+ The neighbour of the new value to be added to the num type's list of values. Like all
+ enum literals it needs to be quoted.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</para>
</refsect1>
***************
*** 232,237 **** ALTER TYPE email SET SCHEMA customers;
--- 285,297 ----
ALTER TYPE compfoo ADD ATTRIBUTE f3 int;
</programlisting>
</para>
+
+ <para>
+ To add a new value to an enum type in a particular sort position:
+ <programlisting>
+ ALTER TYPE colors ADD 'orange' AFTER 'red';
+ </programlisting>
+ </para>
</refsect1>
<refsect1>
*** a/src/backend/catalog/heap.c
--- b/src/backend/catalog/heap.c
***************
*** 855,861 **** AddNewRelationType(const char *typeName,
'x', /* fully TOASTable */
-1, /* typmod */
0, /* array dimensions for typBaseType */
! false); /* Type NOT NULL */
}
/* --------------------------------
--- 855,863 ----
'x', /* fully TOASTable */
-1, /* typmod */
0, /* array dimensions for typBaseType */
! false, /* Type NOT NULL */
! -1, /* no enum labels */
! false); /* type is not an enum, so not sorted */
}
/* --------------------------------
***************
*** 1108,1114 **** heap_create_with_catalog(const char *relname,
'x', /* fully TOASTable */
-1, /* typmod */
0, /* array dimensions for typBaseType */
! false); /* Type NOT NULL */
pfree(relarrayname);
}
--- 1110,1118 ----
'x', /* fully TOASTable */
-1, /* typmod */
0, /* array dimensions for typBaseType */
! false, /* Type NOT NULL */
! -1, /* no enum labels */
! false); /* type is not an enum, so not sorted */
pfree(relarrayname);
}
*** a/src/backend/catalog/pg_enum.c
--- b/src/backend/catalog/pg_enum.c
***************
*** 18,29 ****
--- 18,219 ----
#include "catalog/catalog.h"
#include "catalog/indexing.h"
#include "catalog/pg_enum.h"
+ #include "catalog/pg_type.h"
+ #include "utils/lsyscache.h"
+ #include "utils/syscache.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/rel.h"
#include "utils/tqual.h"
static int oid_cmp(const void *p1, const void *p2);
+ static int sort_order_cmp(const void *p1, const void *p2);
+
+ Oid binary_upgrade_next_pg_enum_oid = InvalidOid;
+
+ /*
+ * AddEnumLabel
+ * Add a new label to the enum set. By default it goes at
+ * the end, but the user can choose to place it before or
+ * after any existing set member.
+ *
+ * Returns true iff the labels are sorted by oid after the addition.
+ */
+
+ bool
+ AddEnumLabel(Oid enumTypeOid,
+ char *newVal,
+ char *neighbour,
+ bool newValIsAfter,
+ int nelems,
+ bool elems_are_sorted)
+ {
+ Oid newOid;
+ Relation pg_enum;
+ TupleDesc tupDesc;
+ Datum values[Natts_pg_enum];
+ bool nulls[Natts_pg_enum];
+ NameData enumlabel;
+ HeapTuple enum_tup;
+ bool result = elems_are_sorted;
+ int newelemorder;
+
+ /* check length of new label is ok */
+ if (strlen(newVal) > (NAMEDATALEN - 1))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_NAME),
+ errmsg("invalid enum label \"%s\"", newVal),
+ errdetail("Labels must be %d characters or less.",
+ NAMEDATALEN - 1)));
+
+ /* get a new OID for the label */
+ pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
+
+ if (OidIsValid(binary_upgrade_next_pg_enum_oid))
+ {
+ if (neighbour != NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("BEFORE/AFTER incompatible with binary upgrade.")));
+
+ newOid = binary_upgrade_next_pg_enum_oid;
+ binary_upgrade_next_pg_enum_oid = InvalidOid;
+ }
+ else
+ {
+ /* non upgrade case */
+ newOid = GetNewOid(pg_enum);
+ }
+
+ if (neighbour == NULL)
+ {
+ /*
+ * Put the new label at the end of the list.
+ * No change to existing tuples is required.
+ */
+ newelemorder = nelems + 1;
+ /* are the elements still sorted? */
+ if (elems_are_sorted)
+ {
+ CatCList *list;
+ int i;
+ bool still_sorted = true;
+
+ list = SearchSysCacheList1(ENUMTYPOIDNAME,
+ ObjectIdGetDatum(enumTypeOid));
+ for (i = 0; i < nelems; i++)
+ {
+ HeapTuple tup = &(list->members[i]->tuple);
+ Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
+
+ if (en->enumsortorder == nelems)
+ {
+ if (HeapTupleGetOid(tup) > newOid)
+ still_sorted = false;
+ break;
+ }
+ }
+ ReleaseCatCacheList(list);
+ if (! still_sorted)
+ result = false;
+ }
+ }
+ else
+ {
+ /* BEFORE or AFTER specified */
+ CatCList *list;
+ int i;
+ HeapTuple *existing;
+ HeapTuple nbr = NULL;
+ Form_pg_enum nbr_en;
+
+ /* get a list of the existing elements and sort them by enumsortorder */
+ list = SearchSysCacheList1(ENUMTYPOIDNAME,
+ ObjectIdGetDatum(enumTypeOid));
+ existing = palloc(nelems * sizeof(HeapTuple));
+
+ for (i = 0; i < nelems; i++)
+ existing[i] = &(list->members[i]->tuple);
+
+ qsort(existing, nelems, sizeof(HeapTuple), sort_order_cmp);
+
+ /* locate the neighbour element */
+ for (i = 0; i < nelems; i++)
+ {
+ Form_pg_enum exists_en;
+ exists_en = (Form_pg_enum) GETSTRUCT(existing[i]);
+ if (strcmp(NameStr(exists_en->enumlabel),neighbour) == 0)
+ nbr = existing[i];
+
+ }
+
+ if (nbr == NULL)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("\"%s\" is not an existing label.", neighbour)));
+ }
+
+ nbr_en = (Form_pg_enum) GETSTRUCT(nbr);
+
+ /*
+ * If BEFORE was specified, the new label goes in the neighbour's
+ * position. Otherwise, it goes in the position after that.
+ */
+ newelemorder = nbr_en->enumsortorder;
+ if (newValIsAfter)
+ newelemorder++;
+
+ /*
+ * Add 1 to the sortorder of all the labels after where the
+ * new label goes. Do it from the end back so we don't get
+ * uniqueness violations.
+ */
+ for (i = nelems - 1; i>= 0; i--)
+ {
+ HeapTuple newtup;
+ Form_pg_enum exst_en = (Form_pg_enum) GETSTRUCT(existing[i]);
+ if (exst_en->enumsortorder < newelemorder)
+ break;
+
+ newtup = heap_copytuple(existing[i]);
+ exst_en = (Form_pg_enum) GETSTRUCT(newtup);
+ exst_en->enumsortorder ++;
+
+ simple_heap_update(pg_enum, &newtup->t_self, newtup);
+
+ CatalogUpdateIndexes(pg_enum, newtup);
+
+ }
+
+ ReleaseCatCacheList(list);
+
+ /* are the labels sorted by OID? */
+ if (result && newelemorder > 1)
+ result = newOid > HeapTupleGetOid(existing[newelemorder-2]);
+ if (result && newelemorder < nelems + 1)
+ result = newOid < HeapTupleGetOid(existing[newelemorder-1]);
+
+ }
+
+ /* set up the new entry */
+ tupDesc = pg_enum->rd_att;
+ memset(nulls, false, sizeof(nulls));
+ values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid);
+ namestrcpy(&enumlabel, newVal);
+ values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel);
+ values[Anum_pg_enum_enumsortorder -1] = Int32GetDatum(newelemorder);
+ enum_tup = heap_form_tuple(tupDesc, values, nulls);
+ HeapTupleSetOid(enum_tup, newOid);
+ simple_heap_insert(pg_enum, enum_tup);
+ CatalogUpdateIndexes(pg_enum, enum_tup);
+ heap_freetuple(enum_tup);
+
+ heap_close(pg_enum, RowExclusiveLock);
+
+ return result;
+
+ }
/*
***************
*** 33,40 **** static int oid_cmp(const void *p1, const void *p2);
* vals is a list of Value strings.
*/
void
! EnumValuesCreate(Oid enumTypeOid, List *vals,
! Oid binary_upgrade_next_pg_enum_oid)
{
Relation pg_enum;
TupleDesc tupDesc;
--- 223,229 ----
* vals is a list of Value strings.
*/
void
! EnumValuesCreate(Oid enumTypeOid, List *vals)
{
Relation pg_enum;
TupleDesc tupDesc;
***************
*** 50,58 **** EnumValuesCreate(Oid enumTypeOid, List *vals,
num_elems = list_length(vals);
/*
! * XXX we do not bother to check the list of values for duplicates --- if
* you have any, you'll get a less-than-friendly unique-index violation.
! * Is it worth trying harder?
*/
pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
--- 239,247 ----
num_elems = list_length(vals);
/*
! * We do not bother to check the list of values for duplicates --- if
* you have any, you'll get a less-than-friendly unique-index violation.
! * It is probably not worth trying harder.
*/
pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
***************
*** 62,96 **** EnumValuesCreate(Oid enumTypeOid, List *vals,
* Allocate oids
*/
oids = (Oid *) palloc(num_elems * sizeof(Oid));
! if (OidIsValid(binary_upgrade_next_pg_enum_oid))
! {
! if (num_elems != 1)
! ereport(ERROR,
! (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("EnumValuesCreate() can only set a single OID")));
! oids[0] = binary_upgrade_next_pg_enum_oid;
! binary_upgrade_next_pg_enum_oid = InvalidOid;
! }
! else
{
/*
! * While this method does not absolutely guarantee that we generate no
! * duplicate oids (since we haven't entered each oid into the table
! * before allocating the next), trouble could only occur if the oid
! * counter wraps all the way around before we finish. Which seems
! * unlikely.
*/
! for (elemno = 0; elemno < num_elems; elemno++)
! {
! /*
! * The pg_enum.oid is stored in user tables. This oid must be
! * preserved by binary upgrades.
! */
! oids[elemno] = GetNewOid(pg_enum);
! }
! /* sort them, just in case counter wrapped from high to low */
! qsort(oids, num_elems, sizeof(Oid), oid_cmp);
}
/* and make the entries */
memset(nulls, false, sizeof(nulls));
--- 251,274 ----
* Allocate oids
*/
oids = (Oid *) palloc(num_elems * sizeof(Oid));
!
! /*
! * While this method does not absolutely guarantee that we generate no
! * duplicate oids (since we haven't entered each oid into the table
! * before allocating the next), trouble could only occur if the oid
! * counter wraps all the way around before we finish. Which seems
! * unlikely.
! */
! for (elemno = 0; elemno < num_elems; elemno++)
{
/*
! * The pg_enum.oid is stored in user tables. This oid must be
! * preserved by binary upgrades.
*/
! oids[elemno] = GetNewOid(pg_enum);
}
+ /* sort them, just in case counter wrapped from high to low */
+ qsort(oids, num_elems, sizeof(Oid), oid_cmp);
/* and make the entries */
memset(nulls, false, sizeof(nulls));
***************
*** 114,119 **** EnumValuesCreate(Oid enumTypeOid, List *vals,
--- 292,298 ----
values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid);
namestrcpy(&enumlabel, lab);
values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel);
+ values[Anum_pg_enum_enumsortorder -1] = Int32GetDatum(elemno+1);
tup = heap_form_tuple(tupDesc, values, nulls);
HeapTupleSetOid(tup, oids[elemno]);
***************
*** 164,170 **** EnumValuesDelete(Oid enumTypeOid)
}
! /* qsort comparison function */
static int
oid_cmp(const void *p1, const void *p2)
{
--- 343,349 ----
}
! /* qsort comparison for oids */
static int
oid_cmp(const void *p1, const void *p2)
{
***************
*** 177,179 **** oid_cmp(const void *p1, const void *p2)
--- 356,371 ----
return 1;
return 0;
}
+
+ /* qsort comparison function for tuples by sort order */
+ static int
+ sort_order_cmp(const void *p1, const void *p2)
+ {
+ HeapTuple v1 = *((const HeapTuple *) p1);
+ HeapTuple v2 = *((const HeapTuple *) p2);
+ Form_pg_enum en1 = (Form_pg_enum) GETSTRUCT(v1);
+ Form_pg_enum en2 = (Form_pg_enum) GETSTRUCT(v2);
+
+ return en1->enumsortorder - en2->enumsortorder;
+ }
+
*** a/src/backend/catalog/pg_type.c
--- b/src/backend/catalog/pg_type.c
***************
*** 112,117 **** TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
--- 112,119 ----
values[i++] = ObjectIdGetDatum(InvalidOid); /* typbasetype */
values[i++] = Int32GetDatum(-1); /* typtypmod */
values[i++] = Int32GetDatum(0); /* typndims */
+ values[i++] = Int32GetDatum(-1); /* typnlabels */
+ values[i++] = BoolGetDatum(false); /* typsorted */
nulls[i++] = true; /* typdefaultbin */
nulls[i++] = true; /* typdefault */
***************
*** 204,210 **** TypeCreate(Oid newTypeOid,
char storage,
int32 typeMod,
int32 typNDims, /* Array dimensions for baseType */
! bool typeNotNull)
{
Relation pg_type_desc;
Oid typeObjectId;
--- 206,214 ----
char storage,
int32 typeMod,
int32 typNDims, /* Array dimensions for baseType */
! bool typeNotNull,
! int32 typeNLabels,
! bool typeSorted)
{
Relation pg_type_desc;
Oid typeObjectId;
***************
*** 342,347 **** TypeCreate(Oid newTypeOid,
--- 346,353 ----
values[i++] = ObjectIdGetDatum(baseType); /* typbasetype */
values[i++] = Int32GetDatum(typeMod); /* typtypmod */
values[i++] = Int32GetDatum(typNDims); /* typndims */
+ values[i++] = Int32GetDatum(typeNLabels); /* typnlabels */
+ values[i++] = BoolGetDatum(typeSorted); /* typsorted */
/*
* initialize the default binary value for this type. Check for nulls of
*** a/src/backend/commands/typecmds.c
--- b/src/backend/commands/typecmds.c
***************
*** 85,96 **** static Oid findTypeTypmodoutFunction(List *procname);
static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid);
static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
static void checkDomainOwner(HeapTuple tup, TypeName *typename);
static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
Oid baseTypeOid,
int typMod, Constraint *constr,
char *domainName);
-
-
/*
* DefineType
* Registers a new base type.
--- 85,95 ----
static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid);
static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
static void checkDomainOwner(HeapTuple tup, TypeName *typename);
+ static void checkEnumOwner(HeapTuple tup, TypeName *typename);
static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
Oid baseTypeOid,
int typMod, Constraint *constr,
char *domainName);
/*
* DefineType
* Registers a new base type.
***************
*** 562,569 **** DefineType(List *names, List *parameters)
storage, /* TOAST strategy */
-1, /* typMod (Domains only) */
0, /* Array Dimensions of typbasetype */
! false); /* Type NOT NULL */
!
/*
* Create the array type that goes with it.
*/
--- 561,569 ----
storage, /* TOAST strategy */
-1, /* typMod (Domains only) */
0, /* Array Dimensions of typbasetype */
! false, /* Type NOT NULL */
! -1, /* no enum labels */
! false); /* type is not an enum, so not sorted */
/*
* Create the array type that goes with it.
*/
***************
*** 601,607 **** DefineType(List *names, List *parameters)
'x', /* ARRAY is always toastable */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
! false); /* Type NOT NULL */
pfree(array_type);
}
--- 601,609 ----
'x', /* ARRAY is always toastable */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
! false, /* Type NOT NULL */
! -1, /* no enum labels */
! false); /* type is not an enum, so not sorted */
pfree(array_type);
}
***************
*** 1044,1050 **** DefineDomain(CreateDomainStmt *stmt)
storage, /* TOAST strategy */
basetypeMod, /* typeMod value */
typNDims, /* Array dimensions for base type */
! typNotNull); /* Type NOT NULL */
/*
* Process constraints which refer to the domain ID returned by TypeCreate
--- 1046,1054 ----
storage, /* TOAST strategy */
basetypeMod, /* typeMod value */
typNDims, /* Array dimensions for base type */
! typNotNull, /* Type NOT NULL */
! -1, /* no enum labels */
! false); /* type is not an enum, so not sorted */
/*
* Process constraints which refer to the domain ID returned by TypeCreate
***************
*** 1094,1099 **** DefineEnum(CreateEnumStmt *stmt)
--- 1098,1106 ----
AclResult aclresult;
Oid old_type_oid;
Oid enumArrayOid;
+ int num_labels;
+
+ num_labels = list_length(stmt->vals);
/* Convert list of names to a name and namespace */
enumNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
***************
*** 1153,1162 **** DefineEnum(CreateEnumStmt *stmt)
'p', /* TOAST strategy always plain */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
! false); /* Type NOT NULL */
/* Enter the enum's values into pg_enum */
! EnumValuesCreate(enumTypeOid, stmt->vals, InvalidOid);
/*
* Create the array type that goes with it.
--- 1160,1171 ----
'p', /* TOAST strategy always plain */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
! false, /* Type NOT NULL */
! num_labels, /* count enum labels */
! true); /* enums always start sorted */
/* Enter the enum's values into pg_enum */
! EnumValuesCreate(enumTypeOid, stmt->vals);
/*
* Create the array type that goes with it.
***************
*** 1192,1202 **** DefineEnum(CreateEnumStmt *stmt)
'x', /* ARRAY is always toastable */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
! false); /* Type NOT NULL */
pfree(enumArrayName);
}
/*
* Find suitable I/O functions for a type.
--- 1201,1288 ----
'x', /* ARRAY is always toastable */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
! false, /* Type NOT NULL */
! -1, /* no enum labels */
! false); /* type is not an enum, so not sorted */
pfree(enumArrayName);
}
+ /*
+ * AlterEnum
+ * Registers a new label for an existing enum.
+ */
+ void
+ AlterEnum (AlterEnumStmt *stmt)
+ {
+ Oid enum_type_oid;
+ TypeName *typename;
+ bool sorted;
+ HeapTuple tup, newtup;
+ Relation rel;
+ Form_pg_type typTup;
+
+ /* Make a TypeName so we can use standard type lookup machinery */
+ typename = makeTypeNameFromNameList(stmt->typeName);
+ enum_type_oid = typenameTypeId(NULL, typename, NULL);
+
+ /* Look up the row in the type table */
+ rel = heap_open(TypeRelationId, RowExclusiveLock);
+
+ tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(enum_type_oid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for type %u", enum_type_oid);
+
+ /* Copy the syscache entry so we can scribble on it below */
+ newtup = heap_copytuple(tup);
+ ReleaseSysCache(tup);
+ tup = newtup;
+ typTup = (Form_pg_type) GETSTRUCT(tup);
+
+ /* Check it's an enum and check user has permission to ALTER the enum */
+ checkEnumOwner(tup, typename);
+
+ /* Add the new label */
+ sorted = AddEnumLabel (enum_type_oid, stmt->newVal,
+ stmt->newValNeighbour, stmt->newValIsAfter,
+ typTup->typnlabels, typTup->typsorted);
+
+ /* Update the row in pg_type */
+ typTup->typnlabels += 1;
+ typTup->typsorted = sorted;
+
+ simple_heap_update(rel, &tup->t_self, tup);
+
+ CatalogUpdateIndexes(rel, tup);
+
+ /* Clean up */
+ heap_close(rel, RowExclusiveLock);
+ }
+
+
+ /*
+ * checkEnumOwner
+ *
+ * Check that the type is actually an enum and that the current user
+ * has permission to do ALTER TYPE on it. Throw an error if not.
+ */
+ static void
+ checkEnumOwner(HeapTuple tup, TypeName *typename)
+ {
+ Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup);
+
+ /* Check that this is actually a domain */
+ if (typTup->typtype != TYPTYPE_ENUM)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not an enum",
+ TypeNameToString(typename))));
+
+ /* Permission check: must own type */
+ if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
+ format_type_be(HeapTupleGetOid(tup)));
+ }
/*
* Find suitable I/O functions for a type.
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 2873,2878 **** _copyCreateEnumStmt(CreateEnumStmt *from)
--- 2873,2891 ----
return newnode;
}
+ static AlterEnumStmt *
+ _copyAlterEnumStmt(AlterEnumStmt *from)
+ {
+ AlterEnumStmt *newnode = makeNode(AlterEnumStmt);
+
+ COPY_NODE_FIELD(typeName);
+ COPY_STRING_FIELD(newVal);
+ COPY_STRING_FIELD(newValNeighbour);
+ COPY_SCALAR_FIELD(newValIsAfter);
+
+ return newnode;
+ }
+
static ViewStmt *
_copyViewStmt(ViewStmt *from)
{
***************
*** 4033,4038 **** copyObject(void *from)
--- 4046,4054 ----
case T_CreateEnumStmt:
retval = _copyCreateEnumStmt(from);
break;
+ case T_AlterEnumStmt:
+ retval = _copyAlterEnumStmt(from);
+ break;
case T_ViewStmt:
retval = _copyViewStmt(from);
break;
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
***************
*** 1390,1395 **** _equalCreateEnumStmt(CreateEnumStmt *a, CreateEnumStmt *b)
--- 1390,1406 ----
}
static bool
+ _equalAlterEnumStmt(AlterEnumStmt *a, AlterEnumStmt *b)
+ {
+ COMPARE_NODE_FIELD(typeName);
+ COMPARE_STRING_FIELD(newVal);
+ COMPARE_STRING_FIELD(newValNeighbour);
+ COMPARE_SCALAR_FIELD(newValIsAfter);
+
+ return true;
+ }
+
+ static bool
_equalViewStmt(ViewStmt *a, ViewStmt *b)
{
COMPARE_NODE_FIELD(view);
***************
*** 2697,2702 **** equal(void *a, void *b)
--- 2708,2716 ----
case T_CreateEnumStmt:
retval = _equalCreateEnumStmt(a, b);
break;
+ case T_AlterEnumStmt:
+ retval = _equalAlterEnumStmt(a, b);
+ break;
case T_ViewStmt:
retval = _equalViewStmt(a, b);
break;
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 182,189 **** static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
}
%type <node> stmt schema_stmt
! AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterFdwStmt
! AlterForeignServerStmt AlterGroupStmt
AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
AlterCompositeTypeStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
AlterRoleStmt AlterRoleSetStmt
--- 182,189 ----
}
%type <node> stmt schema_stmt
! AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt
! AlterFdwStmt AlterForeignServerStmt AlterGroupStmt
AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
AlterCompositeTypeStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
AlterRoleStmt AlterRoleSetStmt
***************
*** 652,657 **** stmt :
--- 652,658 ----
| AlterDatabaseSetStmt
| AlterDefaultPrivilegesStmt
| AlterDomainStmt
+ | AlterEnumStmt
| AlterFdwStmt
| AlterForeignServerStmt
| AlterFunctionStmt
***************
*** 3862,3867 **** enum_val_list: Sconst
--- 3863,3908 ----
{ $$ = lappend($1, makeString($3)); }
;
+ /*****************************************************************************
+ *
+ * ALTER TYPE enumtype ADD ...
+ *
+ *****************************************************************************/
+
+ AlterEnumStmt:
+ ALTER TYPE_P any_name ADD_P Sconst
+ {
+ AlterEnumStmt *n = makeNode(AlterEnumStmt);
+ n->typeName = $3;
+ n->newVal = $5;
+ n->newValNeighbour = NULL;
+ n->newValIsAfter = true;
+
+ $$ = (Node *) n;
+ }
+ | ALTER TYPE_P any_name ADD_P Sconst BEFORE Sconst
+ {
+ AlterEnumStmt *n = makeNode(AlterEnumStmt);
+ n->typeName = $3;
+ n->newVal = $5;
+ n->newValNeighbour = $7;
+ n->newValIsAfter = false;
+
+ $$ = (Node *) n;
+ }
+ | ALTER TYPE_P any_name ADD_P Sconst AFTER Sconst
+ {
+ AlterEnumStmt *n = makeNode(AlterEnumStmt);
+ n->typeName = $3;
+ n->newVal = $5;
+ n->newValNeighbour = $7;
+ n->newValIsAfter = true;
+
+ $$ = (Node *) n;
+ }
+ ;
+
+
/*****************************************************************************
*
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
***************
*** 190,195 **** check_xact_readonly(Node *parsetree)
--- 190,196 ----
case T_CreateTrigStmt:
case T_CompositeTypeStmt:
case T_CreateEnumStmt:
+ case T_AlterEnumStmt:
case T_ViewStmt:
case T_DropCastStmt:
case T_DropStmt:
***************
*** 860,865 **** standard_ProcessUtility(Node *parsetree,
--- 861,870 ----
DefineEnum((CreateEnumStmt *) parsetree);
break;
+ case T_AlterEnumStmt: /* ALTER TYPE (enum) */
+ AlterEnum((AlterEnumStmt *) parsetree);
+ break;
+
case T_ViewStmt: /* CREATE VIEW */
DefineView((ViewStmt *) parsetree, queryString);
break;
***************
*** 1868,1873 **** CreateCommandTag(Node *parsetree)
--- 1873,1882 ----
tag = "CREATE TYPE";
break;
+ case T_AlterEnumStmt:
+ tag = "ALTER TYPE";
+ break;
+
case T_ViewStmt:
tag = "CREATE VIEW";
break;
***************
*** 2410,2415 **** GetCommandLogLevel(Node *parsetree)
--- 2419,2428 ----
lev = LOGSTMT_DDL;
break;
+ case T_AlterEnumStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
case T_ViewStmt:
lev = LOGSTMT_DDL;
break;
*** a/src/backend/utils/adt/enum.c
--- b/src/backend/utils/adt/enum.c
***************
*** 14,19 ****
--- 14,20 ----
#include "postgres.h"
#include "catalog/pg_enum.h"
+ #include "catalog/pg_type.h"
#include "fmgr.h"
#include "utils/array.h"
#include "utils/builtins.h"
***************
*** 22,30 ****
#include "libpq/pqformat.h"
#include "miscadmin.h"
static ArrayType *enum_range_internal(Oid enumtypoid, Oid lower, Oid upper);
! static int enum_elem_cmp(const void *left, const void *right);
/* Basic I/O support */
--- 23,48 ----
#include "libpq/pqformat.h"
#include "miscadmin.h"
+ typedef struct
+ {
+ Oid enum_oid;
+ int32 sort_order;
+ } enum_sort;
+
+ typedef struct
+ {
+ Oid enumtypoid;
+ bool oids_are_sorted;
+ int sort_list_length;
+ int label_count;
+ enum_sort sort_order_list[1];
+ } enum_sort_cache;
+
+
static ArrayType *enum_range_internal(Oid enumtypoid, Oid lower, Oid upper);
! static int enum_sort_cmp(const void *left, const void *right);
! static int enum_oid_cmp(const void *left, const void *right);
/* Basic I/O support */
***************
*** 155,167 **** enum_send(PG_FUNCTION_ARGS)
/* Comparison functions and related */
Datum
enum_lt(PG_FUNCTION_ARGS)
{
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(a < b);
}
Datum
--- 173,347 ----
/* Comparison functions and related */
+
+ /* enum_ccmp is the common engine for all the visible comparison functions */
+
+ static inline int
+ enum_ccmp(Oid arg1, Oid arg2, FunctionCallInfo fcinfo)
+ {
+
+ /*
+ * Keep a cache of type information in fcinfo->flinfo->fn_extra.
+ * If the Oids are sorted, that's all the info we need, and
+ * we can just return the difference of arg1 and arg2.
+ * If not, we keep a list of Oid/sortorder pairs in the cache,
+ * and look up the sortorder by Oid, using a binary search,
+ * and return the difference between the sortorders.
+ *
+ * To keep down the cost of retail comparisons even if
+ * the label set is very large, we start by just putting
+ * Oids we are actually comparing into the cache. If this
+ * grows beyond a handful (10 in fact) it looks like it's a
+ * bulk operation, and so we just fetch the all the labels
+ * and sort them. This keeps down the number of times we
+ * might need to call quicksort.
+ */
+
+ enum_sort_cache * mycache;
+ enum_sort *es1, *es2, srch;
+ int sort1, sort2;
+ bool added = false;
+ HeapTuple enum_tup, type_tup;
+ Form_pg_enum en;
+ Oid typeoid;
+ Form_pg_type typ;
+
+ /*
+ * Fast path return for equal Oids, sorted or not.
+ * This shouldn't happen, but it's here just in case.
+ */
+ if (arg1 == arg2)
+ return 0;
+
+ mycache = (enum_sort_cache *) fcinfo->flinfo->fn_extra;
+
+ /* Initialize the cache if it's not already there */
+ if (mycache == NULL )
+ {
+ enum_tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(arg1));
+ en = (Form_pg_enum) GETSTRUCT(enum_tup);
+ typeoid = en->enumtypid;
+ type_tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeoid));
+ typ = (Form_pg_type) GETSTRUCT(type_tup);
+ if (typ->typtype != 'e')
+ elog(ERROR,"wrong type for oid %u",typeoid);
+ fcinfo->flinfo->fn_extra =
+ MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+ sizeof(enum_sort_cache) +
+ (typ->typnlabels * sizeof(enum_sort)));
+ mycache = (enum_sort_cache *) fcinfo->flinfo->fn_extra;
+ mycache->enumtypoid = typeoid;
+ mycache->sort_list_length = 1;
+ mycache->label_count = typ->typnlabels;
+ mycache->oids_are_sorted = typ->typsorted;
+ mycache->sort_order_list[0].enum_oid = arg1;
+ mycache->sort_order_list[0].sort_order = en->enumsortorder;
+ ReleaseSysCache(type_tup);
+ ReleaseSysCache(enum_tup);
+ }
+
+ /* Fast path return for Oids are sorted case */
+ if (mycache->oids_are_sorted)
+ return arg1 - arg2;
+
+ /* Look up the Oids in the cache */
+ srch.enum_oid = arg1;
+ es1 = bsearch(&srch,
+ mycache->sort_order_list,
+ mycache->sort_list_length,
+ sizeof(enum_sort),
+ enum_oid_cmp);
+ srch.enum_oid = arg2;
+ es2 = bsearch(&srch,
+ mycache->sort_order_list,
+ mycache->sort_list_length,
+ sizeof(enum_sort),
+ enum_oid_cmp);
+
+ /* Handle cache misses, or get the sort order from the search results */
+
+ if (es1 == NULL)
+ {
+
+ enum_tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(arg1));
+ en = (Form_pg_enum) GETSTRUCT(enum_tup);
+ mycache->sort_order_list[mycache->sort_list_length].enum_oid = arg1;
+ sort1 = en->enumsortorder;
+ mycache->sort_order_list[mycache->sort_list_length].sort_order =
+ sort1;
+ ReleaseSysCache(enum_tup);
+ mycache->sort_list_length ++;
+ added = true;
+ }
+ else
+ {
+ /* already in cache */
+ sort1 = es1->sort_order;
+ }
+
+ if (es2 == NULL)
+ {
+
+ enum_tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(arg2));
+ en = (Form_pg_enum) GETSTRUCT(enum_tup);
+ sort2 = en->enumsortorder;
+ mycache->sort_order_list[mycache->sort_list_length].enum_oid = arg2;
+ mycache->sort_order_list[mycache->sort_list_length].sort_order =
+ sort2;
+ ReleaseSysCache(enum_tup);
+ mycache->sort_list_length ++;
+ added = true;
+ }
+ else
+ {
+ /* already in cache */
+ sort2 = es2->sort_order;
+ }
+
+ /* Sort the cache, and possibly complete it, for the next function call */
+ if (added)
+ {
+ /*
+ * If we have more than a handful, just fetch them all, so we limit
+ * the number of sort operations required.
+ */
+ if (mycache->sort_list_length > 10 &&
+ mycache->sort_list_length < mycache->label_count)
+ {
+ CatCList *nlist;
+ int num, i;
+
+ nlist = SearchSysCacheList1(ENUMTYPOIDNAME,
+ ObjectIdGetDatum(mycache->enumtypoid));
+ num = nlist->n_members;
+ for (i = 0; i < num; i++)
+ {
+ HeapTuple tup = &(nlist->members[i]->tuple);
+ Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
+
+ mycache->sort_order_list[i].enum_oid = HeapTupleGetOid(tup);
+ mycache->sort_order_list[i].sort_order = en->enumsortorder;
+ }
+ mycache->sort_list_length = mycache->label_count;
+
+ ReleaseCatCacheList(nlist);
+ }
+
+ qsort(mycache->sort_order_list,mycache->sort_list_length,
+ sizeof(enum_sort),enum_oid_cmp);
+ }
+
+ /* and we're done */
+ return sort1 - sort2;
+ }
+
Datum
enum_lt(PG_FUNCTION_ARGS)
{
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(enum_ccmp(a,b,fcinfo) < 0);
}
Datum
***************
*** 170,176 **** enum_le(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(a <= b);
}
Datum
--- 350,356 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(enum_ccmp(a,b,fcinfo) <= 0);
}
Datum
***************
*** 197,203 **** enum_ge(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(a >= b);
}
Datum
--- 377,383 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(enum_ccmp(a,b,fcinfo) >= 0);
}
Datum
***************
*** 206,212 **** enum_gt(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(a > b);
}
Datum
--- 386,392 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(enum_ccmp(a,b,fcinfo) > 0);
}
Datum
***************
*** 215,221 **** enum_smaller(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_OID(a <= b ? a : b);
}
Datum
--- 395,401 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_OID(enum_ccmp(a,b,fcinfo) < 0 ? a : b);
}
Datum
***************
*** 224,230 **** enum_larger(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_OID(a >= b ? a : b);
}
Datum
--- 404,410 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_OID(enum_ccmp(a,b,fcinfo) > 0 ? a : b);
}
Datum
***************
*** 233,242 **** enum_cmp(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! if (a > b)
! PG_RETURN_INT32(1);
! else if (a == b)
PG_RETURN_INT32(0);
else
PG_RETURN_INT32(-1);
}
--- 413,422 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! if (a == b)
PG_RETURN_INT32(0);
+ else if (enum_ccmp(a,b,fcinfo) > 0)
+ PG_RETURN_INT32(1);
else
PG_RETURN_INT32(-1);
}
***************
*** 248,253 **** enum_first(PG_FUNCTION_ARGS)
--- 428,434 ----
{
Oid enumtypoid;
Oid min = InvalidOid;
+ int min_sort = -1; /* value will never in fact be used */
CatCList *list;
int num,
i;
***************
*** 267,276 **** enum_first(PG_FUNCTION_ARGS)
num = list->n_members;
for (i = 0; i < num; i++)
{
! Oid valoid = HeapTupleHeaderGetOid(list->members[i]->tuple.t_data);
!
! if (!OidIsValid(min) || valoid < min)
! min = valoid;
}
ReleaseCatCacheList(list);
--- 448,461 ----
num = list->n_members;
for (i = 0; i < num; i++)
{
! HeapTuple tup = &(list->members[i]->tuple);
! Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
!
! if (!OidIsValid(min) || en->enumsortorder < min_sort)
! {
! min = HeapTupleGetOid(tup);
! min_sort = en->enumsortorder;
! }
}
ReleaseCatCacheList(list);
***************
*** 287,292 **** enum_last(PG_FUNCTION_ARGS)
--- 472,478 ----
{
Oid enumtypoid;
Oid max = InvalidOid;
+ int max_sort = -1; /* value will never in fact be used */
CatCList *list;
int num,
i;
***************
*** 306,315 **** enum_last(PG_FUNCTION_ARGS)
num = list->n_members;
for (i = 0; i < num; i++)
{
! Oid valoid = HeapTupleHeaderGetOid(list->members[i]->tuple.t_data);
!
! if (!OidIsValid(max) || valoid > max)
! max = valoid;
}
ReleaseCatCacheList(list);
--- 492,505 ----
num = list->n_members;
for (i = 0; i < num; i++)
{
! HeapTuple tup = &(list->members[i]->tuple);
! Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
!
! if (!OidIsValid(max) || en->enumsortorder > max_sort)
! {
! max = HeapTupleGetOid(tup);
! max_sort = en->enumsortorder;
! }
}
ReleaseCatCacheList(list);
***************
*** 382,427 **** enum_range_internal(Oid enumtypoid, Oid lower, Oid upper)
i,
j;
Datum *elems;
list = SearchSysCacheList1(ENUMTYPOIDNAME, ObjectIdGetDatum(enumtypoid));
total = list->n_members;
elems = (Datum *) palloc(total * sizeof(Datum));
- j = 0;
for (i = 0; i < total; i++)
{
! Oid val = HeapTupleGetOid(&(list->members[i]->tuple));
- if ((!OidIsValid(lower) || lower <= val) &&
- (!OidIsValid(upper) || val <= upper))
- elems[j++] = ObjectIdGetDatum(val);
}
/* shouldn't need the cache anymore */
ReleaseCatCacheList(list);
! /* sort results into OID order */
! qsort(elems, j, sizeof(Datum), enum_elem_cmp);
/* note this hardwires some details about the representation of Oid */
result = construct_array(elems, j, enumtypoid, sizeof(Oid), true, 'i');
pfree(elems);
return result;
}
! /* qsort comparison function for Datums that are OIDs */
static int
! enum_elem_cmp(const void *left, const void *right)
{
! Oid l = DatumGetObjectId(*((const Datum *) left));
! Oid r = DatumGetObjectId(*((const Datum *) right));
!
! if (l < r)
! return -1;
! if (l > r)
! return 1;
! return 0;
}
--- 572,647 ----
i,
j;
Datum *elems;
+ enum_sort *sort_items;
+ bool left_found;
list = SearchSysCacheList1(ENUMTYPOIDNAME, ObjectIdGetDatum(enumtypoid));
total = list->n_members;
elems = (Datum *) palloc(total * sizeof(Datum));
+ sort_items = (enum_sort *) palloc(total * sizeof(enum_sort));
for (i = 0; i < total; i++)
{
! HeapTuple tup = &(list->members[i]->tuple);
! Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
!
! sort_items[i].enum_oid = HeapTupleGetOid(tup);
! sort_items[i].sort_order = en->enumsortorder;
}
/* shouldn't need the cache anymore */
ReleaseCatCacheList(list);
! /* sort results into sort_order sequence */
! qsort(sort_items, total, sizeof(enum_sort), enum_sort_cmp);
!
! j = 0;
! left_found = !OidIsValid(lower);
! for (i=0; i < total; i++)
! {
! if (! left_found && lower == sort_items[i].enum_oid)
! left_found = true;
!
! if (left_found)
! elems[j++] = ObjectIdGetDatum(sort_items[i].enum_oid);
!
! if (OidIsValid(upper) && upper == sort_items[i].enum_oid)
! break;
! }
/* note this hardwires some details about the representation of Oid */
result = construct_array(elems, j, enumtypoid, sizeof(Oid), true, 'i');
pfree(elems);
+ pfree(sort_items);
return result;
}
! /*
! * qsort comparison using sort order, for range routines
! */
static int
! enum_sort_cmp(const void *left, const void *right)
{
! enum_sort *l = (enum_sort *) left;
! enum_sort *r = (enum_sort *) right;
!
! return l->sort_order - r->sort_order;
}
+
+ /*
+ * qsort comparison using OID order for comparison search cache
+ */
+ static int
+ enum_oid_cmp(const void *es1, const void *es2)
+ {
+ enum_sort *p1, *p2;
+ p1 = (enum_sort *)es1;
+ p2 = (enum_sort *)es2;
+ return p1->enum_oid - p2->enum_oid;
+ }
+
+
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
***************
*** 6653,6669 **** dumpEnumType(Archive *fout, TypeInfo *tyinfo)
PQExpBuffer query = createPQExpBuffer();
PGresult *res;
int num,
! i;
Oid enum_oid;
char *label;
/* Set proper schema search path so regproc references list correctly */
selectSourceSchema(tyinfo->dobj.namespace->dobj.name);
! appendPQExpBuffer(query, "SELECT oid, enumlabel "
! "FROM pg_catalog.pg_enum "
! "WHERE enumtypid = '%u'"
! "ORDER BY oid",
tyinfo->dobj.catId.oid);
res = PQexec(g_conn, query->data);
--- 6653,6676 ----
PQExpBuffer query = createPQExpBuffer();
PGresult *res;
int num,
! i;
Oid enum_oid;
char *label;
/* Set proper schema search path so regproc references list correctly */
selectSourceSchema(tyinfo->dobj.namespace->dobj.name);
! if (fout->remoteVersion > 90000)
! appendPQExpBuffer(query, "SELECT oid, enumlabel "
! "FROM pg_catalog.pg_enum "
! "WHERE enumtypid = '%u'"
! "ORDER BY enumsortorder",
! tyinfo->dobj.catId.oid);
! else
! appendPQExpBuffer(query, "SELECT oid, enumlabel "
! "FROM pg_catalog.pg_enum "
! "WHERE enumtypid = '%u'"
! "ORDER BY oid",
tyinfo->dobj.catId.oid);
res = PQexec(g_conn, query->data);
***************
*** 6709,6725 **** dumpEnumType(Archive *fout, TypeInfo *tyinfo)
{
enum_oid = atooid(PQgetvalue(res, i, PQfnumber(res, "oid")));
label = PQgetvalue(res, i, PQfnumber(res, "enumlabel"));
!
if (i == 0)
appendPQExpBuffer(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
appendPQExpBuffer(q,
! "SELECT binary_upgrade.add_pg_enum_label('%u'::pg_catalog.oid, "
! "'%u'::pg_catalog.oid, ",
! enum_oid, tyinfo->dobj.catId.oid);
! appendStringLiteralAH(q, label, fout);
! appendPQExpBuffer(q, ");\n");
}
- appendPQExpBuffer(q, "\n");
}
ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
--- 6716,6734 ----
{
enum_oid = atooid(PQgetvalue(res, i, PQfnumber(res, "oid")));
label = PQgetvalue(res, i, PQfnumber(res, "enumlabel"));
!
if (i == 0)
appendPQExpBuffer(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
appendPQExpBuffer(q,
! "SELECT binary_upgrade.set_next_pg_enum_oid('%u'::pg_catalog.oid);\n",
! enum_oid);
! appendPQExpBuffer(q, "ALTER TYPE %s.",
! fmtId(tyinfo->dobj.namespace->dobj.name));
! appendPQExpBuffer(q, "%s ADD ",
! fmtId(tyinfo->dobj.name));
! appendStringLiteralAH(q, label, fout);
! appendPQExpBuffer(q, ";\n\n");
}
}
ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
*** a/src/bin/psql/describe.c
--- b/src/bin/psql/describe.c
***************
*** 473,489 **** describeTypes(const char *pattern, bool verbose, bool showSystem)
gettext_noop("Internal name"),
gettext_noop("Size"));
if (verbose && pset.sversion >= 80300)
appendPQExpBuffer(&buf,
" pg_catalog.array_to_string(\n"
" ARRAY(\n"
" SELECT e.enumlabel\n"
" FROM pg_catalog.pg_enum e\n"
! " WHERE e.enumtypid = t.oid\n"
! " ORDER BY e.oid\n"
" ),\n"
" E'\\n'\n"
" ) AS \"%s\",\n",
gettext_noop("Elements"));
appendPQExpBuffer(&buf,
" pg_catalog.obj_description(t.oid, 'pg_type') as \"%s\"\n",
--- 473,499 ----
gettext_noop("Internal name"),
gettext_noop("Size"));
if (verbose && pset.sversion >= 80300)
+ {
appendPQExpBuffer(&buf,
" pg_catalog.array_to_string(\n"
" ARRAY(\n"
" SELECT e.enumlabel\n"
" FROM pg_catalog.pg_enum e\n"
! " WHERE e.enumtypid = t.oid\n");
!
! if (pset.sversion >= 90100 )
! appendPQExpBuffer(&buf,
! " ORDER BY e.enumsortorder\n");
! else
! appendPQExpBuffer(&buf,
! " ORDER BY e.oid\n");
!
! appendPQExpBuffer(&buf,
" ),\n"
" E'\\n'\n"
" ) AS \"%s\",\n",
gettext_noop("Elements"));
+ }
appendPQExpBuffer(&buf,
" pg_catalog.obj_description(t.oid, 'pg_type') as \"%s\"\n",
*** a/src/include/catalog/indexing.h
--- b/src/include/catalog/indexing.h
***************
*** 147,152 **** DECLARE_UNIQUE_INDEX(pg_enum_oid_index, 3502, on pg_enum using btree(oid oid_ops
--- 147,154 ----
#define EnumOidIndexId 3502
DECLARE_UNIQUE_INDEX(pg_enum_typid_label_index, 3503, on pg_enum using btree(enumtypid oid_ops, enumlabel name_ops));
#define EnumTypIdLabelIndexId 3503
+ DECLARE_UNIQUE_INDEX(pg_enum_typid_sortorder_index, 3539, on pg_enum using btree(enumtypid oid_ops, enumsortorder int4_ops));
+ #define EnumTypIdSortOrderIndexId 3539
/* This following index is not used for a cache and is not unique */
DECLARE_INDEX(pg_index_indrelid_index, 2678, on pg_index using btree(indrelid oid_ops));
*** a/src/include/catalog/pg_class.h
--- b/src/include/catalog/pg_class.h
***************
*** 132,138 **** typedef FormData_pg_class *Form_pg_class;
*/
/* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId */
! DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f f r 28 0 t f f f f f 3 _null_ _null_ ));
DESCR("");
DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f f r 19 0 f f f f f f 3 _null_ _null_ ));
DESCR("");
--- 132,138 ----
*/
/* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId */
! DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f f r 30 0 t f f f f f 3 _null_ _null_ ));
DESCR("");
DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f f r 19 0 f f f f f f 3 _null_ _null_ ));
DESCR("");
*** a/src/include/catalog/pg_enum.h
--- b/src/include/catalog/pg_enum.h
***************
*** 35,40 **** CATALOG(pg_enum,3501)
--- 35,41 ----
{
Oid enumtypid; /* OID of owning enum type */
NameData enumlabel; /* text representation of enum value */
+ int4 enumsortorder; /* sort order for this enum label */
} FormData_pg_enum;
/* ----------------
***************
*** 48,56 **** typedef FormData_pg_enum *Form_pg_enum;
* compiler constants for pg_enum
* ----------------
*/
! #define Natts_pg_enum 2
#define Anum_pg_enum_enumtypid 1
#define Anum_pg_enum_enumlabel 2
/* ----------------
* pg_enum has no initial contents
--- 49,58 ----
* compiler constants for pg_enum
* ----------------
*/
! #define Natts_pg_enum 3
#define Anum_pg_enum_enumtypid 1
#define Anum_pg_enum_enumlabel 2
+ #define Anum_pg_enum_enumsortorder 3
/* ----------------
* pg_enum has no initial contents
***************
*** 60,67 **** typedef FormData_pg_enum *Form_pg_enum;
/*
* prototypes for functions in pg_enum.c
*/
! extern void EnumValuesCreate(Oid enumTypeOid, List *vals,
! Oid binary_upgrade_next_pg_enum_oid);
extern void EnumValuesDelete(Oid enumTypeOid);
#endif /* PG_ENUM_H */
--- 62,70 ----
/*
* prototypes for functions in pg_enum.c
*/
! extern void EnumValuesCreate(Oid enumTypeOid, List *vals);
extern void EnumValuesDelete(Oid enumTypeOid);
+ extern bool AddEnumLabel(Oid enumTypeOid, char *newVal, char *neighbour,
+ bool newValIsAfter, int nelems, bool elems_are_sorted);
#endif /* PG_ENUM_H */
*** a/src/include/catalog/pg_type.h
--- b/src/include/catalog/pg_type.h
***************
*** 194,199 **** CATALOG(pg_type,1247) BKI_BOOTSTRAP BKI_ROWTYPE_OID(71) BKI_SCHEMA_MACRO
--- 194,211 ----
*/
int4 typndims;
+ /*
+ * typnlabels, contains a count on the number of labels an enum type has,
+ * -1 for a non-enum type.
+ */
+ int4 typnlabels;
+
+ /*
+ * typsorted is true if the oids of an enum type reflect the type's sort
+ * order, false otherwise including for a non-enum type.
+ */
+ bool typsorted;
+
/*
* If typdefaultbin is not NULL, it is the nodeToString representation of
* a default expression for the type. Currently this is only used for
***************
*** 224,230 **** typedef FormData_pg_type *Form_pg_type;
* compiler constants for pg_type
* ----------------
*/
! #define Natts_pg_type 28
#define Anum_pg_type_typname 1
#define Anum_pg_type_typnamespace 2
#define Anum_pg_type_typowner 3
--- 236,242 ----
* compiler constants for pg_type
* ----------------
*/
! #define Natts_pg_type 30
#define Anum_pg_type_typname 1
#define Anum_pg_type_typnamespace 2
#define Anum_pg_type_typowner 3
***************
*** 251,258 **** typedef FormData_pg_type *Form_pg_type;
#define Anum_pg_type_typbasetype 24
#define Anum_pg_type_typtypmod 25
#define Anum_pg_type_typndims 26
! #define Anum_pg_type_typdefaultbin 27
! #define Anum_pg_type_typdefault 28
/* ----------------
--- 263,272 ----
#define Anum_pg_type_typbasetype 24
#define Anum_pg_type_typtypmod 25
#define Anum_pg_type_typndims 26
! #define Anum_pg_type_typnlabels 27
! #define Anum_pg_type_typsorted 28
! #define Anum_pg_type_typdefaultbin 29
! #define Anum_pg_type_typdefault 30
/* ----------------
***************
*** 269,355 **** typedef FormData_pg_type *Form_pg_type;
*/
/* OIDS 1 - 99 */
! DATA(insert OID = 16 ( bool PGNSP PGUID 1 t b B t t \054 0 0 1000 boolin boolout boolrecv boolsend - - - c p f 0 -1 0 _null_ _null_ ));
DESCR("boolean, 'true'/'false'");
#define BOOLOID 16
! DATA(insert OID = 17 ( bytea PGNSP PGUID -1 f b U f t \054 0 0 1001 byteain byteaout bytearecv byteasend - - - i x f 0 -1 0 _null_ _null_ ));
DESCR("variable-length string, binary values escaped");
#define BYTEAOID 17
! DATA(insert OID = 18 ( char PGNSP PGUID 1 t b S f t \054 0 0 1002 charin charout charrecv charsend - - - c p f 0 -1 0 _null_ _null_ ));
DESCR("single character");
#define CHAROID 18
! DATA(insert OID = 19 ( name PGNSP PGUID NAMEDATALEN f b S f t \054 0 18 1003 namein nameout namerecv namesend - - - c p f 0 -1 0 _null_ _null_ ));
DESCR("63-character type for storing system identifiers");
#define NAMEOID 19
! DATA(insert OID = 20 ( int8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 1016 int8in int8out int8recv int8send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("~18 digit integer, 8-byte storage");
#define INT8OID 20
! DATA(insert OID = 21 ( int2 PGNSP PGUID 2 t b N f t \054 0 0 1005 int2in int2out int2recv int2send - - - s p f 0 -1 0 _null_ _null_ ));
DESCR("-32 thousand to 32 thousand, 2-byte storage");
#define INT2OID 21
! DATA(insert OID = 22 ( int2vector PGNSP PGUID -1 f b A f t \054 0 21 1006 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("array of int2, used in system tables");
#define INT2VECTOROID 22
! DATA(insert OID = 23 ( int4 PGNSP PGUID 4 t b N f t \054 0 0 1007 int4in int4out int4recv int4send - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("-2 billion to 2 billion integer, 4-byte storage");
#define INT4OID 23
! DATA(insert OID = 24 ( regproc PGNSP PGUID 4 t b N f t \054 0 0 1008 regprocin regprocout regprocrecv regprocsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered procedure");
#define REGPROCOID 24
! DATA(insert OID = 25 ( text PGNSP PGUID -1 f b S t t \054 0 0 1009 textin textout textrecv textsend - - - i x f 0 -1 0 _null_ _null_ ));
DESCR("variable-length string, no limit specified");
#define TEXTOID 25
! DATA(insert OID = 26 ( oid PGNSP PGUID 4 t b N t t \054 0 0 1028 oidin oidout oidrecv oidsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("object identifier(oid), maximum 4 billion");
#define OIDOID 26
! DATA(insert OID = 27 ( tid PGNSP PGUID 6 f b U f t \054 0 0 1010 tidin tidout tidrecv tidsend - - - s p f 0 -1 0 _null_ _null_ ));
DESCR("(block, offset), physical location of tuple");
#define TIDOID 27
! DATA(insert OID = 28 ( xid PGNSP PGUID 4 t b U f t \054 0 0 1011 xidin xidout xidrecv xidsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("transaction id");
#define XIDOID 28
! DATA(insert OID = 29 ( cid PGNSP PGUID 4 t b U f t \054 0 0 1012 cidin cidout cidrecv cidsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("command identifier type, sequence in transaction id");
#define CIDOID 29
! DATA(insert OID = 30 ( oidvector PGNSP PGUID -1 f b A f t \054 0 26 1013 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("array of oids, used in system tables");
#define OIDVECTOROID 30
/* hand-built rowtype entries for bootstrapped catalogs */
/* NB: OIDs assigned here must match the BKI_ROWTYPE_OID declarations */
! DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c C f t \054 1247 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c C f t \054 1249 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c C f t \054 1255 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c C f t \054 1259 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
/* OIDS 100 - 199 */
! DATA(insert OID = 142 ( xml PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 _null_ _null_ ));
DESCR("XML content");
#define XMLOID 142
! DATA(insert OID = 143 ( _xml PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 194 ( pg_node_tree PGNSP PGUID -1 f b S f t \054 0 0 0 pg_node_tree_in pg_node_tree_out pg_node_tree_recv pg_node_tree_send - - - i x f 0 -1 0 _null_ _null_ ));
DESCR("string representing an internal node tree");
#define PGNODETREEOID 194
/* OIDS 200 - 299 */
! DATA(insert OID = 210 ( smgr PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f 0 -1 0 _null_ _null_ ));
DESCR("storage manager");
/* OIDS 300 - 399 */
--- 283,369 ----
*/
/* OIDS 1 - 99 */
! DATA(insert OID = 16 ( bool PGNSP PGUID 1 t b B t t \054 0 0 1000 boolin boolout boolrecv boolsend - - - c p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("boolean, 'true'/'false'");
#define BOOLOID 16
! DATA(insert OID = 17 ( bytea PGNSP PGUID -1 f b U f t \054 0 0 1001 byteain byteaout bytearecv byteasend - - - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("variable-length string, binary values escaped");
#define BYTEAOID 17
! DATA(insert OID = 18 ( char PGNSP PGUID 1 t b S f t \054 0 0 1002 charin charout charrecv charsend - - - c p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("single character");
#define CHAROID 18
! DATA(insert OID = 19 ( name PGNSP PGUID NAMEDATALEN f b S f t \054 0 18 1003 namein nameout namerecv namesend - - - c p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("63-character type for storing system identifiers");
#define NAMEOID 19
! DATA(insert OID = 20 ( int8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 1016 int8in int8out int8recv int8send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("~18 digit integer, 8-byte storage");
#define INT8OID 20
! DATA(insert OID = 21 ( int2 PGNSP PGUID 2 t b N f t \054 0 0 1005 int2in int2out int2recv int2send - - - s p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("-32 thousand to 32 thousand, 2-byte storage");
#define INT2OID 21
! DATA(insert OID = 22 ( int2vector PGNSP PGUID -1 f b A f t \054 0 21 1006 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("array of int2, used in system tables");
#define INT2VECTOROID 22
! DATA(insert OID = 23 ( int4 PGNSP PGUID 4 t b N f t \054 0 0 1007 int4in int4out int4recv int4send - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("-2 billion to 2 billion integer, 4-byte storage");
#define INT4OID 23
! DATA(insert OID = 24 ( regproc PGNSP PGUID 4 t b N f t \054 0 0 1008 regprocin regprocout regprocrecv regprocsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered procedure");
#define REGPROCOID 24
! DATA(insert OID = 25 ( text PGNSP PGUID -1 f b S t t \054 0 0 1009 textin textout textrecv textsend - - - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("variable-length string, no limit specified");
#define TEXTOID 25
! DATA(insert OID = 26 ( oid PGNSP PGUID 4 t b N t t \054 0 0 1028 oidin oidout oidrecv oidsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("object identifier(oid), maximum 4 billion");
#define OIDOID 26
! DATA(insert OID = 27 ( tid PGNSP PGUID 6 f b U f t \054 0 0 1010 tidin tidout tidrecv tidsend - - - s p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("(block, offset), physical location of tuple");
#define TIDOID 27
! DATA(insert OID = 28 ( xid PGNSP PGUID 4 t b U f t \054 0 0 1011 xidin xidout xidrecv xidsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("transaction id");
#define XIDOID 28
! DATA(insert OID = 29 ( cid PGNSP PGUID 4 t b U f t \054 0 0 1012 cidin cidout cidrecv cidsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("command identifier type, sequence in transaction id");
#define CIDOID 29
! DATA(insert OID = 30 ( oidvector PGNSP PGUID -1 f b A f t \054 0 26 1013 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("array of oids, used in system tables");
#define OIDVECTOROID 30
/* hand-built rowtype entries for bootstrapped catalogs */
/* NB: OIDs assigned here must match the BKI_ROWTYPE_OID declarations */
! DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c C f t \054 1247 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c C f t \054 1249 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c C f t \054 1255 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c C f t \054 1259 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
/* OIDS 100 - 199 */
! DATA(insert OID = 142 ( xml PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("XML content");
#define XMLOID 142
! DATA(insert OID = 143 ( _xml PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 194 ( pg_node_tree PGNSP PGUID -1 f b S f t \054 0 0 0 pg_node_tree_in pg_node_tree_out pg_node_tree_recv pg_node_tree_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("string representing an internal node tree");
#define PGNODETREEOID 194
/* OIDS 200 - 299 */
! DATA(insert OID = 210 ( smgr PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("storage manager");
/* OIDS 300 - 399 */
***************
*** 359,589 **** DESCR("storage manager");
/* OIDS 500 - 599 */
/* OIDS 600 - 699 */
! DATA(insert OID = 600 ( point PGNSP PGUID 16 f b G f t \054 0 701 1017 point_in point_out point_recv point_send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("geometric point '(x, y)'");
#define POINTOID 600
! DATA(insert OID = 601 ( lseg PGNSP PGUID 32 f b G f t \054 0 600 1018 lseg_in lseg_out lseg_recv lseg_send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("geometric line segment '(pt1,pt2)'");
#define LSEGOID 601
! DATA(insert OID = 602 ( path PGNSP PGUID -1 f b G f t \054 0 0 1019 path_in path_out path_recv path_send - - - d x f 0 -1 0 _null_ _null_ ));
DESCR("geometric path '(pt1,...)'");
#define PATHOID 602
! DATA(insert OID = 603 ( box PGNSP PGUID 32 f b G f t \073 0 600 1020 box_in box_out box_recv box_send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("geometric box '(lower left,upper right)'");
#define BOXOID 603
! DATA(insert OID = 604 ( polygon PGNSP PGUID -1 f b G f t \054 0 0 1027 poly_in poly_out poly_recv poly_send - - - d x f 0 -1 0 _null_ _null_ ));
DESCR("geometric polygon '(pt1,...)'");
#define POLYGONOID 604
! DATA(insert OID = 628 ( line PGNSP PGUID 32 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("geometric line (not implemented)");
#define LINEOID 628
! DATA(insert OID = 629 ( _line PGNSP PGUID -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
DESCR("");
/* OIDS 700 - 799 */
! DATA(insert OID = 700 ( float4 PGNSP PGUID 4 FLOAT4PASSBYVAL b N f t \054 0 0 1021 float4in float4out float4recv float4send - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("single-precision floating point number, 4-byte storage");
#define FLOAT4OID 700
! DATA(insert OID = 701 ( float8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N t t \054 0 0 1022 float8in float8out float8recv float8send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("double-precision floating point number, 8-byte storage");
#define FLOAT8OID 701
! DATA(insert OID = 702 ( abstime PGNSP PGUID 4 t b D f t \054 0 0 1023 abstimein abstimeout abstimerecv abstimesend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("absolute, limited-range date and time (Unix system time)");
#define ABSTIMEOID 702
! DATA(insert OID = 703 ( reltime PGNSP PGUID 4 t b T f t \054 0 0 1024 reltimein reltimeout reltimerecv reltimesend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("relative, limited-range time interval (Unix delta time)");
#define RELTIMEOID 703
! DATA(insert OID = 704 ( tinterval PGNSP PGUID 12 f b T f t \054 0 0 1025 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("(abstime,abstime), time interval");
#define TINTERVALOID 704
! DATA(insert OID = 705 ( unknown PGNSP PGUID -2 f b X f t \054 0 0 0 unknownin unknownout unknownrecv unknownsend - - - c p f 0 -1 0 _null_ _null_ ));
DESCR("");
#define UNKNOWNOID 705
! DATA(insert OID = 718 ( circle PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("geometric circle '(center,radius)'");
#define CIRCLEOID 718
! DATA(insert OID = 719 ( _circle PGNSP PGUID -1 f b A f t \054 0 718 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 790 ( money PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("monetary amounts, $d,ddd.cc");
#define CASHOID 790
! DATA(insert OID = 791 ( _money PGNSP PGUID -1 f b A f t \054 0 790 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
/* OIDS 800 - 899 */
! DATA(insert OID = 829 ( macaddr PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("XX:XX:XX:XX:XX:XX, MAC address");
#define MACADDROID 829
! DATA(insert OID = 869 ( inet PGNSP PGUID -1 f b I t t \054 0 0 1041 inet_in inet_out inet_recv inet_send - - - i m f 0 -1 0 _null_ _null_ ));
DESCR("IP address/netmask, host address, netmask optional");
#define INETOID 869
! DATA(insert OID = 650 ( cidr PGNSP PGUID -1 f b I f t \054 0 0 651 cidr_in cidr_out cidr_recv cidr_send - - - i m f 0 -1 0 _null_ _null_ ));
DESCR("network IP address/netmask, network address");
#define CIDROID 650
/* OIDS 900 - 999 */
/* OIDS 1000 - 1099 */
! DATA(insert OID = 1000 ( _bool PGNSP PGUID -1 f b A f t \054 0 16 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1001 ( _bytea PGNSP PGUID -1 f b A f t \054 0 17 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1002 ( _char PGNSP PGUID -1 f b A f t \054 0 18 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b A f t \054 0 19 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b A f t \054 0 21 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b A f t \054 0 22 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b A f t \054 0 23 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
#define INT4ARRAYOID 1007
! DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b A f t \054 0 24 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b A f t \054 0 25 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
#define TEXTARRAYOID 1009
! DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b A f t \054 0 26 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b A f t \054 0 27 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b A f t \054 0 28 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b A f t \054 0 29 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1013 ( _oidvector PGNSP PGUID -1 f b A f t \054 0 30 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1014 ( _bpchar PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1015 ( _varchar PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1016 ( _int8 PGNSP PGUID -1 f b A f t \054 0 20 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1017 ( _point PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1018 ( _lseg PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1019 ( _path PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1020 ( _box PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1021 ( _float4 PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
#define FLOAT4ARRAYOID 1021
! DATA(insert OID = 1022 ( _float8 PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1033 ( aclitem PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("access control list");
#define ACLITEMOID 1033
! DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1263 ( _cstring PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
#define CSTRINGARRAYOID 1263
! DATA(insert OID = 1042 ( bpchar PGNSP PGUID -1 f b S f t \054 0 0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 _null_ _null_ ));
DESCR("char(length), blank-padded string, fixed storage length");
#define BPCHAROID 1042
! DATA(insert OID = 1043 ( varchar PGNSP PGUID -1 f b S f t \054 0 0 1015 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f 0 -1 0 _null_ _null_ ));
DESCR("varchar(length), non-blank-padded string, variable storage length");
#define VARCHAROID 1043
! DATA(insert OID = 1082 ( date PGNSP PGUID 4 t b D f t \054 0 0 1182 date_in date_out date_recv date_send - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("date");
#define DATEOID 1082
! DATA(insert OID = 1083 ( time PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1183 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f 0 -1 0 _null_ _null_ ));
DESCR("time of day");
#define TIMEOID 1083
/* OIDS 1100 - 1199 */
! DATA(insert OID = 1114 ( timestamp PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 _null_ _null_ ));
DESCR("date and time");
#define TIMESTAMPOID 1114
! DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1182 ( _date PGNSP PGUID -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1183 ( _time PGNSP PGUID -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1184 ( timestamptz PGNSP PGUID 8 FLOAT8PASSBYVAL b D t t \054 0 0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 _null_ _null_ ));
DESCR("date and time with time zone");
#define TIMESTAMPTZOID 1184
! DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0 1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1186 ( interval PGNSP PGUID 16 f b T t t \054 0 0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 _null_ _null_ ));
DESCR("@ <number> <units>, time interval");
#define INTERVALOID 1186
! DATA(insert OID = 1187 ( _interval PGNSP PGUID -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout - d x f 0 -1 0 _null_ _null_ ));
/* OIDS 1200 - 1299 */
! DATA(insert OID = 1231 ( _numeric PGNSP PGUID -1 f b A f t \054 0 1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1266 ( timetz PGNSP PGUID 12 f b D f t \054 0 0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 _null_ _null_ ));
DESCR("time of day with time zone");
#define TIMETZOID 1266
! DATA(insert OID = 1270 ( _timetz PGNSP PGUID -1 f b A f t \054 0 1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout - d x f 0 -1 0 _null_ _null_ ));
/* OIDS 1500 - 1599 */
! DATA(insert OID = 1560 ( bit PGNSP PGUID -1 f b V f t \054 0 0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 _null_ _null_ ));
DESCR("fixed-length bit string");
#define BITOID 1560
! DATA(insert OID = 1561 ( _bit PGNSP PGUID -1 f b A f t \054 0 1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1562 ( varbit PGNSP PGUID -1 f b V t t \054 0 0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 _null_ _null_ ));
DESCR("variable-length bit string");
#define VARBITOID 1562
! DATA(insert OID = 1563 ( _varbit PGNSP PGUID -1 f b A f t \054 0 1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout - i x f 0 -1 0 _null_ _null_ ));
/* OIDS 1600 - 1699 */
/* OIDS 1700 - 1799 */
! DATA(insert OID = 1700 ( numeric PGNSP PGUID -1 f b N f t \054 0 0 1231 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f 0 -1 0 _null_ _null_ ));
DESCR("numeric(precision, decimal), arbitrary precision number");
#define NUMERICOID 1700
! DATA(insert OID = 1790 ( refcursor PGNSP PGUID -1 f b U f t \054 0 0 2201 textin textout textrecv textsend - - - i x f 0 -1 0 _null_ _null_ ));
DESCR("reference to cursor (portal name)");
#define REFCURSOROID 1790
/* OIDS 2200 - 2299 */
! DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 2202 ( regprocedure PGNSP PGUID 4 t b N f t \054 0 0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered procedure (with args)");
#define REGPROCEDUREOID 2202
! DATA(insert OID = 2203 ( regoper PGNSP PGUID 4 t b N f t \054 0 0 2208 regoperin regoperout regoperrecv regopersend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered operator");
#define REGOPEROID 2203
! DATA(insert OID = 2204 ( regoperator PGNSP PGUID 4 t b N f t \054 0 0 2209 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered operator (with args)");
#define REGOPERATOROID 2204
! DATA(insert OID = 2205 ( regclass PGNSP PGUID 4 t b N f t \054 0 0 2210 regclassin regclassout regclassrecv regclasssend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered class");
#define REGCLASSOID 2205
! DATA(insert OID = 2206 ( regtype PGNSP PGUID 4 t b N f t \054 0 0 2211 regtypein regtypeout regtyperecv regtypesend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered type");
#define REGTYPEOID 2206
! DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
#define REGTYPEARRAYOID 2211
/* uuid */
! DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 _null_ _null_ ));
DESCR("UUID datatype");
! DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
/* text search */
! DATA(insert OID = 3614 ( tsvector PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 _null_ _null_ ));
DESCR("text representation for text search");
#define TSVECTOROID 3614
! DATA(insert OID = 3642 ( gtsvector PGNSP PGUID -1 f b U f t \054 0 0 3644 gtsvectorin gtsvectorout - - - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("GiST index internal text representation for text search");
#define GTSVECTOROID 3642
! DATA(insert OID = 3615 ( tsquery PGNSP PGUID -1 f b U f t \054 0 0 3645 tsqueryin tsqueryout tsqueryrecv tsquerysend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("query representation for text search");
#define TSQUERYOID 3615
! DATA(insert OID = 3734 ( regconfig PGNSP PGUID 4 t b N f t \054 0 0 3735 regconfigin regconfigout regconfigrecv regconfigsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered text search configuration");
#define REGCONFIGOID 3734
! DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered text search dictionary");
#define REGDICTIONARYOID 3769
! DATA(insert OID = 3643 ( _tsvector PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 3644 ( _gtsvector PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 _null_ _null_ ));
DESCR("txid snapshot");
! DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
/*
* pseudo-types
--- 373,603 ----
/* OIDS 500 - 599 */
/* OIDS 600 - 699 */
! DATA(insert OID = 600 ( point PGNSP PGUID 16 f b G f t \054 0 701 1017 point_in point_out point_recv point_send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric point '(x, y)'");
#define POINTOID 600
! DATA(insert OID = 601 ( lseg PGNSP PGUID 32 f b G f t \054 0 600 1018 lseg_in lseg_out lseg_recv lseg_send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric line segment '(pt1,pt2)'");
#define LSEGOID 601
! DATA(insert OID = 602 ( path PGNSP PGUID -1 f b G f t \054 0 0 1019 path_in path_out path_recv path_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric path '(pt1,...)'");
#define PATHOID 602
! DATA(insert OID = 603 ( box PGNSP PGUID 32 f b G f t \073 0 600 1020 box_in box_out box_recv box_send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric box '(lower left,upper right)'");
#define BOXOID 603
! DATA(insert OID = 604 ( polygon PGNSP PGUID -1 f b G f t \054 0 0 1027 poly_in poly_out poly_recv poly_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric polygon '(pt1,...)'");
#define POLYGONOID 604
! DATA(insert OID = 628 ( line PGNSP PGUID 32 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric line (not implemented)");
#define LINEOID 628
! DATA(insert OID = 629 ( _line PGNSP PGUID -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("");
/* OIDS 700 - 799 */
! DATA(insert OID = 700 ( float4 PGNSP PGUID 4 FLOAT4PASSBYVAL b N f t \054 0 0 1021 float4in float4out float4recv float4send - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("single-precision floating point number, 4-byte storage");
#define FLOAT4OID 700
! DATA(insert OID = 701 ( float8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N t t \054 0 0 1022 float8in float8out float8recv float8send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("double-precision floating point number, 8-byte storage");
#define FLOAT8OID 701
! DATA(insert OID = 702 ( abstime PGNSP PGUID 4 t b D f t \054 0 0 1023 abstimein abstimeout abstimerecv abstimesend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("absolute, limited-range date and time (Unix system time)");
#define ABSTIMEOID 702
! DATA(insert OID = 703 ( reltime PGNSP PGUID 4 t b T f t \054 0 0 1024 reltimein reltimeout reltimerecv reltimesend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("relative, limited-range time interval (Unix delta time)");
#define RELTIMEOID 703
! DATA(insert OID = 704 ( tinterval PGNSP PGUID 12 f b T f t \054 0 0 1025 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("(abstime,abstime), time interval");
#define TINTERVALOID 704
! DATA(insert OID = 705 ( unknown PGNSP PGUID -2 f b X f t \054 0 0 0 unknownin unknownout unknownrecv unknownsend - - - c p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("");
#define UNKNOWNOID 705
! DATA(insert OID = 718 ( circle PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric circle '(center,radius)'");
#define CIRCLEOID 718
! DATA(insert OID = 719 ( _circle PGNSP PGUID -1 f b A f t \054 0 718 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 790 ( money PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("monetary amounts, $d,ddd.cc");
#define CASHOID 790
! DATA(insert OID = 791 ( _money PGNSP PGUID -1 f b A f t \054 0 790 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
/* OIDS 800 - 899 */
! DATA(insert OID = 829 ( macaddr PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("XX:XX:XX:XX:XX:XX, MAC address");
#define MACADDROID 829
! DATA(insert OID = 869 ( inet PGNSP PGUID -1 f b I t t \054 0 0 1041 inet_in inet_out inet_recv inet_send - - - i m f 0 -1 0 -1 f _null_ _null_ ));
DESCR("IP address/netmask, host address, netmask optional");
#define INETOID 869
! DATA(insert OID = 650 ( cidr PGNSP PGUID -1 f b I f t \054 0 0 651 cidr_in cidr_out cidr_recv cidr_send - - - i m f 0 -1 0 -1 f _null_ _null_ ));
DESCR("network IP address/netmask, network address");
#define CIDROID 650
/* OIDS 900 - 999 */
/* OIDS 1000 - 1099 */
! DATA(insert OID = 1000 ( _bool PGNSP PGUID -1 f b A f t \054 0 16 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1001 ( _bytea PGNSP PGUID -1 f b A f t \054 0 17 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1002 ( _char PGNSP PGUID -1 f b A f t \054 0 18 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b A f t \054 0 19 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b A f t \054 0 21 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b A f t \054 0 22 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b A f t \054 0 23 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
#define INT4ARRAYOID 1007
! DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b A f t \054 0 24 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b A f t \054 0 25 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
#define TEXTARRAYOID 1009
! DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b A f t \054 0 26 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b A f t \054 0 27 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b A f t \054 0 28 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b A f t \054 0 29 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1013 ( _oidvector PGNSP PGUID -1 f b A f t \054 0 30 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1014 ( _bpchar PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1015 ( _varchar PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1016 ( _int8 PGNSP PGUID -1 f b A f t \054 0 20 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1017 ( _point PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1018 ( _lseg PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1019 ( _path PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1020 ( _box PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1021 ( _float4 PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
#define FLOAT4ARRAYOID 1021
! DATA(insert OID = 1022 ( _float8 PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1033 ( aclitem PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("access control list");
#define ACLITEMOID 1033
! DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1263 ( _cstring PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
#define CSTRINGARRAYOID 1263
! DATA(insert OID = 1042 ( bpchar PGNSP PGUID -1 f b S f t \054 0 0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("char(length), blank-padded string, fixed storage length");
#define BPCHAROID 1042
! DATA(insert OID = 1043 ( varchar PGNSP PGUID -1 f b S f t \054 0 0 1015 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("varchar(length), non-blank-padded string, variable storage length");
#define VARCHAROID 1043
! DATA(insert OID = 1082 ( date PGNSP PGUID 4 t b D f t \054 0 0 1182 date_in date_out date_recv date_send - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("date");
#define DATEOID 1082
! DATA(insert OID = 1083 ( time PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1183 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("time of day");
#define TIMEOID 1083
/* OIDS 1100 - 1199 */
! DATA(insert OID = 1114 ( timestamp PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("date and time");
#define TIMESTAMPOID 1114
! DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1182 ( _date PGNSP PGUID -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1183 ( _time PGNSP PGUID -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1184 ( timestamptz PGNSP PGUID 8 FLOAT8PASSBYVAL b D t t \054 0 0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("date and time with time zone");
#define TIMESTAMPTZOID 1184
! DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0 1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1186 ( interval PGNSP PGUID 16 f b T t t \054 0 0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("@ <number> <units>, time interval");
#define INTERVALOID 1186
! DATA(insert OID = 1187 ( _interval PGNSP PGUID -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout - d x f 0 -1 0 -1 f _null_ _null_ ));
/* OIDS 1200 - 1299 */
! DATA(insert OID = 1231 ( _numeric PGNSP PGUID -1 f b A f t \054 0 1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1266 ( timetz PGNSP PGUID 12 f b D f t \054 0 0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("time of day with time zone");
#define TIMETZOID 1266
! DATA(insert OID = 1270 ( _timetz PGNSP PGUID -1 f b A f t \054 0 1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout - d x f 0 -1 0 -1 f _null_ _null_ ));
/* OIDS 1500 - 1599 */
! DATA(insert OID = 1560 ( bit PGNSP PGUID -1 f b V f t \054 0 0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("fixed-length bit string");
#define BITOID 1560
! DATA(insert OID = 1561 ( _bit PGNSP PGUID -1 f b A f t \054 0 1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1562 ( varbit PGNSP PGUID -1 f b V t t \054 0 0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("variable-length bit string");
#define VARBITOID 1562
! DATA(insert OID = 1563 ( _varbit PGNSP PGUID -1 f b A f t \054 0 1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
/* OIDS 1600 - 1699 */
/* OIDS 1700 - 1799 */
! DATA(insert OID = 1700 ( numeric PGNSP PGUID -1 f b N f t \054 0 0 1231 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f 0 -1 0 -1 f _null_ _null_ ));
DESCR("numeric(precision, decimal), arbitrary precision number");
#define NUMERICOID 1700
! DATA(insert OID = 1790 ( refcursor PGNSP PGUID -1 f b U f t \054 0 0 2201 textin textout textrecv textsend - - - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("reference to cursor (portal name)");
#define REFCURSOROID 1790
/* OIDS 2200 - 2299 */
! DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 2202 ( regprocedure PGNSP PGUID 4 t b N f t \054 0 0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered procedure (with args)");
#define REGPROCEDUREOID 2202
! DATA(insert OID = 2203 ( regoper PGNSP PGUID 4 t b N f t \054 0 0 2208 regoperin regoperout regoperrecv regopersend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered operator");
#define REGOPEROID 2203
! DATA(insert OID = 2204 ( regoperator PGNSP PGUID 4 t b N f t \054 0 0 2209 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered operator (with args)");
#define REGOPERATOROID 2204
! DATA(insert OID = 2205 ( regclass PGNSP PGUID 4 t b N f t \054 0 0 2210 regclassin regclassout regclassrecv regclasssend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered class");
#define REGCLASSOID 2205
! DATA(insert OID = 2206 ( regtype PGNSP PGUID 4 t b N f t \054 0 0 2211 regtypein regtypeout regtyperecv regtypesend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered type");
#define REGTYPEOID 2206
! DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
#define REGTYPEARRAYOID 2211
/* uuid */
! DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("UUID datatype");
! DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
/* text search */
! DATA(insert OID = 3614 ( tsvector PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("text representation for text search");
#define TSVECTOROID 3614
! DATA(insert OID = 3642 ( gtsvector PGNSP PGUID -1 f b U f t \054 0 0 3644 gtsvectorin gtsvectorout - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("GiST index internal text representation for text search");
#define GTSVECTOROID 3642
! DATA(insert OID = 3615 ( tsquery PGNSP PGUID -1 f b U f t \054 0 0 3645 tsqueryin tsqueryout tsqueryrecv tsquerysend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("query representation for text search");
#define TSQUERYOID 3615
! DATA(insert OID = 3734 ( regconfig PGNSP PGUID 4 t b N f t \054 0 0 3735 regconfigin regconfigout regconfigrecv regconfigsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered text search configuration");
#define REGCONFIGOID 3734
! DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered text search dictionary");
#define REGDICTIONARYOID 3769
! DATA(insert OID = 3643 ( _tsvector PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 3644 ( _gtsvector PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("txid snapshot");
! DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
/*
* pseudo-types
***************
*** 598,628 **** DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 a
* but there is now support for it in records and arrays. Perhaps we should
* just treat it as a regular base type?
*/
! DATA(insert OID = 2249 ( record PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
#define RECORDOID 2249
! DATA(insert OID = 2287 ( _record PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
#define RECORDARRAYOID 2287
! DATA(insert OID = 2275 ( cstring PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 _null_ _null_ ));
#define CSTRINGOID 2275
! DATA(insert OID = 2276 ( any PGNSP PGUID 4 t p P f t \054 0 0 0 any_in any_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define ANYOID 2276
! DATA(insert OID = 2277 ( anyarray PGNSP PGUID -1 f p P f t \054 0 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f 0 -1 0 _null_ _null_ ));
#define ANYARRAYOID 2277
! DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define VOIDOID 2278
! DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define TRIGGEROID 2279
! DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define LANGUAGE_HANDLEROID 2280
! DATA(insert OID = 2281 ( internal PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 _null_ _null_ ));
#define INTERNALOID 2281
! DATA(insert OID = 2282 ( opaque PGNSP PGUID 4 t p P f t \054 0 0 0 opaque_in opaque_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define OPAQUEOID 2282
! DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p P f t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define ANYELEMENTOID 2283
! DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p P f t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define ANYNONARRAYOID 2776
! DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define ANYENUMOID 3500
--- 612,642 ----
* but there is now support for it in records and arrays. Perhaps we should
* just treat it as a regular base type?
*/
! DATA(insert OID = 2249 ( record PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
#define RECORDOID 2249
! DATA(insert OID = 2287 ( _record PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
#define RECORDARRAYOID 2287
! DATA(insert OID = 2275 ( cstring PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 -1 f _null_ _null_ ));
#define CSTRINGOID 2275
! DATA(insert OID = 2276 ( any PGNSP PGUID 4 t p P f t \054 0 0 0 any_in any_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define ANYOID 2276
! DATA(insert OID = 2277 ( anyarray PGNSP PGUID -1 f p P f t \054 0 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
#define ANYARRAYOID 2277
! DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define VOIDOID 2278
! DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define TRIGGEROID 2279
! DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define LANGUAGE_HANDLEROID 2280
! DATA(insert OID = 2281 ( internal PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 -1 f _null_ _null_ ));
#define INTERNALOID 2281
! DATA(insert OID = 2282 ( opaque PGNSP PGUID 4 t p P f t \054 0 0 0 opaque_in opaque_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define OPAQUEOID 2282
! DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p P f t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define ANYELEMENTOID 2283
! DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p P f t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define ANYNONARRAYOID 2776
! DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define ANYENUMOID 3500
*** a/src/include/catalog/pg_type_fn.h
--- b/src/include/catalog/pg_type_fn.h
***************
*** 50,56 **** extern Oid TypeCreate(Oid newTypeOid,
char storage,
int32 typeMod,
int32 typNDims,
! bool typeNotNull);
extern void GenerateTypeDependencies(Oid typeNamespace,
Oid typeObjectId,
--- 50,58 ----
char storage,
int32 typeMod,
int32 typNDims,
! bool typeNotNull,
! int32 typeNLabels,
! bool typeSorted);
extern void GenerateTypeDependencies(Oid typeNamespace,
Oid typeObjectId,
*** a/src/include/commands/typecmds.h
--- b/src/include/commands/typecmds.h
***************
*** 24,29 **** extern void RemoveTypes(DropStmt *drop);
--- 24,30 ----
extern void RemoveTypeById(Oid typeOid);
extern void DefineDomain(CreateDomainStmt *stmt);
extern void DefineEnum(CreateEnumStmt *stmt);
+ extern void AlterEnum (AlterEnumStmt *stmt);
extern Oid DefineCompositeType(const RangeVar *typevar, List *coldeflist);
extern Oid AssignTypeArrayOid(void);
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
***************
*** 348,354 **** typedef enum NodeTag
T_DropUserMappingStmt,
T_AlterTableSpaceOptionsStmt,
T_SecLabelStmt,
!
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
*/
--- 348,354 ----
T_DropUserMappingStmt,
T_AlterTableSpaceOptionsStmt,
T_SecLabelStmt,
! T_AlterEnumStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
*/
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 2192,2197 **** typedef struct CreateEnumStmt
--- 2192,2211 ----
/* ----------------------
+ * Alter Type Statement, enum types
+ * ----------------------
+ */
+ typedef struct AlterEnumStmt
+ {
+ NodeTag type;
+ List *typeName; /* qualified name (list of Value strings) */
+ char *newVal; /* new enum value */
+ char *newValNeighbour;/* neighbouring enum value */
+ bool newValIsAfter; /* new enum value is after neighbour? */
+ } AlterEnumStmt;
+
+
+ /* ----------------------
* Create View Statement
* ----------------------
*/
*** a/src/test/regress/expected/enum.out
--- b/src/test/regress/expected/enum.out
***************
*** 25,30 **** ERROR: invalid input value for enum rainbow: "mauve"
--- 25,108 ----
LINE 1: SELECT 'mauve'::rainbow;
^
--
+ -- adding new values
+ --
+ CREATE TYPE planets AS ENUM ( 'venus', 'earth', 'mars' );
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+ enumlabel | enumsortorder
+ -----------+---------------
+ venus | 1
+ earth | 2
+ mars | 3
+ (3 rows)
+
+ ALTER TYPE planets ADD 'uranus';
+ SELECT typnlabels, typsorted
+ FROM pg_type
+ WHERE oid = 'planets'::regtype;
+ typnlabels | typsorted
+ ------------+-----------
+ 4 | t
+ (1 row)
+
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+ enumlabel | enumsortorder
+ -----------+---------------
+ venus | 1
+ earth | 2
+ mars | 3
+ uranus | 4
+ (4 rows)
+
+ ALTER TYPE planets ADD 'mercury' BEFORE 'venus';
+ ALTER TYPE planets ADD 'saturn' BEFORE 'uranus';
+ ALTER TYPE planets ADD 'jupiter' AFTER 'mars';
+ ALTER TYPE planets ADD 'neptune' AFTER 'uranus';
+ SELECT typnlabels, typsorted
+ FROM pg_type
+ WHERE oid = 'planets'::regtype;
+ typnlabels | typsorted
+ ------------+-----------
+ 8 | f
+ (1 row)
+
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+ enumlabel | enumsortorder
+ -----------+---------------
+ mercury | 1
+ venus | 2
+ earth | 3
+ mars | 4
+ jupiter | 5
+ saturn | 6
+ uranus | 7
+ neptune | 8
+ (8 rows)
+
+ select 'mars'::planets > 'mercury' as using_sortorder;
+ using_sortorder
+ -----------------
+ t
+ (1 row)
+
+ -- errors for adding labels
+ ALTER TYPE planets ADD
+ 'plutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutopluto';
+ ERROR: invalid enum label "plutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutopluto"
+ DETAIL: Labels must be 63 characters or less.
+ ALTER TYPE planets ADD 'pluto' AFTER 'zeus';
+ ERROR: "zeus" is not an existing label.
+ DROP TYPE planets;
+ --
-- Basic table creation, row selection
--
CREATE TABLE enumtest (col rainbow);
***************
*** 403,409 **** SELECT COUNT(*) FROM pg_type WHERE typname = 'rainbow';
SELECT * FROM pg_enum WHERE NOT EXISTS
(SELECT 1 FROM pg_type WHERE pg_type.oid = enumtypid);
! enumtypid | enumlabel
! -----------+-----------
(0 rows)
--- 481,487 ----
SELECT * FROM pg_enum WHERE NOT EXISTS
(SELECT 1 FROM pg_type WHERE pg_type.oid = enumtypid);
! enumtypid | enumlabel | enumsortorder
! -----------+-----------+---------------
(0 rows)
*** a/src/test/regress/sql/enum.sql
--- b/src/test/regress/sql/enum.sql
***************
*** 16,21 **** SELECT 'red'::rainbow;
--- 16,69 ----
SELECT 'mauve'::rainbow;
--
+ -- adding new values
+ --
+
+ CREATE TYPE planets AS ENUM ( 'venus', 'earth', 'mars' );
+
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+
+ ALTER TYPE planets ADD 'uranus';
+
+ SELECT typnlabels, typsorted
+ FROM pg_type
+ WHERE oid = 'planets'::regtype;
+
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+
+ ALTER TYPE planets ADD 'mercury' BEFORE 'venus';
+
+ ALTER TYPE planets ADD 'saturn' BEFORE 'uranus';
+
+ ALTER TYPE planets ADD 'jupiter' AFTER 'mars';
+
+ ALTER TYPE planets ADD 'neptune' AFTER 'uranus';
+
+ SELECT typnlabels, typsorted
+ FROM pg_type
+ WHERE oid = 'planets'::regtype;
+
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+
+ select 'mars'::planets > 'mercury' as using_sortorder;
+
+ -- errors for adding labels
+ ALTER TYPE planets ADD
+ 'plutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutopluto';
+
+ ALTER TYPE planets ADD 'pluto' AFTER 'zeus';
+
+ DROP TYPE planets;
+ --
-- Basic table creation, row selection
--
CREATE TABLE enumtest (col rainbow);
On Wed, Oct 13, 2010 at 7:33 AM, Andrew Dunstan <andrew@dunslane.net> wrote:
Sorry, got distracted. Here's a new patch that fixes the above and also
contains some documentation.
Someone want to review this and (hopefully) mark it Ready for
Committer? I see that Brendan Jurd is the reviewer of record in the
CF app, but it seems Dean Rasheed is the person who has actually
reviewed it recently. Either way...
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On 13 October 2010 23:17, Robert Haas <robertmhaas@gmail.com> wrote:
On Wed, Oct 13, 2010 at 7:33 AM, Andrew Dunstan <andrew@dunslane.net> wrote:
Sorry, got distracted. Here's a new patch that fixes the above and also
contains some documentation.Someone want to review this and (hopefully) mark it Ready for
Committer? I see that Brendan Jurd is the reviewer of record in the
CF app, but it seems Dean Rasheed is the person who has actually
reviewed it recently. Either way...
I'm happy to take another look at it, but I'm short on time, so I
doubt that I be able to do anything before the weekend. If anyone
wants to jump in before then, feel free.
Regards,
Dean
Show quoted text
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On 14 October 2010 08:39, Dean Rasheed <dean.a.rasheed@gmail.com> wrote:
Someone want to review this and (hopefully) mark it Ready for
Committer? I see that Brendan Jurd is the reviewer of record in the
CF app, but it seems Dean Rasheed is the person who has actually
reviewed it recently. Either way...I'm happy to take another look at it, but I'm short on time, so I
doubt that I be able to do anything before the weekend. If anyone
wants to jump in before then, feel free.
I started looking at this last night, but ran out of time. I'll
continue this evening / over the weekend. Here are my comments so far:
Patch applies cleanly to current git master with no offsets.
Compiles cleanly with no warnings.
Regression tests pass.
The regression tests look reasonable, but I'd like to see a test of
\dT+. Also it could be made to exercise the comparison function more
if the test query did an ORDER BY CAST(enumlabel as planets).
The docs for ALTER TYPE have been updated. I found a few minor typos,
and also a couple of other sections of the manual that needed
updating.
Attached is an updated version with these changes. Andrew, let me know
if you're happy with these tweaks and I'll continue reviewing over the
weekend.
Regards,
Dean
Attachments:
venum5.patchtext/x-patch; charset=US-ASCII; name=venum5.patchDownload
diff --git a/contrib/pg_upgrade/function.c b/contrib/pg_upgrade/function.c
index c70f23f..7dc1656 100644
*** a/contrib/pg_upgrade/function.c
--- b/contrib/pg_upgrade/function.c
*************** install_support_functions(migratorContex
*** 61,85 ****
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_heap_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_toast_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_index_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.add_pg_enum_label(OID, OID, NAME) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
--- 61,85 ----
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_pg_enum_oid(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_heap_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_toast_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_index_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
diff --git a/contrib/pg_upgrade_support/pg_upgrade_support.c b/contrib/pg_upgrade_support/pg_upgrade_support.c
index c956be1..24f5e59 100644
*** a/contrib/pg_upgrade_support/pg_upgrade_support.c
--- b/contrib/pg_upgrade_support/pg_upgrade_support.c
*************** PG_MODULE_MAGIC;
*** 30,35 ****
--- 30,36 ----
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_array_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_toast_oid;
+ extern PGDLLIMPORT Oid binary_upgrade_next_pg_enum_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_heap_relfilenode;
extern PGDLLIMPORT Oid binary_upgrade_next_toast_relfilenode;
extern PGDLLIMPORT Oid binary_upgrade_next_index_relfilenode;
*************** extern PGDLLIMPORT Oid binary_upgrade_ne
*** 37,54 ****
Datum set_next_pg_type_oid(PG_FUNCTION_ARGS);
Datum set_next_pg_type_array_oid(PG_FUNCTION_ARGS);
Datum set_next_pg_type_toast_oid(PG_FUNCTION_ARGS);
Datum set_next_heap_relfilenode(PG_FUNCTION_ARGS);
Datum set_next_toast_relfilenode(PG_FUNCTION_ARGS);
Datum set_next_index_relfilenode(PG_FUNCTION_ARGS);
- Datum add_pg_enum_label(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(set_next_pg_type_oid);
PG_FUNCTION_INFO_V1(set_next_pg_type_array_oid);
PG_FUNCTION_INFO_V1(set_next_pg_type_toast_oid);
PG_FUNCTION_INFO_V1(set_next_heap_relfilenode);
PG_FUNCTION_INFO_V1(set_next_toast_relfilenode);
PG_FUNCTION_INFO_V1(set_next_index_relfilenode);
- PG_FUNCTION_INFO_V1(add_pg_enum_label);
Datum
set_next_pg_type_oid(PG_FUNCTION_ARGS)
--- 38,55 ----
Datum set_next_pg_type_oid(PG_FUNCTION_ARGS);
Datum set_next_pg_type_array_oid(PG_FUNCTION_ARGS);
Datum set_next_pg_type_toast_oid(PG_FUNCTION_ARGS);
+ Datum set_next_pg_enum_oid(PG_FUNCTION_ARGS);
Datum set_next_heap_relfilenode(PG_FUNCTION_ARGS);
Datum set_next_toast_relfilenode(PG_FUNCTION_ARGS);
Datum set_next_index_relfilenode(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(set_next_pg_type_oid);
PG_FUNCTION_INFO_V1(set_next_pg_type_array_oid);
PG_FUNCTION_INFO_V1(set_next_pg_type_toast_oid);
+ PG_FUNCTION_INFO_V1(set_next_pg_enum_oid);
PG_FUNCTION_INFO_V1(set_next_heap_relfilenode);
PG_FUNCTION_INFO_V1(set_next_toast_relfilenode);
PG_FUNCTION_INFO_V1(set_next_index_relfilenode);
Datum
set_next_pg_type_oid(PG_FUNCTION_ARGS)
*************** set_next_pg_type_toast_oid(PG_FUNCTION_A
*** 81,86 ****
--- 82,97 ----
}
Datum
+ set_next_pg_enum_oid(PG_FUNCTION_ARGS)
+ {
+ Oid enumoid = PG_GETARG_OID(0);
+
+ binary_upgrade_next_pg_enum_oid = enumoid;
+
+ PG_RETURN_VOID();
+ }
+
+ Datum
set_next_heap_relfilenode(PG_FUNCTION_ARGS)
{
Oid relfilenode = PG_GETARG_OID(0);
*************** set_next_index_relfilenode(PG_FUNCTION_A
*** 110,124 ****
PG_RETURN_VOID();
}
- Datum
- add_pg_enum_label(PG_FUNCTION_ARGS)
- {
- Oid enumoid = PG_GETARG_OID(0);
- Oid typoid = PG_GETARG_OID(1);
- Name label = PG_GETARG_NAME(2);
-
- EnumValuesCreate(typoid, list_make1(makeString(NameStr(*label))),
- enumoid);
-
- PG_RETURN_VOID();
- }
--- 121,123 ----
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index c37b995..0db463e 100644
*** a/doc/src/sgml/catalogs.sgml
--- b/doc/src/sgml/catalogs.sgml
***************
*** 2626,2634 ****
matching enum types to their associated values and labels. The
internal representation of a given enum value is actually the OID
of its associated row in <structname>pg_enum</structname>. The
! OIDs for a particular enum type are guaranteed to be ordered in
! the way the type should sort, but there is no guarantee about the
! ordering of OIDs of unrelated enum types.
</para>
<table>
--- 2626,2633 ----
matching enum types to their associated values and labels. The
internal representation of a given enum value is actually the OID
of its associated row in <structname>pg_enum</structname>. The
! enum values for a particular enum type are ordered using their
! <structfield>enumsortorder</> values.
</para>
<table>
***************
*** 2658,2663 ****
--- 2657,2670 ----
<entry></entry>
<entry>The textual label for this enum value</entry>
</row>
+
+ <row>
+ <entry><structfield>enumsortorder</structfield></entry>
+ <entry><type>nameinteger</type></entry>
+ <entry></entry>
+ <entry>The position of this enum value within the list of values for the
+ enum type</entry>
+ </row>
</tbody>
</tgroup>
</table>
diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index 02eaedf..78c2dac 100644
*** a/doc/src/sgml/datatype.sgml
--- b/doc/src/sgml/datatype.sgml
*************** SELECT * FROM person WHERE current_mood
*** 2898,2904 ****
<para>
The ordering of the values in an enum type is the
! order in which the values were listed when the type was created.
All standard comparison operators and related
aggregate functions are supported for enums. For example:
--- 2898,2906 ----
<para>
The ordering of the values in an enum type is the
! order in which the values were listed when the type was created,
! but additional values may be inserted at any position within this
! list using <command>ALTER TYPE</>.
All standard comparison operators and related
aggregate functions are supported for enums. For example:
*************** SELECT * FROM person WHERE current_mood
*** 2912,2923 ****
Curly | ok
(2 rows)
SELECT * FROM person WHERE current_mood > 'sad' ORDER BY current_mood;
name | current_mood
-------+--------------
Curly | ok
Moe | happy
! (2 rows)
SELECT name
FROM person
--- 2914,2928 ----
Curly | ok
(2 rows)
+ ALTER TYPE mood ADD 'content' BEFORE 'happy';
+ INSERT INTO person VALUES ('Sue', 'content');
SELECT * FROM person WHERE current_mood > 'sad' ORDER BY current_mood;
name | current_mood
-------+--------------
Curly | ok
+ Sue | content
Moe | happy
! (3 rows)
SELECT name
FROM person
diff --git a/doc/src/sgml/ref/alter_type.sgml b/doc/src/sgml/ref/alter_type.sgml
index 315922e..b05b000 100644
*** a/doc/src/sgml/ref/alter_type.sgml
--- b/doc/src/sgml/ref/alter_type.sgml
*************** ALTER TYPE <replaceable class="PARAMETER
*** 28,33 ****
--- 28,34 ----
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> RENAME ATTRIBUTE <replaceable class="PARAMETER">attribute_name</replaceable> TO <replaceable class="PARAMETER">new_attribute_name</replaceable>
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> RENAME TO <replaceable class="PARAMETER">new_name</replaceable>
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> SET SCHEMA <replaceable class="PARAMETER">new_schema</replaceable>
+ ALTER TYPE <replaceable class="PARAMETER">name</replaceable> ADD <replaceable class="PARAMETER">new_enum_value</replaceable> [ BEFORE | AFTER <replaceable class="PARAMETER">existing_enum_value</replaceable> ]
<phrase>where <replaceable class="PARAMETER">action</replaceable> is one of:</phrase>
*************** ALTER TYPE <replaceable class="PARAMETER
*** 103,108 ****
--- 104,131 ----
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><literal>ADD [ BEFORE | AFTER ]</literal></term>
+ <listitem>
+ <para>
+ This form adds a new value to an enum type. If the new value's place
+ in the sort order is not set using <literal>BEFORE</literal> or
+ <literal>AFTER</literal>, then the new item is placed at the end of
+ the list of values.
+ </para>
+ <note>
+ <para>
+ Adding a new enum value will in some cases lower the comparison and
+ sorting performance of the enum type. This will only usually occur if
+ <literal>BEFORE</literal> or <literal>AFTER</literal> are used to set
+ the new value's sort order position somewhere other than at the end
+ of the list. Optimal performance will be restored if the database is
+ dumped and reloaded.
+ </para>
+ </note>
+ </listitem>
+ </varlistentry>
</variablelist>
</para>
*************** ALTER TYPE <replaceable class="PARAMETER
*** 196,201 ****
--- 219,244 ----
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><replaceable class="PARAMETER">new_enum_value</replaceable></term>
+ <listitem>
+ <para>
+ The new value to be added to the enum type's list of values. Like all
+ enum literals it needs to be quoted.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="PARAMETER">existing_enum_value</replaceable></term>
+ <listitem>
+ <para>
+ The neighbor of the new value to be added to the enum type's list of
+ values. Like all enum literals it needs to be quoted.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</para>
</refsect1>
*************** ALTER TYPE email SET SCHEMA customers;
*** 232,237 ****
--- 275,287 ----
ALTER TYPE compfoo ADD ATTRIBUTE f3 int;
</programlisting>
</para>
+
+ <para>
+ To add a new value to an enum type in a particular sort position:
+ <programlisting>
+ ALTER TYPE colors ADD 'orange' AFTER 'red';
+ </programlisting>
+ </para>
</refsect1>
<refsect1>
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index dcc53e1..f42aecb 100644
*** a/src/backend/catalog/heap.c
--- b/src/backend/catalog/heap.c
*************** AddNewRelationType(const char *typeName,
*** 855,861 ****
'x', /* fully TOASTable */
-1, /* typmod */
0, /* array dimensions for typBaseType */
! false); /* Type NOT NULL */
}
/* --------------------------------
--- 855,863 ----
'x', /* fully TOASTable */
-1, /* typmod */
0, /* array dimensions for typBaseType */
! false, /* Type NOT NULL */
! -1, /* no enum labels */
! false); /* type is not an enum, so not sorted */
}
/* --------------------------------
*************** heap_create_with_catalog(const char *rel
*** 1108,1114 ****
'x', /* fully TOASTable */
-1, /* typmod */
0, /* array dimensions for typBaseType */
! false); /* Type NOT NULL */
pfree(relarrayname);
}
--- 1110,1118 ----
'x', /* fully TOASTable */
-1, /* typmod */
0, /* array dimensions for typBaseType */
! false, /* Type NOT NULL */
! -1, /* no enum labels */
! false); /* type is not an enum, so not sorted */
pfree(relarrayname);
}
diff --git a/src/backend/catalog/pg_enum.c b/src/backend/catalog/pg_enum.c
index d544c1f..65f9c71 100644
*** a/src/backend/catalog/pg_enum.c
--- b/src/backend/catalog/pg_enum.c
***************
*** 18,29 ****
--- 18,219 ----
#include "catalog/catalog.h"
#include "catalog/indexing.h"
#include "catalog/pg_enum.h"
+ #include "catalog/pg_type.h"
+ #include "utils/lsyscache.h"
+ #include "utils/syscache.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/rel.h"
#include "utils/tqual.h"
static int oid_cmp(const void *p1, const void *p2);
+ static int sort_order_cmp(const void *p1, const void *p2);
+
+ Oid binary_upgrade_next_pg_enum_oid = InvalidOid;
+
+ /*
+ * AddEnumLabel
+ * Add a new label to the enum set. By default it goes at
+ * the end, but the user can choose to place it before or
+ * after any existing set member.
+ *
+ * Returns true iff the labels are sorted by oid after the addition.
+ */
+
+ bool
+ AddEnumLabel(Oid enumTypeOid,
+ char *newVal,
+ char *neighbour,
+ bool newValIsAfter,
+ int nelems,
+ bool elems_are_sorted)
+ {
+ Oid newOid;
+ Relation pg_enum;
+ TupleDesc tupDesc;
+ Datum values[Natts_pg_enum];
+ bool nulls[Natts_pg_enum];
+ NameData enumlabel;
+ HeapTuple enum_tup;
+ bool result = elems_are_sorted;
+ int newelemorder;
+
+ /* check length of new label is ok */
+ if (strlen(newVal) > (NAMEDATALEN - 1))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_NAME),
+ errmsg("invalid enum label \"%s\"", newVal),
+ errdetail("Labels must be %d characters or less.",
+ NAMEDATALEN - 1)));
+
+ /* get a new OID for the label */
+ pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
+
+ if (OidIsValid(binary_upgrade_next_pg_enum_oid))
+ {
+ if (neighbour != NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("BEFORE/AFTER incompatible with binary upgrade.")));
+
+ newOid = binary_upgrade_next_pg_enum_oid;
+ binary_upgrade_next_pg_enum_oid = InvalidOid;
+ }
+ else
+ {
+ /* non upgrade case */
+ newOid = GetNewOid(pg_enum);
+ }
+
+ if (neighbour == NULL)
+ {
+ /*
+ * Put the new label at the end of the list.
+ * No change to existing tuples is required.
+ */
+ newelemorder = nelems + 1;
+ /* are the elements still sorted? */
+ if (elems_are_sorted)
+ {
+ CatCList *list;
+ int i;
+ bool still_sorted = true;
+
+ list = SearchSysCacheList1(ENUMTYPOIDNAME,
+ ObjectIdGetDatum(enumTypeOid));
+ for (i = 0; i < nelems; i++)
+ {
+ HeapTuple tup = &(list->members[i]->tuple);
+ Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
+
+ if (en->enumsortorder == nelems)
+ {
+ if (HeapTupleGetOid(tup) > newOid)
+ still_sorted = false;
+ break;
+ }
+ }
+ ReleaseCatCacheList(list);
+ if (! still_sorted)
+ result = false;
+ }
+ }
+ else
+ {
+ /* BEFORE or AFTER specified */
+ CatCList *list;
+ int i;
+ HeapTuple *existing;
+ HeapTuple nbr = NULL;
+ Form_pg_enum nbr_en;
+
+ /* get a list of the existing elements and sort them by enumsortorder */
+ list = SearchSysCacheList1(ENUMTYPOIDNAME,
+ ObjectIdGetDatum(enumTypeOid));
+ existing = palloc(nelems * sizeof(HeapTuple));
+
+ for (i = 0; i < nelems; i++)
+ existing[i] = &(list->members[i]->tuple);
+
+ qsort(existing, nelems, sizeof(HeapTuple), sort_order_cmp);
+
+ /* locate the neighbour element */
+ for (i = 0; i < nelems; i++)
+ {
+ Form_pg_enum exists_en;
+ exists_en = (Form_pg_enum) GETSTRUCT(existing[i]);
+ if (strcmp(NameStr(exists_en->enumlabel),neighbour) == 0)
+ nbr = existing[i];
+
+ }
+
+ if (nbr == NULL)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("\"%s\" is not an existing label.", neighbour)));
+ }
+
+ nbr_en = (Form_pg_enum) GETSTRUCT(nbr);
+
+ /*
+ * If BEFORE was specified, the new label goes in the neighbour's
+ * position. Otherwise, it goes in the position after that.
+ */
+ newelemorder = nbr_en->enumsortorder;
+ if (newValIsAfter)
+ newelemorder++;
+
+ /*
+ * Add 1 to the sortorder of all the labels after where the
+ * new label goes. Do it from the end back so we don't get
+ * uniqueness violations.
+ */
+ for (i = nelems - 1; i>= 0; i--)
+ {
+ HeapTuple newtup;
+ Form_pg_enum exst_en = (Form_pg_enum) GETSTRUCT(existing[i]);
+ if (exst_en->enumsortorder < newelemorder)
+ break;
+
+ newtup = heap_copytuple(existing[i]);
+ exst_en = (Form_pg_enum) GETSTRUCT(newtup);
+ exst_en->enumsortorder ++;
+
+ simple_heap_update(pg_enum, &newtup->t_self, newtup);
+
+ CatalogUpdateIndexes(pg_enum, newtup);
+
+ }
+
+ ReleaseCatCacheList(list);
+
+ /* are the labels sorted by OID? */
+ if (result && newelemorder > 1)
+ result = newOid > HeapTupleGetOid(existing[newelemorder-2]);
+ if (result && newelemorder < nelems + 1)
+ result = newOid < HeapTupleGetOid(existing[newelemorder-1]);
+
+ }
+
+ /* set up the new entry */
+ tupDesc = pg_enum->rd_att;
+ memset(nulls, false, sizeof(nulls));
+ values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid);
+ namestrcpy(&enumlabel, newVal);
+ values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel);
+ values[Anum_pg_enum_enumsortorder -1] = Int32GetDatum(newelemorder);
+ enum_tup = heap_form_tuple(tupDesc, values, nulls);
+ HeapTupleSetOid(enum_tup, newOid);
+ simple_heap_insert(pg_enum, enum_tup);
+ CatalogUpdateIndexes(pg_enum, enum_tup);
+ heap_freetuple(enum_tup);
+
+ heap_close(pg_enum, RowExclusiveLock);
+
+ return result;
+
+ }
/*
*************** static int oid_cmp(const void *p1, const
*** 33,40 ****
* vals is a list of Value strings.
*/
void
! EnumValuesCreate(Oid enumTypeOid, List *vals,
! Oid binary_upgrade_next_pg_enum_oid)
{
Relation pg_enum;
TupleDesc tupDesc;
--- 223,229 ----
* vals is a list of Value strings.
*/
void
! EnumValuesCreate(Oid enumTypeOid, List *vals)
{
Relation pg_enum;
TupleDesc tupDesc;
*************** EnumValuesCreate(Oid enumTypeOid, List *
*** 50,58 ****
num_elems = list_length(vals);
/*
! * XXX we do not bother to check the list of values for duplicates --- if
* you have any, you'll get a less-than-friendly unique-index violation.
! * Is it worth trying harder?
*/
pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
--- 239,247 ----
num_elems = list_length(vals);
/*
! * We do not bother to check the list of values for duplicates --- if
* you have any, you'll get a less-than-friendly unique-index violation.
! * It is probably not worth trying harder.
*/
pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
*************** EnumValuesCreate(Oid enumTypeOid, List *
*** 62,96 ****
* Allocate oids
*/
oids = (Oid *) palloc(num_elems * sizeof(Oid));
! if (OidIsValid(binary_upgrade_next_pg_enum_oid))
! {
! if (num_elems != 1)
! ereport(ERROR,
! (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("EnumValuesCreate() can only set a single OID")));
! oids[0] = binary_upgrade_next_pg_enum_oid;
! binary_upgrade_next_pg_enum_oid = InvalidOid;
! }
! else
{
/*
! * While this method does not absolutely guarantee that we generate no
! * duplicate oids (since we haven't entered each oid into the table
! * before allocating the next), trouble could only occur if the oid
! * counter wraps all the way around before we finish. Which seems
! * unlikely.
*/
! for (elemno = 0; elemno < num_elems; elemno++)
! {
! /*
! * The pg_enum.oid is stored in user tables. This oid must be
! * preserved by binary upgrades.
! */
! oids[elemno] = GetNewOid(pg_enum);
! }
! /* sort them, just in case counter wrapped from high to low */
! qsort(oids, num_elems, sizeof(Oid), oid_cmp);
}
/* and make the entries */
memset(nulls, false, sizeof(nulls));
--- 251,274 ----
* Allocate oids
*/
oids = (Oid *) palloc(num_elems * sizeof(Oid));
!
! /*
! * While this method does not absolutely guarantee that we generate no
! * duplicate oids (since we haven't entered each oid into the table
! * before allocating the next), trouble could only occur if the oid
! * counter wraps all the way around before we finish. Which seems
! * unlikely.
! */
! for (elemno = 0; elemno < num_elems; elemno++)
{
/*
! * The pg_enum.oid is stored in user tables. This oid must be
! * preserved by binary upgrades.
*/
! oids[elemno] = GetNewOid(pg_enum);
}
+ /* sort them, just in case counter wrapped from high to low */
+ qsort(oids, num_elems, sizeof(Oid), oid_cmp);
/* and make the entries */
memset(nulls, false, sizeof(nulls));
*************** EnumValuesCreate(Oid enumTypeOid, List *
*** 114,119 ****
--- 292,298 ----
values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid);
namestrcpy(&enumlabel, lab);
values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel);
+ values[Anum_pg_enum_enumsortorder -1] = Int32GetDatum(elemno+1);
tup = heap_form_tuple(tupDesc, values, nulls);
HeapTupleSetOid(tup, oids[elemno]);
*************** EnumValuesDelete(Oid enumTypeOid)
*** 164,170 ****
}
! /* qsort comparison function */
static int
oid_cmp(const void *p1, const void *p2)
{
--- 343,349 ----
}
! /* qsort comparison for oids */
static int
oid_cmp(const void *p1, const void *p2)
{
*************** oid_cmp(const void *p1, const void *p2)
*** 177,179 ****
--- 356,371 ----
return 1;
return 0;
}
+
+ /* qsort comparison function for tuples by sort order */
+ static int
+ sort_order_cmp(const void *p1, const void *p2)
+ {
+ HeapTuple v1 = *((const HeapTuple *) p1);
+ HeapTuple v2 = *((const HeapTuple *) p2);
+ Form_pg_enum en1 = (Form_pg_enum) GETSTRUCT(v1);
+ Form_pg_enum en2 = (Form_pg_enum) GETSTRUCT(v2);
+
+ return en1->enumsortorder - en2->enumsortorder;
+ }
+
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index d7fccdf..56e0dc1 100644
*** a/src/backend/catalog/pg_type.c
--- b/src/backend/catalog/pg_type.c
*************** TypeShellMake(const char *typeName, Oid
*** 112,117 ****
--- 112,119 ----
values[i++] = ObjectIdGetDatum(InvalidOid); /* typbasetype */
values[i++] = Int32GetDatum(-1); /* typtypmod */
values[i++] = Int32GetDatum(0); /* typndims */
+ values[i++] = Int32GetDatum(-1); /* typnlabels */
+ values[i++] = BoolGetDatum(false); /* typsorted */
nulls[i++] = true; /* typdefaultbin */
nulls[i++] = true; /* typdefault */
*************** TypeCreate(Oid newTypeOid,
*** 204,210 ****
char storage,
int32 typeMod,
int32 typNDims, /* Array dimensions for baseType */
! bool typeNotNull)
{
Relation pg_type_desc;
Oid typeObjectId;
--- 206,214 ----
char storage,
int32 typeMod,
int32 typNDims, /* Array dimensions for baseType */
! bool typeNotNull,
! int32 typeNLabels,
! bool typeSorted)
{
Relation pg_type_desc;
Oid typeObjectId;
*************** TypeCreate(Oid newTypeOid,
*** 342,347 ****
--- 346,353 ----
values[i++] = ObjectIdGetDatum(baseType); /* typbasetype */
values[i++] = Int32GetDatum(typeMod); /* typtypmod */
values[i++] = Int32GetDatum(typNDims); /* typndims */
+ values[i++] = Int32GetDatum(typeNLabels); /* typnlabels */
+ values[i++] = BoolGetDatum(typeSorted); /* typsorted */
/*
* initialize the default binary value for this type. Check for nulls of
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 25503bd..01e19cf 100644
*** a/src/backend/commands/typecmds.c
--- b/src/backend/commands/typecmds.c
*************** static Oid findTypeTypmodoutFunction(Lis
*** 85,96 ****
static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid);
static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
static void checkDomainOwner(HeapTuple tup, TypeName *typename);
static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
Oid baseTypeOid,
int typMod, Constraint *constr,
char *domainName);
-
-
/*
* DefineType
* Registers a new base type.
--- 85,95 ----
static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid);
static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
static void checkDomainOwner(HeapTuple tup, TypeName *typename);
+ static void checkEnumOwner(HeapTuple tup, TypeName *typename);
static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
Oid baseTypeOid,
int typMod, Constraint *constr,
char *domainName);
/*
* DefineType
* Registers a new base type.
*************** DefineType(List *names, List *parameters
*** 562,569 ****
storage, /* TOAST strategy */
-1, /* typMod (Domains only) */
0, /* Array Dimensions of typbasetype */
! false); /* Type NOT NULL */
!
/*
* Create the array type that goes with it.
*/
--- 561,569 ----
storage, /* TOAST strategy */
-1, /* typMod (Domains only) */
0, /* Array Dimensions of typbasetype */
! false, /* Type NOT NULL */
! -1, /* no enum labels */
! false); /* type is not an enum, so not sorted */
/*
* Create the array type that goes with it.
*/
*************** DefineType(List *names, List *parameters
*** 601,607 ****
'x', /* ARRAY is always toastable */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
! false); /* Type NOT NULL */
pfree(array_type);
}
--- 601,609 ----
'x', /* ARRAY is always toastable */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
! false, /* Type NOT NULL */
! -1, /* no enum labels */
! false); /* type is not an enum, so not sorted */
pfree(array_type);
}
*************** DefineDomain(CreateDomainStmt *stmt)
*** 1044,1050 ****
storage, /* TOAST strategy */
basetypeMod, /* typeMod value */
typNDims, /* Array dimensions for base type */
! typNotNull); /* Type NOT NULL */
/*
* Process constraints which refer to the domain ID returned by TypeCreate
--- 1046,1054 ----
storage, /* TOAST strategy */
basetypeMod, /* typeMod value */
typNDims, /* Array dimensions for base type */
! typNotNull, /* Type NOT NULL */
! -1, /* no enum labels */
! false); /* type is not an enum, so not sorted */
/*
* Process constraints which refer to the domain ID returned by TypeCreate
*************** DefineEnum(CreateEnumStmt *stmt)
*** 1094,1099 ****
--- 1098,1106 ----
AclResult aclresult;
Oid old_type_oid;
Oid enumArrayOid;
+ int num_labels;
+
+ num_labels = list_length(stmt->vals);
/* Convert list of names to a name and namespace */
enumNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
*************** DefineEnum(CreateEnumStmt *stmt)
*** 1153,1162 ****
'p', /* TOAST strategy always plain */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
! false); /* Type NOT NULL */
/* Enter the enum's values into pg_enum */
! EnumValuesCreate(enumTypeOid, stmt->vals, InvalidOid);
/*
* Create the array type that goes with it.
--- 1160,1171 ----
'p', /* TOAST strategy always plain */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
! false, /* Type NOT NULL */
! num_labels, /* count enum labels */
! true); /* enums always start sorted */
/* Enter the enum's values into pg_enum */
! EnumValuesCreate(enumTypeOid, stmt->vals);
/*
* Create the array type that goes with it.
*************** DefineEnum(CreateEnumStmt *stmt)
*** 1192,1202 ****
'x', /* ARRAY is always toastable */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
! false); /* Type NOT NULL */
pfree(enumArrayName);
}
/*
* Find suitable I/O functions for a type.
--- 1201,1288 ----
'x', /* ARRAY is always toastable */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
! false, /* Type NOT NULL */
! -1, /* no enum labels */
! false); /* type is not an enum, so not sorted */
pfree(enumArrayName);
}
+ /*
+ * AlterEnum
+ * Registers a new label for an existing enum.
+ */
+ void
+ AlterEnum (AlterEnumStmt *stmt)
+ {
+ Oid enum_type_oid;
+ TypeName *typename;
+ bool sorted;
+ HeapTuple tup, newtup;
+ Relation rel;
+ Form_pg_type typTup;
+
+ /* Make a TypeName so we can use standard type lookup machinery */
+ typename = makeTypeNameFromNameList(stmt->typeName);
+ enum_type_oid = typenameTypeId(NULL, typename, NULL);
+
+ /* Look up the row in the type table */
+ rel = heap_open(TypeRelationId, RowExclusiveLock);
+
+ tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(enum_type_oid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for type %u", enum_type_oid);
+
+ /* Copy the syscache entry so we can scribble on it below */
+ newtup = heap_copytuple(tup);
+ ReleaseSysCache(tup);
+ tup = newtup;
+ typTup = (Form_pg_type) GETSTRUCT(tup);
+
+ /* Check it's an enum and check user has permission to ALTER the enum */
+ checkEnumOwner(tup, typename);
+
+ /* Add the new label */
+ sorted = AddEnumLabel (enum_type_oid, stmt->newVal,
+ stmt->newValNeighbour, stmt->newValIsAfter,
+ typTup->typnlabels, typTup->typsorted);
+
+ /* Update the row in pg_type */
+ typTup->typnlabels += 1;
+ typTup->typsorted = sorted;
+
+ simple_heap_update(rel, &tup->t_self, tup);
+
+ CatalogUpdateIndexes(rel, tup);
+
+ /* Clean up */
+ heap_close(rel, RowExclusiveLock);
+ }
+
+
+ /*
+ * checkEnumOwner
+ *
+ * Check that the type is actually an enum and that the current user
+ * has permission to do ALTER TYPE on it. Throw an error if not.
+ */
+ static void
+ checkEnumOwner(HeapTuple tup, TypeName *typename)
+ {
+ Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup);
+
+ /* Check that this is actually a domain */
+ if (typTup->typtype != TYPTYPE_ENUM)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not an enum",
+ TypeNameToString(typename))));
+
+ /* Permission check: must own type */
+ if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
+ format_type_be(HeapTupleGetOid(tup)));
+ }
/*
* Find suitable I/O functions for a type.
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 2118a33..dcc063a 100644
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
*************** _copyCreateEnumStmt(CreateEnumStmt *from
*** 2873,2878 ****
--- 2873,2891 ----
return newnode;
}
+ static AlterEnumStmt *
+ _copyAlterEnumStmt(AlterEnumStmt *from)
+ {
+ AlterEnumStmt *newnode = makeNode(AlterEnumStmt);
+
+ COPY_NODE_FIELD(typeName);
+ COPY_STRING_FIELD(newVal);
+ COPY_STRING_FIELD(newValNeighbour);
+ COPY_SCALAR_FIELD(newValIsAfter);
+
+ return newnode;
+ }
+
static ViewStmt *
_copyViewStmt(ViewStmt *from)
{
*************** copyObject(void *from)
*** 4033,4038 ****
--- 4046,4054 ----
case T_CreateEnumStmt:
retval = _copyCreateEnumStmt(from);
break;
+ case T_AlterEnumStmt:
+ retval = _copyAlterEnumStmt(from);
+ break;
case T_ViewStmt:
retval = _copyViewStmt(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 6bad724..2439e7f 100644
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
*************** _equalCreateEnumStmt(CreateEnumStmt *a,
*** 1390,1395 ****
--- 1390,1406 ----
}
static bool
+ _equalAlterEnumStmt(AlterEnumStmt *a, AlterEnumStmt *b)
+ {
+ COMPARE_NODE_FIELD(typeName);
+ COMPARE_STRING_FIELD(newVal);
+ COMPARE_STRING_FIELD(newValNeighbour);
+ COMPARE_SCALAR_FIELD(newValIsAfter);
+
+ return true;
+ }
+
+ static bool
_equalViewStmt(ViewStmt *a, ViewStmt *b)
{
COMPARE_NODE_FIELD(view);
*************** equal(void *a, void *b)
*** 2697,2702 ****
--- 2708,2716 ----
case T_CreateEnumStmt:
retval = _equalCreateEnumStmt(a, b);
break;
+ case T_AlterEnumStmt:
+ retval = _equalAlterEnumStmt(a, b);
+ break;
case T_ViewStmt:
retval = _equalViewStmt(a, b);
break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 3a74fa5..90d3e4d 100644
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
*************** static RangeVar *makeRangeVarFromAnyName
*** 182,189 ****
}
%type <node> stmt schema_stmt
! AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterFdwStmt
! AlterForeignServerStmt AlterGroupStmt
AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
AlterCompositeTypeStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
AlterRoleStmt AlterRoleSetStmt
--- 182,189 ----
}
%type <node> stmt schema_stmt
! AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt
! AlterFdwStmt AlterForeignServerStmt AlterGroupStmt
AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
AlterCompositeTypeStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
AlterRoleStmt AlterRoleSetStmt
*************** stmt :
*** 652,657 ****
--- 652,658 ----
| AlterDatabaseSetStmt
| AlterDefaultPrivilegesStmt
| AlterDomainStmt
+ | AlterEnumStmt
| AlterFdwStmt
| AlterForeignServerStmt
| AlterFunctionStmt
*************** enum_val_list: Sconst
*** 3862,3867 ****
--- 3863,3908 ----
{ $$ = lappend($1, makeString($3)); }
;
+ /*****************************************************************************
+ *
+ * ALTER TYPE enumtype ADD ...
+ *
+ *****************************************************************************/
+
+ AlterEnumStmt:
+ ALTER TYPE_P any_name ADD_P Sconst
+ {
+ AlterEnumStmt *n = makeNode(AlterEnumStmt);
+ n->typeName = $3;
+ n->newVal = $5;
+ n->newValNeighbour = NULL;
+ n->newValIsAfter = true;
+
+ $$ = (Node *) n;
+ }
+ | ALTER TYPE_P any_name ADD_P Sconst BEFORE Sconst
+ {
+ AlterEnumStmt *n = makeNode(AlterEnumStmt);
+ n->typeName = $3;
+ n->newVal = $5;
+ n->newValNeighbour = $7;
+ n->newValIsAfter = false;
+
+ $$ = (Node *) n;
+ }
+ | ALTER TYPE_P any_name ADD_P Sconst AFTER Sconst
+ {
+ AlterEnumStmt *n = makeNode(AlterEnumStmt);
+ n->typeName = $3;
+ n->newVal = $5;
+ n->newValNeighbour = $7;
+ n->newValIsAfter = true;
+
+ $$ = (Node *) n;
+ }
+ ;
+
+
/*****************************************************************************
*
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 75cb354..2c8b7aa 100644
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
*************** check_xact_readonly(Node *parsetree)
*** 190,195 ****
--- 190,196 ----
case T_CreateTrigStmt:
case T_CompositeTypeStmt:
case T_CreateEnumStmt:
+ case T_AlterEnumStmt:
case T_ViewStmt:
case T_DropCastStmt:
case T_DropStmt:
*************** standard_ProcessUtility(Node *parsetree,
*** 860,865 ****
--- 861,870 ----
DefineEnum((CreateEnumStmt *) parsetree);
break;
+ case T_AlterEnumStmt: /* ALTER TYPE (enum) */
+ AlterEnum((AlterEnumStmt *) parsetree);
+ break;
+
case T_ViewStmt: /* CREATE VIEW */
DefineView((ViewStmt *) parsetree, queryString);
break;
*************** CreateCommandTag(Node *parsetree)
*** 1868,1873 ****
--- 1873,1882 ----
tag = "CREATE TYPE";
break;
+ case T_AlterEnumStmt:
+ tag = "ALTER TYPE";
+ break;
+
case T_ViewStmt:
tag = "CREATE VIEW";
break;
*************** GetCommandLogLevel(Node *parsetree)
*** 2410,2415 ****
--- 2419,2428 ----
lev = LOGSTMT_DDL;
break;
+ case T_AlterEnumStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
case T_ViewStmt:
lev = LOGSTMT_DDL;
break;
diff --git a/src/backend/utils/adt/enum.c b/src/backend/utils/adt/enum.c
index e5747a4..f9feda5 100644
*** a/src/backend/utils/adt/enum.c
--- b/src/backend/utils/adt/enum.c
***************
*** 14,19 ****
--- 14,20 ----
#include "postgres.h"
#include "catalog/pg_enum.h"
+ #include "catalog/pg_type.h"
#include "fmgr.h"
#include "utils/array.h"
#include "utils/builtins.h"
***************
*** 22,30 ****
#include "libpq/pqformat.h"
#include "miscadmin.h"
static ArrayType *enum_range_internal(Oid enumtypoid, Oid lower, Oid upper);
! static int enum_elem_cmp(const void *left, const void *right);
/* Basic I/O support */
--- 23,48 ----
#include "libpq/pqformat.h"
#include "miscadmin.h"
+ typedef struct
+ {
+ Oid enum_oid;
+ int32 sort_order;
+ } enum_sort;
+
+ typedef struct
+ {
+ Oid enumtypoid;
+ bool oids_are_sorted;
+ int sort_list_length;
+ int label_count;
+ enum_sort sort_order_list[1];
+ } enum_sort_cache;
+
+
static ArrayType *enum_range_internal(Oid enumtypoid, Oid lower, Oid upper);
! static int enum_sort_cmp(const void *left, const void *right);
! static int enum_oid_cmp(const void *left, const void *right);
/* Basic I/O support */
*************** enum_send(PG_FUNCTION_ARGS)
*** 155,167 ****
/* Comparison functions and related */
Datum
enum_lt(PG_FUNCTION_ARGS)
{
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(a < b);
}
Datum
--- 173,347 ----
/* Comparison functions and related */
+
+ /* enum_ccmp is the common engine for all the visible comparison functions */
+
+ static inline int
+ enum_ccmp(Oid arg1, Oid arg2, FunctionCallInfo fcinfo)
+ {
+
+ /*
+ * Keep a cache of type information in fcinfo->flinfo->fn_extra.
+ * If the Oids are sorted, that's all the info we need, and
+ * we can just return the difference of arg1 and arg2.
+ * If not, we keep a list of Oid/sortorder pairs in the cache,
+ * and look up the sortorder by Oid, using a binary search,
+ * and return the difference between the sortorders.
+ *
+ * To keep down the cost of retail comparisons even if
+ * the label set is very large, we start by just putting
+ * Oids we are actually comparing into the cache. If this
+ * grows beyond a handful (10 in fact) it looks like it's a
+ * bulk operation, and so we just fetch the all the labels
+ * and sort them. This keeps down the number of times we
+ * might need to call quicksort.
+ */
+
+ enum_sort_cache * mycache;
+ enum_sort *es1, *es2, srch;
+ int sort1, sort2;
+ bool added = false;
+ HeapTuple enum_tup, type_tup;
+ Form_pg_enum en;
+ Oid typeoid;
+ Form_pg_type typ;
+
+ /*
+ * Fast path return for equal Oids, sorted or not.
+ * This shouldn't happen, but it's here just in case.
+ */
+ if (arg1 == arg2)
+ return 0;
+
+ mycache = (enum_sort_cache *) fcinfo->flinfo->fn_extra;
+
+ /* Initialize the cache if it's not already there */
+ if (mycache == NULL )
+ {
+ enum_tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(arg1));
+ en = (Form_pg_enum) GETSTRUCT(enum_tup);
+ typeoid = en->enumtypid;
+ type_tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeoid));
+ typ = (Form_pg_type) GETSTRUCT(type_tup);
+ if (typ->typtype != 'e')
+ elog(ERROR,"wrong type for oid %u",typeoid);
+ fcinfo->flinfo->fn_extra =
+ MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+ sizeof(enum_sort_cache) +
+ (typ->typnlabels * sizeof(enum_sort)));
+ mycache = (enum_sort_cache *) fcinfo->flinfo->fn_extra;
+ mycache->enumtypoid = typeoid;
+ mycache->sort_list_length = 1;
+ mycache->label_count = typ->typnlabels;
+ mycache->oids_are_sorted = typ->typsorted;
+ mycache->sort_order_list[0].enum_oid = arg1;
+ mycache->sort_order_list[0].sort_order = en->enumsortorder;
+ ReleaseSysCache(type_tup);
+ ReleaseSysCache(enum_tup);
+ }
+
+ /* Fast path return for Oids are sorted case */
+ if (mycache->oids_are_sorted)
+ return arg1 - arg2;
+
+ /* Look up the Oids in the cache */
+ srch.enum_oid = arg1;
+ es1 = bsearch(&srch,
+ mycache->sort_order_list,
+ mycache->sort_list_length,
+ sizeof(enum_sort),
+ enum_oid_cmp);
+ srch.enum_oid = arg2;
+ es2 = bsearch(&srch,
+ mycache->sort_order_list,
+ mycache->sort_list_length,
+ sizeof(enum_sort),
+ enum_oid_cmp);
+
+ /* Handle cache misses, or get the sort order from the search results */
+
+ if (es1 == NULL)
+ {
+
+ enum_tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(arg1));
+ en = (Form_pg_enum) GETSTRUCT(enum_tup);
+ mycache->sort_order_list[mycache->sort_list_length].enum_oid = arg1;
+ sort1 = en->enumsortorder;
+ mycache->sort_order_list[mycache->sort_list_length].sort_order =
+ sort1;
+ ReleaseSysCache(enum_tup);
+ mycache->sort_list_length ++;
+ added = true;
+ }
+ else
+ {
+ /* already in cache */
+ sort1 = es1->sort_order;
+ }
+
+ if (es2 == NULL)
+ {
+
+ enum_tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(arg2));
+ en = (Form_pg_enum) GETSTRUCT(enum_tup);
+ sort2 = en->enumsortorder;
+ mycache->sort_order_list[mycache->sort_list_length].enum_oid = arg2;
+ mycache->sort_order_list[mycache->sort_list_length].sort_order =
+ sort2;
+ ReleaseSysCache(enum_tup);
+ mycache->sort_list_length ++;
+ added = true;
+ }
+ else
+ {
+ /* already in cache */
+ sort2 = es2->sort_order;
+ }
+
+ /* Sort the cache, and possibly complete it, for the next function call */
+ if (added)
+ {
+ /*
+ * If we have more than a handful, just fetch them all, so we limit
+ * the number of sort operations required.
+ */
+ if (mycache->sort_list_length > 10 &&
+ mycache->sort_list_length < mycache->label_count)
+ {
+ CatCList *nlist;
+ int num, i;
+
+ nlist = SearchSysCacheList1(ENUMTYPOIDNAME,
+ ObjectIdGetDatum(mycache->enumtypoid));
+ num = nlist->n_members;
+ for (i = 0; i < num; i++)
+ {
+ HeapTuple tup = &(nlist->members[i]->tuple);
+ Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
+
+ mycache->sort_order_list[i].enum_oid = HeapTupleGetOid(tup);
+ mycache->sort_order_list[i].sort_order = en->enumsortorder;
+ }
+ mycache->sort_list_length = mycache->label_count;
+
+ ReleaseCatCacheList(nlist);
+ }
+
+ qsort(mycache->sort_order_list,mycache->sort_list_length,
+ sizeof(enum_sort),enum_oid_cmp);
+ }
+
+ /* and we're done */
+ return sort1 - sort2;
+ }
+
Datum
enum_lt(PG_FUNCTION_ARGS)
{
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(enum_ccmp(a,b,fcinfo) < 0);
}
Datum
*************** enum_le(PG_FUNCTION_ARGS)
*** 170,176 ****
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(a <= b);
}
Datum
--- 350,356 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(enum_ccmp(a,b,fcinfo) <= 0);
}
Datum
*************** enum_ge(PG_FUNCTION_ARGS)
*** 197,203 ****
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(a >= b);
}
Datum
--- 377,383 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(enum_ccmp(a,b,fcinfo) >= 0);
}
Datum
*************** enum_gt(PG_FUNCTION_ARGS)
*** 206,212 ****
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(a > b);
}
Datum
--- 386,392 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(enum_ccmp(a,b,fcinfo) > 0);
}
Datum
*************** enum_smaller(PG_FUNCTION_ARGS)
*** 215,221 ****
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_OID(a <= b ? a : b);
}
Datum
--- 395,401 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_OID(enum_ccmp(a,b,fcinfo) < 0 ? a : b);
}
Datum
*************** enum_larger(PG_FUNCTION_ARGS)
*** 224,230 ****
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_OID(a >= b ? a : b);
}
Datum
--- 404,410 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_OID(enum_ccmp(a,b,fcinfo) > 0 ? a : b);
}
Datum
*************** enum_cmp(PG_FUNCTION_ARGS)
*** 233,242 ****
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! if (a > b)
! PG_RETURN_INT32(1);
! else if (a == b)
PG_RETURN_INT32(0);
else
PG_RETURN_INT32(-1);
}
--- 413,422 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! if (a == b)
PG_RETURN_INT32(0);
+ else if (enum_ccmp(a,b,fcinfo) > 0)
+ PG_RETURN_INT32(1);
else
PG_RETURN_INT32(-1);
}
*************** enum_first(PG_FUNCTION_ARGS)
*** 248,253 ****
--- 428,434 ----
{
Oid enumtypoid;
Oid min = InvalidOid;
+ int min_sort = -1; /* value will never in fact be used */
CatCList *list;
int num,
i;
*************** enum_first(PG_FUNCTION_ARGS)
*** 267,276 ****
num = list->n_members;
for (i = 0; i < num; i++)
{
! Oid valoid = HeapTupleHeaderGetOid(list->members[i]->tuple.t_data);
! if (!OidIsValid(min) || valoid < min)
! min = valoid;
}
ReleaseCatCacheList(list);
--- 448,461 ----
num = list->n_members;
for (i = 0; i < num; i++)
{
! HeapTuple tup = &(list->members[i]->tuple);
! Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
! if (!OidIsValid(min) || en->enumsortorder < min_sort)
! {
! min = HeapTupleGetOid(tup);
! min_sort = en->enumsortorder;
! }
}
ReleaseCatCacheList(list);
*************** enum_last(PG_FUNCTION_ARGS)
*** 287,292 ****
--- 472,478 ----
{
Oid enumtypoid;
Oid max = InvalidOid;
+ int max_sort = -1; /* value will never in fact be used */
CatCList *list;
int num,
i;
*************** enum_last(PG_FUNCTION_ARGS)
*** 306,315 ****
num = list->n_members;
for (i = 0; i < num; i++)
{
! Oid valoid = HeapTupleHeaderGetOid(list->members[i]->tuple.t_data);
! if (!OidIsValid(max) || valoid > max)
! max = valoid;
}
ReleaseCatCacheList(list);
--- 492,505 ----
num = list->n_members;
for (i = 0; i < num; i++)
{
! HeapTuple tup = &(list->members[i]->tuple);
! Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
! if (!OidIsValid(max) || en->enumsortorder > max_sort)
! {
! max = HeapTupleGetOid(tup);
! max_sort = en->enumsortorder;
! }
}
ReleaseCatCacheList(list);
*************** enum_range_internal(Oid enumtypoid, Oid
*** 382,427 ****
i,
j;
Datum *elems;
list = SearchSysCacheList1(ENUMTYPOIDNAME, ObjectIdGetDatum(enumtypoid));
total = list->n_members;
elems = (Datum *) palloc(total * sizeof(Datum));
- j = 0;
for (i = 0; i < total; i++)
{
! Oid val = HeapTupleGetOid(&(list->members[i]->tuple));
- if ((!OidIsValid(lower) || lower <= val) &&
- (!OidIsValid(upper) || val <= upper))
- elems[j++] = ObjectIdGetDatum(val);
}
/* shouldn't need the cache anymore */
ReleaseCatCacheList(list);
! /* sort results into OID order */
! qsort(elems, j, sizeof(Datum), enum_elem_cmp);
/* note this hardwires some details about the representation of Oid */
result = construct_array(elems, j, enumtypoid, sizeof(Oid), true, 'i');
pfree(elems);
return result;
}
! /* qsort comparison function for Datums that are OIDs */
static int
! enum_elem_cmp(const void *left, const void *right)
{
! Oid l = DatumGetObjectId(*((const Datum *) left));
! Oid r = DatumGetObjectId(*((const Datum *) right));
! if (l < r)
! return -1;
! if (l > r)
! return 1;
! return 0;
}
--- 572,647 ----
i,
j;
Datum *elems;
+ enum_sort *sort_items;
+ bool left_found;
list = SearchSysCacheList1(ENUMTYPOIDNAME, ObjectIdGetDatum(enumtypoid));
total = list->n_members;
elems = (Datum *) palloc(total * sizeof(Datum));
+ sort_items = (enum_sort *) palloc(total * sizeof(enum_sort));
for (i = 0; i < total; i++)
{
! HeapTuple tup = &(list->members[i]->tuple);
! Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
!
! sort_items[i].enum_oid = HeapTupleGetOid(tup);
! sort_items[i].sort_order = en->enumsortorder;
}
/* shouldn't need the cache anymore */
ReleaseCatCacheList(list);
! /* sort results into sort_order sequence */
! qsort(sort_items, total, sizeof(enum_sort), enum_sort_cmp);
!
! j = 0;
! left_found = !OidIsValid(lower);
! for (i=0; i < total; i++)
! {
! if (! left_found && lower == sort_items[i].enum_oid)
! left_found = true;
!
! if (left_found)
! elems[j++] = ObjectIdGetDatum(sort_items[i].enum_oid);
!
! if (OidIsValid(upper) && upper == sort_items[i].enum_oid)
! break;
! }
/* note this hardwires some details about the representation of Oid */
result = construct_array(elems, j, enumtypoid, sizeof(Oid), true, 'i');
pfree(elems);
+ pfree(sort_items);
return result;
}
! /*
! * qsort comparison using sort order, for range routines
! */
static int
! enum_sort_cmp(const void *left, const void *right)
{
! enum_sort *l = (enum_sort *) left;
! enum_sort *r = (enum_sort *) right;
! return l->sort_order - r->sort_order;
}
+
+ /*
+ * qsort comparison using OID order for comparison search cache
+ */
+ static int
+ enum_oid_cmp(const void *es1, const void *es2)
+ {
+ enum_sort *p1, *p2;
+ p1 = (enum_sort *)es1;
+ p2 = (enum_sort *)es2;
+ return p1->enum_oid - p2->enum_oid;
+ }
+
+
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 6a4557b..d413fe5 100644
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
*************** dumpEnumType(Archive *fout, TypeInfo *ty
*** 6653,6669 ****
PQExpBuffer query = createPQExpBuffer();
PGresult *res;
int num,
! i;
Oid enum_oid;
char *label;
/* Set proper schema search path so regproc references list correctly */
selectSourceSchema(tyinfo->dobj.namespace->dobj.name);
! appendPQExpBuffer(query, "SELECT oid, enumlabel "
! "FROM pg_catalog.pg_enum "
! "WHERE enumtypid = '%u'"
! "ORDER BY oid",
tyinfo->dobj.catId.oid);
res = PQexec(g_conn, query->data);
--- 6653,6676 ----
PQExpBuffer query = createPQExpBuffer();
PGresult *res;
int num,
! i;
Oid enum_oid;
char *label;
/* Set proper schema search path so regproc references list correctly */
selectSourceSchema(tyinfo->dobj.namespace->dobj.name);
! if (fout->remoteVersion > 90000)
! appendPQExpBuffer(query, "SELECT oid, enumlabel "
! "FROM pg_catalog.pg_enum "
! "WHERE enumtypid = '%u'"
! "ORDER BY enumsortorder",
! tyinfo->dobj.catId.oid);
! else
! appendPQExpBuffer(query, "SELECT oid, enumlabel "
! "FROM pg_catalog.pg_enum "
! "WHERE enumtypid = '%u'"
! "ORDER BY oid",
tyinfo->dobj.catId.oid);
res = PQexec(g_conn, query->data);
*************** dumpEnumType(Archive *fout, TypeInfo *ty
*** 6709,6725 ****
{
enum_oid = atooid(PQgetvalue(res, i, PQfnumber(res, "oid")));
label = PQgetvalue(res, i, PQfnumber(res, "enumlabel"));
!
if (i == 0)
appendPQExpBuffer(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
appendPQExpBuffer(q,
! "SELECT binary_upgrade.add_pg_enum_label('%u'::pg_catalog.oid, "
! "'%u'::pg_catalog.oid, ",
! enum_oid, tyinfo->dobj.catId.oid);
! appendStringLiteralAH(q, label, fout);
! appendPQExpBuffer(q, ");\n");
}
- appendPQExpBuffer(q, "\n");
}
ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
--- 6716,6734 ----
{
enum_oid = atooid(PQgetvalue(res, i, PQfnumber(res, "oid")));
label = PQgetvalue(res, i, PQfnumber(res, "enumlabel"));
!
if (i == 0)
appendPQExpBuffer(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
appendPQExpBuffer(q,
! "SELECT binary_upgrade.set_next_pg_enum_oid('%u'::pg_catalog.oid);\n",
! enum_oid);
! appendPQExpBuffer(q, "ALTER TYPE %s.",
! fmtId(tyinfo->dobj.namespace->dobj.name));
! appendPQExpBuffer(q, "%s ADD ",
! fmtId(tyinfo->dobj.name));
! appendStringLiteralAH(q, label, fout);
! appendPQExpBuffer(q, ";\n\n");
}
}
ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 57d74e1..a077948 100644
*** a/src/bin/psql/describe.c
--- b/src/bin/psql/describe.c
*************** describeTypes(const char *pattern, bool
*** 473,489 ****
gettext_noop("Internal name"),
gettext_noop("Size"));
if (verbose && pset.sversion >= 80300)
appendPQExpBuffer(&buf,
" pg_catalog.array_to_string(\n"
" ARRAY(\n"
" SELECT e.enumlabel\n"
" FROM pg_catalog.pg_enum e\n"
! " WHERE e.enumtypid = t.oid\n"
! " ORDER BY e.oid\n"
" ),\n"
" E'\\n'\n"
" ) AS \"%s\",\n",
gettext_noop("Elements"));
appendPQExpBuffer(&buf,
" pg_catalog.obj_description(t.oid, 'pg_type') as \"%s\"\n",
--- 473,499 ----
gettext_noop("Internal name"),
gettext_noop("Size"));
if (verbose && pset.sversion >= 80300)
+ {
appendPQExpBuffer(&buf,
" pg_catalog.array_to_string(\n"
" ARRAY(\n"
" SELECT e.enumlabel\n"
" FROM pg_catalog.pg_enum e\n"
! " WHERE e.enumtypid = t.oid\n");
!
! if (pset.sversion >= 90100 )
! appendPQExpBuffer(&buf,
! " ORDER BY e.enumsortorder\n");
! else
! appendPQExpBuffer(&buf,
! " ORDER BY e.oid\n");
!
! appendPQExpBuffer(&buf,
" ),\n"
" E'\\n'\n"
" ) AS \"%s\",\n",
gettext_noop("Elements"));
+ }
appendPQExpBuffer(&buf,
" pg_catalog.obj_description(t.oid, 'pg_type') as \"%s\"\n",
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index b7c9849..c63724a 100644
*** a/src/include/catalog/indexing.h
--- b/src/include/catalog/indexing.h
*************** DECLARE_UNIQUE_INDEX(pg_enum_oid_index,
*** 147,152 ****
--- 147,154 ----
#define EnumOidIndexId 3502
DECLARE_UNIQUE_INDEX(pg_enum_typid_label_index, 3503, on pg_enum using btree(enumtypid oid_ops, enumlabel name_ops));
#define EnumTypIdLabelIndexId 3503
+ DECLARE_UNIQUE_INDEX(pg_enum_typid_sortorder_index, 3539, on pg_enum using btree(enumtypid oid_ops, enumsortorder int4_ops));
+ #define EnumTypIdSortOrderIndexId 3539
/* This following index is not used for a cache and is not unique */
DECLARE_INDEX(pg_index_indrelid_index, 2678, on pg_index using btree(indrelid oid_ops));
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index f50cf9d..824a690 100644
*** a/src/include/catalog/pg_class.h
--- b/src/include/catalog/pg_class.h
*************** typedef FormData_pg_class *Form_pg_class
*** 132,138 ****
*/
/* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId */
! DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f f r 28 0 t f f f f f 3 _null_ _null_ ));
DESCR("");
DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f f r 19 0 f f f f f f 3 _null_ _null_ ));
DESCR("");
--- 132,138 ----
*/
/* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId */
! DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f f r 30 0 t f f f f f 3 _null_ _null_ ));
DESCR("");
DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f f r 19 0 f f f f f f 3 _null_ _null_ ));
DESCR("");
diff --git a/src/include/catalog/pg_enum.h b/src/include/catalog/pg_enum.h
index 28da42b..06269c6 100644
*** a/src/include/catalog/pg_enum.h
--- b/src/include/catalog/pg_enum.h
*************** CATALOG(pg_enum,3501)
*** 35,40 ****
--- 35,41 ----
{
Oid enumtypid; /* OID of owning enum type */
NameData enumlabel; /* text representation of enum value */
+ int4 enumsortorder; /* sort order for this enum label */
} FormData_pg_enum;
/* ----------------
*************** typedef FormData_pg_enum *Form_pg_enum;
*** 48,56 ****
* compiler constants for pg_enum
* ----------------
*/
! #define Natts_pg_enum 2
#define Anum_pg_enum_enumtypid 1
#define Anum_pg_enum_enumlabel 2
/* ----------------
* pg_enum has no initial contents
--- 49,58 ----
* compiler constants for pg_enum
* ----------------
*/
! #define Natts_pg_enum 3
#define Anum_pg_enum_enumtypid 1
#define Anum_pg_enum_enumlabel 2
+ #define Anum_pg_enum_enumsortorder 3
/* ----------------
* pg_enum has no initial contents
*************** typedef FormData_pg_enum *Form_pg_enum;
*** 60,67 ****
/*
* prototypes for functions in pg_enum.c
*/
! extern void EnumValuesCreate(Oid enumTypeOid, List *vals,
! Oid binary_upgrade_next_pg_enum_oid);
extern void EnumValuesDelete(Oid enumTypeOid);
#endif /* PG_ENUM_H */
--- 62,70 ----
/*
* prototypes for functions in pg_enum.c
*/
! extern void EnumValuesCreate(Oid enumTypeOid, List *vals);
extern void EnumValuesDelete(Oid enumTypeOid);
+ extern bool AddEnumLabel(Oid enumTypeOid, char *newVal, char *neighbour,
+ bool newValIsAfter, int nelems, bool elems_are_sorted);
#endif /* PG_ENUM_H */
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index fc2c306..f79c0e3 100644
*** a/src/include/catalog/pg_type.h
--- b/src/include/catalog/pg_type.h
*************** CATALOG(pg_type,1247) BKI_BOOTSTRAP BKI_
*** 194,199 ****
--- 194,211 ----
*/
int4 typndims;
+ /*
+ * typnlabels, contains a count on the number of labels an enum type has,
+ * -1 for a non-enum type.
+ */
+ int4 typnlabels;
+
+ /*
+ * typsorted is true if the oids of an enum type reflect the type's sort
+ * order, false otherwise including for a non-enum type.
+ */
+ bool typsorted;
+
/*
* If typdefaultbin is not NULL, it is the nodeToString representation of
* a default expression for the type. Currently this is only used for
*************** typedef FormData_pg_type *Form_pg_type;
*** 224,230 ****
* compiler constants for pg_type
* ----------------
*/
! #define Natts_pg_type 28
#define Anum_pg_type_typname 1
#define Anum_pg_type_typnamespace 2
#define Anum_pg_type_typowner 3
--- 236,242 ----
* compiler constants for pg_type
* ----------------
*/
! #define Natts_pg_type 30
#define Anum_pg_type_typname 1
#define Anum_pg_type_typnamespace 2
#define Anum_pg_type_typowner 3
*************** typedef FormData_pg_type *Form_pg_type;
*** 251,258 ****
#define Anum_pg_type_typbasetype 24
#define Anum_pg_type_typtypmod 25
#define Anum_pg_type_typndims 26
! #define Anum_pg_type_typdefaultbin 27
! #define Anum_pg_type_typdefault 28
/* ----------------
--- 263,272 ----
#define Anum_pg_type_typbasetype 24
#define Anum_pg_type_typtypmod 25
#define Anum_pg_type_typndims 26
! #define Anum_pg_type_typnlabels 27
! #define Anum_pg_type_typsorted 28
! #define Anum_pg_type_typdefaultbin 29
! #define Anum_pg_type_typdefault 30
/* ----------------
*************** typedef FormData_pg_type *Form_pg_type;
*** 269,355 ****
*/
/* OIDS 1 - 99 */
! DATA(insert OID = 16 ( bool PGNSP PGUID 1 t b B t t \054 0 0 1000 boolin boolout boolrecv boolsend - - - c p f 0 -1 0 _null_ _null_ ));
DESCR("boolean, 'true'/'false'");
#define BOOLOID 16
! DATA(insert OID = 17 ( bytea PGNSP PGUID -1 f b U f t \054 0 0 1001 byteain byteaout bytearecv byteasend - - - i x f 0 -1 0 _null_ _null_ ));
DESCR("variable-length string, binary values escaped");
#define BYTEAOID 17
! DATA(insert OID = 18 ( char PGNSP PGUID 1 t b S f t \054 0 0 1002 charin charout charrecv charsend - - - c p f 0 -1 0 _null_ _null_ ));
DESCR("single character");
#define CHAROID 18
! DATA(insert OID = 19 ( name PGNSP PGUID NAMEDATALEN f b S f t \054 0 18 1003 namein nameout namerecv namesend - - - c p f 0 -1 0 _null_ _null_ ));
DESCR("63-character type for storing system identifiers");
#define NAMEOID 19
! DATA(insert OID = 20 ( int8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 1016 int8in int8out int8recv int8send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("~18 digit integer, 8-byte storage");
#define INT8OID 20
! DATA(insert OID = 21 ( int2 PGNSP PGUID 2 t b N f t \054 0 0 1005 int2in int2out int2recv int2send - - - s p f 0 -1 0 _null_ _null_ ));
DESCR("-32 thousand to 32 thousand, 2-byte storage");
#define INT2OID 21
! DATA(insert OID = 22 ( int2vector PGNSP PGUID -1 f b A f t \054 0 21 1006 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("array of int2, used in system tables");
#define INT2VECTOROID 22
! DATA(insert OID = 23 ( int4 PGNSP PGUID 4 t b N f t \054 0 0 1007 int4in int4out int4recv int4send - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("-2 billion to 2 billion integer, 4-byte storage");
#define INT4OID 23
! DATA(insert OID = 24 ( regproc PGNSP PGUID 4 t b N f t \054 0 0 1008 regprocin regprocout regprocrecv regprocsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered procedure");
#define REGPROCOID 24
! DATA(insert OID = 25 ( text PGNSP PGUID -1 f b S t t \054 0 0 1009 textin textout textrecv textsend - - - i x f 0 -1 0 _null_ _null_ ));
DESCR("variable-length string, no limit specified");
#define TEXTOID 25
! DATA(insert OID = 26 ( oid PGNSP PGUID 4 t b N t t \054 0 0 1028 oidin oidout oidrecv oidsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("object identifier(oid), maximum 4 billion");
#define OIDOID 26
! DATA(insert OID = 27 ( tid PGNSP PGUID 6 f b U f t \054 0 0 1010 tidin tidout tidrecv tidsend - - - s p f 0 -1 0 _null_ _null_ ));
DESCR("(block, offset), physical location of tuple");
#define TIDOID 27
! DATA(insert OID = 28 ( xid PGNSP PGUID 4 t b U f t \054 0 0 1011 xidin xidout xidrecv xidsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("transaction id");
#define XIDOID 28
! DATA(insert OID = 29 ( cid PGNSP PGUID 4 t b U f t \054 0 0 1012 cidin cidout cidrecv cidsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("command identifier type, sequence in transaction id");
#define CIDOID 29
! DATA(insert OID = 30 ( oidvector PGNSP PGUID -1 f b A f t \054 0 26 1013 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("array of oids, used in system tables");
#define OIDVECTOROID 30
/* hand-built rowtype entries for bootstrapped catalogs */
/* NB: OIDs assigned here must match the BKI_ROWTYPE_OID declarations */
! DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c C f t \054 1247 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c C f t \054 1249 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c C f t \054 1255 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c C f t \054 1259 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
/* OIDS 100 - 199 */
! DATA(insert OID = 142 ( xml PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 _null_ _null_ ));
DESCR("XML content");
#define XMLOID 142
! DATA(insert OID = 143 ( _xml PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 194 ( pg_node_tree PGNSP PGUID -1 f b S f t \054 0 0 0 pg_node_tree_in pg_node_tree_out pg_node_tree_recv pg_node_tree_send - - - i x f 0 -1 0 _null_ _null_ ));
DESCR("string representing an internal node tree");
#define PGNODETREEOID 194
/* OIDS 200 - 299 */
! DATA(insert OID = 210 ( smgr PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f 0 -1 0 _null_ _null_ ));
DESCR("storage manager");
/* OIDS 300 - 399 */
--- 283,369 ----
*/
/* OIDS 1 - 99 */
! DATA(insert OID = 16 ( bool PGNSP PGUID 1 t b B t t \054 0 0 1000 boolin boolout boolrecv boolsend - - - c p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("boolean, 'true'/'false'");
#define BOOLOID 16
! DATA(insert OID = 17 ( bytea PGNSP PGUID -1 f b U f t \054 0 0 1001 byteain byteaout bytearecv byteasend - - - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("variable-length string, binary values escaped");
#define BYTEAOID 17
! DATA(insert OID = 18 ( char PGNSP PGUID 1 t b S f t \054 0 0 1002 charin charout charrecv charsend - - - c p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("single character");
#define CHAROID 18
! DATA(insert OID = 19 ( name PGNSP PGUID NAMEDATALEN f b S f t \054 0 18 1003 namein nameout namerecv namesend - - - c p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("63-character type for storing system identifiers");
#define NAMEOID 19
! DATA(insert OID = 20 ( int8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 1016 int8in int8out int8recv int8send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("~18 digit integer, 8-byte storage");
#define INT8OID 20
! DATA(insert OID = 21 ( int2 PGNSP PGUID 2 t b N f t \054 0 0 1005 int2in int2out int2recv int2send - - - s p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("-32 thousand to 32 thousand, 2-byte storage");
#define INT2OID 21
! DATA(insert OID = 22 ( int2vector PGNSP PGUID -1 f b A f t \054 0 21 1006 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("array of int2, used in system tables");
#define INT2VECTOROID 22
! DATA(insert OID = 23 ( int4 PGNSP PGUID 4 t b N f t \054 0 0 1007 int4in int4out int4recv int4send - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("-2 billion to 2 billion integer, 4-byte storage");
#define INT4OID 23
! DATA(insert OID = 24 ( regproc PGNSP PGUID 4 t b N f t \054 0 0 1008 regprocin regprocout regprocrecv regprocsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered procedure");
#define REGPROCOID 24
! DATA(insert OID = 25 ( text PGNSP PGUID -1 f b S t t \054 0 0 1009 textin textout textrecv textsend - - - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("variable-length string, no limit specified");
#define TEXTOID 25
! DATA(insert OID = 26 ( oid PGNSP PGUID 4 t b N t t \054 0 0 1028 oidin oidout oidrecv oidsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("object identifier(oid), maximum 4 billion");
#define OIDOID 26
! DATA(insert OID = 27 ( tid PGNSP PGUID 6 f b U f t \054 0 0 1010 tidin tidout tidrecv tidsend - - - s p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("(block, offset), physical location of tuple");
#define TIDOID 27
! DATA(insert OID = 28 ( xid PGNSP PGUID 4 t b U f t \054 0 0 1011 xidin xidout xidrecv xidsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("transaction id");
#define XIDOID 28
! DATA(insert OID = 29 ( cid PGNSP PGUID 4 t b U f t \054 0 0 1012 cidin cidout cidrecv cidsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("command identifier type, sequence in transaction id");
#define CIDOID 29
! DATA(insert OID = 30 ( oidvector PGNSP PGUID -1 f b A f t \054 0 26 1013 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("array of oids, used in system tables");
#define OIDVECTOROID 30
/* hand-built rowtype entries for bootstrapped catalogs */
/* NB: OIDs assigned here must match the BKI_ROWTYPE_OID declarations */
! DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c C f t \054 1247 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c C f t \054 1249 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c C f t \054 1255 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c C f t \054 1259 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
/* OIDS 100 - 199 */
! DATA(insert OID = 142 ( xml PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("XML content");
#define XMLOID 142
! DATA(insert OID = 143 ( _xml PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 194 ( pg_node_tree PGNSP PGUID -1 f b S f t \054 0 0 0 pg_node_tree_in pg_node_tree_out pg_node_tree_recv pg_node_tree_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("string representing an internal node tree");
#define PGNODETREEOID 194
/* OIDS 200 - 299 */
! DATA(insert OID = 210 ( smgr PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("storage manager");
/* OIDS 300 - 399 */
*************** DESCR("storage manager");
*** 359,589 ****
/* OIDS 500 - 599 */
/* OIDS 600 - 699 */
! DATA(insert OID = 600 ( point PGNSP PGUID 16 f b G f t \054 0 701 1017 point_in point_out point_recv point_send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("geometric point '(x, y)'");
#define POINTOID 600
! DATA(insert OID = 601 ( lseg PGNSP PGUID 32 f b G f t \054 0 600 1018 lseg_in lseg_out lseg_recv lseg_send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("geometric line segment '(pt1,pt2)'");
#define LSEGOID 601
! DATA(insert OID = 602 ( path PGNSP PGUID -1 f b G f t \054 0 0 1019 path_in path_out path_recv path_send - - - d x f 0 -1 0 _null_ _null_ ));
DESCR("geometric path '(pt1,...)'");
#define PATHOID 602
! DATA(insert OID = 603 ( box PGNSP PGUID 32 f b G f t \073 0 600 1020 box_in box_out box_recv box_send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("geometric box '(lower left,upper right)'");
#define BOXOID 603
! DATA(insert OID = 604 ( polygon PGNSP PGUID -1 f b G f t \054 0 0 1027 poly_in poly_out poly_recv poly_send - - - d x f 0 -1 0 _null_ _null_ ));
DESCR("geometric polygon '(pt1,...)'");
#define POLYGONOID 604
! DATA(insert OID = 628 ( line PGNSP PGUID 32 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("geometric line (not implemented)");
#define LINEOID 628
! DATA(insert OID = 629 ( _line PGNSP PGUID -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
DESCR("");
/* OIDS 700 - 799 */
! DATA(insert OID = 700 ( float4 PGNSP PGUID 4 FLOAT4PASSBYVAL b N f t \054 0 0 1021 float4in float4out float4recv float4send - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("single-precision floating point number, 4-byte storage");
#define FLOAT4OID 700
! DATA(insert OID = 701 ( float8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N t t \054 0 0 1022 float8in float8out float8recv float8send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("double-precision floating point number, 8-byte storage");
#define FLOAT8OID 701
! DATA(insert OID = 702 ( abstime PGNSP PGUID 4 t b D f t \054 0 0 1023 abstimein abstimeout abstimerecv abstimesend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("absolute, limited-range date and time (Unix system time)");
#define ABSTIMEOID 702
! DATA(insert OID = 703 ( reltime PGNSP PGUID 4 t b T f t \054 0 0 1024 reltimein reltimeout reltimerecv reltimesend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("relative, limited-range time interval (Unix delta time)");
#define RELTIMEOID 703
! DATA(insert OID = 704 ( tinterval PGNSP PGUID 12 f b T f t \054 0 0 1025 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("(abstime,abstime), time interval");
#define TINTERVALOID 704
! DATA(insert OID = 705 ( unknown PGNSP PGUID -2 f b X f t \054 0 0 0 unknownin unknownout unknownrecv unknownsend - - - c p f 0 -1 0 _null_ _null_ ));
DESCR("");
#define UNKNOWNOID 705
! DATA(insert OID = 718 ( circle PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("geometric circle '(center,radius)'");
#define CIRCLEOID 718
! DATA(insert OID = 719 ( _circle PGNSP PGUID -1 f b A f t \054 0 718 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 790 ( money PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 _null_ _null_ ));
DESCR("monetary amounts, $d,ddd.cc");
#define CASHOID 790
! DATA(insert OID = 791 ( _money PGNSP PGUID -1 f b A f t \054 0 790 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
/* OIDS 800 - 899 */
! DATA(insert OID = 829 ( macaddr PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("XX:XX:XX:XX:XX:XX, MAC address");
#define MACADDROID 829
! DATA(insert OID = 869 ( inet PGNSP PGUID -1 f b I t t \054 0 0 1041 inet_in inet_out inet_recv inet_send - - - i m f 0 -1 0 _null_ _null_ ));
DESCR("IP address/netmask, host address, netmask optional");
#define INETOID 869
! DATA(insert OID = 650 ( cidr PGNSP PGUID -1 f b I f t \054 0 0 651 cidr_in cidr_out cidr_recv cidr_send - - - i m f 0 -1 0 _null_ _null_ ));
DESCR("network IP address/netmask, network address");
#define CIDROID 650
/* OIDS 900 - 999 */
/* OIDS 1000 - 1099 */
! DATA(insert OID = 1000 ( _bool PGNSP PGUID -1 f b A f t \054 0 16 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1001 ( _bytea PGNSP PGUID -1 f b A f t \054 0 17 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1002 ( _char PGNSP PGUID -1 f b A f t \054 0 18 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b A f t \054 0 19 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b A f t \054 0 21 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b A f t \054 0 22 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b A f t \054 0 23 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
#define INT4ARRAYOID 1007
! DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b A f t \054 0 24 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b A f t \054 0 25 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
#define TEXTARRAYOID 1009
! DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b A f t \054 0 26 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b A f t \054 0 27 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b A f t \054 0 28 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b A f t \054 0 29 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1013 ( _oidvector PGNSP PGUID -1 f b A f t \054 0 30 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1014 ( _bpchar PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1015 ( _varchar PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1016 ( _int8 PGNSP PGUID -1 f b A f t \054 0 20 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1017 ( _point PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1018 ( _lseg PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1019 ( _path PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1020 ( _box PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1021 ( _float4 PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
#define FLOAT4ARRAYOID 1021
! DATA(insert OID = 1022 ( _float8 PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1033 ( aclitem PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("access control list");
#define ACLITEMOID 1033
! DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1263 ( _cstring PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
#define CSTRINGARRAYOID 1263
! DATA(insert OID = 1042 ( bpchar PGNSP PGUID -1 f b S f t \054 0 0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 _null_ _null_ ));
DESCR("char(length), blank-padded string, fixed storage length");
#define BPCHAROID 1042
! DATA(insert OID = 1043 ( varchar PGNSP PGUID -1 f b S f t \054 0 0 1015 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f 0 -1 0 _null_ _null_ ));
DESCR("varchar(length), non-blank-padded string, variable storage length");
#define VARCHAROID 1043
! DATA(insert OID = 1082 ( date PGNSP PGUID 4 t b D f t \054 0 0 1182 date_in date_out date_recv date_send - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("date");
#define DATEOID 1082
! DATA(insert OID = 1083 ( time PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1183 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f 0 -1 0 _null_ _null_ ));
DESCR("time of day");
#define TIMEOID 1083
/* OIDS 1100 - 1199 */
! DATA(insert OID = 1114 ( timestamp PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 _null_ _null_ ));
DESCR("date and time");
#define TIMESTAMPOID 1114
! DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1182 ( _date PGNSP PGUID -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1183 ( _time PGNSP PGUID -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1184 ( timestamptz PGNSP PGUID 8 FLOAT8PASSBYVAL b D t t \054 0 0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 _null_ _null_ ));
DESCR("date and time with time zone");
#define TIMESTAMPTZOID 1184
! DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0 1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1186 ( interval PGNSP PGUID 16 f b T t t \054 0 0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 _null_ _null_ ));
DESCR("@ <number> <units>, time interval");
#define INTERVALOID 1186
! DATA(insert OID = 1187 ( _interval PGNSP PGUID -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout - d x f 0 -1 0 _null_ _null_ ));
/* OIDS 1200 - 1299 */
! DATA(insert OID = 1231 ( _numeric PGNSP PGUID -1 f b A f t \054 0 1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1266 ( timetz PGNSP PGUID 12 f b D f t \054 0 0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 _null_ _null_ ));
DESCR("time of day with time zone");
#define TIMETZOID 1266
! DATA(insert OID = 1270 ( _timetz PGNSP PGUID -1 f b A f t \054 0 1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout - d x f 0 -1 0 _null_ _null_ ));
/* OIDS 1500 - 1599 */
! DATA(insert OID = 1560 ( bit PGNSP PGUID -1 f b V f t \054 0 0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 _null_ _null_ ));
DESCR("fixed-length bit string");
#define BITOID 1560
! DATA(insert OID = 1561 ( _bit PGNSP PGUID -1 f b A f t \054 0 1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1562 ( varbit PGNSP PGUID -1 f b V t t \054 0 0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 _null_ _null_ ));
DESCR("variable-length bit string");
#define VARBITOID 1562
! DATA(insert OID = 1563 ( _varbit PGNSP PGUID -1 f b A f t \054 0 1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout - i x f 0 -1 0 _null_ _null_ ));
/* OIDS 1600 - 1699 */
/* OIDS 1700 - 1799 */
! DATA(insert OID = 1700 ( numeric PGNSP PGUID -1 f b N f t \054 0 0 1231 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f 0 -1 0 _null_ _null_ ));
DESCR("numeric(precision, decimal), arbitrary precision number");
#define NUMERICOID 1700
! DATA(insert OID = 1790 ( refcursor PGNSP PGUID -1 f b U f t \054 0 0 2201 textin textout textrecv textsend - - - i x f 0 -1 0 _null_ _null_ ));
DESCR("reference to cursor (portal name)");
#define REFCURSOROID 1790
/* OIDS 2200 - 2299 */
! DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 2202 ( regprocedure PGNSP PGUID 4 t b N f t \054 0 0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered procedure (with args)");
#define REGPROCEDUREOID 2202
! DATA(insert OID = 2203 ( regoper PGNSP PGUID 4 t b N f t \054 0 0 2208 regoperin regoperout regoperrecv regopersend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered operator");
#define REGOPEROID 2203
! DATA(insert OID = 2204 ( regoperator PGNSP PGUID 4 t b N f t \054 0 0 2209 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered operator (with args)");
#define REGOPERATOROID 2204
! DATA(insert OID = 2205 ( regclass PGNSP PGUID 4 t b N f t \054 0 0 2210 regclassin regclassout regclassrecv regclasssend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered class");
#define REGCLASSOID 2205
! DATA(insert OID = 2206 ( regtype PGNSP PGUID 4 t b N f t \054 0 0 2211 regtypein regtypeout regtyperecv regtypesend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered type");
#define REGTYPEOID 2206
! DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
#define REGTYPEARRAYOID 2211
/* uuid */
! DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 _null_ _null_ ));
DESCR("UUID datatype");
! DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
/* text search */
! DATA(insert OID = 3614 ( tsvector PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 _null_ _null_ ));
DESCR("text representation for text search");
#define TSVECTOROID 3614
! DATA(insert OID = 3642 ( gtsvector PGNSP PGUID -1 f b U f t \054 0 0 3644 gtsvectorin gtsvectorout - - - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("GiST index internal text representation for text search");
#define GTSVECTOROID 3642
! DATA(insert OID = 3615 ( tsquery PGNSP PGUID -1 f b U f t \054 0 0 3645 tsqueryin tsqueryout tsqueryrecv tsquerysend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("query representation for text search");
#define TSQUERYOID 3615
! DATA(insert OID = 3734 ( regconfig PGNSP PGUID 4 t b N f t \054 0 0 3735 regconfigin regconfigout regconfigrecv regconfigsend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered text search configuration");
#define REGCONFIGOID 3734
! DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("registered text search dictionary");
#define REGDICTIONARYOID 3769
! DATA(insert OID = 3643 ( _tsvector PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 3644 ( _gtsvector PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 _null_ _null_ ));
DESCR("txid snapshot");
! DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
/*
* pseudo-types
--- 373,603 ----
/* OIDS 500 - 599 */
/* OIDS 600 - 699 */
! DATA(insert OID = 600 ( point PGNSP PGUID 16 f b G f t \054 0 701 1017 point_in point_out point_recv point_send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric point '(x, y)'");
#define POINTOID 600
! DATA(insert OID = 601 ( lseg PGNSP PGUID 32 f b G f t \054 0 600 1018 lseg_in lseg_out lseg_recv lseg_send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric line segment '(pt1,pt2)'");
#define LSEGOID 601
! DATA(insert OID = 602 ( path PGNSP PGUID -1 f b G f t \054 0 0 1019 path_in path_out path_recv path_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric path '(pt1,...)'");
#define PATHOID 602
! DATA(insert OID = 603 ( box PGNSP PGUID 32 f b G f t \073 0 600 1020 box_in box_out box_recv box_send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric box '(lower left,upper right)'");
#define BOXOID 603
! DATA(insert OID = 604 ( polygon PGNSP PGUID -1 f b G f t \054 0 0 1027 poly_in poly_out poly_recv poly_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric polygon '(pt1,...)'");
#define POLYGONOID 604
! DATA(insert OID = 628 ( line PGNSP PGUID 32 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric line (not implemented)");
#define LINEOID 628
! DATA(insert OID = 629 ( _line PGNSP PGUID -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("");
/* OIDS 700 - 799 */
! DATA(insert OID = 700 ( float4 PGNSP PGUID 4 FLOAT4PASSBYVAL b N f t \054 0 0 1021 float4in float4out float4recv float4send - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("single-precision floating point number, 4-byte storage");
#define FLOAT4OID 700
! DATA(insert OID = 701 ( float8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N t t \054 0 0 1022 float8in float8out float8recv float8send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("double-precision floating point number, 8-byte storage");
#define FLOAT8OID 701
! DATA(insert OID = 702 ( abstime PGNSP PGUID 4 t b D f t \054 0 0 1023 abstimein abstimeout abstimerecv abstimesend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("absolute, limited-range date and time (Unix system time)");
#define ABSTIMEOID 702
! DATA(insert OID = 703 ( reltime PGNSP PGUID 4 t b T f t \054 0 0 1024 reltimein reltimeout reltimerecv reltimesend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("relative, limited-range time interval (Unix delta time)");
#define RELTIMEOID 703
! DATA(insert OID = 704 ( tinterval PGNSP PGUID 12 f b T f t \054 0 0 1025 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("(abstime,abstime), time interval");
#define TINTERVALOID 704
! DATA(insert OID = 705 ( unknown PGNSP PGUID -2 f b X f t \054 0 0 0 unknownin unknownout unknownrecv unknownsend - - - c p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("");
#define UNKNOWNOID 705
! DATA(insert OID = 718 ( circle PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("geometric circle '(center,radius)'");
#define CIRCLEOID 718
! DATA(insert OID = 719 ( _circle PGNSP PGUID -1 f b A f t \054 0 718 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 790 ( money PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("monetary amounts, $d,ddd.cc");
#define CASHOID 790
! DATA(insert OID = 791 ( _money PGNSP PGUID -1 f b A f t \054 0 790 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
/* OIDS 800 - 899 */
! DATA(insert OID = 829 ( macaddr PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("XX:XX:XX:XX:XX:XX, MAC address");
#define MACADDROID 829
! DATA(insert OID = 869 ( inet PGNSP PGUID -1 f b I t t \054 0 0 1041 inet_in inet_out inet_recv inet_send - - - i m f 0 -1 0 -1 f _null_ _null_ ));
DESCR("IP address/netmask, host address, netmask optional");
#define INETOID 869
! DATA(insert OID = 650 ( cidr PGNSP PGUID -1 f b I f t \054 0 0 651 cidr_in cidr_out cidr_recv cidr_send - - - i m f 0 -1 0 -1 f _null_ _null_ ));
DESCR("network IP address/netmask, network address");
#define CIDROID 650
/* OIDS 900 - 999 */
/* OIDS 1000 - 1099 */
! DATA(insert OID = 1000 ( _bool PGNSP PGUID -1 f b A f t \054 0 16 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1001 ( _bytea PGNSP PGUID -1 f b A f t \054 0 17 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1002 ( _char PGNSP PGUID -1 f b A f t \054 0 18 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b A f t \054 0 19 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b A f t \054 0 21 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b A f t \054 0 22 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b A f t \054 0 23 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
#define INT4ARRAYOID 1007
! DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b A f t \054 0 24 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b A f t \054 0 25 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
#define TEXTARRAYOID 1009
! DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b A f t \054 0 26 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b A f t \054 0 27 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b A f t \054 0 28 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b A f t \054 0 29 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1013 ( _oidvector PGNSP PGUID -1 f b A f t \054 0 30 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1014 ( _bpchar PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1015 ( _varchar PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1016 ( _int8 PGNSP PGUID -1 f b A f t \054 0 20 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1017 ( _point PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1018 ( _lseg PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1019 ( _path PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1020 ( _box PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1021 ( _float4 PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
#define FLOAT4ARRAYOID 1021
! DATA(insert OID = 1022 ( _float8 PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1033 ( aclitem PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("access control list");
#define ACLITEMOID 1033
! DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1263 ( _cstring PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
#define CSTRINGARRAYOID 1263
! DATA(insert OID = 1042 ( bpchar PGNSP PGUID -1 f b S f t \054 0 0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("char(length), blank-padded string, fixed storage length");
#define BPCHAROID 1042
! DATA(insert OID = 1043 ( varchar PGNSP PGUID -1 f b S f t \054 0 0 1015 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("varchar(length), non-blank-padded string, variable storage length");
#define VARCHAROID 1043
! DATA(insert OID = 1082 ( date PGNSP PGUID 4 t b D f t \054 0 0 1182 date_in date_out date_recv date_send - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("date");
#define DATEOID 1082
! DATA(insert OID = 1083 ( time PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1183 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("time of day");
#define TIMEOID 1083
/* OIDS 1100 - 1199 */
! DATA(insert OID = 1114 ( timestamp PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("date and time");
#define TIMESTAMPOID 1114
! DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1182 ( _date PGNSP PGUID -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1183 ( _time PGNSP PGUID -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1184 ( timestamptz PGNSP PGUID 8 FLOAT8PASSBYVAL b D t t \054 0 0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("date and time with time zone");
#define TIMESTAMPTZOID 1184
! DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0 1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout - d x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1186 ( interval PGNSP PGUID 16 f b T t t \054 0 0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("@ <number> <units>, time interval");
#define INTERVALOID 1186
! DATA(insert OID = 1187 ( _interval PGNSP PGUID -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout - d x f 0 -1 0 -1 f _null_ _null_ ));
/* OIDS 1200 - 1299 */
! DATA(insert OID = 1231 ( _numeric PGNSP PGUID -1 f b A f t \054 0 1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1266 ( timetz PGNSP PGUID 12 f b D f t \054 0 0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("time of day with time zone");
#define TIMETZOID 1266
! DATA(insert OID = 1270 ( _timetz PGNSP PGUID -1 f b A f t \054 0 1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout - d x f 0 -1 0 -1 f _null_ _null_ ));
/* OIDS 1500 - 1599 */
! DATA(insert OID = 1560 ( bit PGNSP PGUID -1 f b V f t \054 0 0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("fixed-length bit string");
#define BITOID 1560
! DATA(insert OID = 1561 ( _bit PGNSP PGUID -1 f b A f t \054 0 1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 1562 ( varbit PGNSP PGUID -1 f b V t t \054 0 0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("variable-length bit string");
#define VARBITOID 1562
! DATA(insert OID = 1563 ( _varbit PGNSP PGUID -1 f b A f t \054 0 1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout - i x f 0 -1 0 -1 f _null_ _null_ ));
/* OIDS 1600 - 1699 */
/* OIDS 1700 - 1799 */
! DATA(insert OID = 1700 ( numeric PGNSP PGUID -1 f b N f t \054 0 0 1231 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f 0 -1 0 -1 f _null_ _null_ ));
DESCR("numeric(precision, decimal), arbitrary precision number");
#define NUMERICOID 1700
! DATA(insert OID = 1790 ( refcursor PGNSP PGUID -1 f b U f t \054 0 0 2201 textin textout textrecv textsend - - - i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("reference to cursor (portal name)");
#define REFCURSOROID 1790
/* OIDS 2200 - 2299 */
! DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 2202 ( regprocedure PGNSP PGUID 4 t b N f t \054 0 0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered procedure (with args)");
#define REGPROCEDUREOID 2202
! DATA(insert OID = 2203 ( regoper PGNSP PGUID 4 t b N f t \054 0 0 2208 regoperin regoperout regoperrecv regopersend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered operator");
#define REGOPEROID 2203
! DATA(insert OID = 2204 ( regoperator PGNSP PGUID 4 t b N f t \054 0 0 2209 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered operator (with args)");
#define REGOPERATOROID 2204
! DATA(insert OID = 2205 ( regclass PGNSP PGUID 4 t b N f t \054 0 0 2210 regclassin regclassout regclassrecv regclasssend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered class");
#define REGCLASSOID 2205
! DATA(insert OID = 2206 ( regtype PGNSP PGUID 4 t b N f t \054 0 0 2211 regtypein regtypeout regtyperecv regtypesend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered type");
#define REGTYPEOID 2206
! DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
#define REGTYPEARRAYOID 2211
/* uuid */
! DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("UUID datatype");
! DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
/* text search */
! DATA(insert OID = 3614 ( tsvector PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("text representation for text search");
#define TSVECTOROID 3614
! DATA(insert OID = 3642 ( gtsvector PGNSP PGUID -1 f b U f t \054 0 0 3644 gtsvectorin gtsvectorout - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("GiST index internal text representation for text search");
#define GTSVECTOROID 3642
! DATA(insert OID = 3615 ( tsquery PGNSP PGUID -1 f b U f t \054 0 0 3645 tsqueryin tsqueryout tsqueryrecv tsquerysend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("query representation for text search");
#define TSQUERYOID 3615
! DATA(insert OID = 3734 ( regconfig PGNSP PGUID 4 t b N f t \054 0 0 3735 regconfigin regconfigout regconfigrecv regconfigsend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered text search configuration");
#define REGCONFIGOID 3734
! DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend - - - i p f 0 -1 0 -1 f _null_ _null_ ));
DESCR("registered text search dictionary");
#define REGDICTIONARYOID 3769
! DATA(insert OID = 3643 ( _tsvector PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 3644 ( _gtsvector PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 -1 f _null_ _null_ ));
! DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
DESCR("txid snapshot");
! DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
/*
* pseudo-types
*************** DATA(insert OID = 2949 ( _txid_snapshot
*** 598,628 ****
* but there is now support for it in records and arrays. Perhaps we should
* just treat it as a regular base type?
*/
! DATA(insert OID = 2249 ( record PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
#define RECORDOID 2249
! DATA(insert OID = 2287 ( _record PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
#define RECORDARRAYOID 2287
! DATA(insert OID = 2275 ( cstring PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 _null_ _null_ ));
#define CSTRINGOID 2275
! DATA(insert OID = 2276 ( any PGNSP PGUID 4 t p P f t \054 0 0 0 any_in any_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define ANYOID 2276
! DATA(insert OID = 2277 ( anyarray PGNSP PGUID -1 f p P f t \054 0 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f 0 -1 0 _null_ _null_ ));
#define ANYARRAYOID 2277
! DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define VOIDOID 2278
! DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define TRIGGEROID 2279
! DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define LANGUAGE_HANDLEROID 2280
! DATA(insert OID = 2281 ( internal PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 _null_ _null_ ));
#define INTERNALOID 2281
! DATA(insert OID = 2282 ( opaque PGNSP PGUID 4 t p P f t \054 0 0 0 opaque_in opaque_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define OPAQUEOID 2282
! DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p P f t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define ANYELEMENTOID 2283
! DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p P f t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define ANYNONARRAYOID 2776
! DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define ANYENUMOID 3500
--- 612,642 ----
* but there is now support for it in records and arrays. Perhaps we should
* just treat it as a regular base type?
*/
! DATA(insert OID = 2249 ( record PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
#define RECORDOID 2249
! DATA(insert OID = 2287 ( _record PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
#define RECORDARRAYOID 2287
! DATA(insert OID = 2275 ( cstring PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 -1 f _null_ _null_ ));
#define CSTRINGOID 2275
! DATA(insert OID = 2276 ( any PGNSP PGUID 4 t p P f t \054 0 0 0 any_in any_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define ANYOID 2276
! DATA(insert OID = 2277 ( anyarray PGNSP PGUID -1 f p P f t \054 0 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f 0 -1 0 -1 f _null_ _null_ ));
#define ANYARRAYOID 2277
! DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define VOIDOID 2278
! DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define TRIGGEROID 2279
! DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define LANGUAGE_HANDLEROID 2280
! DATA(insert OID = 2281 ( internal PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 -1 f _null_ _null_ ));
#define INTERNALOID 2281
! DATA(insert OID = 2282 ( opaque PGNSP PGUID 4 t p P f t \054 0 0 0 opaque_in opaque_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define OPAQUEOID 2282
! DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p P f t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define ANYELEMENTOID 2283
! DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p P f t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define ANYNONARRAYOID 2776
! DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 -1 f _null_ _null_ ));
#define ANYENUMOID 3500
diff --git a/src/include/catalog/pg_type_fn.h b/src/include/catalog/pg_type_fn.h
index cc72350..bc34568 100644
*** a/src/include/catalog/pg_type_fn.h
--- b/src/include/catalog/pg_type_fn.h
*************** extern Oid TypeCreate(Oid newTypeOid,
*** 50,56 ****
char storage,
int32 typeMod,
int32 typNDims,
! bool typeNotNull);
extern void GenerateTypeDependencies(Oid typeNamespace,
Oid typeObjectId,
--- 50,58 ----
char storage,
int32 typeMod,
int32 typNDims,
! bool typeNotNull,
! int32 typeNLabels,
! bool typeSorted);
extern void GenerateTypeDependencies(Oid typeNamespace,
Oid typeObjectId,
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index 2bff7e1..9faf3ea 100644
*** a/src/include/commands/typecmds.h
--- b/src/include/commands/typecmds.h
*************** extern void RemoveTypes(DropStmt *drop);
*** 24,29 ****
--- 24,30 ----
extern void RemoveTypeById(Oid typeOid);
extern void DefineDomain(CreateDomainStmt *stmt);
extern void DefineEnum(CreateEnumStmt *stmt);
+ extern void AlterEnum (AlterEnumStmt *stmt);
extern Oid DefineCompositeType(const RangeVar *typevar, List *coldeflist);
extern Oid AssignTypeArrayOid(void);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 0d33a2e..139c8ed 100644
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
*************** typedef enum NodeTag
*** 348,354 ****
T_DropUserMappingStmt,
T_AlterTableSpaceOptionsStmt,
T_SecLabelStmt,
!
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
*/
--- 348,354 ----
T_DropUserMappingStmt,
T_AlterTableSpaceOptionsStmt,
T_SecLabelStmt,
! T_AlterEnumStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
*/
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index ca225d0..1c3397e 100644
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
*************** typedef struct CreateEnumStmt
*** 2192,2197 ****
--- 2192,2211 ----
/* ----------------------
+ * Alter Type Statement, enum types
+ * ----------------------
+ */
+ typedef struct AlterEnumStmt
+ {
+ NodeTag type;
+ List *typeName; /* qualified name (list of Value strings) */
+ char *newVal; /* new enum value */
+ char *newValNeighbour;/* neighbouring enum value */
+ bool newValIsAfter; /* new enum value is after neighbour? */
+ } AlterEnumStmt;
+
+
+ /* ----------------------
* Create View Statement
* ----------------------
*/
diff --git a/src/test/regress/expected/enum.out b/src/test/regress/expected/enum.out
index 56240c0..f3c86b1 100644
*** a/src/test/regress/expected/enum.out
--- b/src/test/regress/expected/enum.out
*************** ERROR: invalid input value for enum rai
*** 25,30 ****
--- 25,122 ----
LINE 1: SELECT 'mauve'::rainbow;
^
--
+ -- adding new values
+ --
+ CREATE TYPE planets AS ENUM ( 'venus', 'earth', 'mars' );
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+ enumlabel | enumsortorder
+ -----------+---------------
+ venus | 1
+ earth | 2
+ mars | 3
+ (3 rows)
+
+ ALTER TYPE planets ADD 'uranus';
+ SELECT typnlabels, typsorted
+ FROM pg_type
+ WHERE oid = 'planets'::regtype;
+ typnlabels | typsorted
+ ------------+-----------
+ 4 | t
+ (1 row)
+
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+ enumlabel | enumsortorder
+ -----------+---------------
+ venus | 1
+ earth | 2
+ mars | 3
+ uranus | 4
+ (4 rows)
+
+ ALTER TYPE planets ADD 'mercury' BEFORE 'venus';
+ ALTER TYPE planets ADD 'saturn' BEFORE 'uranus';
+ ALTER TYPE planets ADD 'jupiter' AFTER 'mars';
+ ALTER TYPE planets ADD 'neptune' AFTER 'uranus';
+ SELECT typnlabels, typsorted
+ FROM pg_type
+ WHERE oid = 'planets'::regtype;
+ typnlabels | typsorted
+ ------------+-----------
+ 8 | f
+ (1 row)
+
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by CAST(enumlabel AS planets);
+ enumlabel | enumsortorder
+ -----------+---------------
+ mercury | 1
+ venus | 2
+ earth | 3
+ mars | 4
+ jupiter | 5
+ saturn | 6
+ uranus | 7
+ neptune | 8
+ (8 rows)
+
+ select 'mars'::planets > 'mercury' as using_sortorder;
+ using_sortorder
+ -----------------
+ t
+ (1 row)
+
+ \dT+ planets
+ List of data types
+ Schema | Name | Internal name | Size | Elements | Description
+ --------+---------+---------------+------+----------+-------------
+ public | planets | planets | 4 | mercury +|
+ | | | | venus +|
+ | | | | earth +|
+ | | | | mars +|
+ | | | | jupiter +|
+ | | | | saturn +|
+ | | | | uranus +|
+ | | | | neptune |
+ (1 row)
+
+ -- errors for adding labels
+ ALTER TYPE planets ADD
+ 'plutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutopluto';
+ ERROR: invalid enum label "plutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutopluto"
+ DETAIL: Labels must be 63 characters or less.
+ ALTER TYPE planets ADD 'pluto' AFTER 'zeus';
+ ERROR: "zeus" is not an existing label.
+ DROP TYPE planets;
+ --
-- Basic table creation, row selection
--
CREATE TABLE enumtest (col rainbow);
*************** SELECT COUNT(*) FROM pg_type WHERE typna
*** 403,409 ****
SELECT * FROM pg_enum WHERE NOT EXISTS
(SELECT 1 FROM pg_type WHERE pg_type.oid = enumtypid);
! enumtypid | enumlabel
! -----------+-----------
(0 rows)
--- 495,501 ----
SELECT * FROM pg_enum WHERE NOT EXISTS
(SELECT 1 FROM pg_type WHERE pg_type.oid = enumtypid);
! enumtypid | enumlabel | enumsortorder
! -----------+-----------+---------------
(0 rows)
diff --git a/src/test/regress/sql/enum.sql b/src/test/regress/sql/enum.sql
index 387e8e7..6092706 100644
*** a/src/test/regress/sql/enum.sql
--- b/src/test/regress/sql/enum.sql
*************** SELECT 'red'::rainbow;
*** 16,21 ****
--- 16,71 ----
SELECT 'mauve'::rainbow;
--
+ -- adding new values
+ --
+
+ CREATE TYPE planets AS ENUM ( 'venus', 'earth', 'mars' );
+
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+
+ ALTER TYPE planets ADD 'uranus';
+
+ SELECT typnlabels, typsorted
+ FROM pg_type
+ WHERE oid = 'planets'::regtype;
+
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+
+ ALTER TYPE planets ADD 'mercury' BEFORE 'venus';
+
+ ALTER TYPE planets ADD 'saturn' BEFORE 'uranus';
+
+ ALTER TYPE planets ADD 'jupiter' AFTER 'mars';
+
+ ALTER TYPE planets ADD 'neptune' AFTER 'uranus';
+
+ SELECT typnlabels, typsorted
+ FROM pg_type
+ WHERE oid = 'planets'::regtype;
+
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by CAST(enumlabel AS planets);
+
+ select 'mars'::planets > 'mercury' as using_sortorder;
+
+ \dT+ planets
+
+ -- errors for adding labels
+ ALTER TYPE planets ADD
+ 'plutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutopluto';
+
+ ALTER TYPE planets ADD 'pluto' AFTER 'zeus';
+
+ DROP TYPE planets;
+ --
-- Basic table creation, row selection
--
CREATE TABLE enumtest (col rainbow);
On 10/15/2010 04:33 AM, Dean Rasheed wrote:
I started looking at this last night, but ran out of time. I'll
continue this evening / over the weekend. Here are my comments so far:Patch applies cleanly to current git master with no offsets.
Compiles cleanly with no warnings.
Regression tests pass.The regression tests look reasonable, but I'd like to see a test of
\dT+. Also it could be made to exercise the comparison function more
if the test query did an ORDER BY CAST(enumlabel as planets).The docs for ALTER TYPE have been updated. I found a few minor typos,
and also a couple of other sections of the manual that needed
updating.Attached is an updated version with these changes. Andrew, let me know
if you're happy with these tweaks and I'll continue reviewing over the
weekend.
Thanks for the review. This looks good.
cheers
andrew
On 10/15/2010 04:33 AM, Dean Rasheed wrote:
I started looking at this last night, but ran out of time. I'll
continue this evening / over the weekend.
Continuing my review of this patch...
Usability review
----------------
What the patch does:
This patch adds syntax to allow additional enum values to be added to
an enum type, at any position in the list. The syntax has already been
discussed and a broad consensus reached. To me the new syntax seems
very logical and easy to use/remember.
Do we want that?
Yes, I think so. I can think of a few past projects where I could have
used this. A possible future extension would be the ability to remove
enum values, but that is likely to have additional complications, and
I think the number of use cases for it is smaller. I see no reason to
insist that it be part of this patch.
Do we already have it?
No.
Does it follow SQL spec, or the community-agreed behavior?
Not in the SQL spec, but it does follow agreed behaviour.
Does it include pg_dump support (if applicable)?
Yes.
Are there dangers?
None that I can think of.
Have all the bases been covered?
I just noticed that a couple of columns have been added to pg_type, so
there's another bit documentation that needs updating.
Feature test
------------
Does the feature work as advertised?
Yes.
Are there corner cases the author has failed to consider?
None that I could find.
Are there any assertion failures or crashes?
Yes, I'm afraid I managed to provoke a crash by running the regression
tests with -DCATCACHE_FORCE_RELEASE, after spotting a suspicious bit
of code (see below).
Performance review
------------------
Does the patch slow down simple tests?
There is a documented performance penalty when working with enum
values that are not in OID order. To attempt to measure this I created
2 tables, foo and bar, each with 800,000 rows. foo had an enum column
using the planets enum from the regression test, with values not in
OID order. bar had a similar enum column, but with values in the
default OID order.
Performance differences for comparison-heavy operations are most noticable:
SELECT MAX(p) FROM foo;
max
---------
neptune
(1 row)
Time: 132.305 ms
SELECT MAX(p) FROM bar;
max
---------
neptune
(1 row)
Time: 93.313 ms
SELECT p FROM foo ORDER BY p OFFSET 500000 LIMIT 1;
p
--------
saturn
(1 row)
Time: 1516.725 ms
SELECT p FROM bar ORDER BY p OFFSET 500000 LIMIT 1;
p
--------
saturn
(1 row)
Time: 1043.010 ms
(optimised build, asserts off)
That's a bigger performance hit than a I would have expected, even
though it is documented.
enum_ccmp() is using bsearch(), so there's a fair amount of function
call overhead there, and perhaps for small enums, a simple linear scan
would be faster. I'm not sure to what extent this is worth worrying
about.
Code review
-----------
I'm not familar enough with the pg_upgrade code to really comment on it.
Looking at AddEnumLabel() I found it a bit hard to follow, but near
the end of the block used when BEFORE/AFTER is specified, it does
this:
ReleaseCatCacheList(list);
/* are the labels sorted by OID? */
if (result && newelemorder > 1)
result = newOid > HeapTupleGetOid(existing[newelemorder-2]);
if (result && newelemorder < nelems + 1)
result = newOid < HeapTupleGetOid(existing[newelemorder-1]);
It looks to me as though 'existing[...]' is a pointer into a member of
the list that's just been freed, so it risks reading freed memory.
That seems to be confirmed by running the tests with
-DCATCACHE_FORCE_RELEASE. Doing so causes a number of the tests to
fail/crash, but I didn't dig any deeper to confirm that this was the
cause.
For the most part, this patch looks good, but I think there is still a
bit of tidying up to do.
Regards,
Dean
On 16 October 2010 18:25, Dean Rasheed <dean.a.rasheed@gmail.com> wrote:
Are there corner cases the author has failed to consider?
None that I could find.
Are there any assertion failures or crashes?
I just thought of another corner case, which can lead to a crash. The
comparison code assumes that the number of elements in the enumeration
is constant during a query, but that's not necessarily the case. For
example the following will crash it:
CREATE TYPE test_enum AS ENUM('Elem 1');
CREATE OR REPLACE FUNCTION test_fn(a int) RETURNS test_enum AS
$$
DECLARE
new_label text;
BEGIN
new_label := 'Elem '||a;
EXECUTE 'ALTER TYPE test_enum ADD '''||new_label||''' BEFORE ''Elem 1''';
RETURN new_label::test_enum;
END;
$$
LANGUAGE plpgsql;
WITH t(a) AS (SELECT test_fn(i) FROM generate_series(2, 10) g(i))
SELECT MAX(a) from t;
Of course that's a pathalogical example, but we should protect against
it, preferrably without compromising performance in more normal cases.
Regards,
Dean
On 10/17/2010 05:30 AM, Dean Rasheed wrote:
On 16 October 2010 18:25, Dean Rasheed<dean.a.rasheed@gmail.com> wrote:
Are there corner cases the author has failed to consider?
None that I could find.
Are there any assertion failures or crashes?
I just thought of another corner case, which can lead to a crash. The
comparison code assumes that the number of elements in the enumeration
is constant during a query, but that's not necessarily the case. For
example the following will crash it:CREATE TYPE test_enum AS ENUM('Elem 1');
CREATE OR REPLACE FUNCTION test_fn(a int) RETURNS test_enum AS
$$
DECLARE
new_label text;
BEGIN
new_label := 'Elem '||a;
EXECUTE 'ALTER TYPE test_enum ADD '''||new_label||''' BEFORE ''Elem 1''';
RETURN new_label::test_enum;
END;
$$
LANGUAGE plpgsql;WITH t(a) AS (SELECT test_fn(i) FROM generate_series(2, 10) g(i))
SELECT MAX(a) from t;Of course that's a pathalogical example, but we should protect against
it, preferrably without compromising performance in more normal cases.
Yeah, good point. But how do we manage that? Looking up the number of
elements on each function call will cause a performance degradation, I
suspect. I'll think about it, but if you have any ideas please speak up.
I'm fairly sure we should also recheck the cached sorted property for
the same reason, incidentally.
cheers
andrew
Andrew Dunstan <andrew@dunslane.net> writes:
On 10/17/2010 05:30 AM, Dean Rasheed wrote:
I just thought of another corner case, which can lead to a crash. The
comparison code assumes that the number of elements in the enumeration
is constant during a query, but that's not necessarily the case.
...
Of course that's a pathalogical example, but we should protect against
it, preferrably without compromising performance in more normal cases.
Yeah, good point. But how do we manage that?
Why is it crashing? I can see that this sort of thing might lead to
nonsensical answers, but a crash is harder to understand.
regards, tom "haven't read the patch" lane
On 17 October 2010 15:38, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Andrew Dunstan <andrew@dunslane.net> writes:
On 10/17/2010 05:30 AM, Dean Rasheed wrote:
I just thought of another corner case, which can lead to a crash. The
comparison code assumes that the number of elements in the enumeration
is constant during a query, but that's not necessarily the case.
...
Of course that's a pathalogical example, but we should protect against
it, preferrably without compromising performance in more normal cases.Yeah, good point. But how do we manage that?
Why is it crashing? I can see that this sort of thing might lead to
nonsensical answers, but a crash is harder to understand.
Hmm, it's harder than I thought. The crash is because each time it
finds a label it hasn't seen before it adds it to the array of cached
values without checking the array bounds. That array is only as big as
the number of elements in the enum the first time it was called.
Allowing the array to grow would prevent crashes, but not protect
again returning incorrect answers.
Perhaps it should just read and cache all the values the first time it
is called. Then if it ever fails to find a value in the array, it
knows that the enum must have grown, and it can rebuild the whole
array.
Regards,
Dean
Show quoted text
regards, tom "haven't read the patch" lane
On 10/17/2010 10:38 AM, Tom Lane wrote:
Andrew Dunstan<andrew@dunslane.net> writes:
On 10/17/2010 05:30 AM, Dean Rasheed wrote:
I just thought of another corner case, which can lead to a crash. The
comparison code assumes that the number of elements in the enumeration
is constant during a query, but that's not necessarily the case.
...
Of course that's a pathalogical example, but we should protect against
it, preferrably without compromising performance in more normal cases.Yeah, good point. But how do we manage that?
Why is it crashing? I can see that this sort of thing might lead to
nonsensical answers, but a crash is harder to understand.regards, tom "haven't read the patch" lane
Heh.
I've been deep in buildfarm work, but I'll look at this now to see what
I can find.
cheers
andrew
Dean Rasheed <dean.a.rasheed@gmail.com> writes:
Hmm, it's harder than I thought. The crash is because each time it
finds a label it hasn't seen before it adds it to the array of cached
values without checking the array bounds. That array is only as big as
the number of elements in the enum the first time it was called.
[ scratches head... ] And where does it get that number of elements
from, if not by doing the same work that would allow it to fill the
array completely? Something seems ill-designed here.
Allowing the array to grow would prevent crashes, but not protect
again returning incorrect answers.
Well, one of the questions here is exactly how wrong the answers can
be. Offhand, it seems to me that comparisons of two existing entries
can never be falsified by adding a new entry, so I'm not seeing that
there could be any real problem. If we allowed rearrangement of the
sort order of existing entries, it'd be problematic.
Perhaps it should just read and cache all the values the first time it
is called. Then if it ever fails to find a value in the array, it
knows that the enum must have grown, and it can rebuild the whole
array.
This is kept in typcache, right? ISTM the right thing to do is arrange
to invalidate the cached array when a cache flush event occurs, and
rebuild the whole array on next use. Then you just throw an error if
you're passed a value that isn't there.
regards, tom lane
On 17 October 2010 16:49, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Dean Rasheed <dean.a.rasheed@gmail.com> writes:
Hmm, it's harder than I thought. The crash is because each time it
finds a label it hasn't seen before it adds it to the array of cached
values without checking the array bounds. That array is only as big as
the number of elements in the enum the first time it was called.[ scratches head... ] And where does it get that number of elements
from, if not by doing the same work that would allow it to fill the
array completely? Something seems ill-designed here.
Hmm. That's coming from a new column added to pg_type (typnlabels).
Perhaps that's not safe though. Are there potential race conditions
there?
Allowing the array to grow would prevent crashes, but not protect
again returning incorrect answers.Well, one of the questions here is exactly how wrong the answers can
be. Offhand, it seems to me that comparisons of two existing entries
can never be falsified by adding a new entry, so I'm not seeing that
there could be any real problem. If we allowed rearrangement of the
sort order of existing entries, it'd be problematic.Perhaps it should just read and cache all the values the first time it
is called. Then if it ever fails to find a value in the array, it
knows that the enum must have grown, and it can rebuild the whole
array.This is kept in typcache, right? ISTM the right thing to do is arrange
to invalidate the cached array when a cache flush event occurs, and
rebuild the whole array on next use. Then you just throw an error if
you're passed a value that isn't there.
Makes sense.
Regards,
Dean
Show quoted text
regards, tom lane
Dean Rasheed <dean.a.rasheed@gmail.com> writes:
On 17 October 2010 16:49, Tom Lane <tgl@sss.pgh.pa.us> wrote:
[ scratches head... ] �And where does it get that number of elements
from, if not by doing the same work that would allow it to fill the
array completely? �Something seems ill-designed here.
Hmm. That's coming from a new column added to pg_type (typnlabels).
Perhaps that's not safe though. Are there potential race conditions
there?
I knew I shoulda read this patch ;-). That seems a lot more invasive
than this feature justifies. And I share your qualms about whether it's
race-condition-proof. We don't have very much locking on pg_type
entries, so making a hard assumption about consistency between two
different catalogs seems pretty risky.
The way I'd be inclined to design this is that altering an enum doesn't
change its pg_type entry at all, just add another row to pg_enum.
When first needing to compare values of an enum, load up the typcache
entry for it. This involves scanning all the entries for that type OID
in pg_enum, and determining from that whether you can compare the easy
way or not. If not, build the array that tells you how to sort, and put
it in the typcache entry.
The missing piece in this is how to determine when the typcache entry
has to be flushed. That should be driven by sinval signalling. There
are two different ways you could do it:
1. Watch for SI events on pg_enum. The problem here is we don't have
a syscache on pg_enum, and there's no obvious other reason to want one.
Also I think you'd have to flush *all* enum typcache entries, since you
couldn't always tell which one had been modified.
2. Watch for SI events on the pg_type row. However, since according
to what I said above an ALTER TYPE wouldn't actually modify the pg_type
row, you'd need to have the ALTER send out a "dummy" SI event. This
is not hard to do --- we have the concept of dummy inval events on
relations already --- but it's a bit ugly because of the risk of
omission. But the number of places that would need to do this seems
small, so I don't think that risk is large.
On balance I like the second idea better.
regards, tom lane
I wrote:
The missing piece in this is how to determine when the typcache entry
has to be flushed. That should be driven by sinval signalling.
On reflection that doesn't seem good enough. Immediately after someone
else has committed an ALTER TYPE, your typcache entry is out of date,
and won't be updated until you get around to noticing the SI signal.
I was thinking that wouldn't matter because you'd never need to make a
comparison involving the new enum value --- it couldn't be in any table
rows you'd see as committed good. But this is wrong because you might
have to make index comparisons involving the new value, even before you
consider that the rows the index entries reference are good.
We could fix that with Dean's idea of reloading the cache whenever
we see that we are being asked to compare a value we don't have in the
cache entry. However, that assumes that we even notice that it's not
in the cache entry. If we're trying to use "fast" comparison then we
wouldn't notice that.
So the hard part of this really is to force other backends to switch
from "fast" to "slow" comparison in a timely fashion when an ALTER makes
that necessary. Right offhand I don't see any good way to do that,
at least not while having the "fast" method as fast as it is now.
regards, tom lane
On 17 October 2010 18:53, Tom Lane <tgl@sss.pgh.pa.us> wrote:
I wrote:
The missing piece in this is how to determine when the typcache entry
has to be flushed. That should be driven by sinval signalling.On reflection that doesn't seem good enough. Immediately after someone
else has committed an ALTER TYPE, your typcache entry is out of date,
and won't be updated until you get around to noticing the SI signal.
I was thinking that wouldn't matter because you'd never need to make a
comparison involving the new enum value --- it couldn't be in any table
rows you'd see as committed good. But this is wrong because you might
have to make index comparisons involving the new value, even before you
consider that the rows the index entries reference are good.We could fix that with Dean's idea of reloading the cache whenever
we see that we are being asked to compare a value we don't have in the
cache entry. However, that assumes that we even notice that it's not
in the cache entry. If we're trying to use "fast" comparison then we
wouldn't notice that.
That makes me think maybe the "fast" and "slow" comparisons should
both be done the same way, having a cache so that we notice
immediately if we get a new value.
Obviously that's not going to be as fast as the current "fast" method,
but the question is, can it be made sufficiently close? I think the
current sort+bsearch method is always going to be significantly
slower, but perhaps a dedicated hash table algorithm might work.
Regards,
Dean
Show quoted text
So the hard part of this really is to force other backends to switch
from "fast" to "slow" comparison in a timely fashion when an ALTER makes
that necessary. Right offhand I don't see any good way to do that,
at least not while having the "fast" method as fast as it is now.regards, tom lane
On 10/17/2010 01:20 PM, Tom Lane wrote:
I knew I shoulda read this patch ;-). That seems a lot more invasive
than this feature justifies. And I share your qualms about whether it's
race-condition-proof. We don't have very much locking on pg_type
entries, so making a hard assumption about consistency between two
different catalogs seems pretty risky.The way I'd be inclined to design this is that altering an enum doesn't
change its pg_type entry at all, just add another row to pg_enum.
When first needing to compare values of an enum, load up the typcache
entry for it. This involves scanning all the entries for that type OID
in pg_enum, and determining from that whether you can compare the easy
way or not. If not, build the array that tells you how to sort, and put
it in the typcache entry.
Perhaps mistakenly I wanted to avoid doing that as it would slow down a
retail comparison quite a lot, especially in the case of an enum with a
very large label set. That's why I put the sorted property and label
count in pg_type.
cheers
andrew
Dean Rasheed <dean.a.rasheed@gmail.com> writes:
On 17 October 2010 18:53, Tom Lane <tgl@sss.pgh.pa.us> wrote:
We could fix that with Dean's idea of reloading the cache whenever
we see that we are being asked to compare a value we don't have in the
cache entry. �However, that assumes that we even notice that it's not
in the cache entry. �If we're trying to use "fast" comparison then we
wouldn't notice that.
That makes me think maybe the "fast" and "slow" comparisons should
both be done the same way, having a cache so that we notice
immediately if we get a new value.
Actually ... the race conditions can be a lot worse than just a race.
Consider
begin;
alter type myenum add 'some-value';
insert into mytab values('some-value');
rollback;
If mytab has an index on the enum col, we now have an index entry that
contains an enum value that isn't valid according to anybody, and nobody
knows how to compare it. If that entry is near the root then the index
is hopelessly corrupt: no one can tell which way to descend when
comparing it to some valid value.
I think what this says is that we cannot allow any manipulations that
involve an uncommitted enum value. Probably the easiest way is to make
the ALTER TYPE operation disallowed-inside-transaction-block. That's
pretty ugly, but doesn't seem like a serious restriction in practice
(though for example it'd mean we couldn't use it in pg_dump).
I'm not sure if enforcing such a restriction helps much in terms of
managing cache invalidations. Even with that, it seems possible to
encounter index entries for values that you haven't yet noticed the
invalidation message for.
regards, tom lane
On 10/17/2010 02:19 PM, Dean Rasheed wrote:
That makes me think maybe the "fast" and "slow" comparisons should
both be done the same way, having a cache so that we notice
immediately if we get a new value.Obviously that's not going to be as fast as the current "fast" method,
but the question is, can it be made sufficiently close? I think the
current sort+bsearch method is always going to be significantly
slower, but perhaps a dedicated hash table algorithm might work.
Making that as fast as "Is this sorted? If yes, compare the two oids" or
even acceptably slower seems likely to be a challenge. I thought about
the sort of approach you suggest initially and didn't come up with
anything that seemed likely to work well enough.
cheers
andrew
On 10/17/2010 03:17 PM, Tom Lane wrote:
Dean Rasheed<dean.a.rasheed@gmail.com> writes:
On 17 October 2010 18:53, Tom Lane<tgl@sss.pgh.pa.us> wrote:
We could fix that with Dean's idea of reloading the cache whenever
we see that we are being asked to compare a value we don't have in the
cache entry. However, that assumes that we even notice that it's not
in the cache entry. If we're trying to use "fast" comparison then we
wouldn't notice that.That makes me think maybe the "fast" and "slow" comparisons should
both be done the same way, having a cache so that we notice
immediately if we get a new value.Actually ... the race conditions can be a lot worse than just a race.
Considerbegin;
alter type myenum add 'some-value';
insert into mytab values('some-value');
rollback;If mytab has an index on the enum col, we now have an index entry that
contains an enum value that isn't valid according to anybody, and nobody
knows how to compare it. If that entry is near the root then the index
is hopelessly corrupt: no one can tell which way to descend when
comparing it to some valid value.I think what this says is that we cannot allow any manipulations that
involve an uncommitted enum value. Probably the easiest way is to make
the ALTER TYPE operation disallowed-inside-transaction-block. That's
pretty ugly, but doesn't seem like a serious restriction in practice
(though for example it'd mean we couldn't use it in pg_dump).
Even in binary upgrade mode?
cheers
andrew
Andrew Dunstan <andrew@dunslane.net> writes:
Making that as fast as "Is this sorted? If yes, compare the two oids" or
even acceptably slower seems likely to be a challenge. I thought about
the sort of approach you suggest initially and didn't come up with
anything that seemed likely to work well enough.
The fundamental problem here is that we can't tell by examining an enum
value whether we have to apply the "fast" or "slow" comparison method.
But what if we could?
The sneaky idea that just came to me is to declare that even-numbered
OID values can be sorted by direct comparison, whereas odd-numbered OIDs
can't. It seems fairly easy to ensure that that property holds while
creating the values, as long as you don't mind "burning" OIDs: if you
get a value you don't like, just demand another one. Then, as long as
both OIDs involved in a comparison are even, you just do a direct
comparison and no cache entry is needed at all. When either is odd, you
know you need a cache entry. You can also tell whether an existing
cache entry is stale: if it doesn't contain both values then you need to
refresh it. If it does have both, then it's good enough for the
immediate purpose, even if there are other values it doesn't know
about. So with this design we don't actually have to watch for inval
events at all ... we just refresh the cache entry whenever a comparison
finds that that's needed.
The one problem I can see with this is that it's only partially
on-disk-compatible with existing enum types: it'll almost certainly
think that they require slow comparison, even when they don't.
Maybe that's acceptable.
regards, tom lane
Andrew Dunstan <andrew@dunslane.net> writes:
On 10/17/2010 03:17 PM, Tom Lane wrote:
I think what this says is that we cannot allow any manipulations that
involve an uncommitted enum value. Probably the easiest way is to make
the ALTER TYPE operation disallowed-inside-transaction-block. That's
pretty ugly, but doesn't seem like a serious restriction in practice
(though for example it'd mean we couldn't use it in pg_dump).
Even in binary upgrade mode?
Binary upgrade can probably be treated as a special case.
regards, tom lane
Andrew Dunstan <andrew@dunslane.net> writes:
On 10/17/2010 01:20 PM, Tom Lane wrote:
The way I'd be inclined to design this is that altering an enum doesn't
change its pg_type entry at all, just add another row to pg_enum.
When first needing to compare values of an enum, load up the typcache
entry for it.
Perhaps mistakenly I wanted to avoid doing that as it would slow down a
retail comparison quite a lot, especially in the case of an enum with a
very large label set. That's why I put the sorted property and label
count in pg_type.
Just going back to this point: I don't buy that argument at all.
If you have to consult pg_type to find out whether fast or slow
comparison is needed, you've already burned all the cycles required
for a cache lookup. The only way that a large typcache entry would
really be a performance issue compared to just consulting pg_type
is if it had to be refreshed a lot, which doesn't seem like a likely
problem.
regards, tom lane
On 10/17/2010 03:56 PM, Tom Lane wrote:
Andrew Dunstan<andrew@dunslane.net> writes:
Making that as fast as "Is this sorted? If yes, compare the two oids" or
even acceptably slower seems likely to be a challenge. I thought about
the sort of approach you suggest initially and didn't come up with
anything that seemed likely to work well enough.The fundamental problem here is that we can't tell by examining an enum
value whether we have to apply the "fast" or "slow" comparison method.
But what if we could?The sneaky idea that just came to me is to declare that even-numbered
OID values can be sorted by direct comparison, whereas odd-numbered OIDs
can't. It seems fairly easy to ensure that that property holds while
creating the values, as long as you don't mind "burning" OIDs: if you
get a value you don't like, just demand another one. Then, as long as
both OIDs involved in a comparison are even, you just do a direct
comparison and no cache entry is needed at all. When either is odd, you
know you need a cache entry. You can also tell whether an existing
cache entry is stale: if it doesn't contain both values then you need to
refresh it. If it does have both, then it's good enough for the
immediate purpose, even if there are other values it doesn't know
about. So with this design we don't actually have to watch for inval
events at all ... we just refresh the cache entry whenever a comparison
finds that that's needed.
Hmm, nice. What I like about this is that it's not an all or nothing
deal. If you add one label that needs an odd Oid, most of the
comparisons will still be fast.
I think the rule for choosing the Oid for the new entry would go
something like this:
* if we're adding a label at the end and the Oid of the last entry
is even, take the first Oid that is either even and greater than
the Oid of the last entry, or odd and less than the Oid of the
last entry
* for all other positions take the first odd Oid
The one problem I can see with this is that it's only partially
on-disk-compatible with existing enum types: it'll almost certainly
think that they require slow comparison, even when they don't.
Maybe that's acceptable.
Yeah, not sure about that.
cheers
andrew
On Sun, Oct 17, 2010 at 12:17 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
begin;
alter type myenum add 'some-value';
insert into mytab values('some-value');
rollback;
....
I think what this says is that we cannot allow any manipulations that
involve an uncommitted enum value. Probably the easiest way is to make
the ALTER TYPE operation disallowed-inside-transaction-block. That's
pretty ugly, but doesn't seem like a serious restriction in practice
(though for example it'd mean we couldn't use it in pg_dump).
I fear this is the camel's nose under the door towards making all DDL
non-transactional a la Oracle.
The alternative is that there are two steps to creating an enum. A
low-level modification which adds the new value and its collation
value to the list of valid things to compare. This would be done as
something like an autonomous transaction and be committed regardless
of whether the outer transaction commits. It wouldn't add the value to
the user-visible list of values or allowable values for inserting.
Only once that was committed could we then make the transactional
modification to the user visible DDL.
The main problem with this is of course that we don't actually have
autonomous transactions...
--
greg
On 10/17/2010 03:56 PM, Tom Lane wrote:
[clever scheme to treat even numbered enum Oids as sorted]
The one problem I can see with this is that it's only partially
on-disk-compatible with existing enum types: it'll almost certainly
think that they require slow comparison, even when they don't.
Maybe that's acceptable.
Thinking more about this, could we do some sort of hybrid scheme that
stashes the 'oids are sorted correctly' property of the type somewhat
like what I proposed?
Binary upgrade would be mildly tricky but not impossible.
cheers
andrew
Andrew Dunstan <andrew@dunslane.net> writes:
On 10/17/2010 03:56 PM, Tom Lane wrote:
[clever scheme to treat even numbered enum Oids as sorted]
The one problem I can see with this is that it's only partially
on-disk-compatible with existing enum types: it'll almost certainly
think that they require slow comparison, even when they don't.
Maybe that's acceptable.
Thinking more about this, could we do some sort of hybrid scheme that
stashes the 'oids are sorted correctly' property of the type somewhat
like what I proposed?
I think that fundamentally doesn't work in the face of a concurrent
ALTER TYPE. And even if it did work, by the time you've done the
cache lookup to check your stashed flag, what have you really saved?
The only way to have comparisons that are on par with current
performance is to not need to do any lookup at all.
The scenario that seems the most dangerous to me is
1. Begin transaction T1.
2. T1 does cache lookup, or any other pushup you want, and
decides that the enum type is correctly sorted.
3. T2 commits an ALTER TYPE that adds a non-sorted OID.
4. T3 inserts that OID in an index.
5. T1 encounters the out-of-order OID in the index.
If T1 is not able to recognize that the OID it's looking at is not
in-order, despite its previously cached information, it's going to lose
badly. It's impractical to do an AcceptInvalidationMessages on every
single comparison, so AFAICT you *have to* be able to deal correctly
with enum values that were added since your cache entry was made.
We could possibly deal with enum types that follow the existing
convention if we made the cache entry hold a list of all the original,
known-to-be-sorted OIDs. (This could be reasonably compact and cheap to
probe if it were represented as a starting OID and a Bitmapset of delta
values, since we can assume that the initial set of OIDs is pretty close
together.) But we have to have that cache entry, and we have to consult
it on every single comparison, so it's definitely going to be slower
than before.
So I'm thinking the comparison procedure goes like this:
1. Both OIDs even?
If so, just compare them numerically, and we're done.
2. Lookup cache entry for enum type.
3. Both OIDs in list of known-sorted OIDs?
If so, just compare them numerically, and we're done.
4. Search the part of the cache entry that lists sort positions.
If not both present, refresh the cache entry.
If still not present, throw error.
5. Compare by sort positions.
Step 4 is the slowest part but would be avoided in most cases.
However, step 2 is none too speedy either, and would usually
be required when dealing with pre-existing enums.
regards, tom lane
On 10/18/2010 10:52 AM, Tom Lane wrote:
We could possibly deal with enum types that follow the existing
convention if we made the cache entry hold a list of all the original,
known-to-be-sorted OIDs. (This could be reasonably compact and cheap to
probe if it were represented as a starting OID and a Bitmapset of delta
values, since we can assume that the initial set of OIDs is pretty close
together.)
How are we going to know what those are?
We could keep track of the beginning and end of the original range maybe
and refuse to create any new enum values for the type inside that range.
That might make binary upgrade a bit ugly, but it's probably manageable
even so.
cheers
andrew
Andrew Dunstan <andrew@dunslane.net> writes:
On 10/18/2010 10:52 AM, Tom Lane wrote:
We could possibly deal with enum types that follow the existing
convention if we made the cache entry hold a list of all the original,
known-to-be-sorted OIDs. (This could be reasonably compact and cheap to
probe if it were represented as a starting OID and a Bitmapset of delta
values, since we can assume that the initial set of OIDs is pretty close
together.)
How are we going to know what those are?
You read pg_enum while constructing the cache entry, sort by nominal
sort position, and look to see how many OIDs form a sorted and reasonably
compact range. I don't see a need to know which of those OIDs were
actually original; you can get close enough with heuristic reverse
engineering. The only real limitation is not wanting the bitmapset to
get too enormous, so you might often be able to incorporate OIDs that
in fact weren't original, so long as they chanced to be OK order-wise.
This'd be a win anytime it saved you from having to proceed to step 4
in comparisons.
regards, tom lane
On 18 October 2010 15:52, Tom Lane <tgl@sss.pgh.pa.us> wrote:
So I'm thinking the comparison procedure goes like this:
1. Both OIDs even?
If so, just compare them numerically, and we're done.2. Lookup cache entry for enum type.
3. Both OIDs in list of known-sorted OIDs?
If so, just compare them numerically, and we're done.4. Search the part of the cache entry that lists sort positions.
If not both present, refresh the cache entry.
If still not present, throw error.5. Compare by sort positions.
Step 4 is the slowest part but would be avoided in most cases.
However, step 2 is none too speedy either, and would usually
be required when dealing with pre-existing enums.
I was thinking that steps 2-5 could be sped up by doing something like:
2. If first time in:
Build a lightweight hash map: [ oid -> sort position ] with
all the enum values
3. Look up each oid in the hash map
If not both present, rebuild the hash map
If still not present, throw error.
4. Compare by sort positions.
I think the hash map lookups could be made pretty quick, if we're
prepared to write a bit of dedicated code there rather than relying on
the more general-purpose caching code.
Regards,
Dean
Show quoted text
regards, tom lane
Dean Rasheed <dean.a.rasheed@gmail.com> writes:
I think the hash map lookups could be made pretty quick, if we're
prepared to write a bit of dedicated code there rather than relying on
the more general-purpose caching code.
It's still going to be very significantly slower than what I'm
suggesting --- I'm *already* assuming that step 4 is using a hash
or something else smarter than just traversing a list. My step 3
is just a bit-array index operation (well, 2 of 'em).
(I'm pretty dubious of unsubstantiated claims that you can build a hash
table that's significantly faster than our existing hash code, anyway.)
regards, tom lane
On 10/18/2010 01:40 PM, Dean Rasheed wrote:
On 18 October 2010 15:52, Tom Lane<tgl@sss.pgh.pa.us> wrote:
So I'm thinking the comparison procedure goes like this:
1. Both OIDs even?
If so, just compare them numerically, and we're done.2. Lookup cache entry for enum type.
3. Both OIDs in list of known-sorted OIDs?
If so, just compare them numerically, and we're done.4. Search the part of the cache entry that lists sort positions.
If not both present, refresh the cache entry.
If still not present, throw error.5. Compare by sort positions.
Step 4 is the slowest part but would be avoided in most cases.
However, step 2 is none too speedy either, and would usually
be required when dealing with pre-existing enums.I was thinking that steps 2-5 could be sped up by doing something like:
2. If first time in:
Build a lightweight hash map: [ oid -> sort position ] with
all the enum values3. Look up each oid in the hash map
If not both present, rebuild the hash map
If still not present, throw error.4. Compare by sort positions.
I think the hash map lookups could be made pretty quick, if we're
prepared to write a bit of dedicated code there rather than relying on
the more general-purpose caching code.
If you have want to work on it and prove it's going to be better, please
do. I'm not convinced it will do a whole lot better than a binary search
that in most cases will do no more than a handful of probes.
cheers
andrew
Andrew Dunstan <andrew@dunslane.net> writes:
If you have want to work on it and prove it's going to be better, please
do. I'm not convinced it will do a whole lot better than a binary search
that in most cases will do no more than a handful of probes.
Yeah, that's a good point. There's a range of table sizes where hashing
is faster than binary search, but I'm not sure that typical enums will
fall into that range.
regards, tom lane
On 10/18/2010 10:52 AM, Tom Lane wrote:
We could possibly deal with enum types that follow the existing
convention if we made the cache entry hold a list of all the original,
known-to-be-sorted OIDs. (This could be reasonably compact and cheap to
probe if it were represented as a starting OID and a Bitmapset of delta
values, since we can assume that the initial set of OIDs is pretty close
together.) But we have to have that cache entry, and we have to consult
it on every single comparison, so it's definitely going to be slower
than before.So I'm thinking the comparison procedure goes like this:
1. Both OIDs even?
If so, just compare them numerically, and we're done.2. Lookup cache entry for enum type.
3. Both OIDs in list of known-sorted OIDs?
If so, just compare them numerically, and we're done.4. Search the part of the cache entry that lists sort positions.
If not both present, refresh the cache entry.
If still not present, throw error.5. Compare by sort positions.
Step 4 is the slowest part but would be avoided in most cases.
However, step 2 is none too speedy either, and would usually
be required when dealing with pre-existing enums.
OK, I've made adjustments that I think do what you're suggesting.
Patch is attached.
Alternatively this can be pulled from
<git@github.com:adunstan/postgresql-dev.git>
cheers
andrew
Attachments:
venum6.patchtext/x-patch; name=venum6.patchDownload
*** a/contrib/pg_upgrade/function.c
--- b/contrib/pg_upgrade/function.c
***************
*** 61,85 **** install_support_functions(migratorContext *ctx)
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_heap_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_toast_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_index_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.add_pg_enum_label(OID, OID, NAME) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
--- 61,85 ----
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_pg_enum_oid(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_heap_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_toast_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_index_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
*** a/contrib/pg_upgrade_support/pg_upgrade_support.c
--- b/contrib/pg_upgrade_support/pg_upgrade_support.c
***************
*** 30,35 **** PG_MODULE_MAGIC;
--- 30,36 ----
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_array_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_toast_oid;
+ extern PGDLLIMPORT Oid binary_upgrade_next_pg_enum_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_heap_relfilenode;
extern PGDLLIMPORT Oid binary_upgrade_next_toast_relfilenode;
extern PGDLLIMPORT Oid binary_upgrade_next_index_relfilenode;
***************
*** 37,54 **** extern PGDLLIMPORT Oid binary_upgrade_next_index_relfilenode;
Datum set_next_pg_type_oid(PG_FUNCTION_ARGS);
Datum set_next_pg_type_array_oid(PG_FUNCTION_ARGS);
Datum set_next_pg_type_toast_oid(PG_FUNCTION_ARGS);
Datum set_next_heap_relfilenode(PG_FUNCTION_ARGS);
Datum set_next_toast_relfilenode(PG_FUNCTION_ARGS);
Datum set_next_index_relfilenode(PG_FUNCTION_ARGS);
- Datum add_pg_enum_label(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(set_next_pg_type_oid);
PG_FUNCTION_INFO_V1(set_next_pg_type_array_oid);
PG_FUNCTION_INFO_V1(set_next_pg_type_toast_oid);
PG_FUNCTION_INFO_V1(set_next_heap_relfilenode);
PG_FUNCTION_INFO_V1(set_next_toast_relfilenode);
PG_FUNCTION_INFO_V1(set_next_index_relfilenode);
- PG_FUNCTION_INFO_V1(add_pg_enum_label);
Datum
set_next_pg_type_oid(PG_FUNCTION_ARGS)
--- 38,55 ----
Datum set_next_pg_type_oid(PG_FUNCTION_ARGS);
Datum set_next_pg_type_array_oid(PG_FUNCTION_ARGS);
Datum set_next_pg_type_toast_oid(PG_FUNCTION_ARGS);
+ Datum set_next_pg_enum_oid(PG_FUNCTION_ARGS);
Datum set_next_heap_relfilenode(PG_FUNCTION_ARGS);
Datum set_next_toast_relfilenode(PG_FUNCTION_ARGS);
Datum set_next_index_relfilenode(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(set_next_pg_type_oid);
PG_FUNCTION_INFO_V1(set_next_pg_type_array_oid);
PG_FUNCTION_INFO_V1(set_next_pg_type_toast_oid);
+ PG_FUNCTION_INFO_V1(set_next_pg_enum_oid);
PG_FUNCTION_INFO_V1(set_next_heap_relfilenode);
PG_FUNCTION_INFO_V1(set_next_toast_relfilenode);
PG_FUNCTION_INFO_V1(set_next_index_relfilenode);
Datum
set_next_pg_type_oid(PG_FUNCTION_ARGS)
***************
*** 81,86 **** set_next_pg_type_toast_oid(PG_FUNCTION_ARGS)
--- 82,97 ----
}
Datum
+ set_next_pg_enum_oid(PG_FUNCTION_ARGS)
+ {
+ Oid enumoid = PG_GETARG_OID(0);
+
+ binary_upgrade_next_pg_enum_oid = enumoid;
+
+ PG_RETURN_VOID();
+ }
+
+ Datum
set_next_heap_relfilenode(PG_FUNCTION_ARGS)
{
Oid relfilenode = PG_GETARG_OID(0);
***************
*** 110,124 **** set_next_index_relfilenode(PG_FUNCTION_ARGS)
PG_RETURN_VOID();
}
- Datum
- add_pg_enum_label(PG_FUNCTION_ARGS)
- {
- Oid enumoid = PG_GETARG_OID(0);
- Oid typoid = PG_GETARG_OID(1);
- Name label = PG_GETARG_NAME(2);
-
- EnumValuesCreate(typoid, list_make1(makeString(NameStr(*label))),
- enumoid);
-
- PG_RETURN_VOID();
- }
--- 121,123 ----
*** a/doc/src/sgml/ref/alter_type.sgml
--- b/doc/src/sgml/ref/alter_type.sgml
***************
*** 23,33 **** PostgreSQL documentation
<refsynopsisdiv>
<synopsis>
! ALTER TYPE <replaceable class="PARAMETER">name</replaceable> <replaceable class="PARAMETER">action</replaceable> [, ... ]
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> OWNER TO <replaceable class="PARAMETER">new_owner</replaceable>
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> RENAME ATTRIBUTE <replaceable class="PARAMETER">attribute_name</replaceable> TO <replaceable class="PARAMETER">new_attribute_name</replaceable>
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> RENAME TO <replaceable class="PARAMETER">new_name</replaceable>
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> SET SCHEMA <replaceable class="PARAMETER">new_schema</replaceable>
<phrase>where <replaceable class="PARAMETER">action</replaceable> is one of:</phrase>
--- 23,34 ----
<refsynopsisdiv>
<synopsis>
! ALTER TYPE <replaceable class="PARAMETER">name</replaceable> <replaceable class="PARAMETER">action</replaceable> [, ... ]
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> OWNER TO <replaceable class="PARAMETER">new_owner</replaceable>
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> RENAME ATTRIBUTE <replaceable class="PARAMETER">attribute_name</replaceable> TO <replaceable class="PARAMETER">new_attribute_name</replaceable>
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> RENAME TO <replaceable class="PARAMETER">new_name</replaceable>
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> SET SCHEMA <replaceable class="PARAMETER">new_schema</replaceable>
+ ALTER TYPE <replaceable class="PARAMETER">name</replaceable> ADD <replaceable class="PARAMETER">new_enum_value</replaceable> [ BEFORE | AFTER <replaceable class="PARAMETER">existing_enum_value</replaceable>
<phrase>where <replaceable class="PARAMETER">action</replaceable> is one of:</phrase>
***************
*** 103,108 **** ALTER TYPE <replaceable class="PARAMETER">name</replaceable> SET SCHEMA <replace
--- 104,131 ----
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><literal>ADD [ BEFORE | AFTER ]</literal></term>
+ <listitem>
+ <para>
+ This form adds a new value to an enum type. If the new value's place
+ in the sort order is not set using <literal>BEFORE</literal> or
+ <literal>AFTER</literal>, then the new item is placed at the end of
+ the list of values.
+ </para>
+ <note>
+ <para>
+ Adding a new enum value will in some cases lower the comparison and
+ sorting performance of the enum type. This will only usually occur if
+ <literal>BEFORE</literal> or <literal>AFTER</literal> are used to set
+ the new value's sort order position somewhere other than at the end
+ of the list. Optimal performance will be restored if the database is
+ dumped and reloaded.
+ </para>
+ </note>
+ </listitem>
+ </varlistentry>
</variablelist>
</para>
***************
*** 196,201 **** ALTER TYPE <replaceable class="PARAMETER">name</replaceable> SET SCHEMA <replace
--- 219,254 ----
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><replaceable class="PARAMETER">data_type</replaceable></term>
+ <listitem>
+ <para>
+ The data type of the attribute to add, or the new type of the
+ attribute to alter.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="PARAMETER">new_enum_value</replaceable></term>
+ <listitem>
+ <para>
+ The new value to be added to the num type's list of values. Like all
+ enum literals it needs to be quoted.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="PARAMETER">exisiting_enum_value</replaceable></term>
+ <listitem>
+ <para>
+ The neighbour of the new value to be added to the num type's list of values. Like all
+ enum literals it needs to be quoted.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</para>
</refsect1>
***************
*** 232,237 **** ALTER TYPE email SET SCHEMA customers;
--- 285,297 ----
ALTER TYPE compfoo ADD ATTRIBUTE f3 int;
</programlisting>
</para>
+
+ <para>
+ To add a new value to an enum type in a particular sort position:
+ <programlisting>
+ ALTER TYPE colors ADD 'orange' AFTER 'red';
+ </programlisting>
+ </para>
</refsect1>
<refsect1>
*** a/src/backend/catalog/pg_enum.c
--- b/src/backend/catalog/pg_enum.c
***************
*** 18,29 ****
--- 18,226 ----
#include "catalog/catalog.h"
#include "catalog/indexing.h"
#include "catalog/pg_enum.h"
+ #include "catalog/pg_type.h"
+ #include "utils/lsyscache.h"
+ #include "utils/syscache.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/rel.h"
#include "utils/tqual.h"
static int oid_cmp(const void *p1, const void *p2);
+ static int sort_order_cmp(const void *p1, const void *p2);
+
+ Oid binary_upgrade_next_pg_enum_oid = InvalidOid;
+
+ /*
+ * AddEnumLabel
+ * Add a new label to the enum set. By default it goes at
+ * the end, but the user can choose to place it before or
+ * after any existing set member.
+ *
+ *
+ */
+
+ void
+ AddEnumLabel(Oid enumTypeOid,
+ char *newVal,
+ char *neighbour,
+ bool newValIsAfter)
+ {
+ Oid newOid;
+ Relation pg_enum;
+ TupleDesc tupDesc;
+ Datum values[Natts_pg_enum];
+ bool nulls[Natts_pg_enum];
+ NameData enumlabel;
+ HeapTuple enum_tup;
+ int newelemorder;
+ CatCList *list;
+ int nelems;
+
+ /* check length of new label is ok */
+ if (strlen(newVal) > (NAMEDATALEN - 1))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_NAME),
+ errmsg("invalid enum label \"%s\"", newVal),
+ errdetail("Labels must be %d characters or less.",
+ NAMEDATALEN - 1)));
+
+ /* get a new OID for the label */
+ pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
+
+ list = SearchSysCacheList1(ENUMTYPOIDNAME,
+ ObjectIdGetDatum(enumTypeOid));
+ nelems = list->n_members;
+
+ if (neighbour == NULL)
+ {
+ /*
+ * Put the new label at the end of the list.
+ * No change to existing tuples is required.
+ */
+ newelemorder = nelems + 1;
+ }
+ else
+ {
+ /* BEFORE or AFTER specified */
+ int i;
+ HeapTuple *existing;
+ HeapTuple nbr = NULL;
+ Form_pg_enum nbr_en;
+
+ /* sort the list of the existing elements by enumsortorder */
+ existing = palloc(nelems * sizeof(HeapTuple));
+
+ for (i = 0; i < nelems; i++)
+ existing[i] = &(list->members[i]->tuple);
+
+ qsort(existing, nelems, sizeof(HeapTuple), sort_order_cmp);
+
+ /* locate the neighbour element */
+ for (i = 0; i < nelems; i++)
+ {
+ Form_pg_enum exists_en;
+ exists_en = (Form_pg_enum) GETSTRUCT(existing[i]);
+ if (strcmp(NameStr(exists_en->enumlabel),neighbour) == 0)
+ nbr = existing[i];
+
+ }
+
+ if (nbr == NULL)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("\"%s\" is not an existing label.", neighbour)));
+ }
+
+ nbr_en = (Form_pg_enum) GETSTRUCT(nbr);
+
+ /*
+ * If BEFORE was specified, the new label goes in the neighbour's
+ * position. Otherwise, it goes in the position after that.
+ */
+ newelemorder = nbr_en->enumsortorder;
+ if (newValIsAfter)
+ newelemorder++;
+
+ /*
+ * Add 1 to the sortorder of all the labels after where the
+ * new label goes. Do it from the end back so we don't get
+ * uniqueness violations.
+ */
+ for (i = nelems - 1; i>= 0; i--)
+ {
+ HeapTuple newtup;
+ Form_pg_enum exst_en = (Form_pg_enum) GETSTRUCT(existing[i]);
+ if (exst_en->enumsortorder < newelemorder)
+ break;
+
+ newtup = heap_copytuple(existing[i]);
+ exst_en = (Form_pg_enum) GETSTRUCT(newtup);
+ exst_en->enumsortorder ++;
+
+ simple_heap_update(pg_enum, &newtup->t_self, newtup);
+
+ CatalogUpdateIndexes(pg_enum, newtup);
+
+ }
+
+ }
+
+ if (OidIsValid(binary_upgrade_next_pg_enum_oid))
+ {
+ if (neighbour != NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("BEFORE/AFTER incompatible with binary upgrade.")));
+
+ newOid = binary_upgrade_next_pg_enum_oid;
+ binary_upgrade_next_pg_enum_oid = InvalidOid;
+ }
+ else
+ {
+ /*
+ * Non upgrade case, we allocate a new Oid for the value.
+ *
+ * We try to give the new element an even numbered Oid if it's safe,
+ * which should be true if these conditions hold:
+ * . the Oid of the highest sorted exiting value is even, and
+ * . the new value is to sort after that value, and
+ * . there hasn't been Oid wraparound.
+ * For all other cases we allocate an odd Oid.
+ */
+
+
+ newOid = GetNewOid(pg_enum);
+ if (newelemorder > nelems)
+ {
+ Oid last_elem_oid = InvalidOid;
+ int i;
+
+ for (i = nelems - 1; i>= 0; i--)
+ {
+ Form_pg_enum last_en;
+ last_en = (Form_pg_enum) GETSTRUCT(&(list->members[i]->tuple));
+ if (last_en->enumsortorder == nelems)
+ {
+ last_elem_oid = HeapTupleGetOid(&(list->members[i]->tuple));
+ break;
+ }
+ }
+
+ if (last_elem_oid %2 == 0)
+ while ((newOid %2 == 1 && newOid > last_elem_oid) ||
+ (newOid %2 == 0 && newOid < last_elem_oid))
+ newOid = GetNewOid(pg_enum);
+ else
+ while (newOid %2 == 0)
+ newOid = GetNewOid(pg_enum);
+ }
+ else
+ {
+ while (newOid %2 == 0)
+ newOid = GetNewOid(pg_enum);
+ }
+ }
+
+ /* set up the new entry */
+ tupDesc = pg_enum->rd_att;
+ memset(nulls, false, sizeof(nulls));
+ values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid);
+ namestrcpy(&enumlabel, newVal);
+ values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel);
+ values[Anum_pg_enum_enumsortorder -1] = Int32GetDatum(newelemorder);
+ enum_tup = heap_form_tuple(tupDesc, values, nulls);
+ HeapTupleSetOid(enum_tup, newOid);
+ simple_heap_insert(pg_enum, enum_tup);
+ CatalogUpdateIndexes(pg_enum, enum_tup);
+ heap_freetuple(enum_tup);
+
+ heap_close(pg_enum, RowExclusiveLock);
+
+ ReleaseCatCacheList(list);
+
+ }
/*
***************
*** 33,40 **** static int oid_cmp(const void *p1, const void *p2);
* vals is a list of Value strings.
*/
void
! EnumValuesCreate(Oid enumTypeOid, List *vals,
! Oid binary_upgrade_next_pg_enum_oid)
{
Relation pg_enum;
TupleDesc tupDesc;
--- 230,236 ----
* vals is a list of Value strings.
*/
void
! EnumValuesCreate(Oid enumTypeOid, List *vals)
{
Relation pg_enum;
TupleDesc tupDesc;
***************
*** 50,58 **** EnumValuesCreate(Oid enumTypeOid, List *vals,
num_elems = list_length(vals);
/*
! * XXX we do not bother to check the list of values for duplicates --- if
* you have any, you'll get a less-than-friendly unique-index violation.
! * Is it worth trying harder?
*/
pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
--- 246,254 ----
num_elems = list_length(vals);
/*
! * We do not bother to check the list of values for duplicates --- if
* you have any, you'll get a less-than-friendly unique-index violation.
! * It is probably not worth trying harder.
*/
pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
***************
*** 62,96 **** EnumValuesCreate(Oid enumTypeOid, List *vals,
* Allocate oids
*/
oids = (Oid *) palloc(num_elems * sizeof(Oid));
! if (OidIsValid(binary_upgrade_next_pg_enum_oid))
! {
! if (num_elems != 1)
! ereport(ERROR,
! (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("EnumValuesCreate() can only set a single OID")));
! oids[0] = binary_upgrade_next_pg_enum_oid;
! binary_upgrade_next_pg_enum_oid = InvalidOid;
! }
! else
{
/*
! * While this method does not absolutely guarantee that we generate no
! * duplicate oids (since we haven't entered each oid into the table
! * before allocating the next), trouble could only occur if the oid
! * counter wraps all the way around before we finish. Which seems
! * unlikely.
*/
! for (elemno = 0; elemno < num_elems; elemno++)
! {
! /*
! * The pg_enum.oid is stored in user tables. This oid must be
! * preserved by binary upgrades.
! */
! oids[elemno] = GetNewOid(pg_enum);
! }
! /* sort them, just in case counter wrapped from high to low */
! qsort(oids, num_elems, sizeof(Oid), oid_cmp);
}
/* and make the entries */
memset(nulls, false, sizeof(nulls));
--- 258,287 ----
* Allocate oids
*/
oids = (Oid *) palloc(num_elems * sizeof(Oid));
!
! /*
! * While this method does not absolutely guarantee that we generate no
! * duplicate oids (since we haven't entered each oid into the table
! * before allocating the next), trouble could only occur if the oid
! * counter wraps all the way around before we finish. Which seems
! * unlikely.
! */
! for (elemno = 0; elemno < num_elems; elemno++)
{
/*
! * The pg_enum.oid is stored in user tables. This oid must be
! * preserved by binary upgrades.
! *
! * We allocate all new enums as evenly numbered Oids
! * so they will sort fast.
*/
! Oid new_oid;
! while ((new_oid = GetNewOid(pg_enum)) %2 == 1)
! ;
! oids[elemno] = new_oid;
}
+ /* sort them, just in case counter wrapped from high to low */
+ qsort(oids, num_elems, sizeof(Oid), oid_cmp);
/* and make the entries */
memset(nulls, false, sizeof(nulls));
***************
*** 114,119 **** EnumValuesCreate(Oid enumTypeOid, List *vals,
--- 305,311 ----
values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid);
namestrcpy(&enumlabel, lab);
values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel);
+ values[Anum_pg_enum_enumsortorder -1] = Int32GetDatum(elemno+1);
tup = heap_form_tuple(tupDesc, values, nulls);
HeapTupleSetOid(tup, oids[elemno]);
***************
*** 164,170 **** EnumValuesDelete(Oid enumTypeOid)
}
! /* qsort comparison function */
static int
oid_cmp(const void *p1, const void *p2)
{
--- 356,362 ----
}
! /* qsort comparison for oids */
static int
oid_cmp(const void *p1, const void *p2)
{
***************
*** 177,179 **** oid_cmp(const void *p1, const void *p2)
--- 369,384 ----
return 1;
return 0;
}
+
+ /* qsort comparison function for tuples by sort order */
+ static int
+ sort_order_cmp(const void *p1, const void *p2)
+ {
+ HeapTuple v1 = *((const HeapTuple *) p1);
+ HeapTuple v2 = *((const HeapTuple *) p2);
+ Form_pg_enum en1 = (Form_pg_enum) GETSTRUCT(v1);
+ Form_pg_enum en2 = (Form_pg_enum) GETSTRUCT(v2);
+
+ return en1->enumsortorder - en2->enumsortorder;
+ }
+
*** a/src/backend/commands/typecmds.c
--- b/src/backend/commands/typecmds.c
***************
*** 85,96 **** static Oid findTypeTypmodoutFunction(List *procname);
static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid);
static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
static void checkDomainOwner(HeapTuple tup, TypeName *typename);
static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
Oid baseTypeOid,
int typMod, Constraint *constr,
char *domainName);
-
-
/*
* DefineType
* Registers a new base type.
--- 85,95 ----
static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid);
static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
static void checkDomainOwner(HeapTuple tup, TypeName *typename);
+ static void checkEnumOwner(HeapTuple tup, TypeName *typename);
static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
Oid baseTypeOid,
int typMod, Constraint *constr,
char *domainName);
/*
* DefineType
* Registers a new base type.
***************
*** 563,569 **** DefineType(List *names, List *parameters)
-1, /* typMod (Domains only) */
0, /* Array Dimensions of typbasetype */
false); /* Type NOT NULL */
-
/*
* Create the array type that goes with it.
*/
--- 562,567 ----
***************
*** 1044,1050 **** DefineDomain(CreateDomainStmt *stmt)
storage, /* TOAST strategy */
basetypeMod, /* typeMod value */
typNDims, /* Array dimensions for base type */
! typNotNull); /* Type NOT NULL */
/*
* Process constraints which refer to the domain ID returned by TypeCreate
--- 1042,1048 ----
storage, /* TOAST strategy */
basetypeMod, /* typeMod value */
typNDims, /* Array dimensions for base type */
! typNotNull); /* Type NOT NULL */
/*
* Process constraints which refer to the domain ID returned by TypeCreate
***************
*** 1094,1099 **** DefineEnum(CreateEnumStmt *stmt)
--- 1092,1100 ----
AclResult aclresult;
Oid old_type_oid;
Oid enumArrayOid;
+ int num_labels;
+
+ num_labels = list_length(stmt->vals);
/* Convert list of names to a name and namespace */
enumNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
***************
*** 1156,1162 **** DefineEnum(CreateEnumStmt *stmt)
false); /* Type NOT NULL */
/* Enter the enum's values into pg_enum */
! EnumValuesCreate(enumTypeOid, stmt->vals, InvalidOid);
/*
* Create the array type that goes with it.
--- 1157,1163 ----
false); /* Type NOT NULL */
/* Enter the enum's values into pg_enum */
! EnumValuesCreate(enumTypeOid, stmt->vals);
/*
* Create the array type that goes with it.
***************
*** 1197,1202 **** DefineEnum(CreateEnumStmt *stmt)
--- 1198,1259 ----
pfree(enumArrayName);
}
+ /*
+ * AlterEnum
+ * Registers a new label for an existing enum.
+ */
+ void
+ AlterEnum (AlterEnumStmt *stmt)
+ {
+ Oid enum_type_oid;
+ TypeName *typename;
+ HeapTuple tup;
+ Form_pg_type typTup;
+
+ /* Make a TypeName so we can use standard type lookup machinery */
+ typename = makeTypeNameFromNameList(stmt->typeName);
+ enum_type_oid = typenameTypeId(NULL, typename, NULL);
+
+ tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(enum_type_oid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for type %u", enum_type_oid);
+
+ typTup = (Form_pg_type) GETSTRUCT(tup);
+
+ /* Check it's an enum and check user has permission to ALTER the enum */
+ checkEnumOwner(tup, typename);
+
+ /* Add the new label */
+ AddEnumLabel (enum_type_oid, stmt->newVal,
+ stmt->newValNeighbour, stmt->newValIsAfter);
+
+ ReleaseSysCache(tup);
+ }
+
+
+ /*
+ * checkEnumOwner
+ *
+ * Check that the type is actually an enum and that the current user
+ * has permission to do ALTER TYPE on it. Throw an error if not.
+ */
+ static void
+ checkEnumOwner(HeapTuple tup, TypeName *typename)
+ {
+ Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup);
+
+ /* Check that this is actually a domain */
+ if (typTup->typtype != TYPTYPE_ENUM)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not an enum",
+ TypeNameToString(typename))));
+
+ /* Permission check: must own type */
+ if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
+ format_type_be(HeapTupleGetOid(tup)));
+ }
/*
* Find suitable I/O functions for a type.
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 2901,2906 **** _copyCreateEnumStmt(CreateEnumStmt *from)
--- 2901,2919 ----
return newnode;
}
+ static AlterEnumStmt *
+ _copyAlterEnumStmt(AlterEnumStmt *from)
+ {
+ AlterEnumStmt *newnode = makeNode(AlterEnumStmt);
+
+ COPY_NODE_FIELD(typeName);
+ COPY_STRING_FIELD(newVal);
+ COPY_STRING_FIELD(newValNeighbour);
+ COPY_SCALAR_FIELD(newValIsAfter);
+
+ return newnode;
+ }
+
static ViewStmt *
_copyViewStmt(ViewStmt *from)
{
***************
*** 4064,4069 **** copyObject(void *from)
--- 4077,4085 ----
case T_CreateEnumStmt:
retval = _copyCreateEnumStmt(from);
break;
+ case T_AlterEnumStmt:
+ retval = _copyAlterEnumStmt(from);
+ break;
case T_ViewStmt:
retval = _copyViewStmt(from);
break;
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
***************
*** 1393,1398 **** _equalCreateEnumStmt(CreateEnumStmt *a, CreateEnumStmt *b)
--- 1393,1409 ----
}
static bool
+ _equalAlterEnumStmt(AlterEnumStmt *a, AlterEnumStmt *b)
+ {
+ COMPARE_NODE_FIELD(typeName);
+ COMPARE_STRING_FIELD(newVal);
+ COMPARE_STRING_FIELD(newValNeighbour);
+ COMPARE_SCALAR_FIELD(newValIsAfter);
+
+ return true;
+ }
+
+ static bool
_equalViewStmt(ViewStmt *a, ViewStmt *b)
{
COMPARE_NODE_FIELD(view);
***************
*** 2700,2705 **** equal(void *a, void *b)
--- 2711,2719 ----
case T_CreateEnumStmt:
retval = _equalCreateEnumStmt(a, b);
break;
+ case T_AlterEnumStmt:
+ retval = _equalAlterEnumStmt(a, b);
+ break;
case T_ViewStmt:
retval = _equalViewStmt(a, b);
break;
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 182,189 **** static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
}
%type <node> stmt schema_stmt
! AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterFdwStmt
! AlterForeignServerStmt AlterGroupStmt
AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
AlterCompositeTypeStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
AlterRoleStmt AlterRoleSetStmt
--- 182,189 ----
}
%type <node> stmt schema_stmt
! AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt
! AlterFdwStmt AlterForeignServerStmt AlterGroupStmt
AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
AlterCompositeTypeStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
AlterRoleStmt AlterRoleSetStmt
***************
*** 652,657 **** stmt :
--- 652,658 ----
| AlterDatabaseSetStmt
| AlterDefaultPrivilegesStmt
| AlterDomainStmt
+ | AlterEnumStmt
| AlterFdwStmt
| AlterForeignServerStmt
| AlterFunctionStmt
***************
*** 3862,3867 **** enum_val_list: Sconst
--- 3863,3908 ----
{ $$ = lappend($1, makeString($3)); }
;
+ /*****************************************************************************
+ *
+ * ALTER TYPE enumtype ADD ...
+ *
+ *****************************************************************************/
+
+ AlterEnumStmt:
+ ALTER TYPE_P any_name ADD_P Sconst
+ {
+ AlterEnumStmt *n = makeNode(AlterEnumStmt);
+ n->typeName = $3;
+ n->newVal = $5;
+ n->newValNeighbour = NULL;
+ n->newValIsAfter = true;
+
+ $$ = (Node *) n;
+ }
+ | ALTER TYPE_P any_name ADD_P Sconst BEFORE Sconst
+ {
+ AlterEnumStmt *n = makeNode(AlterEnumStmt);
+ n->typeName = $3;
+ n->newVal = $5;
+ n->newValNeighbour = $7;
+ n->newValIsAfter = false;
+
+ $$ = (Node *) n;
+ }
+ | ALTER TYPE_P any_name ADD_P Sconst AFTER Sconst
+ {
+ AlterEnumStmt *n = makeNode(AlterEnumStmt);
+ n->typeName = $3;
+ n->newVal = $5;
+ n->newValNeighbour = $7;
+ n->newValIsAfter = true;
+
+ $$ = (Node *) n;
+ }
+ ;
+
+
/*****************************************************************************
*
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
***************
*** 190,195 **** check_xact_readonly(Node *parsetree)
--- 190,196 ----
case T_CreateTrigStmt:
case T_CompositeTypeStmt:
case T_CreateEnumStmt:
+ case T_AlterEnumStmt:
case T_ViewStmt:
case T_DropCastStmt:
case T_DropStmt:
***************
*** 860,865 **** standard_ProcessUtility(Node *parsetree,
--- 861,870 ----
DefineEnum((CreateEnumStmt *) parsetree);
break;
+ case T_AlterEnumStmt: /* ALTER TYPE (enum) */
+ AlterEnum((AlterEnumStmt *) parsetree);
+ break;
+
case T_ViewStmt: /* CREATE VIEW */
DefineView((ViewStmt *) parsetree, queryString);
break;
***************
*** 1868,1873 **** CreateCommandTag(Node *parsetree)
--- 1873,1882 ----
tag = "CREATE TYPE";
break;
+ case T_AlterEnumStmt:
+ tag = "ALTER TYPE";
+ break;
+
case T_ViewStmt:
tag = "CREATE VIEW";
break;
***************
*** 2410,2415 **** GetCommandLogLevel(Node *parsetree)
--- 2419,2428 ----
lev = LOGSTMT_DDL;
break;
+ case T_AlterEnumStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
case T_ViewStmt:
lev = LOGSTMT_DDL;
break;
*** a/src/backend/utils/adt/enum.c
--- b/src/backend/utils/adt/enum.c
***************
*** 14,19 ****
--- 14,20 ----
#include "postgres.h"
#include "catalog/pg_enum.h"
+ #include "catalog/pg_type.h"
#include "fmgr.h"
#include "utils/array.h"
#include "utils/builtins.h"
***************
*** 22,30 ****
#include "libpq/pqformat.h"
#include "miscadmin.h"
static ArrayType *enum_range_internal(Oid enumtypoid, Oid lower, Oid upper);
! static int enum_elem_cmp(const void *left, const void *right);
/* Basic I/O support */
--- 23,52 ----
#include "libpq/pqformat.h"
#include "miscadmin.h"
+ #define BITMAPSIZE 1024
+ #define BITMAPBYTES (BITMAPSIZE / 8)
+
+ typedef struct
+ {
+ Oid enum_oid;
+ int32 sort_order;
+ } enum_sort;
+
+ typedef struct
+ {
+ Oid enumtypoid;
+ Oid bitmap_base;
+ char bitmap[BITMAPBYTES];
+ int label_count;
+ enum_sort sort_order_list[1];
+ } enum_sort_cache;
+
+
static ArrayType *enum_range_internal(Oid enumtypoid, Oid lower, Oid upper);
! static int enum_sort_cmp(const void *left, const void *right);
! static int enum_oid_cmp(const void *left, const void *right);
! static enum_sort_cache *initcache(Oid arg, FunctionCallInfo fcinfo);
/* Basic I/O support */
***************
*** 155,167 **** enum_send(PG_FUNCTION_ARGS)
/* Comparison functions and related */
Datum
enum_lt(PG_FUNCTION_ARGS)
{
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(a < b);
}
Datum
--- 177,366 ----
/* Comparison functions and related */
+ static enum_sort_cache *
+ initcache(Oid arg, FunctionCallInfo fcinfo)
+ {
+ HeapTuple enum_tup;
+ Form_pg_enum en;
+ Oid typeoid;
+ enum_sort_cache *mycache;
+ CatCList *list;
+ int num,i, bm_start, bm_len, start_pos, list_end;
+
+ /* free up anything we've used before) */
+ if (fcinfo->flinfo->fn_extra != NULL)
+ pfree(fcinfo->flinfo->fn_extra);
+
+ /* get the typeoid, label count and the list of values */
+ enum_tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(arg));
+ en = (Form_pg_enum) GETSTRUCT(enum_tup);
+ typeoid = en->enumtypid;
+ ReleaseSysCache(enum_tup);
+ list = SearchSysCacheList1(ENUMTYPOIDNAME, ObjectIdGetDatum(typeoid));
+ num = list->n_members;
+ fcinfo->flinfo->fn_extra =
+ MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+ sizeof(enum_sort_cache) +
+ (num * sizeof(enum_sort)));
+ mycache = (enum_sort_cache *) fcinfo->flinfo->fn_extra;
+ mycache->enumtypoid = typeoid;
+ mycache->label_count = num;
+ memset(mycache->bitmap, 0, BITMAPBYTES);
+
+ /* set up the list sorted by Oid */
+ for (i = 0; i < num; i++)
+ {
+ HeapTuple tup = &(list->members[i]->tuple);
+ Form_pg_enum list_en = (Form_pg_enum) GETSTRUCT(tup);
+
+ mycache->sort_order_list[i].sort_order = list_en->enumsortorder;
+ mycache->sort_order_list[i].enum_oid = HeapTupleGetOid(tup);
+ }
+
+ ReleaseCatCacheList(list);
+
+ qsort(mycache->sort_order_list,mycache->label_count,
+ sizeof(enum_sort),enum_oid_cmp);
+
+
+ /* look for the longest suitable range for the bitmap */
+ bm_len = 0;
+ bm_start = 0;
+
+ for (start_pos = 0; start_pos < num -1; start_pos ++)
+ {
+ for (list_end = start_pos+1; list_end < num; list_end++)
+ if (mycache->sort_order_list[list_end].sort_order <
+ mycache->sort_order_list[list_end - 1].sort_order ||
+ mycache->sort_order_list[list_end].enum_oid -
+ mycache->sort_order_list[list_end].enum_oid >= BITMAPSIZE)
+ break;
+ if (list_end - start_pos > bm_len)
+ {
+ bm_len = list_end - start_pos;
+ bm_start = start_pos;
+ }
+ if (bm_len == num)
+ break;
+ }
+
+ /* if we found a suitable range, for the bitmap, set it up */
+ if (bm_len > 1)
+ {
+ int base = mycache->sort_order_list[bm_start].enum_oid;
+ for (i = 0; i < bm_len; i++)
+ {
+ Oid enoid = mycache->sort_order_list[i].enum_oid;
+ int offset = enoid - base;
+ int bytenum = offset / 8;
+ int bitmask = 1 << (offset % 8);
+ mycache->bitmap[bytenum] |= bitmask;
+ }
+ }
+
+ return mycache;
+ }
+
+ /* fast lookup for Oids known to be in order */
+
+ static inline bool
+ bitmap_lookup(enum_sort_cache *mycache, Oid arg1, Oid arg2)
+ {
+ int offset1 = arg1 - mycache->bitmap_base;
+ int offset2 = arg2 - mycache->bitmap_base;
+ int bytenum, bitmask;
+ if (offset1 < 0 || offset2 < 0 ||
+ offset1 > BITMAPSIZE || offset2 > BITMAPSIZE)
+ return false;
+ bytenum = offset1 / 8;
+ bitmask = 1 << (offset1 % 8);
+ if ((mycache->bitmap[bytenum] & bitmask) == 0)
+ return false;
+ bytenum = offset2 / 8;
+ bitmask = 1 << (offset2 % 8);
+ if ((mycache->bitmap[bytenum] & bitmask) == 0)
+ return false;
+ return true;
+ }
+
+ /* slower lookup for Oids not known to be in order */
+
+ static inline int
+ find_sortorder(enum_sort_cache *mycache, Oid arg)
+ {
+ enum_sort *es;
+ enum_sort srch;
+
+ srch.enum_oid = arg;
+ es = bsearch(&srch,
+ mycache->sort_order_list,
+ mycache->label_count,
+ sizeof(enum_sort),
+ enum_oid_cmp);
+ if (es == NULL)
+ return -1;
+
+ return es->sort_order;
+
+ }
+
+ /* enum_ccmp is the common engine for all the visible comparison functions */
+
+ static inline int
+ enum_ccmp(Oid arg1, Oid arg2, FunctionCallInfo fcinfo)
+ {
+
+ enum_sort_cache *mycache;
+ int sort_1, sort_2;
+
+ /* evenly numbered Oids are know to sort right */
+ if (arg1 % 2 == 0 && arg2 % 2 == 0)
+ return arg1 - arg2;
+
+
+ /* so are oids that are equal */
+ if (arg1 == arg2)
+ return 0;
+
+ /* get the cached info, set it up if absent */
+ mycache = (enum_sort_cache *) fcinfo->flinfo->fn_extra;
+ if (mycache == NULL )
+ mycache = initcache(arg1, fcinfo);
+
+ /* first try the fast lookup */
+ if (bitmap_lookup(mycache, arg1, arg2))
+ return arg1 - arg2;
+
+ /* try the slow lookup */
+ sort_1 = find_sortorder(mycache,arg1);
+ sort_2 = find_sortorder(mycache,arg2);
+
+ if (sort_1 <= 0 || sort_2 <= 0)
+ {
+ /*
+ * We couldn't find one or both values.
+ * That means the enum has changed under us, so
+ * re-initialize the cache and try again.
+ */
+ mycache = initcache(arg1, fcinfo);
+ sort_1 = find_sortorder(mycache,arg1);
+ sort_2 = find_sortorder(mycache,arg2);
+ /* if we fail the secind time around, give up */
+ if (sort_1 <= 0 || sort_2 <= 0)
+ elog(ERROR, "values missing for enum %s",
+ format_type_be(mycache->enumtypoid));
+ }
+
+ return sort_1 - sort_2;
+ }
+
Datum
enum_lt(PG_FUNCTION_ARGS)
{
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(enum_ccmp(a,b,fcinfo) < 0);
}
Datum
***************
*** 170,176 **** enum_le(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(a <= b);
}
Datum
--- 369,375 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(enum_ccmp(a,b,fcinfo) <= 0);
}
Datum
***************
*** 197,203 **** enum_ge(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(a >= b);
}
Datum
--- 396,402 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(enum_ccmp(a,b,fcinfo) >= 0);
}
Datum
***************
*** 206,212 **** enum_gt(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(a > b);
}
Datum
--- 405,411 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(enum_ccmp(a,b,fcinfo) > 0);
}
Datum
***************
*** 215,221 **** enum_smaller(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_OID(a <= b ? a : b);
}
Datum
--- 414,420 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_OID(enum_ccmp(a,b,fcinfo) < 0 ? a : b);
}
Datum
***************
*** 224,230 **** enum_larger(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_OID(a >= b ? a : b);
}
Datum
--- 423,429 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_OID(enum_ccmp(a,b,fcinfo) > 0 ? a : b);
}
Datum
***************
*** 233,242 **** enum_cmp(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! if (a > b)
! PG_RETURN_INT32(1);
! else if (a == b)
PG_RETURN_INT32(0);
else
PG_RETURN_INT32(-1);
}
--- 432,441 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! if (a == b)
PG_RETURN_INT32(0);
+ else if (enum_ccmp(a,b,fcinfo) > 0)
+ PG_RETURN_INT32(1);
else
PG_RETURN_INT32(-1);
}
***************
*** 248,253 **** enum_first(PG_FUNCTION_ARGS)
--- 447,453 ----
{
Oid enumtypoid;
Oid min = InvalidOid;
+ int min_sort = -1; /* value will never in fact be used */
CatCList *list;
int num,
i;
***************
*** 267,276 **** enum_first(PG_FUNCTION_ARGS)
num = list->n_members;
for (i = 0; i < num; i++)
{
! Oid valoid = HeapTupleHeaderGetOid(list->members[i]->tuple.t_data);
!
! if (!OidIsValid(min) || valoid < min)
! min = valoid;
}
ReleaseCatCacheList(list);
--- 467,480 ----
num = list->n_members;
for (i = 0; i < num; i++)
{
! HeapTuple tup = &(list->members[i]->tuple);
! Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
!
! if (!OidIsValid(min) || en->enumsortorder < min_sort)
! {
! min = HeapTupleGetOid(tup);
! min_sort = en->enumsortorder;
! }
}
ReleaseCatCacheList(list);
***************
*** 287,292 **** enum_last(PG_FUNCTION_ARGS)
--- 491,497 ----
{
Oid enumtypoid;
Oid max = InvalidOid;
+ int max_sort = -1; /* value will never in fact be used */
CatCList *list;
int num,
i;
***************
*** 306,315 **** enum_last(PG_FUNCTION_ARGS)
num = list->n_members;
for (i = 0; i < num; i++)
{
! Oid valoid = HeapTupleHeaderGetOid(list->members[i]->tuple.t_data);
!
! if (!OidIsValid(max) || valoid > max)
! max = valoid;
}
ReleaseCatCacheList(list);
--- 511,524 ----
num = list->n_members;
for (i = 0; i < num; i++)
{
! HeapTuple tup = &(list->members[i]->tuple);
! Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
!
! if (!OidIsValid(max) || en->enumsortorder > max_sort)
! {
! max = HeapTupleGetOid(tup);
! max_sort = en->enumsortorder;
! }
}
ReleaseCatCacheList(list);
***************
*** 382,427 **** enum_range_internal(Oid enumtypoid, Oid lower, Oid upper)
i,
j;
Datum *elems;
list = SearchSysCacheList1(ENUMTYPOIDNAME, ObjectIdGetDatum(enumtypoid));
total = list->n_members;
elems = (Datum *) palloc(total * sizeof(Datum));
- j = 0;
for (i = 0; i < total; i++)
{
! Oid val = HeapTupleGetOid(&(list->members[i]->tuple));
- if ((!OidIsValid(lower) || lower <= val) &&
- (!OidIsValid(upper) || val <= upper))
- elems[j++] = ObjectIdGetDatum(val);
}
/* shouldn't need the cache anymore */
ReleaseCatCacheList(list);
! /* sort results into OID order */
! qsort(elems, j, sizeof(Datum), enum_elem_cmp);
/* note this hardwires some details about the representation of Oid */
result = construct_array(elems, j, enumtypoid, sizeof(Oid), true, 'i');
pfree(elems);
return result;
}
! /* qsort comparison function for Datums that are OIDs */
static int
! enum_elem_cmp(const void *left, const void *right)
{
! Oid l = DatumGetObjectId(*((const Datum *) left));
! Oid r = DatumGetObjectId(*((const Datum *) right));
! if (l < r)
! return -1;
! if (l > r)
! return 1;
! return 0;
}
--- 591,666 ----
i,
j;
Datum *elems;
+ enum_sort *sort_items;
+ bool left_found;
list = SearchSysCacheList1(ENUMTYPOIDNAME, ObjectIdGetDatum(enumtypoid));
total = list->n_members;
elems = (Datum *) palloc(total * sizeof(Datum));
+ sort_items = (enum_sort *) palloc(total * sizeof(enum_sort));
for (i = 0; i < total; i++)
{
! HeapTuple tup = &(list->members[i]->tuple);
! Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
!
! sort_items[i].enum_oid = HeapTupleGetOid(tup);
! sort_items[i].sort_order = en->enumsortorder;
}
/* shouldn't need the cache anymore */
ReleaseCatCacheList(list);
! /* sort results into sort_order sequence */
! qsort(sort_items, total, sizeof(enum_sort), enum_sort_cmp);
!
! j = 0;
! left_found = !OidIsValid(lower);
! for (i=0; i < total; i++)
! {
! if (! left_found && lower == sort_items[i].enum_oid)
! left_found = true;
!
! if (left_found)
! elems[j++] = ObjectIdGetDatum(sort_items[i].enum_oid);
!
! if (OidIsValid(upper) && upper == sort_items[i].enum_oid)
! break;
! }
/* note this hardwires some details about the representation of Oid */
result = construct_array(elems, j, enumtypoid, sizeof(Oid), true, 'i');
pfree(elems);
+ pfree(sort_items);
return result;
}
! /*
! * qsort comparison using sort order, for range routines
! */
static int
! enum_sort_cmp(const void *left, const void *right)
{
! enum_sort *l = (enum_sort *) left;
! enum_sort *r = (enum_sort *) right;
! return l->sort_order - r->sort_order;
! }
!
! /*
! * qsort comparison using OID order for comparison search cache
! */
! static int
! enum_oid_cmp(const void *es1, const void *es2)
! {
! enum_sort *p1, *p2;
! p1 = (enum_sort *)es1;
! p2 = (enum_sort *)es2;
! return p1->enum_oid - p2->enum_oid;
}
+
+
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
***************
*** 6653,6669 **** dumpEnumType(Archive *fout, TypeInfo *tyinfo)
PQExpBuffer query = createPQExpBuffer();
PGresult *res;
int num,
! i;
Oid enum_oid;
char *label;
/* Set proper schema search path so regproc references list correctly */
selectSourceSchema(tyinfo->dobj.namespace->dobj.name);
! appendPQExpBuffer(query, "SELECT oid, enumlabel "
! "FROM pg_catalog.pg_enum "
! "WHERE enumtypid = '%u'"
! "ORDER BY oid",
tyinfo->dobj.catId.oid);
res = PQexec(g_conn, query->data);
--- 6653,6676 ----
PQExpBuffer query = createPQExpBuffer();
PGresult *res;
int num,
! i;
Oid enum_oid;
char *label;
/* Set proper schema search path so regproc references list correctly */
selectSourceSchema(tyinfo->dobj.namespace->dobj.name);
! if (fout->remoteVersion > 90000)
! appendPQExpBuffer(query, "SELECT oid, enumlabel "
! "FROM pg_catalog.pg_enum "
! "WHERE enumtypid = '%u'"
! "ORDER BY enumsortorder",
! tyinfo->dobj.catId.oid);
! else
! appendPQExpBuffer(query, "SELECT oid, enumlabel "
! "FROM pg_catalog.pg_enum "
! "WHERE enumtypid = '%u'"
! "ORDER BY oid",
tyinfo->dobj.catId.oid);
res = PQexec(g_conn, query->data);
***************
*** 6709,6725 **** dumpEnumType(Archive *fout, TypeInfo *tyinfo)
{
enum_oid = atooid(PQgetvalue(res, i, PQfnumber(res, "oid")));
label = PQgetvalue(res, i, PQfnumber(res, "enumlabel"));
!
if (i == 0)
appendPQExpBuffer(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
appendPQExpBuffer(q,
! "SELECT binary_upgrade.add_pg_enum_label('%u'::pg_catalog.oid, "
! "'%u'::pg_catalog.oid, ",
! enum_oid, tyinfo->dobj.catId.oid);
! appendStringLiteralAH(q, label, fout);
! appendPQExpBuffer(q, ");\n");
}
- appendPQExpBuffer(q, "\n");
}
ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
--- 6716,6734 ----
{
enum_oid = atooid(PQgetvalue(res, i, PQfnumber(res, "oid")));
label = PQgetvalue(res, i, PQfnumber(res, "enumlabel"));
!
if (i == 0)
appendPQExpBuffer(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
appendPQExpBuffer(q,
! "SELECT binary_upgrade.set_next_pg_enum_oid('%u'::pg_catalog.oid);\n",
! enum_oid);
! appendPQExpBuffer(q, "ALTER TYPE %s.",
! fmtId(tyinfo->dobj.namespace->dobj.name));
! appendPQExpBuffer(q, "%s ADD ",
! fmtId(tyinfo->dobj.name));
! appendStringLiteralAH(q, label, fout);
! appendPQExpBuffer(q, ";\n\n");
}
}
ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
*** a/src/bin/psql/describe.c
--- b/src/bin/psql/describe.c
***************
*** 473,489 **** describeTypes(const char *pattern, bool verbose, bool showSystem)
gettext_noop("Internal name"),
gettext_noop("Size"));
if (verbose && pset.sversion >= 80300)
appendPQExpBuffer(&buf,
" pg_catalog.array_to_string(\n"
" ARRAY(\n"
" SELECT e.enumlabel\n"
" FROM pg_catalog.pg_enum e\n"
! " WHERE e.enumtypid = t.oid\n"
! " ORDER BY e.oid\n"
" ),\n"
" E'\\n'\n"
" ) AS \"%s\",\n",
gettext_noop("Elements"));
appendPQExpBuffer(&buf,
" pg_catalog.obj_description(t.oid, 'pg_type') as \"%s\"\n",
--- 473,499 ----
gettext_noop("Internal name"),
gettext_noop("Size"));
if (verbose && pset.sversion >= 80300)
+ {
appendPQExpBuffer(&buf,
" pg_catalog.array_to_string(\n"
" ARRAY(\n"
" SELECT e.enumlabel\n"
" FROM pg_catalog.pg_enum e\n"
! " WHERE e.enumtypid = t.oid\n");
!
! if (pset.sversion >= 90100 )
! appendPQExpBuffer(&buf,
! " ORDER BY e.enumsortorder\n");
! else
! appendPQExpBuffer(&buf,
! " ORDER BY e.oid\n");
!
! appendPQExpBuffer(&buf,
" ),\n"
" E'\\n'\n"
" ) AS \"%s\",\n",
gettext_noop("Elements"));
+ }
appendPQExpBuffer(&buf,
" pg_catalog.obj_description(t.oid, 'pg_type') as \"%s\"\n",
*** a/src/include/catalog/indexing.h
--- b/src/include/catalog/indexing.h
***************
*** 147,152 **** DECLARE_UNIQUE_INDEX(pg_enum_oid_index, 3502, on pg_enum using btree(oid oid_ops
--- 147,154 ----
#define EnumOidIndexId 3502
DECLARE_UNIQUE_INDEX(pg_enum_typid_label_index, 3503, on pg_enum using btree(enumtypid oid_ops, enumlabel name_ops));
#define EnumTypIdLabelIndexId 3503
+ DECLARE_UNIQUE_INDEX(pg_enum_typid_sortorder_index, 3539, on pg_enum using btree(enumtypid oid_ops, enumsortorder int4_ops));
+ #define EnumTypIdSortOrderIndexId 3539
/* This following index is not used for a cache and is not unique */
DECLARE_INDEX(pg_index_indrelid_index, 2678, on pg_index using btree(indrelid oid_ops));
*** a/src/include/catalog/pg_enum.h
--- b/src/include/catalog/pg_enum.h
***************
*** 35,40 **** CATALOG(pg_enum,3501)
--- 35,41 ----
{
Oid enumtypid; /* OID of owning enum type */
NameData enumlabel; /* text representation of enum value */
+ int4 enumsortorder; /* sort order for this enum label */
} FormData_pg_enum;
/* ----------------
***************
*** 48,56 **** typedef FormData_pg_enum *Form_pg_enum;
* compiler constants for pg_enum
* ----------------
*/
! #define Natts_pg_enum 2
#define Anum_pg_enum_enumtypid 1
#define Anum_pg_enum_enumlabel 2
/* ----------------
* pg_enum has no initial contents
--- 49,58 ----
* compiler constants for pg_enum
* ----------------
*/
! #define Natts_pg_enum 3
#define Anum_pg_enum_enumtypid 1
#define Anum_pg_enum_enumlabel 2
+ #define Anum_pg_enum_enumsortorder 3
/* ----------------
* pg_enum has no initial contents
***************
*** 60,67 **** typedef FormData_pg_enum *Form_pg_enum;
/*
* prototypes for functions in pg_enum.c
*/
! extern void EnumValuesCreate(Oid enumTypeOid, List *vals,
! Oid binary_upgrade_next_pg_enum_oid);
extern void EnumValuesDelete(Oid enumTypeOid);
#endif /* PG_ENUM_H */
--- 62,70 ----
/*
* prototypes for functions in pg_enum.c
*/
! extern void EnumValuesCreate(Oid enumTypeOid, List *vals);
extern void EnumValuesDelete(Oid enumTypeOid);
+ extern void AddEnumLabel(Oid enumTypeOid, char *newVal, char *neighbour,
+ bool newValIsAfter);
#endif /* PG_ENUM_H */
*** a/src/include/catalog/pg_type_fn.h
--- b/src/include/catalog/pg_type_fn.h
***************
*** 50,56 **** extern Oid TypeCreate(Oid newTypeOid,
char storage,
int32 typeMod,
int32 typNDims,
! bool typeNotNull);
extern void GenerateTypeDependencies(Oid typeNamespace,
Oid typeObjectId,
--- 50,56 ----
char storage,
int32 typeMod,
int32 typNDims,
! bool typeNotNull);
extern void GenerateTypeDependencies(Oid typeNamespace,
Oid typeObjectId,
*** a/src/include/commands/typecmds.h
--- b/src/include/commands/typecmds.h
***************
*** 24,29 **** extern void RemoveTypes(DropStmt *drop);
--- 24,30 ----
extern void RemoveTypeById(Oid typeOid);
extern void DefineDomain(CreateDomainStmt *stmt);
extern void DefineEnum(CreateEnumStmt *stmt);
+ extern void AlterEnum (AlterEnumStmt *stmt);
extern Oid DefineCompositeType(const RangeVar *typevar, List *coldeflist);
extern Oid AssignTypeArrayOid(void);
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
***************
*** 351,357 **** typedef enum NodeTag
T_DropUserMappingStmt,
T_AlterTableSpaceOptionsStmt,
T_SecLabelStmt,
!
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
*/
--- 351,357 ----
T_DropUserMappingStmt,
T_AlterTableSpaceOptionsStmt,
T_SecLabelStmt,
! T_AlterEnumStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
*/
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 2195,2200 **** typedef struct CreateEnumStmt
--- 2195,2214 ----
/* ----------------------
+ * Alter Type Statement, enum types
+ * ----------------------
+ */
+ typedef struct AlterEnumStmt
+ {
+ NodeTag type;
+ List *typeName; /* qualified name (list of Value strings) */
+ char *newVal; /* new enum value */
+ char *newValNeighbour;/* neighbouring enum value */
+ bool newValIsAfter; /* new enum value is after neighbour? */
+ } AlterEnumStmt;
+
+
+ /* ----------------------
* Create View Statement
* ----------------------
*/
*** a/src/test/regress/expected/enum.out
--- b/src/test/regress/expected/enum.out
***************
*** 25,30 **** ERROR: invalid input value for enum rainbow: "mauve"
--- 25,92 ----
LINE 1: SELECT 'mauve'::rainbow;
^
--
+ -- adding new values
+ --
+ CREATE TYPE planets AS ENUM ( 'venus', 'earth', 'mars' );
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+ enumlabel | enumsortorder
+ -----------+---------------
+ venus | 1
+ earth | 2
+ mars | 3
+ (3 rows)
+
+ ALTER TYPE planets ADD 'uranus';
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+ enumlabel | enumsortorder
+ -----------+---------------
+ venus | 1
+ earth | 2
+ mars | 3
+ uranus | 4
+ (4 rows)
+
+ ALTER TYPE planets ADD 'mercury' BEFORE 'venus';
+ ALTER TYPE planets ADD 'saturn' BEFORE 'uranus';
+ ALTER TYPE planets ADD 'jupiter' AFTER 'mars';
+ ALTER TYPE planets ADD 'neptune' AFTER 'uranus';
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+ enumlabel | enumsortorder
+ -----------+---------------
+ mercury | 1
+ venus | 2
+ earth | 3
+ mars | 4
+ jupiter | 5
+ saturn | 6
+ uranus | 7
+ neptune | 8
+ (8 rows)
+
+ select 'mars'::planets > 'mercury' as using_sortorder;
+ using_sortorder
+ -----------------
+ t
+ (1 row)
+
+ -- errors for adding labels
+ ALTER TYPE planets ADD
+ 'plutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutopluto';
+ ERROR: invalid enum label "plutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutopluto"
+ DETAIL: Labels must be 63 characters or less.
+ ALTER TYPE planets ADD 'pluto' AFTER 'zeus';
+ ERROR: "zeus" is not an existing label.
+ DROP TYPE planets;
+ --
-- Basic table creation, row selection
--
CREATE TABLE enumtest (col rainbow);
***************
*** 403,409 **** SELECT COUNT(*) FROM pg_type WHERE typname = 'rainbow';
SELECT * FROM pg_enum WHERE NOT EXISTS
(SELECT 1 FROM pg_type WHERE pg_type.oid = enumtypid);
! enumtypid | enumlabel
! -----------+-----------
(0 rows)
--- 465,471 ----
SELECT * FROM pg_enum WHERE NOT EXISTS
(SELECT 1 FROM pg_type WHERE pg_type.oid = enumtypid);
! enumtypid | enumlabel | enumsortorder
! -----------+-----------+---------------
(0 rows)
*** a/src/test/regress/sql/enum.sql
--- b/src/test/regress/sql/enum.sql
***************
*** 16,21 **** SELECT 'red'::rainbow;
--- 16,61 ----
SELECT 'mauve'::rainbow;
--
+ -- adding new values
+ --
+
+ CREATE TYPE planets AS ENUM ( 'venus', 'earth', 'mars' );
+
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+
+ ALTER TYPE planets ADD 'uranus';
+
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+
+ ALTER TYPE planets ADD 'mercury' BEFORE 'venus';
+
+ ALTER TYPE planets ADD 'saturn' BEFORE 'uranus';
+
+ ALTER TYPE planets ADD 'jupiter' AFTER 'mars';
+
+ ALTER TYPE planets ADD 'neptune' AFTER 'uranus';
+
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+
+ select 'mars'::planets > 'mercury' as using_sortorder;
+
+ -- errors for adding labels
+ ALTER TYPE planets ADD
+ 'plutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutopluto';
+
+ ALTER TYPE planets ADD 'pluto' AFTER 'zeus';
+
+ DROP TYPE planets;
+ --
-- Basic table creation, row selection
--
CREATE TABLE enumtest (col rainbow);
On 19 October 2010 05:21, Andrew Dunstan <andrew@dunslane.net> wrote:
On 10/18/2010 10:52 AM, Tom Lane wrote:
We could possibly deal with enum types that follow the existing
convention if we made the cache entry hold a list of all the original,
known-to-be-sorted OIDs. (This could be reasonably compact and cheap to
probe if it were represented as a starting OID and a Bitmapset of delta
values, since we can assume that the initial set of OIDs is pretty close
together.) But we have to have that cache entry, and we have to consult
it on every single comparison, so it's definitely going to be slower
than before.So I'm thinking the comparison procedure goes like this:
1. Both OIDs even?
If so, just compare them numerically, and we're done.2. Lookup cache entry for enum type.
3. Both OIDs in list of known-sorted OIDs?
If so, just compare them numerically, and we're done.4. Search the part of the cache entry that lists sort positions.
If not both present, refresh the cache entry.
If still not present, throw error.5. Compare by sort positions.
Step 4 is the slowest part but would be avoided in most cases.
However, step 2 is none too speedy either, and would usually
be required when dealing with pre-existing enums.OK, I've made adjustments that I think do what you're suggesting.
Patch is attached.
Ah, I'd missed the point about the bitmapset. In the most common case,
most of the enum elements are probably going to be in the right order,
so you save a lot by identifying that case quickly.
I didn't have time to play with hash maps myself, but I don't think it
will make much difference now because hopefully the binary search
isn't going to be hit a lot anyway.
There are a couple of things that look odd about this code though (I
haven't tested it, but they look wrong):
In the loop identifying the longest range:
for (list_end = start_pos+1; list_end < num; list_end++)
if (mycache->sort_order_list[list_end].sort_order <
mycache->sort_order_list[list_end - 1].sort_order ||
mycache->sort_order_list[list_end].enum_oid -
mycache->sort_order_list[list_end].enum_oid >= BITMAPSIZE)
That last test isn't doing anything. Shouldn't the second "list_end"
should be "start_pos"?
In the loop that sets bits in the bitmap, it looks like it's assuming
the range starts at 0. I haven't had any caffeine yet, so maybe I'm
misunderstanding it, but I can't see anywhere that sets bitmap_base.
Regards,
Dean
Show quoted text
Alternatively this can be pulled from
<git@github.com:adunstan/postgresql-dev.git>cheers
andrew
On 19 October 2010 05:21, Andrew Dunstan <andrew@dunslane.net> wrote:
On 10/18/2010 10:52 AM, Tom Lane wrote:
We could possibly deal with enum types that follow the existing
convention if we made the cache entry hold a list of all the original,
known-to-be-sorted OIDs. (This could be reasonably compact and cheap to
probe if it were represented as a starting OID and a Bitmapset of delta
values, since we can assume that the initial set of OIDs is pretty close
together.) But we have to have that cache entry, and we have to consult
it on every single comparison, so it's definitely going to be slower
than before.So I'm thinking the comparison procedure goes like this:
1. Both OIDs even?
If so, just compare them numerically, and we're done.2. Lookup cache entry for enum type.
3. Both OIDs in list of known-sorted OIDs?
If so, just compare them numerically, and we're done.4. Search the part of the cache entry that lists sort positions.
If not both present, refresh the cache entry.
If still not present, throw error.5. Compare by sort positions.
Step 4 is the slowest part but would be avoided in most cases.
However, step 2 is none too speedy either, and would usually
be required when dealing with pre-existing enums.OK, I've made adjustments that I think do what you're suggesting.
Patch is attached.
Alternatively this can be pulled from
<git@github.com:adunstan/postgresql-dev.git>
Andrew, can't you get your own repo at git.postgresql.org?
--
Thom Brown
Twitter: @darkixion
IRC (freenode): dark_ixion
Registered Linux user: #516935
On 10/19/2010 04:00 AM, Dean Rasheed wrote:
There are a couple of things that look odd about this code though (I
haven't tested it, but they look wrong):
You're right, thanks. I have fixed those. New patch attached.
cheers
andrew
Attachments:
venum7.patchtext/x-patch; name=venum7.patchDownload
*** a/contrib/pg_upgrade/function.c
--- b/contrib/pg_upgrade/function.c
***************
*** 61,85 **** install_support_functions(migratorContext *ctx)
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_heap_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_toast_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_index_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.add_pg_enum_label(OID, OID, NAME) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
--- 61,85 ----
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_pg_enum_oid(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_heap_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_toast_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_index_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
*** a/contrib/pg_upgrade_support/pg_upgrade_support.c
--- b/contrib/pg_upgrade_support/pg_upgrade_support.c
***************
*** 30,35 **** PG_MODULE_MAGIC;
--- 30,36 ----
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_array_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_toast_oid;
+ extern PGDLLIMPORT Oid binary_upgrade_next_pg_enum_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_heap_relfilenode;
extern PGDLLIMPORT Oid binary_upgrade_next_toast_relfilenode;
extern PGDLLIMPORT Oid binary_upgrade_next_index_relfilenode;
***************
*** 37,54 **** extern PGDLLIMPORT Oid binary_upgrade_next_index_relfilenode;
Datum set_next_pg_type_oid(PG_FUNCTION_ARGS);
Datum set_next_pg_type_array_oid(PG_FUNCTION_ARGS);
Datum set_next_pg_type_toast_oid(PG_FUNCTION_ARGS);
Datum set_next_heap_relfilenode(PG_FUNCTION_ARGS);
Datum set_next_toast_relfilenode(PG_FUNCTION_ARGS);
Datum set_next_index_relfilenode(PG_FUNCTION_ARGS);
- Datum add_pg_enum_label(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(set_next_pg_type_oid);
PG_FUNCTION_INFO_V1(set_next_pg_type_array_oid);
PG_FUNCTION_INFO_V1(set_next_pg_type_toast_oid);
PG_FUNCTION_INFO_V1(set_next_heap_relfilenode);
PG_FUNCTION_INFO_V1(set_next_toast_relfilenode);
PG_FUNCTION_INFO_V1(set_next_index_relfilenode);
- PG_FUNCTION_INFO_V1(add_pg_enum_label);
Datum
set_next_pg_type_oid(PG_FUNCTION_ARGS)
--- 38,55 ----
Datum set_next_pg_type_oid(PG_FUNCTION_ARGS);
Datum set_next_pg_type_array_oid(PG_FUNCTION_ARGS);
Datum set_next_pg_type_toast_oid(PG_FUNCTION_ARGS);
+ Datum set_next_pg_enum_oid(PG_FUNCTION_ARGS);
Datum set_next_heap_relfilenode(PG_FUNCTION_ARGS);
Datum set_next_toast_relfilenode(PG_FUNCTION_ARGS);
Datum set_next_index_relfilenode(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(set_next_pg_type_oid);
PG_FUNCTION_INFO_V1(set_next_pg_type_array_oid);
PG_FUNCTION_INFO_V1(set_next_pg_type_toast_oid);
+ PG_FUNCTION_INFO_V1(set_next_pg_enum_oid);
PG_FUNCTION_INFO_V1(set_next_heap_relfilenode);
PG_FUNCTION_INFO_V1(set_next_toast_relfilenode);
PG_FUNCTION_INFO_V1(set_next_index_relfilenode);
Datum
set_next_pg_type_oid(PG_FUNCTION_ARGS)
***************
*** 81,86 **** set_next_pg_type_toast_oid(PG_FUNCTION_ARGS)
--- 82,97 ----
}
Datum
+ set_next_pg_enum_oid(PG_FUNCTION_ARGS)
+ {
+ Oid enumoid = PG_GETARG_OID(0);
+
+ binary_upgrade_next_pg_enum_oid = enumoid;
+
+ PG_RETURN_VOID();
+ }
+
+ Datum
set_next_heap_relfilenode(PG_FUNCTION_ARGS)
{
Oid relfilenode = PG_GETARG_OID(0);
***************
*** 110,124 **** set_next_index_relfilenode(PG_FUNCTION_ARGS)
PG_RETURN_VOID();
}
- Datum
- add_pg_enum_label(PG_FUNCTION_ARGS)
- {
- Oid enumoid = PG_GETARG_OID(0);
- Oid typoid = PG_GETARG_OID(1);
- Name label = PG_GETARG_NAME(2);
-
- EnumValuesCreate(typoid, list_make1(makeString(NameStr(*label))),
- enumoid);
-
- PG_RETURN_VOID();
- }
--- 121,123 ----
*** a/doc/src/sgml/ref/alter_type.sgml
--- b/doc/src/sgml/ref/alter_type.sgml
***************
*** 23,33 **** PostgreSQL documentation
<refsynopsisdiv>
<synopsis>
! ALTER TYPE <replaceable class="PARAMETER">name</replaceable> <replaceable class="PARAMETER">action</replaceable> [, ... ]
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> OWNER TO <replaceable class="PARAMETER">new_owner</replaceable>
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> RENAME ATTRIBUTE <replaceable class="PARAMETER">attribute_name</replaceable> TO <replaceable class="PARAMETER">new_attribute_name</replaceable>
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> RENAME TO <replaceable class="PARAMETER">new_name</replaceable>
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> SET SCHEMA <replaceable class="PARAMETER">new_schema</replaceable>
<phrase>where <replaceable class="PARAMETER">action</replaceable> is one of:</phrase>
--- 23,34 ----
<refsynopsisdiv>
<synopsis>
! ALTER TYPE <replaceable class="PARAMETER">name</replaceable> <replaceable class="PARAMETER">action</replaceable> [, ... ]
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> OWNER TO <replaceable class="PARAMETER">new_owner</replaceable>
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> RENAME ATTRIBUTE <replaceable class="PARAMETER">attribute_name</replaceable> TO <replaceable class="PARAMETER">new_attribute_name</replaceable>
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> RENAME TO <replaceable class="PARAMETER">new_name</replaceable>
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> SET SCHEMA <replaceable class="PARAMETER">new_schema</replaceable>
+ ALTER TYPE <replaceable class="PARAMETER">name</replaceable> ADD <replaceable class="PARAMETER">new_enum_value</replaceable> [ BEFORE | AFTER <replaceable class="PARAMETER">existing_enum_value</replaceable>
<phrase>where <replaceable class="PARAMETER">action</replaceable> is one of:</phrase>
***************
*** 103,108 **** ALTER TYPE <replaceable class="PARAMETER">name</replaceable> SET SCHEMA <replace
--- 104,131 ----
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><literal>ADD [ BEFORE | AFTER ]</literal></term>
+ <listitem>
+ <para>
+ This form adds a new value to an enum type. If the new value's place
+ in the sort order is not set using <literal>BEFORE</literal> or
+ <literal>AFTER</literal>, then the new item is placed at the end of
+ the list of values.
+ </para>
+ <note>
+ <para>
+ Adding a new enum value will in some cases lower the comparison and
+ sorting performance of the enum type. This will only usually occur if
+ <literal>BEFORE</literal> or <literal>AFTER</literal> are used to set
+ the new value's sort order position somewhere other than at the end
+ of the list. Optimal performance will be restored if the database is
+ dumped and reloaded.
+ </para>
+ </note>
+ </listitem>
+ </varlistentry>
</variablelist>
</para>
***************
*** 196,201 **** ALTER TYPE <replaceable class="PARAMETER">name</replaceable> SET SCHEMA <replace
--- 219,254 ----
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><replaceable class="PARAMETER">data_type</replaceable></term>
+ <listitem>
+ <para>
+ The data type of the attribute to add, or the new type of the
+ attribute to alter.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="PARAMETER">new_enum_value</replaceable></term>
+ <listitem>
+ <para>
+ The new value to be added to the num type's list of values. Like all
+ enum literals it needs to be quoted.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="PARAMETER">exisiting_enum_value</replaceable></term>
+ <listitem>
+ <para>
+ The neighbour of the new value to be added to the num type's list of values. Like all
+ enum literals it needs to be quoted.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</para>
</refsect1>
***************
*** 232,237 **** ALTER TYPE email SET SCHEMA customers;
--- 285,297 ----
ALTER TYPE compfoo ADD ATTRIBUTE f3 int;
</programlisting>
</para>
+
+ <para>
+ To add a new value to an enum type in a particular sort position:
+ <programlisting>
+ ALTER TYPE colors ADD 'orange' AFTER 'red';
+ </programlisting>
+ </para>
</refsect1>
<refsect1>
*** a/src/backend/catalog/pg_enum.c
--- b/src/backend/catalog/pg_enum.c
***************
*** 18,29 ****
--- 18,226 ----
#include "catalog/catalog.h"
#include "catalog/indexing.h"
#include "catalog/pg_enum.h"
+ #include "catalog/pg_type.h"
+ #include "utils/lsyscache.h"
+ #include "utils/syscache.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/rel.h"
#include "utils/tqual.h"
static int oid_cmp(const void *p1, const void *p2);
+ static int sort_order_cmp(const void *p1, const void *p2);
+
+ Oid binary_upgrade_next_pg_enum_oid = InvalidOid;
+
+ /*
+ * AddEnumLabel
+ * Add a new label to the enum set. By default it goes at
+ * the end, but the user can choose to place it before or
+ * after any existing set member.
+ *
+ *
+ */
+
+ void
+ AddEnumLabel(Oid enumTypeOid,
+ char *newVal,
+ char *neighbour,
+ bool newValIsAfter)
+ {
+ Oid newOid;
+ Relation pg_enum;
+ TupleDesc tupDesc;
+ Datum values[Natts_pg_enum];
+ bool nulls[Natts_pg_enum];
+ NameData enumlabel;
+ HeapTuple enum_tup;
+ int newelemorder;
+ CatCList *list;
+ int nelems;
+
+ /* check length of new label is ok */
+ if (strlen(newVal) > (NAMEDATALEN - 1))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_NAME),
+ errmsg("invalid enum label \"%s\"", newVal),
+ errdetail("Labels must be %d characters or less.",
+ NAMEDATALEN - 1)));
+
+ /* get a new OID for the label */
+ pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
+
+ list = SearchSysCacheList1(ENUMTYPOIDNAME,
+ ObjectIdGetDatum(enumTypeOid));
+ nelems = list->n_members;
+
+ if (neighbour == NULL)
+ {
+ /*
+ * Put the new label at the end of the list.
+ * No change to existing tuples is required.
+ */
+ newelemorder = nelems + 1;
+ }
+ else
+ {
+ /* BEFORE or AFTER specified */
+ int i;
+ HeapTuple *existing;
+ HeapTuple nbr = NULL;
+ Form_pg_enum nbr_en;
+
+ /* sort the list of the existing elements by enumsortorder */
+ existing = palloc(nelems * sizeof(HeapTuple));
+
+ for (i = 0; i < nelems; i++)
+ existing[i] = &(list->members[i]->tuple);
+
+ qsort(existing, nelems, sizeof(HeapTuple), sort_order_cmp);
+
+ /* locate the neighbour element */
+ for (i = 0; i < nelems; i++)
+ {
+ Form_pg_enum exists_en;
+ exists_en = (Form_pg_enum) GETSTRUCT(existing[i]);
+ if (strcmp(NameStr(exists_en->enumlabel),neighbour) == 0)
+ nbr = existing[i];
+
+ }
+
+ if (nbr == NULL)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("\"%s\" is not an existing label.", neighbour)));
+ }
+
+ nbr_en = (Form_pg_enum) GETSTRUCT(nbr);
+
+ /*
+ * If BEFORE was specified, the new label goes in the neighbour's
+ * position. Otherwise, it goes in the position after that.
+ */
+ newelemorder = nbr_en->enumsortorder;
+ if (newValIsAfter)
+ newelemorder++;
+
+ /*
+ * Add 1 to the sortorder of all the labels after where the
+ * new label goes. Do it from the end back so we don't get
+ * uniqueness violations.
+ */
+ for (i = nelems - 1; i>= 0; i--)
+ {
+ HeapTuple newtup;
+ Form_pg_enum exst_en = (Form_pg_enum) GETSTRUCT(existing[i]);
+ if (exst_en->enumsortorder < newelemorder)
+ break;
+
+ newtup = heap_copytuple(existing[i]);
+ exst_en = (Form_pg_enum) GETSTRUCT(newtup);
+ exst_en->enumsortorder ++;
+
+ simple_heap_update(pg_enum, &newtup->t_self, newtup);
+
+ CatalogUpdateIndexes(pg_enum, newtup);
+
+ }
+
+ }
+
+ if (OidIsValid(binary_upgrade_next_pg_enum_oid))
+ {
+ if (neighbour != NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("BEFORE/AFTER incompatible with binary upgrade.")));
+
+ newOid = binary_upgrade_next_pg_enum_oid;
+ binary_upgrade_next_pg_enum_oid = InvalidOid;
+ }
+ else
+ {
+ /*
+ * Non upgrade case, we allocate a new Oid for the value.
+ *
+ * We try to give the new element an even numbered Oid if it's safe,
+ * which should be true if these conditions hold:
+ * . the Oid of the highest sorted exiting value is even, and
+ * . the new value is to sort after that value, and
+ * . there hasn't been Oid wraparound.
+ * For all other cases we allocate an odd Oid.
+ */
+
+
+ newOid = GetNewOid(pg_enum);
+ if (newelemorder > nelems)
+ {
+ Oid last_elem_oid = InvalidOid;
+ int i;
+
+ for (i = nelems - 1; i>= 0; i--)
+ {
+ Form_pg_enum last_en;
+ last_en = (Form_pg_enum) GETSTRUCT(&(list->members[i]->tuple));
+ if (last_en->enumsortorder == nelems)
+ {
+ last_elem_oid = HeapTupleGetOid(&(list->members[i]->tuple));
+ break;
+ }
+ }
+
+ if (last_elem_oid %2 == 0)
+ while ((newOid %2 == 1 && newOid > last_elem_oid) ||
+ (newOid %2 == 0 && newOid < last_elem_oid))
+ newOid = GetNewOid(pg_enum);
+ else
+ while (newOid %2 == 0)
+ newOid = GetNewOid(pg_enum);
+ }
+ else
+ {
+ while (newOid %2 == 0)
+ newOid = GetNewOid(pg_enum);
+ }
+ }
+
+ /* set up the new entry */
+ tupDesc = pg_enum->rd_att;
+ memset(nulls, false, sizeof(nulls));
+ values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid);
+ namestrcpy(&enumlabel, newVal);
+ values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel);
+ values[Anum_pg_enum_enumsortorder -1] = Int32GetDatum(newelemorder);
+ enum_tup = heap_form_tuple(tupDesc, values, nulls);
+ HeapTupleSetOid(enum_tup, newOid);
+ simple_heap_insert(pg_enum, enum_tup);
+ CatalogUpdateIndexes(pg_enum, enum_tup);
+ heap_freetuple(enum_tup);
+
+ heap_close(pg_enum, RowExclusiveLock);
+
+ ReleaseCatCacheList(list);
+
+ }
/*
***************
*** 33,40 **** static int oid_cmp(const void *p1, const void *p2);
* vals is a list of Value strings.
*/
void
! EnumValuesCreate(Oid enumTypeOid, List *vals,
! Oid binary_upgrade_next_pg_enum_oid)
{
Relation pg_enum;
TupleDesc tupDesc;
--- 230,236 ----
* vals is a list of Value strings.
*/
void
! EnumValuesCreate(Oid enumTypeOid, List *vals)
{
Relation pg_enum;
TupleDesc tupDesc;
***************
*** 50,58 **** EnumValuesCreate(Oid enumTypeOid, List *vals,
num_elems = list_length(vals);
/*
! * XXX we do not bother to check the list of values for duplicates --- if
* you have any, you'll get a less-than-friendly unique-index violation.
! * Is it worth trying harder?
*/
pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
--- 246,254 ----
num_elems = list_length(vals);
/*
! * We do not bother to check the list of values for duplicates --- if
* you have any, you'll get a less-than-friendly unique-index violation.
! * It is probably not worth trying harder.
*/
pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
***************
*** 62,96 **** EnumValuesCreate(Oid enumTypeOid, List *vals,
* Allocate oids
*/
oids = (Oid *) palloc(num_elems * sizeof(Oid));
! if (OidIsValid(binary_upgrade_next_pg_enum_oid))
! {
! if (num_elems != 1)
! ereport(ERROR,
! (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("EnumValuesCreate() can only set a single OID")));
! oids[0] = binary_upgrade_next_pg_enum_oid;
! binary_upgrade_next_pg_enum_oid = InvalidOid;
! }
! else
{
/*
! * While this method does not absolutely guarantee that we generate no
! * duplicate oids (since we haven't entered each oid into the table
! * before allocating the next), trouble could only occur if the oid
! * counter wraps all the way around before we finish. Which seems
! * unlikely.
*/
! for (elemno = 0; elemno < num_elems; elemno++)
! {
! /*
! * The pg_enum.oid is stored in user tables. This oid must be
! * preserved by binary upgrades.
! */
! oids[elemno] = GetNewOid(pg_enum);
! }
! /* sort them, just in case counter wrapped from high to low */
! qsort(oids, num_elems, sizeof(Oid), oid_cmp);
}
/* and make the entries */
memset(nulls, false, sizeof(nulls));
--- 258,287 ----
* Allocate oids
*/
oids = (Oid *) palloc(num_elems * sizeof(Oid));
!
! /*
! * While this method does not absolutely guarantee that we generate no
! * duplicate oids (since we haven't entered each oid into the table
! * before allocating the next), trouble could only occur if the oid
! * counter wraps all the way around before we finish. Which seems
! * unlikely.
! */
! for (elemno = 0; elemno < num_elems; elemno++)
{
/*
! * The pg_enum.oid is stored in user tables. This oid must be
! * preserved by binary upgrades.
! *
! * We allocate all new enums as evenly numbered Oids
! * so they will sort fast.
*/
! Oid new_oid;
! while ((new_oid = GetNewOid(pg_enum)) %2 == 1)
! ;
! oids[elemno] = new_oid;
}
+ /* sort them, just in case counter wrapped from high to low */
+ qsort(oids, num_elems, sizeof(Oid), oid_cmp);
/* and make the entries */
memset(nulls, false, sizeof(nulls));
***************
*** 114,119 **** EnumValuesCreate(Oid enumTypeOid, List *vals,
--- 305,311 ----
values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid);
namestrcpy(&enumlabel, lab);
values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel);
+ values[Anum_pg_enum_enumsortorder -1] = Int32GetDatum(elemno+1);
tup = heap_form_tuple(tupDesc, values, nulls);
HeapTupleSetOid(tup, oids[elemno]);
***************
*** 164,170 **** EnumValuesDelete(Oid enumTypeOid)
}
! /* qsort comparison function */
static int
oid_cmp(const void *p1, const void *p2)
{
--- 356,362 ----
}
! /* qsort comparison for oids */
static int
oid_cmp(const void *p1, const void *p2)
{
***************
*** 177,179 **** oid_cmp(const void *p1, const void *p2)
--- 369,384 ----
return 1;
return 0;
}
+
+ /* qsort comparison function for tuples by sort order */
+ static int
+ sort_order_cmp(const void *p1, const void *p2)
+ {
+ HeapTuple v1 = *((const HeapTuple *) p1);
+ HeapTuple v2 = *((const HeapTuple *) p2);
+ Form_pg_enum en1 = (Form_pg_enum) GETSTRUCT(v1);
+ Form_pg_enum en2 = (Form_pg_enum) GETSTRUCT(v2);
+
+ return en1->enumsortorder - en2->enumsortorder;
+ }
+
*** a/src/backend/commands/typecmds.c
--- b/src/backend/commands/typecmds.c
***************
*** 85,96 **** static Oid findTypeTypmodoutFunction(List *procname);
static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid);
static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
static void checkDomainOwner(HeapTuple tup, TypeName *typename);
static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
Oid baseTypeOid,
int typMod, Constraint *constr,
char *domainName);
-
-
/*
* DefineType
* Registers a new base type.
--- 85,95 ----
static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid);
static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
static void checkDomainOwner(HeapTuple tup, TypeName *typename);
+ static void checkEnumOwner(HeapTuple tup, TypeName *typename);
static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
Oid baseTypeOid,
int typMod, Constraint *constr,
char *domainName);
/*
* DefineType
* Registers a new base type.
***************
*** 563,569 **** DefineType(List *names, List *parameters)
-1, /* typMod (Domains only) */
0, /* Array Dimensions of typbasetype */
false); /* Type NOT NULL */
-
/*
* Create the array type that goes with it.
*/
--- 562,567 ----
***************
*** 1044,1050 **** DefineDomain(CreateDomainStmt *stmt)
storage, /* TOAST strategy */
basetypeMod, /* typeMod value */
typNDims, /* Array dimensions for base type */
! typNotNull); /* Type NOT NULL */
/*
* Process constraints which refer to the domain ID returned by TypeCreate
--- 1042,1048 ----
storage, /* TOAST strategy */
basetypeMod, /* typeMod value */
typNDims, /* Array dimensions for base type */
! typNotNull); /* Type NOT NULL */
/*
* Process constraints which refer to the domain ID returned by TypeCreate
***************
*** 1094,1099 **** DefineEnum(CreateEnumStmt *stmt)
--- 1092,1100 ----
AclResult aclresult;
Oid old_type_oid;
Oid enumArrayOid;
+ int num_labels;
+
+ num_labels = list_length(stmt->vals);
/* Convert list of names to a name and namespace */
enumNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
***************
*** 1156,1162 **** DefineEnum(CreateEnumStmt *stmt)
false); /* Type NOT NULL */
/* Enter the enum's values into pg_enum */
! EnumValuesCreate(enumTypeOid, stmt->vals, InvalidOid);
/*
* Create the array type that goes with it.
--- 1157,1163 ----
false); /* Type NOT NULL */
/* Enter the enum's values into pg_enum */
! EnumValuesCreate(enumTypeOid, stmt->vals);
/*
* Create the array type that goes with it.
***************
*** 1197,1202 **** DefineEnum(CreateEnumStmt *stmt)
--- 1198,1259 ----
pfree(enumArrayName);
}
+ /*
+ * AlterEnum
+ * Registers a new label for an existing enum.
+ */
+ void
+ AlterEnum (AlterEnumStmt *stmt)
+ {
+ Oid enum_type_oid;
+ TypeName *typename;
+ HeapTuple tup;
+ Form_pg_type typTup;
+
+ /* Make a TypeName so we can use standard type lookup machinery */
+ typename = makeTypeNameFromNameList(stmt->typeName);
+ enum_type_oid = typenameTypeId(NULL, typename, NULL);
+
+ tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(enum_type_oid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for type %u", enum_type_oid);
+
+ typTup = (Form_pg_type) GETSTRUCT(tup);
+
+ /* Check it's an enum and check user has permission to ALTER the enum */
+ checkEnumOwner(tup, typename);
+
+ /* Add the new label */
+ AddEnumLabel (enum_type_oid, stmt->newVal,
+ stmt->newValNeighbour, stmt->newValIsAfter);
+
+ ReleaseSysCache(tup);
+ }
+
+
+ /*
+ * checkEnumOwner
+ *
+ * Check that the type is actually an enum and that the current user
+ * has permission to do ALTER TYPE on it. Throw an error if not.
+ */
+ static void
+ checkEnumOwner(HeapTuple tup, TypeName *typename)
+ {
+ Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup);
+
+ /* Check that this is actually a domain */
+ if (typTup->typtype != TYPTYPE_ENUM)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not an enum",
+ TypeNameToString(typename))));
+
+ /* Permission check: must own type */
+ if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
+ format_type_be(HeapTupleGetOid(tup)));
+ }
/*
* Find suitable I/O functions for a type.
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 2901,2906 **** _copyCreateEnumStmt(CreateEnumStmt *from)
--- 2901,2919 ----
return newnode;
}
+ static AlterEnumStmt *
+ _copyAlterEnumStmt(AlterEnumStmt *from)
+ {
+ AlterEnumStmt *newnode = makeNode(AlterEnumStmt);
+
+ COPY_NODE_FIELD(typeName);
+ COPY_STRING_FIELD(newVal);
+ COPY_STRING_FIELD(newValNeighbour);
+ COPY_SCALAR_FIELD(newValIsAfter);
+
+ return newnode;
+ }
+
static ViewStmt *
_copyViewStmt(ViewStmt *from)
{
***************
*** 4064,4069 **** copyObject(void *from)
--- 4077,4085 ----
case T_CreateEnumStmt:
retval = _copyCreateEnumStmt(from);
break;
+ case T_AlterEnumStmt:
+ retval = _copyAlterEnumStmt(from);
+ break;
case T_ViewStmt:
retval = _copyViewStmt(from);
break;
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
***************
*** 1393,1398 **** _equalCreateEnumStmt(CreateEnumStmt *a, CreateEnumStmt *b)
--- 1393,1409 ----
}
static bool
+ _equalAlterEnumStmt(AlterEnumStmt *a, AlterEnumStmt *b)
+ {
+ COMPARE_NODE_FIELD(typeName);
+ COMPARE_STRING_FIELD(newVal);
+ COMPARE_STRING_FIELD(newValNeighbour);
+ COMPARE_SCALAR_FIELD(newValIsAfter);
+
+ return true;
+ }
+
+ static bool
_equalViewStmt(ViewStmt *a, ViewStmt *b)
{
COMPARE_NODE_FIELD(view);
***************
*** 2700,2705 **** equal(void *a, void *b)
--- 2711,2719 ----
case T_CreateEnumStmt:
retval = _equalCreateEnumStmt(a, b);
break;
+ case T_AlterEnumStmt:
+ retval = _equalAlterEnumStmt(a, b);
+ break;
case T_ViewStmt:
retval = _equalViewStmt(a, b);
break;
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 182,189 **** static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
}
%type <node> stmt schema_stmt
! AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterFdwStmt
! AlterForeignServerStmt AlterGroupStmt
AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
AlterCompositeTypeStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
AlterRoleStmt AlterRoleSetStmt
--- 182,189 ----
}
%type <node> stmt schema_stmt
! AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt
! AlterFdwStmt AlterForeignServerStmt AlterGroupStmt
AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
AlterCompositeTypeStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
AlterRoleStmt AlterRoleSetStmt
***************
*** 652,657 **** stmt :
--- 652,658 ----
| AlterDatabaseSetStmt
| AlterDefaultPrivilegesStmt
| AlterDomainStmt
+ | AlterEnumStmt
| AlterFdwStmt
| AlterForeignServerStmt
| AlterFunctionStmt
***************
*** 3862,3867 **** enum_val_list: Sconst
--- 3863,3908 ----
{ $$ = lappend($1, makeString($3)); }
;
+ /*****************************************************************************
+ *
+ * ALTER TYPE enumtype ADD ...
+ *
+ *****************************************************************************/
+
+ AlterEnumStmt:
+ ALTER TYPE_P any_name ADD_P Sconst
+ {
+ AlterEnumStmt *n = makeNode(AlterEnumStmt);
+ n->typeName = $3;
+ n->newVal = $5;
+ n->newValNeighbour = NULL;
+ n->newValIsAfter = true;
+
+ $$ = (Node *) n;
+ }
+ | ALTER TYPE_P any_name ADD_P Sconst BEFORE Sconst
+ {
+ AlterEnumStmt *n = makeNode(AlterEnumStmt);
+ n->typeName = $3;
+ n->newVal = $5;
+ n->newValNeighbour = $7;
+ n->newValIsAfter = false;
+
+ $$ = (Node *) n;
+ }
+ | ALTER TYPE_P any_name ADD_P Sconst AFTER Sconst
+ {
+ AlterEnumStmt *n = makeNode(AlterEnumStmt);
+ n->typeName = $3;
+ n->newVal = $5;
+ n->newValNeighbour = $7;
+ n->newValIsAfter = true;
+
+ $$ = (Node *) n;
+ }
+ ;
+
+
/*****************************************************************************
*
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
***************
*** 190,195 **** check_xact_readonly(Node *parsetree)
--- 190,196 ----
case T_CreateTrigStmt:
case T_CompositeTypeStmt:
case T_CreateEnumStmt:
+ case T_AlterEnumStmt:
case T_ViewStmt:
case T_DropCastStmt:
case T_DropStmt:
***************
*** 860,865 **** standard_ProcessUtility(Node *parsetree,
--- 861,870 ----
DefineEnum((CreateEnumStmt *) parsetree);
break;
+ case T_AlterEnumStmt: /* ALTER TYPE (enum) */
+ AlterEnum((AlterEnumStmt *) parsetree);
+ break;
+
case T_ViewStmt: /* CREATE VIEW */
DefineView((ViewStmt *) parsetree, queryString);
break;
***************
*** 1868,1873 **** CreateCommandTag(Node *parsetree)
--- 1873,1882 ----
tag = "CREATE TYPE";
break;
+ case T_AlterEnumStmt:
+ tag = "ALTER TYPE";
+ break;
+
case T_ViewStmt:
tag = "CREATE VIEW";
break;
***************
*** 2410,2415 **** GetCommandLogLevel(Node *parsetree)
--- 2419,2428 ----
lev = LOGSTMT_DDL;
break;
+ case T_AlterEnumStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
case T_ViewStmt:
lev = LOGSTMT_DDL;
break;
*** a/src/backend/utils/adt/enum.c
--- b/src/backend/utils/adt/enum.c
***************
*** 14,19 ****
--- 14,20 ----
#include "postgres.h"
#include "catalog/pg_enum.h"
+ #include "catalog/pg_type.h"
#include "fmgr.h"
#include "utils/array.h"
#include "utils/builtins.h"
***************
*** 22,30 ****
#include "libpq/pqformat.h"
#include "miscadmin.h"
static ArrayType *enum_range_internal(Oid enumtypoid, Oid lower, Oid upper);
! static int enum_elem_cmp(const void *left, const void *right);
/* Basic I/O support */
--- 23,52 ----
#include "libpq/pqformat.h"
#include "miscadmin.h"
+ #define BITMAPSIZE 1024
+ #define BITMAPBYTES (BITMAPSIZE / 8)
+
+ typedef struct
+ {
+ Oid enum_oid;
+ int32 sort_order;
+ } enum_sort;
+
+ typedef struct
+ {
+ Oid enumtypoid;
+ Oid bitmap_base;
+ char bitmap[BITMAPBYTES];
+ int label_count;
+ enum_sort sort_order_list[1];
+ } enum_sort_cache;
+
+
static ArrayType *enum_range_internal(Oid enumtypoid, Oid lower, Oid upper);
! static int enum_sort_cmp(const void *left, const void *right);
! static int enum_oid_cmp(const void *left, const void *right);
! static enum_sort_cache *initcache(Oid arg, FunctionCallInfo fcinfo);
/* Basic I/O support */
***************
*** 155,167 **** enum_send(PG_FUNCTION_ARGS)
/* Comparison functions and related */
Datum
enum_lt(PG_FUNCTION_ARGS)
{
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(a < b);
}
Datum
--- 177,368 ----
/* Comparison functions and related */
+ static enum_sort_cache *
+ initcache(Oid arg, FunctionCallInfo fcinfo)
+ {
+ HeapTuple enum_tup;
+ Form_pg_enum en;
+ Oid typeoid;
+ enum_sort_cache *mycache;
+ CatCList *list;
+ int num,i, bm_start, bm_len, start_pos, list_end;
+
+ /* free up anything we've used before) */
+ if (fcinfo->flinfo->fn_extra != NULL)
+ pfree(fcinfo->flinfo->fn_extra);
+
+ /* get the typeoid, label count and the list of values */
+ enum_tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(arg));
+ en = (Form_pg_enum) GETSTRUCT(enum_tup);
+ typeoid = en->enumtypid;
+ ReleaseSysCache(enum_tup);
+ list = SearchSysCacheList1(ENUMTYPOIDNAME, ObjectIdGetDatum(typeoid));
+ num = list->n_members;
+ fcinfo->flinfo->fn_extra =
+ MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+ sizeof(enum_sort_cache) +
+ (num * sizeof(enum_sort)));
+ mycache = (enum_sort_cache *) fcinfo->flinfo->fn_extra;
+ mycache->enumtypoid = typeoid;
+ mycache->label_count = num;
+ memset(mycache->bitmap, 0, BITMAPBYTES);
+
+ /* set up the list sorted by Oid */
+ for (i = 0; i < num; i++)
+ {
+ HeapTuple tup = &(list->members[i]->tuple);
+ Form_pg_enum list_en = (Form_pg_enum) GETSTRUCT(tup);
+
+ mycache->sort_order_list[i].sort_order = list_en->enumsortorder;
+ mycache->sort_order_list[i].enum_oid = HeapTupleGetOid(tup);
+ }
+
+ ReleaseCatCacheList(list);
+
+ qsort(mycache->sort_order_list,mycache->label_count,
+ sizeof(enum_sort),enum_oid_cmp);
+
+
+ /* look for the longest suitable range for the bitmap */
+ bm_len = 0;
+ bm_start = 0;
+
+ for (start_pos = 0; start_pos < num -1; start_pos ++)
+ {
+ for (list_end = start_pos+1; list_end < num; list_end++)
+ if (mycache->sort_order_list[list_end].sort_order <
+ mycache->sort_order_list[list_end - 1].sort_order ||
+ mycache->sort_order_list[list_end].enum_oid -
+ mycache->sort_order_list[start_pos].enum_oid >= BITMAPSIZE)
+ break;
+ if (list_end - start_pos > bm_len)
+ {
+ bm_len = list_end - start_pos;
+ bm_start = start_pos;
+ }
+ if (bm_len == num)
+ break;
+ }
+
+ /* if we found a suitable range, for the bitmap, set it up */
+ if (bm_len > 1)
+ {
+ int base = mycache->sort_order_list[bm_start].enum_oid;
+
+ mycache->bitmap_base = base;
+ for (i = 0; i < bm_len; i++)
+ {
+ Oid enoid = mycache->sort_order_list[bm_start+i].enum_oid;
+ int offset = enoid - base;
+ int bytenum = offset / 8;
+ int bitmask = 1 << (offset % 8);
+ mycache->bitmap[bytenum] |= bitmask;
+ }
+ }
+
+ return mycache;
+ }
+
+ /* fast lookup for Oids known to be in order */
+
+ static inline bool
+ bitmap_lookup(enum_sort_cache *mycache, Oid arg1, Oid arg2)
+ {
+ int offset1 = arg1 - mycache->bitmap_base;
+ int offset2 = arg2 - mycache->bitmap_base;
+ int bytenum, bitmask;
+ if (offset1 < 0 || offset2 < 0 ||
+ offset1 > BITMAPSIZE || offset2 > BITMAPSIZE)
+ return false;
+ bytenum = offset1 / 8;
+ bitmask = 1 << (offset1 % 8);
+ if ((mycache->bitmap[bytenum] & bitmask) == 0)
+ return false;
+ bytenum = offset2 / 8;
+ bitmask = 1 << (offset2 % 8);
+ if ((mycache->bitmap[bytenum] & bitmask) == 0)
+ return false;
+ return true;
+ }
+
+ /* slower lookup for Oids not known to be in order */
+
+ static inline int
+ find_sortorder(enum_sort_cache *mycache, Oid arg)
+ {
+ enum_sort *es;
+ enum_sort srch;
+
+ srch.enum_oid = arg;
+ es = bsearch(&srch,
+ mycache->sort_order_list,
+ mycache->label_count,
+ sizeof(enum_sort),
+ enum_oid_cmp);
+ if (es == NULL)
+ return -1;
+
+ return es->sort_order;
+
+ }
+
+ /* enum_ccmp is the common engine for all the visible comparison functions */
+
+ static inline int
+ enum_ccmp(Oid arg1, Oid arg2, FunctionCallInfo fcinfo)
+ {
+
+ enum_sort_cache *mycache;
+ int sort_1, sort_2;
+
+ /* evenly numbered Oids are know to sort right */
+ if (arg1 % 2 == 0 && arg2 % 2 == 0)
+ return arg1 - arg2;
+
+
+ /* so are oids that are equal */
+ if (arg1 == arg2)
+ return 0;
+
+ /* get the cached info, set it up if absent */
+ mycache = (enum_sort_cache *) fcinfo->flinfo->fn_extra;
+ if (mycache == NULL )
+ mycache = initcache(arg1, fcinfo);
+
+ /* first try the fast lookup */
+ if (bitmap_lookup(mycache, arg1, arg2))
+ return arg1 - arg2;
+
+ /* try the slow lookup */
+ sort_1 = find_sortorder(mycache,arg1);
+ sort_2 = find_sortorder(mycache,arg2);
+
+ if (sort_1 <= 0 || sort_2 <= 0)
+ {
+ /*
+ * We couldn't find one or both values.
+ * That means the enum has changed under us, so
+ * re-initialize the cache and try again.
+ */
+ mycache = initcache(arg1, fcinfo);
+ sort_1 = find_sortorder(mycache,arg1);
+ sort_2 = find_sortorder(mycache,arg2);
+ /* if we fail the secind time around, give up */
+ if (sort_1 <= 0 || sort_2 <= 0)
+ elog(ERROR, "values missing for enum %s",
+ format_type_be(mycache->enumtypoid));
+ }
+
+ return sort_1 - sort_2;
+ }
+
Datum
enum_lt(PG_FUNCTION_ARGS)
{
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(enum_ccmp(a,b,fcinfo) < 0);
}
Datum
***************
*** 170,176 **** enum_le(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(a <= b);
}
Datum
--- 371,377 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(enum_ccmp(a,b,fcinfo) <= 0);
}
Datum
***************
*** 197,203 **** enum_ge(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(a >= b);
}
Datum
--- 398,404 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(enum_ccmp(a,b,fcinfo) >= 0);
}
Datum
***************
*** 206,212 **** enum_gt(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(a > b);
}
Datum
--- 407,413 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(enum_ccmp(a,b,fcinfo) > 0);
}
Datum
***************
*** 215,221 **** enum_smaller(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_OID(a <= b ? a : b);
}
Datum
--- 416,422 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_OID(enum_ccmp(a,b,fcinfo) < 0 ? a : b);
}
Datum
***************
*** 224,230 **** enum_larger(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_OID(a >= b ? a : b);
}
Datum
--- 425,431 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_OID(enum_ccmp(a,b,fcinfo) > 0 ? a : b);
}
Datum
***************
*** 233,242 **** enum_cmp(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! if (a > b)
! PG_RETURN_INT32(1);
! else if (a == b)
PG_RETURN_INT32(0);
else
PG_RETURN_INT32(-1);
}
--- 434,443 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! if (a == b)
PG_RETURN_INT32(0);
+ else if (enum_ccmp(a,b,fcinfo) > 0)
+ PG_RETURN_INT32(1);
else
PG_RETURN_INT32(-1);
}
***************
*** 248,253 **** enum_first(PG_FUNCTION_ARGS)
--- 449,455 ----
{
Oid enumtypoid;
Oid min = InvalidOid;
+ int min_sort = -1; /* value will never in fact be used */
CatCList *list;
int num,
i;
***************
*** 267,276 **** enum_first(PG_FUNCTION_ARGS)
num = list->n_members;
for (i = 0; i < num; i++)
{
! Oid valoid = HeapTupleHeaderGetOid(list->members[i]->tuple.t_data);
!
! if (!OidIsValid(min) || valoid < min)
! min = valoid;
}
ReleaseCatCacheList(list);
--- 469,482 ----
num = list->n_members;
for (i = 0; i < num; i++)
{
! HeapTuple tup = &(list->members[i]->tuple);
! Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
!
! if (!OidIsValid(min) || en->enumsortorder < min_sort)
! {
! min = HeapTupleGetOid(tup);
! min_sort = en->enumsortorder;
! }
}
ReleaseCatCacheList(list);
***************
*** 287,292 **** enum_last(PG_FUNCTION_ARGS)
--- 493,499 ----
{
Oid enumtypoid;
Oid max = InvalidOid;
+ int max_sort = -1; /* value will never in fact be used */
CatCList *list;
int num,
i;
***************
*** 306,315 **** enum_last(PG_FUNCTION_ARGS)
num = list->n_members;
for (i = 0; i < num; i++)
{
! Oid valoid = HeapTupleHeaderGetOid(list->members[i]->tuple.t_data);
!
! if (!OidIsValid(max) || valoid > max)
! max = valoid;
}
ReleaseCatCacheList(list);
--- 513,526 ----
num = list->n_members;
for (i = 0; i < num; i++)
{
! HeapTuple tup = &(list->members[i]->tuple);
! Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
!
! if (!OidIsValid(max) || en->enumsortorder > max_sort)
! {
! max = HeapTupleGetOid(tup);
! max_sort = en->enumsortorder;
! }
}
ReleaseCatCacheList(list);
***************
*** 382,427 **** enum_range_internal(Oid enumtypoid, Oid lower, Oid upper)
i,
j;
Datum *elems;
list = SearchSysCacheList1(ENUMTYPOIDNAME, ObjectIdGetDatum(enumtypoid));
total = list->n_members;
elems = (Datum *) palloc(total * sizeof(Datum));
- j = 0;
for (i = 0; i < total; i++)
{
! Oid val = HeapTupleGetOid(&(list->members[i]->tuple));
- if ((!OidIsValid(lower) || lower <= val) &&
- (!OidIsValid(upper) || val <= upper))
- elems[j++] = ObjectIdGetDatum(val);
}
/* shouldn't need the cache anymore */
ReleaseCatCacheList(list);
! /* sort results into OID order */
! qsort(elems, j, sizeof(Datum), enum_elem_cmp);
/* note this hardwires some details about the representation of Oid */
result = construct_array(elems, j, enumtypoid, sizeof(Oid), true, 'i');
pfree(elems);
return result;
}
! /* qsort comparison function for Datums that are OIDs */
static int
! enum_elem_cmp(const void *left, const void *right)
{
! Oid l = DatumGetObjectId(*((const Datum *) left));
! Oid r = DatumGetObjectId(*((const Datum *) right));
! if (l < r)
! return -1;
! if (l > r)
! return 1;
! return 0;
}
--- 593,668 ----
i,
j;
Datum *elems;
+ enum_sort *sort_items;
+ bool left_found;
list = SearchSysCacheList1(ENUMTYPOIDNAME, ObjectIdGetDatum(enumtypoid));
total = list->n_members;
elems = (Datum *) palloc(total * sizeof(Datum));
+ sort_items = (enum_sort *) palloc(total * sizeof(enum_sort));
for (i = 0; i < total; i++)
{
! HeapTuple tup = &(list->members[i]->tuple);
! Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
!
! sort_items[i].enum_oid = HeapTupleGetOid(tup);
! sort_items[i].sort_order = en->enumsortorder;
}
/* shouldn't need the cache anymore */
ReleaseCatCacheList(list);
! /* sort results into sort_order sequence */
! qsort(sort_items, total, sizeof(enum_sort), enum_sort_cmp);
!
! j = 0;
! left_found = !OidIsValid(lower);
! for (i=0; i < total; i++)
! {
! if (! left_found && lower == sort_items[i].enum_oid)
! left_found = true;
!
! if (left_found)
! elems[j++] = ObjectIdGetDatum(sort_items[i].enum_oid);
!
! if (OidIsValid(upper) && upper == sort_items[i].enum_oid)
! break;
! }
/* note this hardwires some details about the representation of Oid */
result = construct_array(elems, j, enumtypoid, sizeof(Oid), true, 'i');
pfree(elems);
+ pfree(sort_items);
return result;
}
! /*
! * qsort comparison using sort order, for range routines
! */
static int
! enum_sort_cmp(const void *left, const void *right)
{
! enum_sort *l = (enum_sort *) left;
! enum_sort *r = (enum_sort *) right;
! return l->sort_order - r->sort_order;
! }
!
! /*
! * qsort comparison using OID order for comparison search cache
! */
! static int
! enum_oid_cmp(const void *es1, const void *es2)
! {
! enum_sort *p1, *p2;
! p1 = (enum_sort *)es1;
! p2 = (enum_sort *)es2;
! return p1->enum_oid - p2->enum_oid;
}
+
+
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
***************
*** 6653,6669 **** dumpEnumType(Archive *fout, TypeInfo *tyinfo)
PQExpBuffer query = createPQExpBuffer();
PGresult *res;
int num,
! i;
Oid enum_oid;
char *label;
/* Set proper schema search path so regproc references list correctly */
selectSourceSchema(tyinfo->dobj.namespace->dobj.name);
! appendPQExpBuffer(query, "SELECT oid, enumlabel "
! "FROM pg_catalog.pg_enum "
! "WHERE enumtypid = '%u'"
! "ORDER BY oid",
tyinfo->dobj.catId.oid);
res = PQexec(g_conn, query->data);
--- 6653,6676 ----
PQExpBuffer query = createPQExpBuffer();
PGresult *res;
int num,
! i;
Oid enum_oid;
char *label;
/* Set proper schema search path so regproc references list correctly */
selectSourceSchema(tyinfo->dobj.namespace->dobj.name);
! if (fout->remoteVersion > 90000)
! appendPQExpBuffer(query, "SELECT oid, enumlabel "
! "FROM pg_catalog.pg_enum "
! "WHERE enumtypid = '%u'"
! "ORDER BY enumsortorder",
! tyinfo->dobj.catId.oid);
! else
! appendPQExpBuffer(query, "SELECT oid, enumlabel "
! "FROM pg_catalog.pg_enum "
! "WHERE enumtypid = '%u'"
! "ORDER BY oid",
tyinfo->dobj.catId.oid);
res = PQexec(g_conn, query->data);
***************
*** 6709,6725 **** dumpEnumType(Archive *fout, TypeInfo *tyinfo)
{
enum_oid = atooid(PQgetvalue(res, i, PQfnumber(res, "oid")));
label = PQgetvalue(res, i, PQfnumber(res, "enumlabel"));
!
if (i == 0)
appendPQExpBuffer(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
appendPQExpBuffer(q,
! "SELECT binary_upgrade.add_pg_enum_label('%u'::pg_catalog.oid, "
! "'%u'::pg_catalog.oid, ",
! enum_oid, tyinfo->dobj.catId.oid);
! appendStringLiteralAH(q, label, fout);
! appendPQExpBuffer(q, ");\n");
}
- appendPQExpBuffer(q, "\n");
}
ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
--- 6716,6734 ----
{
enum_oid = atooid(PQgetvalue(res, i, PQfnumber(res, "oid")));
label = PQgetvalue(res, i, PQfnumber(res, "enumlabel"));
!
if (i == 0)
appendPQExpBuffer(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
appendPQExpBuffer(q,
! "SELECT binary_upgrade.set_next_pg_enum_oid('%u'::pg_catalog.oid);\n",
! enum_oid);
! appendPQExpBuffer(q, "ALTER TYPE %s.",
! fmtId(tyinfo->dobj.namespace->dobj.name));
! appendPQExpBuffer(q, "%s ADD ",
! fmtId(tyinfo->dobj.name));
! appendStringLiteralAH(q, label, fout);
! appendPQExpBuffer(q, ";\n\n");
}
}
ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
*** a/src/bin/psql/describe.c
--- b/src/bin/psql/describe.c
***************
*** 473,489 **** describeTypes(const char *pattern, bool verbose, bool showSystem)
gettext_noop("Internal name"),
gettext_noop("Size"));
if (verbose && pset.sversion >= 80300)
appendPQExpBuffer(&buf,
" pg_catalog.array_to_string(\n"
" ARRAY(\n"
" SELECT e.enumlabel\n"
" FROM pg_catalog.pg_enum e\n"
! " WHERE e.enumtypid = t.oid\n"
! " ORDER BY e.oid\n"
" ),\n"
" E'\\n'\n"
" ) AS \"%s\",\n",
gettext_noop("Elements"));
appendPQExpBuffer(&buf,
" pg_catalog.obj_description(t.oid, 'pg_type') as \"%s\"\n",
--- 473,499 ----
gettext_noop("Internal name"),
gettext_noop("Size"));
if (verbose && pset.sversion >= 80300)
+ {
appendPQExpBuffer(&buf,
" pg_catalog.array_to_string(\n"
" ARRAY(\n"
" SELECT e.enumlabel\n"
" FROM pg_catalog.pg_enum e\n"
! " WHERE e.enumtypid = t.oid\n");
!
! if (pset.sversion >= 90100 )
! appendPQExpBuffer(&buf,
! " ORDER BY e.enumsortorder\n");
! else
! appendPQExpBuffer(&buf,
! " ORDER BY e.oid\n");
!
! appendPQExpBuffer(&buf,
" ),\n"
" E'\\n'\n"
" ) AS \"%s\",\n",
gettext_noop("Elements"));
+ }
appendPQExpBuffer(&buf,
" pg_catalog.obj_description(t.oid, 'pg_type') as \"%s\"\n",
*** a/src/include/catalog/indexing.h
--- b/src/include/catalog/indexing.h
***************
*** 147,152 **** DECLARE_UNIQUE_INDEX(pg_enum_oid_index, 3502, on pg_enum using btree(oid oid_ops
--- 147,154 ----
#define EnumOidIndexId 3502
DECLARE_UNIQUE_INDEX(pg_enum_typid_label_index, 3503, on pg_enum using btree(enumtypid oid_ops, enumlabel name_ops));
#define EnumTypIdLabelIndexId 3503
+ DECLARE_UNIQUE_INDEX(pg_enum_typid_sortorder_index, 3539, on pg_enum using btree(enumtypid oid_ops, enumsortorder int4_ops));
+ #define EnumTypIdSortOrderIndexId 3539
/* This following index is not used for a cache and is not unique */
DECLARE_INDEX(pg_index_indrelid_index, 2678, on pg_index using btree(indrelid oid_ops));
*** a/src/include/catalog/pg_enum.h
--- b/src/include/catalog/pg_enum.h
***************
*** 35,40 **** CATALOG(pg_enum,3501)
--- 35,41 ----
{
Oid enumtypid; /* OID of owning enum type */
NameData enumlabel; /* text representation of enum value */
+ int4 enumsortorder; /* sort order for this enum label */
} FormData_pg_enum;
/* ----------------
***************
*** 48,56 **** typedef FormData_pg_enum *Form_pg_enum;
* compiler constants for pg_enum
* ----------------
*/
! #define Natts_pg_enum 2
#define Anum_pg_enum_enumtypid 1
#define Anum_pg_enum_enumlabel 2
/* ----------------
* pg_enum has no initial contents
--- 49,58 ----
* compiler constants for pg_enum
* ----------------
*/
! #define Natts_pg_enum 3
#define Anum_pg_enum_enumtypid 1
#define Anum_pg_enum_enumlabel 2
+ #define Anum_pg_enum_enumsortorder 3
/* ----------------
* pg_enum has no initial contents
***************
*** 60,67 **** typedef FormData_pg_enum *Form_pg_enum;
/*
* prototypes for functions in pg_enum.c
*/
! extern void EnumValuesCreate(Oid enumTypeOid, List *vals,
! Oid binary_upgrade_next_pg_enum_oid);
extern void EnumValuesDelete(Oid enumTypeOid);
#endif /* PG_ENUM_H */
--- 62,70 ----
/*
* prototypes for functions in pg_enum.c
*/
! extern void EnumValuesCreate(Oid enumTypeOid, List *vals);
extern void EnumValuesDelete(Oid enumTypeOid);
+ extern void AddEnumLabel(Oid enumTypeOid, char *newVal, char *neighbour,
+ bool newValIsAfter);
#endif /* PG_ENUM_H */
*** a/src/include/catalog/pg_type_fn.h
--- b/src/include/catalog/pg_type_fn.h
***************
*** 50,56 **** extern Oid TypeCreate(Oid newTypeOid,
char storage,
int32 typeMod,
int32 typNDims,
! bool typeNotNull);
extern void GenerateTypeDependencies(Oid typeNamespace,
Oid typeObjectId,
--- 50,56 ----
char storage,
int32 typeMod,
int32 typNDims,
! bool typeNotNull);
extern void GenerateTypeDependencies(Oid typeNamespace,
Oid typeObjectId,
*** a/src/include/commands/typecmds.h
--- b/src/include/commands/typecmds.h
***************
*** 24,29 **** extern void RemoveTypes(DropStmt *drop);
--- 24,30 ----
extern void RemoveTypeById(Oid typeOid);
extern void DefineDomain(CreateDomainStmt *stmt);
extern void DefineEnum(CreateEnumStmt *stmt);
+ extern void AlterEnum (AlterEnumStmt *stmt);
extern Oid DefineCompositeType(const RangeVar *typevar, List *coldeflist);
extern Oid AssignTypeArrayOid(void);
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
***************
*** 351,357 **** typedef enum NodeTag
T_DropUserMappingStmt,
T_AlterTableSpaceOptionsStmt,
T_SecLabelStmt,
!
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
*/
--- 351,357 ----
T_DropUserMappingStmt,
T_AlterTableSpaceOptionsStmt,
T_SecLabelStmt,
! T_AlterEnumStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
*/
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 2195,2200 **** typedef struct CreateEnumStmt
--- 2195,2214 ----
/* ----------------------
+ * Alter Type Statement, enum types
+ * ----------------------
+ */
+ typedef struct AlterEnumStmt
+ {
+ NodeTag type;
+ List *typeName; /* qualified name (list of Value strings) */
+ char *newVal; /* new enum value */
+ char *newValNeighbour;/* neighbouring enum value */
+ bool newValIsAfter; /* new enum value is after neighbour? */
+ } AlterEnumStmt;
+
+
+ /* ----------------------
* Create View Statement
* ----------------------
*/
*** a/src/test/regress/expected/enum.out
--- b/src/test/regress/expected/enum.out
***************
*** 25,30 **** ERROR: invalid input value for enum rainbow: "mauve"
--- 25,92 ----
LINE 1: SELECT 'mauve'::rainbow;
^
--
+ -- adding new values
+ --
+ CREATE TYPE planets AS ENUM ( 'venus', 'earth', 'mars' );
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+ enumlabel | enumsortorder
+ -----------+---------------
+ venus | 1
+ earth | 2
+ mars | 3
+ (3 rows)
+
+ ALTER TYPE planets ADD 'uranus';
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+ enumlabel | enumsortorder
+ -----------+---------------
+ venus | 1
+ earth | 2
+ mars | 3
+ uranus | 4
+ (4 rows)
+
+ ALTER TYPE planets ADD 'mercury' BEFORE 'venus';
+ ALTER TYPE planets ADD 'saturn' BEFORE 'uranus';
+ ALTER TYPE planets ADD 'jupiter' AFTER 'mars';
+ ALTER TYPE planets ADD 'neptune' AFTER 'uranus';
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+ enumlabel | enumsortorder
+ -----------+---------------
+ mercury | 1
+ venus | 2
+ earth | 3
+ mars | 4
+ jupiter | 5
+ saturn | 6
+ uranus | 7
+ neptune | 8
+ (8 rows)
+
+ select 'mars'::planets > 'mercury' as using_sortorder;
+ using_sortorder
+ -----------------
+ t
+ (1 row)
+
+ -- errors for adding labels
+ ALTER TYPE planets ADD
+ 'plutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutopluto';
+ ERROR: invalid enum label "plutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutopluto"
+ DETAIL: Labels must be 63 characters or less.
+ ALTER TYPE planets ADD 'pluto' AFTER 'zeus';
+ ERROR: "zeus" is not an existing label.
+ DROP TYPE planets;
+ --
-- Basic table creation, row selection
--
CREATE TABLE enumtest (col rainbow);
***************
*** 403,409 **** SELECT COUNT(*) FROM pg_type WHERE typname = 'rainbow';
SELECT * FROM pg_enum WHERE NOT EXISTS
(SELECT 1 FROM pg_type WHERE pg_type.oid = enumtypid);
! enumtypid | enumlabel
! -----------+-----------
(0 rows)
--- 465,471 ----
SELECT * FROM pg_enum WHERE NOT EXISTS
(SELECT 1 FROM pg_type WHERE pg_type.oid = enumtypid);
! enumtypid | enumlabel | enumsortorder
! -----------+-----------+---------------
(0 rows)
*** a/src/test/regress/sql/enum.sql
--- b/src/test/regress/sql/enum.sql
***************
*** 16,21 **** SELECT 'red'::rainbow;
--- 16,61 ----
SELECT 'mauve'::rainbow;
--
+ -- adding new values
+ --
+
+ CREATE TYPE planets AS ENUM ( 'venus', 'earth', 'mars' );
+
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+
+ ALTER TYPE planets ADD 'uranus';
+
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+
+ ALTER TYPE planets ADD 'mercury' BEFORE 'venus';
+
+ ALTER TYPE planets ADD 'saturn' BEFORE 'uranus';
+
+ ALTER TYPE planets ADD 'jupiter' AFTER 'mars';
+
+ ALTER TYPE planets ADD 'neptune' AFTER 'uranus';
+
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+
+ select 'mars'::planets > 'mercury' as using_sortorder;
+
+ -- errors for adding labels
+ ALTER TYPE planets ADD
+ 'plutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutopluto';
+
+ ALTER TYPE planets ADD 'pluto' AFTER 'zeus';
+
+ DROP TYPE planets;
+ --
-- Basic table creation, row selection
--
CREATE TABLE enumtest (col rainbow);
On Tue, Oct 19, 2010 at 10:19, Thom Brown <thom@linux.com> wrote:
On 19 October 2010 05:21, Andrew Dunstan <andrew@dunslane.net> wrote:
On 10/18/2010 10:52 AM, Tom Lane wrote:
We could possibly deal with enum types that follow the existing
convention if we made the cache entry hold a list of all the original,
known-to-be-sorted OIDs. (This could be reasonably compact and cheap to
probe if it were represented as a starting OID and a Bitmapset of delta
values, since we can assume that the initial set of OIDs is pretty close
together.) But we have to have that cache entry, and we have to consult
it on every single comparison, so it's definitely going to be slower
than before.So I'm thinking the comparison procedure goes like this:
1. Both OIDs even?
If so, just compare them numerically, and we're done.2. Lookup cache entry for enum type.
3. Both OIDs in list of known-sorted OIDs?
If so, just compare them numerically, and we're done.4. Search the part of the cache entry that lists sort positions.
If not both present, refresh the cache entry.
If still not present, throw error.5. Compare by sort positions.
Step 4 is the slowest part but would be avoided in most cases.
However, step 2 is none too speedy either, and would usually
be required when dealing with pre-existing enums.OK, I've made adjustments that I think do what you're suggesting.
Patch is attached.
Alternatively this can be pulled from
<git@github.com:adunstan/postgresql-dev.git>Andrew, can't you get your own repo at git.postgresql.org?
He certainly could, but github provides more features and a nicer look
:-) And since it's git, it doesn't matter where the repo is.
--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/
Excerpts from Magnus Hagander's message of mar oct 19 05:23:31 -0300 2010:
He certainly could, but github provides more features and a nicer look
:-) And since it's git, it doesn't matter where the repo is.
Yeah. If you have a checked out copy of the GIT repo (preferably one
with the "master" branch in it), try this:
git remote add venum git://github.com/adunstan/postgresql-dev.git
git fetch venum venums:venums
git checkout venums
Then you have the patch in all its Git glory, in branch "venums".
--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support
On 10/19/2010 12:21 AM, Andrew Dunstan wrote:
On 10/18/2010 10:52 AM, Tom Lane wrote:
We could possibly deal with enum types that follow the existing
convention if we made the cache entry hold a list of all the original,
known-to-be-sorted OIDs. (This could be reasonably compact and cheap to
probe if it were represented as a starting OID and a Bitmapset of delta
values, since we can assume that the initial set of OIDs is pretty close
together.) But we have to have that cache entry, and we have to consult
it on every single comparison, so it's definitely going to be slower
than before.So I'm thinking the comparison procedure goes like this:
1. Both OIDs even?
If so, just compare them numerically, and we're done.2. Lookup cache entry for enum type.
3. Both OIDs in list of known-sorted OIDs?
If so, just compare them numerically, and we're done.4. Search the part of the cache entry that lists sort positions.
If not both present, refresh the cache entry.
If still not present, throw error.5. Compare by sort positions.
Step 4 is the slowest part but would be avoided in most cases.
However, step 2 is none too speedy either, and would usually
be required when dealing with pre-existing enums.OK, I've made adjustments that I think do what you're suggesting.
I've discovered and fixed a couple more bugs in this. I have one or two
more things to fix and then I'll send a new patch.
Meanwhile, I've been testing a database that was upgraded from 9.0, so
it has a lot of odd-numbered Oids. It's not really clear from
performance testing that the bitmap is a huge win, or even a win at all.
(Of course, my implementation might suck too.) I'll continue testing.
cheers
andrew
On 10/19/2010 12:53 PM, Andrew Dunstan wrote:
On 10/19/2010 12:21 AM, Andrew Dunstan wrote:
On 10/18/2010 10:52 AM, Tom Lane wrote:
We could possibly deal with enum types that follow the existing
convention if we made the cache entry hold a list of all the original,
known-to-be-sorted OIDs. (This could be reasonably compact and
cheap to
probe if it were represented as a starting OID and a Bitmapset of delta
values, since we can assume that the initial set of OIDs is pretty
close
together.) But we have to have that cache entry, and we have to
consult
it on every single comparison, so it's definitely going to be slower
than before.So I'm thinking the comparison procedure goes like this:
1. Both OIDs even?
If so, just compare them numerically, and we're done.2. Lookup cache entry for enum type.
3. Both OIDs in list of known-sorted OIDs?
If so, just compare them numerically, and we're done.4. Search the part of the cache entry that lists sort positions.
If not both present, refresh the cache entry.
If still not present, throw error.5. Compare by sort positions.
Step 4 is the slowest part but would be avoided in most cases.
However, step 2 is none too speedy either, and would usually
be required when dealing with pre-existing enums.OK, I've made adjustments that I think do what you're suggesting.
I've discovered and fixed a couple more bugs in this. I have one or
two more things to fix and then I'll send a new patch.Meanwhile, I've been testing a database that was upgraded from 9.0,
so it has a lot of odd-numbered Oids. It's not really clear from
performance testing that the bitmap is a huge win, or even a win at
all. (Of course, my implementation might suck too.) I'll continue
testing.
Well a bit more testing shows some benefit. I've sorted out a few kinks,
so this seems to work. In particular, with the above tables, the version
imported from 9.0 can create have an index created in about the same
time as on the fresh table (identical data, but all even numbered Oids).
Of course, with lots of odd numbered Oids, if a label gets added the
imported version will degrade in performance much more quickly.
The test timed is:
do $$ begin for i in 1 .. 20 loop drop index if exists idx1;
create index idx1 on mydata(label); end loop; end; $$;
Latest patch attached.
cheers
andrew
Attachments:
venum9.patchtext/x-patch; name=venum9.patchDownload
*** a/contrib/pg_upgrade/function.c
--- b/contrib/pg_upgrade/function.c
***************
*** 61,85 **** install_support_functions(migratorContext *ctx)
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_heap_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_toast_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_index_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.add_pg_enum_label(OID, OID, NAME) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
--- 61,85 ----
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_pg_enum_oid(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_heap_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_toast_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
PQclear(executeQueryOrDie(ctx, conn,
"CREATE OR REPLACE FUNCTION "
! " binary_upgrade.set_next_index_relfilenode(OID) "
"RETURNS VOID "
"AS '$libdir/pg_upgrade_support' "
"LANGUAGE C STRICT;"));
*** a/contrib/pg_upgrade_support/pg_upgrade_support.c
--- b/contrib/pg_upgrade_support/pg_upgrade_support.c
***************
*** 30,35 **** PG_MODULE_MAGIC;
--- 30,36 ----
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_array_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_toast_oid;
+ extern PGDLLIMPORT Oid binary_upgrade_next_pg_enum_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_heap_relfilenode;
extern PGDLLIMPORT Oid binary_upgrade_next_toast_relfilenode;
extern PGDLLIMPORT Oid binary_upgrade_next_index_relfilenode;
***************
*** 37,54 **** extern PGDLLIMPORT Oid binary_upgrade_next_index_relfilenode;
Datum set_next_pg_type_oid(PG_FUNCTION_ARGS);
Datum set_next_pg_type_array_oid(PG_FUNCTION_ARGS);
Datum set_next_pg_type_toast_oid(PG_FUNCTION_ARGS);
Datum set_next_heap_relfilenode(PG_FUNCTION_ARGS);
Datum set_next_toast_relfilenode(PG_FUNCTION_ARGS);
Datum set_next_index_relfilenode(PG_FUNCTION_ARGS);
- Datum add_pg_enum_label(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(set_next_pg_type_oid);
PG_FUNCTION_INFO_V1(set_next_pg_type_array_oid);
PG_FUNCTION_INFO_V1(set_next_pg_type_toast_oid);
PG_FUNCTION_INFO_V1(set_next_heap_relfilenode);
PG_FUNCTION_INFO_V1(set_next_toast_relfilenode);
PG_FUNCTION_INFO_V1(set_next_index_relfilenode);
- PG_FUNCTION_INFO_V1(add_pg_enum_label);
Datum
set_next_pg_type_oid(PG_FUNCTION_ARGS)
--- 38,55 ----
Datum set_next_pg_type_oid(PG_FUNCTION_ARGS);
Datum set_next_pg_type_array_oid(PG_FUNCTION_ARGS);
Datum set_next_pg_type_toast_oid(PG_FUNCTION_ARGS);
+ Datum set_next_pg_enum_oid(PG_FUNCTION_ARGS);
Datum set_next_heap_relfilenode(PG_FUNCTION_ARGS);
Datum set_next_toast_relfilenode(PG_FUNCTION_ARGS);
Datum set_next_index_relfilenode(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(set_next_pg_type_oid);
PG_FUNCTION_INFO_V1(set_next_pg_type_array_oid);
PG_FUNCTION_INFO_V1(set_next_pg_type_toast_oid);
+ PG_FUNCTION_INFO_V1(set_next_pg_enum_oid);
PG_FUNCTION_INFO_V1(set_next_heap_relfilenode);
PG_FUNCTION_INFO_V1(set_next_toast_relfilenode);
PG_FUNCTION_INFO_V1(set_next_index_relfilenode);
Datum
set_next_pg_type_oid(PG_FUNCTION_ARGS)
***************
*** 81,86 **** set_next_pg_type_toast_oid(PG_FUNCTION_ARGS)
--- 82,97 ----
}
Datum
+ set_next_pg_enum_oid(PG_FUNCTION_ARGS)
+ {
+ Oid enumoid = PG_GETARG_OID(0);
+
+ binary_upgrade_next_pg_enum_oid = enumoid;
+
+ PG_RETURN_VOID();
+ }
+
+ Datum
set_next_heap_relfilenode(PG_FUNCTION_ARGS)
{
Oid relfilenode = PG_GETARG_OID(0);
***************
*** 110,124 **** set_next_index_relfilenode(PG_FUNCTION_ARGS)
PG_RETURN_VOID();
}
- Datum
- add_pg_enum_label(PG_FUNCTION_ARGS)
- {
- Oid enumoid = PG_GETARG_OID(0);
- Oid typoid = PG_GETARG_OID(1);
- Name label = PG_GETARG_NAME(2);
-
- EnumValuesCreate(typoid, list_make1(makeString(NameStr(*label))),
- enumoid);
-
- PG_RETURN_VOID();
- }
--- 121,123 ----
*** a/doc/src/sgml/ref/alter_type.sgml
--- b/doc/src/sgml/ref/alter_type.sgml
***************
*** 23,33 **** PostgreSQL documentation
<refsynopsisdiv>
<synopsis>
! ALTER TYPE <replaceable class="PARAMETER">name</replaceable> <replaceable class="PARAMETER">action</replaceable> [, ... ]
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> OWNER TO <replaceable class="PARAMETER">new_owner</replaceable>
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> RENAME ATTRIBUTE <replaceable class="PARAMETER">attribute_name</replaceable> TO <replaceable class="PARAMETER">new_attribute_name</replaceable>
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> RENAME TO <replaceable class="PARAMETER">new_name</replaceable>
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> SET SCHEMA <replaceable class="PARAMETER">new_schema</replaceable>
<phrase>where <replaceable class="PARAMETER">action</replaceable> is one of:</phrase>
--- 23,34 ----
<refsynopsisdiv>
<synopsis>
! ALTER TYPE <replaceable class="PARAMETER">name</replaceable> <replaceable class="PARAMETER">action</replaceable> [, ... ]
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> OWNER TO <replaceable class="PARAMETER">new_owner</replaceable>
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> RENAME ATTRIBUTE <replaceable class="PARAMETER">attribute_name</replaceable> TO <replaceable class="PARAMETER">new_attribute_name</replaceable>
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> RENAME TO <replaceable class="PARAMETER">new_name</replaceable>
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> SET SCHEMA <replaceable class="PARAMETER">new_schema</replaceable>
+ ALTER TYPE <replaceable class="PARAMETER">name</replaceable> ADD <replaceable class="PARAMETER">new_enum_value</replaceable> [ BEFORE | AFTER <replaceable class="PARAMETER">existing_enum_value</replaceable>
<phrase>where <replaceable class="PARAMETER">action</replaceable> is one of:</phrase>
***************
*** 103,108 **** ALTER TYPE <replaceable class="PARAMETER">name</replaceable> SET SCHEMA <replace
--- 104,131 ----
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><literal>ADD [ BEFORE | AFTER ]</literal></term>
+ <listitem>
+ <para>
+ This form adds a new value to an enum type. If the new value's place
+ in the sort order is not set using <literal>BEFORE</literal> or
+ <literal>AFTER</literal>, then the new item is placed at the end of
+ the list of values.
+ </para>
+ <note>
+ <para>
+ Adding a new enum value will in some cases lower the comparison and
+ sorting performance of the enum type. This will only usually occur if
+ <literal>BEFORE</literal> or <literal>AFTER</literal> are used to set
+ the new value's sort order position somewhere other than at the end
+ of the list. Optimal performance will be restored if the database is
+ dumped and reloaded.
+ </para>
+ </note>
+ </listitem>
+ </varlistentry>
</variablelist>
</para>
***************
*** 196,201 **** ALTER TYPE <replaceable class="PARAMETER">name</replaceable> SET SCHEMA <replace
--- 219,254 ----
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><replaceable class="PARAMETER">data_type</replaceable></term>
+ <listitem>
+ <para>
+ The data type of the attribute to add, or the new type of the
+ attribute to alter.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="PARAMETER">new_enum_value</replaceable></term>
+ <listitem>
+ <para>
+ The new value to be added to the num type's list of values. Like all
+ enum literals it needs to be quoted.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="PARAMETER">exisiting_enum_value</replaceable></term>
+ <listitem>
+ <para>
+ The neighbour of the new value to be added to the num type's list of values. Like all
+ enum literals it needs to be quoted.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</para>
</refsect1>
***************
*** 232,237 **** ALTER TYPE email SET SCHEMA customers;
--- 285,297 ----
ALTER TYPE compfoo ADD ATTRIBUTE f3 int;
</programlisting>
</para>
+
+ <para>
+ To add a new value to an enum type in a particular sort position:
+ <programlisting>
+ ALTER TYPE colors ADD 'orange' AFTER 'red';
+ </programlisting>
+ </para>
</refsect1>
<refsect1>
*** a/src/backend/catalog/pg_enum.c
--- b/src/backend/catalog/pg_enum.c
***************
*** 18,29 ****
--- 18,226 ----
#include "catalog/catalog.h"
#include "catalog/indexing.h"
#include "catalog/pg_enum.h"
+ #include "catalog/pg_type.h"
+ #include "utils/lsyscache.h"
+ #include "utils/syscache.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/rel.h"
#include "utils/tqual.h"
static int oid_cmp(const void *p1, const void *p2);
+ static int sort_order_cmp(const void *p1, const void *p2);
+
+ Oid binary_upgrade_next_pg_enum_oid = InvalidOid;
+
+ /*
+ * AddEnumLabel
+ * Add a new label to the enum set. By default it goes at
+ * the end, but the user can choose to place it before or
+ * after any existing set member.
+ *
+ *
+ */
+
+ void
+ AddEnumLabel(Oid enumTypeOid,
+ char *newVal,
+ char *neighbour,
+ bool newValIsAfter)
+ {
+ Oid newOid;
+ Relation pg_enum;
+ TupleDesc tupDesc;
+ Datum values[Natts_pg_enum];
+ bool nulls[Natts_pg_enum];
+ NameData enumlabel;
+ HeapTuple enum_tup;
+ int newelemorder;
+ CatCList *list;
+ int nelems;
+
+ /* check length of new label is ok */
+ if (strlen(newVal) > (NAMEDATALEN - 1))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_NAME),
+ errmsg("invalid enum label \"%s\"", newVal),
+ errdetail("Labels must be %d characters or less.",
+ NAMEDATALEN - 1)));
+
+ /* get a new OID for the label */
+ pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
+
+ list = SearchSysCacheList1(ENUMTYPOIDNAME,
+ ObjectIdGetDatum(enumTypeOid));
+ nelems = list->n_members;
+
+ if (neighbour == NULL)
+ {
+ /*
+ * Put the new label at the end of the list.
+ * No change to existing tuples is required.
+ */
+ newelemorder = nelems + 1;
+ }
+ else
+ {
+ /* BEFORE or AFTER specified */
+ int i;
+ HeapTuple *existing;
+ HeapTuple nbr = NULL;
+ Form_pg_enum nbr_en;
+
+ /* sort the list of the existing elements by enumsortorder */
+ existing = palloc(nelems * sizeof(HeapTuple));
+
+ for (i = 0; i < nelems; i++)
+ existing[i] = &(list->members[i]->tuple);
+
+ qsort(existing, nelems, sizeof(HeapTuple), sort_order_cmp);
+
+ /* locate the neighbour element */
+ for (i = 0; i < nelems; i++)
+ {
+ Form_pg_enum exists_en;
+ exists_en = (Form_pg_enum) GETSTRUCT(existing[i]);
+ if (strcmp(NameStr(exists_en->enumlabel),neighbour) == 0)
+ nbr = existing[i];
+
+ }
+
+ if (nbr == NULL)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("\"%s\" is not an existing label.", neighbour)));
+ }
+
+ nbr_en = (Form_pg_enum) GETSTRUCT(nbr);
+
+ /*
+ * If BEFORE was specified, the new label goes in the neighbour's
+ * position. Otherwise, it goes in the position after that.
+ */
+ newelemorder = nbr_en->enumsortorder;
+ if (newValIsAfter)
+ newelemorder++;
+
+ /*
+ * Add 1 to the sortorder of all the labels after where the
+ * new label goes. Do it from the end back so we don't get
+ * uniqueness violations.
+ */
+ for (i = nelems - 1; i>= 0; i--)
+ {
+ HeapTuple newtup;
+ Form_pg_enum exst_en = (Form_pg_enum) GETSTRUCT(existing[i]);
+ if (exst_en->enumsortorder < newelemorder)
+ break;
+
+ newtup = heap_copytuple(existing[i]);
+ exst_en = (Form_pg_enum) GETSTRUCT(newtup);
+ exst_en->enumsortorder ++;
+
+ simple_heap_update(pg_enum, &newtup->t_self, newtup);
+
+ CatalogUpdateIndexes(pg_enum, newtup);
+
+ }
+
+ }
+
+ if (OidIsValid(binary_upgrade_next_pg_enum_oid))
+ {
+ if (neighbour != NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("BEFORE/AFTER incompatible with binary upgrade.")));
+
+ newOid = binary_upgrade_next_pg_enum_oid;
+ binary_upgrade_next_pg_enum_oid = InvalidOid;
+ }
+ else
+ {
+ /*
+ * Non upgrade case, we allocate a new Oid for the value.
+ *
+ * We try to give the new element an even numbered Oid if it's safe,
+ * which should be true if these conditions hold:
+ * . the Oid of the highest sorted exiting value is even, and
+ * . the new value is to sort after that value, and
+ * . there hasn't been Oid wraparound.
+ * For all other cases we allocate an odd Oid.
+ */
+
+
+ newOid = GetNewOid(pg_enum);
+ if (newelemorder > nelems)
+ {
+ Oid last_elem_oid = InvalidOid;
+ int i;
+
+ for (i = nelems - 1; i>= 0; i--)
+ {
+ Form_pg_enum last_en;
+ last_en = (Form_pg_enum) GETSTRUCT(&(list->members[i]->tuple));
+ if (last_en->enumsortorder == nelems)
+ {
+ last_elem_oid = HeapTupleGetOid(&(list->members[i]->tuple));
+ break;
+ }
+ }
+
+ if (last_elem_oid %2 == 0)
+ while ((newOid %2 == 1 && newOid > last_elem_oid) ||
+ (newOid %2 == 0 && newOid < last_elem_oid))
+ newOid = GetNewOid(pg_enum);
+ else
+ while (newOid %2 == 0)
+ newOid = GetNewOid(pg_enum);
+ }
+ else
+ {
+ while (newOid %2 == 0)
+ newOid = GetNewOid(pg_enum);
+ }
+ }
+
+ /* set up the new entry */
+ tupDesc = pg_enum->rd_att;
+ memset(nulls, false, sizeof(nulls));
+ values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid);
+ namestrcpy(&enumlabel, newVal);
+ values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel);
+ values[Anum_pg_enum_enumsortorder -1] = Int32GetDatum(newelemorder);
+ enum_tup = heap_form_tuple(tupDesc, values, nulls);
+ HeapTupleSetOid(enum_tup, newOid);
+ simple_heap_insert(pg_enum, enum_tup);
+ CatalogUpdateIndexes(pg_enum, enum_tup);
+ heap_freetuple(enum_tup);
+
+ heap_close(pg_enum, RowExclusiveLock);
+
+ ReleaseCatCacheList(list);
+
+ }
/*
***************
*** 33,40 **** static int oid_cmp(const void *p1, const void *p2);
* vals is a list of Value strings.
*/
void
! EnumValuesCreate(Oid enumTypeOid, List *vals,
! Oid binary_upgrade_next_pg_enum_oid)
{
Relation pg_enum;
TupleDesc tupDesc;
--- 230,236 ----
* vals is a list of Value strings.
*/
void
! EnumValuesCreate(Oid enumTypeOid, List *vals)
{
Relation pg_enum;
TupleDesc tupDesc;
***************
*** 50,58 **** EnumValuesCreate(Oid enumTypeOid, List *vals,
num_elems = list_length(vals);
/*
! * XXX we do not bother to check the list of values for duplicates --- if
* you have any, you'll get a less-than-friendly unique-index violation.
! * Is it worth trying harder?
*/
pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
--- 246,254 ----
num_elems = list_length(vals);
/*
! * We do not bother to check the list of values for duplicates --- if
* you have any, you'll get a less-than-friendly unique-index violation.
! * It is probably not worth trying harder.
*/
pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
***************
*** 62,96 **** EnumValuesCreate(Oid enumTypeOid, List *vals,
* Allocate oids
*/
oids = (Oid *) palloc(num_elems * sizeof(Oid));
! if (OidIsValid(binary_upgrade_next_pg_enum_oid))
! {
! if (num_elems != 1)
! ereport(ERROR,
! (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("EnumValuesCreate() can only set a single OID")));
! oids[0] = binary_upgrade_next_pg_enum_oid;
! binary_upgrade_next_pg_enum_oid = InvalidOid;
! }
! else
{
/*
! * While this method does not absolutely guarantee that we generate no
! * duplicate oids (since we haven't entered each oid into the table
! * before allocating the next), trouble could only occur if the oid
! * counter wraps all the way around before we finish. Which seems
! * unlikely.
*/
! for (elemno = 0; elemno < num_elems; elemno++)
! {
! /*
! * The pg_enum.oid is stored in user tables. This oid must be
! * preserved by binary upgrades.
! */
! oids[elemno] = GetNewOid(pg_enum);
! }
! /* sort them, just in case counter wrapped from high to low */
! qsort(oids, num_elems, sizeof(Oid), oid_cmp);
}
/* and make the entries */
memset(nulls, false, sizeof(nulls));
--- 258,287 ----
* Allocate oids
*/
oids = (Oid *) palloc(num_elems * sizeof(Oid));
!
! /*
! * While this method does not absolutely guarantee that we generate no
! * duplicate oids (since we haven't entered each oid into the table
! * before allocating the next), trouble could only occur if the oid
! * counter wraps all the way around before we finish. Which seems
! * unlikely.
! */
! for (elemno = 0; elemno < num_elems; elemno++)
{
/*
! * The pg_enum.oid is stored in user tables. This oid must be
! * preserved by binary upgrades.
! *
! * We allocate all new enums as evenly numbered Oids
! * so they will sort fast.
*/
! Oid new_oid;
! while ((new_oid = GetNewOid(pg_enum)) %2 == 1)
! ;
! oids[elemno] = new_oid;
}
+ /* sort them, just in case counter wrapped from high to low */
+ qsort(oids, num_elems, sizeof(Oid), oid_cmp);
/* and make the entries */
memset(nulls, false, sizeof(nulls));
***************
*** 114,119 **** EnumValuesCreate(Oid enumTypeOid, List *vals,
--- 305,311 ----
values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid);
namestrcpy(&enumlabel, lab);
values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel);
+ values[Anum_pg_enum_enumsortorder -1] = Int32GetDatum(elemno+1);
tup = heap_form_tuple(tupDesc, values, nulls);
HeapTupleSetOid(tup, oids[elemno]);
***************
*** 164,170 **** EnumValuesDelete(Oid enumTypeOid)
}
! /* qsort comparison function */
static int
oid_cmp(const void *p1, const void *p2)
{
--- 356,362 ----
}
! /* qsort comparison for oids */
static int
oid_cmp(const void *p1, const void *p2)
{
***************
*** 177,179 **** oid_cmp(const void *p1, const void *p2)
--- 369,384 ----
return 1;
return 0;
}
+
+ /* qsort comparison function for tuples by sort order */
+ static int
+ sort_order_cmp(const void *p1, const void *p2)
+ {
+ HeapTuple v1 = *((const HeapTuple *) p1);
+ HeapTuple v2 = *((const HeapTuple *) p2);
+ Form_pg_enum en1 = (Form_pg_enum) GETSTRUCT(v1);
+ Form_pg_enum en2 = (Form_pg_enum) GETSTRUCT(v2);
+
+ return en1->enumsortorder - en2->enumsortorder;
+ }
+
*** a/src/backend/commands/typecmds.c
--- b/src/backend/commands/typecmds.c
***************
*** 85,96 **** static Oid findTypeTypmodoutFunction(List *procname);
static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid);
static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
static void checkDomainOwner(HeapTuple tup, TypeName *typename);
static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
Oid baseTypeOid,
int typMod, Constraint *constr,
char *domainName);
-
-
/*
* DefineType
* Registers a new base type.
--- 85,95 ----
static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid);
static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
static void checkDomainOwner(HeapTuple tup, TypeName *typename);
+ static void checkEnumOwner(HeapTuple tup, TypeName *typename);
static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
Oid baseTypeOid,
int typMod, Constraint *constr,
char *domainName);
/*
* DefineType
* Registers a new base type.
***************
*** 563,569 **** DefineType(List *names, List *parameters)
-1, /* typMod (Domains only) */
0, /* Array Dimensions of typbasetype */
false); /* Type NOT NULL */
-
/*
* Create the array type that goes with it.
*/
--- 562,567 ----
***************
*** 1044,1050 **** DefineDomain(CreateDomainStmt *stmt)
storage, /* TOAST strategy */
basetypeMod, /* typeMod value */
typNDims, /* Array dimensions for base type */
! typNotNull); /* Type NOT NULL */
/*
* Process constraints which refer to the domain ID returned by TypeCreate
--- 1042,1048 ----
storage, /* TOAST strategy */
basetypeMod, /* typeMod value */
typNDims, /* Array dimensions for base type */
! typNotNull); /* Type NOT NULL */
/*
* Process constraints which refer to the domain ID returned by TypeCreate
***************
*** 1094,1099 **** DefineEnum(CreateEnumStmt *stmt)
--- 1092,1100 ----
AclResult aclresult;
Oid old_type_oid;
Oid enumArrayOid;
+ int num_labels;
+
+ num_labels = list_length(stmt->vals);
/* Convert list of names to a name and namespace */
enumNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
***************
*** 1156,1162 **** DefineEnum(CreateEnumStmt *stmt)
false); /* Type NOT NULL */
/* Enter the enum's values into pg_enum */
! EnumValuesCreate(enumTypeOid, stmt->vals, InvalidOid);
/*
* Create the array type that goes with it.
--- 1157,1163 ----
false); /* Type NOT NULL */
/* Enter the enum's values into pg_enum */
! EnumValuesCreate(enumTypeOid, stmt->vals);
/*
* Create the array type that goes with it.
***************
*** 1197,1202 **** DefineEnum(CreateEnumStmt *stmt)
--- 1198,1259 ----
pfree(enumArrayName);
}
+ /*
+ * AlterEnum
+ * Registers a new label for an existing enum.
+ */
+ void
+ AlterEnum (AlterEnumStmt *stmt)
+ {
+ Oid enum_type_oid;
+ TypeName *typename;
+ HeapTuple tup;
+ Form_pg_type typTup;
+
+ /* Make a TypeName so we can use standard type lookup machinery */
+ typename = makeTypeNameFromNameList(stmt->typeName);
+ enum_type_oid = typenameTypeId(NULL, typename, NULL);
+
+ tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(enum_type_oid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for type %u", enum_type_oid);
+
+ typTup = (Form_pg_type) GETSTRUCT(tup);
+
+ /* Check it's an enum and check user has permission to ALTER the enum */
+ checkEnumOwner(tup, typename);
+
+ /* Add the new label */
+ AddEnumLabel (enum_type_oid, stmt->newVal,
+ stmt->newValNeighbour, stmt->newValIsAfter);
+
+ ReleaseSysCache(tup);
+ }
+
+
+ /*
+ * checkEnumOwner
+ *
+ * Check that the type is actually an enum and that the current user
+ * has permission to do ALTER TYPE on it. Throw an error if not.
+ */
+ static void
+ checkEnumOwner(HeapTuple tup, TypeName *typename)
+ {
+ Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup);
+
+ /* Check that this is actually a domain */
+ if (typTup->typtype != TYPTYPE_ENUM)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not an enum",
+ TypeNameToString(typename))));
+
+ /* Permission check: must own type */
+ if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
+ format_type_be(HeapTupleGetOid(tup)));
+ }
/*
* Find suitable I/O functions for a type.
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 2901,2906 **** _copyCreateEnumStmt(CreateEnumStmt *from)
--- 2901,2919 ----
return newnode;
}
+ static AlterEnumStmt *
+ _copyAlterEnumStmt(AlterEnumStmt *from)
+ {
+ AlterEnumStmt *newnode = makeNode(AlterEnumStmt);
+
+ COPY_NODE_FIELD(typeName);
+ COPY_STRING_FIELD(newVal);
+ COPY_STRING_FIELD(newValNeighbour);
+ COPY_SCALAR_FIELD(newValIsAfter);
+
+ return newnode;
+ }
+
static ViewStmt *
_copyViewStmt(ViewStmt *from)
{
***************
*** 4064,4069 **** copyObject(void *from)
--- 4077,4085 ----
case T_CreateEnumStmt:
retval = _copyCreateEnumStmt(from);
break;
+ case T_AlterEnumStmt:
+ retval = _copyAlterEnumStmt(from);
+ break;
case T_ViewStmt:
retval = _copyViewStmt(from);
break;
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
***************
*** 1393,1398 **** _equalCreateEnumStmt(CreateEnumStmt *a, CreateEnumStmt *b)
--- 1393,1409 ----
}
static bool
+ _equalAlterEnumStmt(AlterEnumStmt *a, AlterEnumStmt *b)
+ {
+ COMPARE_NODE_FIELD(typeName);
+ COMPARE_STRING_FIELD(newVal);
+ COMPARE_STRING_FIELD(newValNeighbour);
+ COMPARE_SCALAR_FIELD(newValIsAfter);
+
+ return true;
+ }
+
+ static bool
_equalViewStmt(ViewStmt *a, ViewStmt *b)
{
COMPARE_NODE_FIELD(view);
***************
*** 2700,2705 **** equal(void *a, void *b)
--- 2711,2719 ----
case T_CreateEnumStmt:
retval = _equalCreateEnumStmt(a, b);
break;
+ case T_AlterEnumStmt:
+ retval = _equalAlterEnumStmt(a, b);
+ break;
case T_ViewStmt:
retval = _equalViewStmt(a, b);
break;
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 182,189 **** static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
}
%type <node> stmt schema_stmt
! AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterFdwStmt
! AlterForeignServerStmt AlterGroupStmt
AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
AlterCompositeTypeStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
AlterRoleStmt AlterRoleSetStmt
--- 182,189 ----
}
%type <node> stmt schema_stmt
! AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt
! AlterFdwStmt AlterForeignServerStmt AlterGroupStmt
AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
AlterCompositeTypeStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
AlterRoleStmt AlterRoleSetStmt
***************
*** 652,657 **** stmt :
--- 652,658 ----
| AlterDatabaseSetStmt
| AlterDefaultPrivilegesStmt
| AlterDomainStmt
+ | AlterEnumStmt
| AlterFdwStmt
| AlterForeignServerStmt
| AlterFunctionStmt
***************
*** 3862,3867 **** enum_val_list: Sconst
--- 3863,3908 ----
{ $$ = lappend($1, makeString($3)); }
;
+ /*****************************************************************************
+ *
+ * ALTER TYPE enumtype ADD ...
+ *
+ *****************************************************************************/
+
+ AlterEnumStmt:
+ ALTER TYPE_P any_name ADD_P Sconst
+ {
+ AlterEnumStmt *n = makeNode(AlterEnumStmt);
+ n->typeName = $3;
+ n->newVal = $5;
+ n->newValNeighbour = NULL;
+ n->newValIsAfter = true;
+
+ $$ = (Node *) n;
+ }
+ | ALTER TYPE_P any_name ADD_P Sconst BEFORE Sconst
+ {
+ AlterEnumStmt *n = makeNode(AlterEnumStmt);
+ n->typeName = $3;
+ n->newVal = $5;
+ n->newValNeighbour = $7;
+ n->newValIsAfter = false;
+
+ $$ = (Node *) n;
+ }
+ | ALTER TYPE_P any_name ADD_P Sconst AFTER Sconst
+ {
+ AlterEnumStmt *n = makeNode(AlterEnumStmt);
+ n->typeName = $3;
+ n->newVal = $5;
+ n->newValNeighbour = $7;
+ n->newValIsAfter = true;
+
+ $$ = (Node *) n;
+ }
+ ;
+
+
/*****************************************************************************
*
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
***************
*** 190,195 **** check_xact_readonly(Node *parsetree)
--- 190,196 ----
case T_CreateTrigStmt:
case T_CompositeTypeStmt:
case T_CreateEnumStmt:
+ case T_AlterEnumStmt:
case T_ViewStmt:
case T_DropCastStmt:
case T_DropStmt:
***************
*** 860,865 **** standard_ProcessUtility(Node *parsetree,
--- 861,870 ----
DefineEnum((CreateEnumStmt *) parsetree);
break;
+ case T_AlterEnumStmt: /* ALTER TYPE (enum) */
+ AlterEnum((AlterEnumStmt *) parsetree);
+ break;
+
case T_ViewStmt: /* CREATE VIEW */
DefineView((ViewStmt *) parsetree, queryString);
break;
***************
*** 1868,1873 **** CreateCommandTag(Node *parsetree)
--- 1873,1882 ----
tag = "CREATE TYPE";
break;
+ case T_AlterEnumStmt:
+ tag = "ALTER TYPE";
+ break;
+
case T_ViewStmt:
tag = "CREATE VIEW";
break;
***************
*** 2410,2415 **** GetCommandLogLevel(Node *parsetree)
--- 2419,2428 ----
lev = LOGSTMT_DDL;
break;
+ case T_AlterEnumStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
case T_ViewStmt:
lev = LOGSTMT_DDL;
break;
*** a/src/backend/utils/adt/enum.c
--- b/src/backend/utils/adt/enum.c
***************
*** 14,19 ****
--- 14,20 ----
#include "postgres.h"
#include "catalog/pg_enum.h"
+ #include "catalog/pg_type.h"
#include "fmgr.h"
#include "utils/array.h"
#include "utils/builtins.h"
***************
*** 22,30 ****
#include "libpq/pqformat.h"
#include "miscadmin.h"
static ArrayType *enum_range_internal(Oid enumtypoid, Oid lower, Oid upper);
! static int enum_elem_cmp(const void *left, const void *right);
/* Basic I/O support */
--- 23,52 ----
#include "libpq/pqformat.h"
#include "miscadmin.h"
+ #define BITMAPSIZE 1024
+ #define BITMAPBYTES (BITMAPSIZE / 8)
+
+ typedef struct
+ {
+ Oid enum_oid;
+ int32 sort_order;
+ } enum_sort;
+
+ typedef struct
+ {
+ Oid enumtypoid;
+ Oid bitmap_base;
+ char bitmap[BITMAPBYTES];
+ int label_count;
+ enum_sort sort_order_list[1];
+ } enum_sort_cache;
+
+
static ArrayType *enum_range_internal(Oid enumtypoid, Oid lower, Oid upper);
! static int enum_sort_cmp(const void *left, const void *right);
! static int enum_oid_cmp(const void *left, const void *right);
! static enum_sort_cache *initcache(Oid arg, FunctionCallInfo fcinfo);
/* Basic I/O support */
***************
*** 155,167 **** enum_send(PG_FUNCTION_ARGS)
/* Comparison functions and related */
Datum
enum_lt(PG_FUNCTION_ARGS)
{
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(a < b);
}
Datum
--- 177,390 ----
/* Comparison functions and related */
+ static enum_sort_cache *
+ initcache(Oid arg, FunctionCallInfo fcinfo)
+ {
+ HeapTuple enum_tup;
+ Form_pg_enum en;
+ Oid typeoid;
+ enum_sort_cache *mycache;
+ CatCList *list;
+ int num,i, bm_start, bm_len, start_pos, list_end;
+
+ /* free up anything we've used before) */
+ if (fcinfo->flinfo->fn_extra != NULL)
+ pfree(fcinfo->flinfo->fn_extra);
+
+ /* get the typeoid, label count and the list of values */
+ enum_tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(arg));
+ en = (Form_pg_enum) GETSTRUCT(enum_tup);
+ typeoid = en->enumtypid;
+ ReleaseSysCache(enum_tup);
+ list = SearchSysCacheList1(ENUMTYPOIDNAME, ObjectIdGetDatum(typeoid));
+ num = list->n_members;
+ fcinfo->flinfo->fn_extra =
+ MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+ sizeof(enum_sort_cache) +
+ (num * sizeof(enum_sort)));
+ mycache = (enum_sort_cache *) fcinfo->flinfo->fn_extra;
+ mycache->enumtypoid = typeoid;
+ mycache->label_count = num;
+ memset(mycache->bitmap, 0, BITMAPBYTES);
+
+ /* set up the list sorted by Oid */
+ for (i = 0; i < num; i++)
+ {
+ HeapTuple tup = &(list->members[i]->tuple);
+ Form_pg_enum list_en = (Form_pg_enum) GETSTRUCT(tup);
+
+ mycache->sort_order_list[i].sort_order = list_en->enumsortorder;
+ mycache->sort_order_list[i].enum_oid = HeapTupleGetOid(tup);
+ }
+
+ ReleaseCatCacheList(list);
+
+ qsort(mycache->sort_order_list,mycache->label_count,
+ sizeof(enum_sort),enum_oid_cmp);
+
+
+ /* look for the longest suitable range for the bitmap */
+
+ bm_len = 0;
+ bm_start = 0;
+
+ for (start_pos = 0; start_pos < num -1; start_pos ++)
+ {
+ for (list_end = start_pos+1; list_end < num; list_end++)
+ if (mycache->sort_order_list[list_end].sort_order <
+ mycache->sort_order_list[list_end - 1].sort_order ||
+ mycache->sort_order_list[list_end].enum_oid -
+ mycache->sort_order_list[start_pos].enum_oid >= BITMAPSIZE)
+ break;
+ if (list_end - start_pos > bm_len)
+ {
+ bm_len = list_end - start_pos;
+ bm_start = start_pos;
+ }
+ /*
+ * If the sequence we've just found is bigger than half the list,
+ * we won't find one bigger
+ */
+ if (bm_len > num / 2)
+ break;
+ /*
+ * If we didn't stop because the oid range was too big, there's no
+ * point starting to look again earlier than the end of the
+ * sequence we just found.
+ */
+ if (mycache->sort_order_list[list_end].enum_oid -
+ mycache->sort_order_list[start_pos].enum_oid >= BITMAPSIZE)
+ start_pos = list_end - 1;
+ }
+
+ /* if we found a suitable range, for the bitmap, set it up */
+ if (bm_len > 1)
+ {
+ int base = mycache->sort_order_list[bm_start].enum_oid;
+
+ mycache->bitmap_base = base;
+ for (i = 0; i < bm_len; i++)
+ {
+ Oid enoid = mycache->sort_order_list[bm_start+i].enum_oid;
+ int offset = enoid - base;
+ int bytenum = offset / 8;
+ int bitmask = 1 << (offset % 8);
+ mycache->bitmap[bytenum] |= bitmask;
+ }
+ }
+ else
+ {
+ /*
+ * This should be a bit of a pathological case. Normally we'd
+ * expect at least some small range in sequence.
+ */
+ mycache->bitmap_base = InvalidOid;
+ }
+
+ return mycache;
+ }
+
+ /* fast lookup for Oids known to be in order */
+
+ static inline bool
+ bitmap_lookup(enum_sort_cache *mycache, Oid arg1, Oid arg2)
+ {
+ int offset1 = arg1 - mycache->bitmap_base;
+ int offset2 = arg2 - mycache->bitmap_base;
+ int bytenum, bitmask;
+ if (offset1 < 0 || offset2 < 0 ||
+ offset1 >= BITMAPSIZE || offset2 >= BITMAPSIZE)
+ return false;
+ bytenum = offset1 / 8;
+ bitmask = 1 << (offset1 % 8);
+ if ((mycache->bitmap[bytenum] & bitmask) == 0)
+ return false;
+ bytenum = offset2 / 8;
+ bitmask = 1 << (offset2 % 8);
+ if ((mycache->bitmap[bytenum] & bitmask) == 0)
+ return false;
+ return true;
+ }
+
+ /* slower lookup for Oids not known to be in order */
+
+ static inline int
+ find_sortorder(enum_sort_cache *mycache, Oid arg)
+ {
+ enum_sort *es;
+ enum_sort srch;
+
+ srch.enum_oid = arg;
+ es = bsearch(&srch,
+ mycache->sort_order_list,
+ mycache->label_count,
+ sizeof(enum_sort),
+ enum_oid_cmp);
+ if (es == NULL)
+ return -1;
+
+ return es->sort_order;
+
+ }
+
+ /* enum_ccmp is the common engine for all the visible comparison functions */
+
+ static inline int
+ enum_ccmp(Oid arg1, Oid arg2, FunctionCallInfo fcinfo)
+ {
+
+ enum_sort_cache *mycache;
+ int sort_1, sort_2;
+
+ /* evenly numbered Oids are known to sort right */
+ if (arg1 % 2 == 0 && arg2 % 2 == 0)
+ return arg1 - arg2;
+
+
+ /* so are oids that are equal */
+ if (arg1 == arg2)
+ return 0;
+
+ /* get the cached info, set it up if absent */
+ mycache = (enum_sort_cache *) fcinfo->flinfo->fn_extra;
+ if (mycache == NULL )
+ mycache = initcache(arg1, fcinfo);
+
+ /* first try the fast lookup, if we can */
+ if (OidIsValid(mycache->bitmap_base) &&
+ bitmap_lookup(mycache, arg1, arg2))
+ return arg1 - arg2;
+
+ /* try the slow lookup */
+ sort_1 = find_sortorder(mycache,arg1);
+ sort_2 = find_sortorder(mycache,arg2);
+
+ if (sort_1 <= 0 || sort_2 <= 0)
+ {
+ /*
+ * We couldn't find one or both values.
+ * That means the enum has changed under us, so
+ * re-initialize the cache and try again.
+ */
+ mycache = initcache(arg1, fcinfo);
+ sort_1 = find_sortorder(mycache,arg1);
+ sort_2 = find_sortorder(mycache,arg2);
+ /* if we fail the second time around, give up */
+ if (sort_1 <= 0 || sort_2 <= 0)
+ elog(ERROR, "values missing for enum %s",
+ format_type_be(mycache->enumtypoid));
+ }
+
+ return sort_1 - sort_2;
+ }
+
Datum
enum_lt(PG_FUNCTION_ARGS)
{
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(enum_ccmp(a,b,fcinfo) < 0);
}
Datum
***************
*** 170,176 **** enum_le(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(a <= b);
}
Datum
--- 393,399 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(enum_ccmp(a,b,fcinfo) <= 0);
}
Datum
***************
*** 197,203 **** enum_ge(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(a >= b);
}
Datum
--- 420,426 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(enum_ccmp(a,b,fcinfo) >= 0);
}
Datum
***************
*** 206,212 **** enum_gt(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(a > b);
}
Datum
--- 429,435 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_BOOL(enum_ccmp(a,b,fcinfo) > 0);
}
Datum
***************
*** 215,221 **** enum_smaller(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_OID(a <= b ? a : b);
}
Datum
--- 438,444 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_OID(enum_ccmp(a,b,fcinfo) < 0 ? a : b);
}
Datum
***************
*** 224,230 **** enum_larger(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_OID(a >= b ? a : b);
}
Datum
--- 447,453 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! PG_RETURN_OID(enum_ccmp(a,b,fcinfo) > 0 ? a : b);
}
Datum
***************
*** 233,242 **** enum_cmp(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! if (a > b)
! PG_RETURN_INT32(1);
! else if (a == b)
PG_RETURN_INT32(0);
else
PG_RETURN_INT32(-1);
}
--- 456,465 ----
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
! if (a == b)
PG_RETURN_INT32(0);
+ else if (enum_ccmp(a,b,fcinfo) > 0)
+ PG_RETURN_INT32(1);
else
PG_RETURN_INT32(-1);
}
***************
*** 248,253 **** enum_first(PG_FUNCTION_ARGS)
--- 471,477 ----
{
Oid enumtypoid;
Oid min = InvalidOid;
+ int min_sort = -1; /* value will never in fact be used */
CatCList *list;
int num,
i;
***************
*** 267,276 **** enum_first(PG_FUNCTION_ARGS)
num = list->n_members;
for (i = 0; i < num; i++)
{
! Oid valoid = HeapTupleHeaderGetOid(list->members[i]->tuple.t_data);
!
! if (!OidIsValid(min) || valoid < min)
! min = valoid;
}
ReleaseCatCacheList(list);
--- 491,504 ----
num = list->n_members;
for (i = 0; i < num; i++)
{
! HeapTuple tup = &(list->members[i]->tuple);
! Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
!
! if (!OidIsValid(min) || en->enumsortorder < min_sort)
! {
! min = HeapTupleGetOid(tup);
! min_sort = en->enumsortorder;
! }
}
ReleaseCatCacheList(list);
***************
*** 287,292 **** enum_last(PG_FUNCTION_ARGS)
--- 515,521 ----
{
Oid enumtypoid;
Oid max = InvalidOid;
+ int max_sort = -1; /* value will never in fact be used */
CatCList *list;
int num,
i;
***************
*** 306,315 **** enum_last(PG_FUNCTION_ARGS)
num = list->n_members;
for (i = 0; i < num; i++)
{
! Oid valoid = HeapTupleHeaderGetOid(list->members[i]->tuple.t_data);
!
! if (!OidIsValid(max) || valoid > max)
! max = valoid;
}
ReleaseCatCacheList(list);
--- 535,548 ----
num = list->n_members;
for (i = 0; i < num; i++)
{
! HeapTuple tup = &(list->members[i]->tuple);
! Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
!
! if (!OidIsValid(max) || en->enumsortorder > max_sort)
! {
! max = HeapTupleGetOid(tup);
! max_sort = en->enumsortorder;
! }
}
ReleaseCatCacheList(list);
***************
*** 382,427 **** enum_range_internal(Oid enumtypoid, Oid lower, Oid upper)
i,
j;
Datum *elems;
list = SearchSysCacheList1(ENUMTYPOIDNAME, ObjectIdGetDatum(enumtypoid));
total = list->n_members;
elems = (Datum *) palloc(total * sizeof(Datum));
- j = 0;
for (i = 0; i < total; i++)
{
! Oid val = HeapTupleGetOid(&(list->members[i]->tuple));
- if ((!OidIsValid(lower) || lower <= val) &&
- (!OidIsValid(upper) || val <= upper))
- elems[j++] = ObjectIdGetDatum(val);
}
/* shouldn't need the cache anymore */
ReleaseCatCacheList(list);
! /* sort results into OID order */
! qsort(elems, j, sizeof(Datum), enum_elem_cmp);
/* note this hardwires some details about the representation of Oid */
result = construct_array(elems, j, enumtypoid, sizeof(Oid), true, 'i');
pfree(elems);
return result;
}
! /* qsort comparison function for Datums that are OIDs */
static int
! enum_elem_cmp(const void *left, const void *right)
{
! Oid l = DatumGetObjectId(*((const Datum *) left));
! Oid r = DatumGetObjectId(*((const Datum *) right));
! if (l < r)
! return -1;
! if (l > r)
! return 1;
! return 0;
}
--- 615,690 ----
i,
j;
Datum *elems;
+ enum_sort *sort_items;
+ bool left_found;
list = SearchSysCacheList1(ENUMTYPOIDNAME, ObjectIdGetDatum(enumtypoid));
total = list->n_members;
elems = (Datum *) palloc(total * sizeof(Datum));
+ sort_items = (enum_sort *) palloc(total * sizeof(enum_sort));
for (i = 0; i < total; i++)
{
! HeapTuple tup = &(list->members[i]->tuple);
! Form_pg_enum en = (Form_pg_enum) GETSTRUCT(tup);
!
! sort_items[i].enum_oid = HeapTupleGetOid(tup);
! sort_items[i].sort_order = en->enumsortorder;
}
/* shouldn't need the cache anymore */
ReleaseCatCacheList(list);
! /* sort results into sort_order sequence */
! qsort(sort_items, total, sizeof(enum_sort), enum_sort_cmp);
!
! j = 0;
! left_found = !OidIsValid(lower);
! for (i=0; i < total; i++)
! {
! if (! left_found && lower == sort_items[i].enum_oid)
! left_found = true;
!
! if (left_found)
! elems[j++] = ObjectIdGetDatum(sort_items[i].enum_oid);
!
! if (OidIsValid(upper) && upper == sort_items[i].enum_oid)
! break;
! }
/* note this hardwires some details about the representation of Oid */
result = construct_array(elems, j, enumtypoid, sizeof(Oid), true, 'i');
pfree(elems);
+ pfree(sort_items);
return result;
}
! /*
! * qsort comparison using sort order, for range routines
! */
static int
! enum_sort_cmp(const void *left, const void *right)
{
! enum_sort *l = (enum_sort *) left;
! enum_sort *r = (enum_sort *) right;
! return l->sort_order - r->sort_order;
! }
!
! /*
! * qsort comparison using OID order for comparison search cache
! */
! static int
! enum_oid_cmp(const void *es1, const void *es2)
! {
! enum_sort *p1, *p2;
! p1 = (enum_sort *)es1;
! p2 = (enum_sort *)es2;
! return p1->enum_oid - p2->enum_oid;
}
+
+
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
***************
*** 6653,6669 **** dumpEnumType(Archive *fout, TypeInfo *tyinfo)
PQExpBuffer query = createPQExpBuffer();
PGresult *res;
int num,
! i;
Oid enum_oid;
char *label;
/* Set proper schema search path so regproc references list correctly */
selectSourceSchema(tyinfo->dobj.namespace->dobj.name);
! appendPQExpBuffer(query, "SELECT oid, enumlabel "
! "FROM pg_catalog.pg_enum "
! "WHERE enumtypid = '%u'"
! "ORDER BY oid",
tyinfo->dobj.catId.oid);
res = PQexec(g_conn, query->data);
--- 6653,6676 ----
PQExpBuffer query = createPQExpBuffer();
PGresult *res;
int num,
! i;
Oid enum_oid;
char *label;
/* Set proper schema search path so regproc references list correctly */
selectSourceSchema(tyinfo->dobj.namespace->dobj.name);
! if (fout->remoteVersion >= 90100)
! appendPQExpBuffer(query, "SELECT oid, enumlabel "
! "FROM pg_catalog.pg_enum "
! "WHERE enumtypid = '%u'"
! "ORDER BY enumsortorder",
! tyinfo->dobj.catId.oid);
! else
! appendPQExpBuffer(query, "SELECT oid, enumlabel "
! "FROM pg_catalog.pg_enum "
! "WHERE enumtypid = '%u'"
! "ORDER BY oid",
tyinfo->dobj.catId.oid);
res = PQexec(g_conn, query->data);
***************
*** 6709,6725 **** dumpEnumType(Archive *fout, TypeInfo *tyinfo)
{
enum_oid = atooid(PQgetvalue(res, i, PQfnumber(res, "oid")));
label = PQgetvalue(res, i, PQfnumber(res, "enumlabel"));
!
if (i == 0)
appendPQExpBuffer(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
appendPQExpBuffer(q,
! "SELECT binary_upgrade.add_pg_enum_label('%u'::pg_catalog.oid, "
! "'%u'::pg_catalog.oid, ",
! enum_oid, tyinfo->dobj.catId.oid);
! appendStringLiteralAH(q, label, fout);
! appendPQExpBuffer(q, ");\n");
}
- appendPQExpBuffer(q, "\n");
}
ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
--- 6716,6734 ----
{
enum_oid = atooid(PQgetvalue(res, i, PQfnumber(res, "oid")));
label = PQgetvalue(res, i, PQfnumber(res, "enumlabel"));
!
if (i == 0)
appendPQExpBuffer(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
appendPQExpBuffer(q,
! "SELECT binary_upgrade.set_next_pg_enum_oid('%u'::pg_catalog.oid);\n",
! enum_oid);
! appendPQExpBuffer(q, "ALTER TYPE %s.",
! fmtId(tyinfo->dobj.namespace->dobj.name));
! appendPQExpBuffer(q, "%s ADD ",
! fmtId(tyinfo->dobj.name));
! appendStringLiteralAH(q, label, fout);
! appendPQExpBuffer(q, ";\n\n");
}
}
ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
*** a/src/bin/psql/describe.c
--- b/src/bin/psql/describe.c
***************
*** 473,489 **** describeTypes(const char *pattern, bool verbose, bool showSystem)
gettext_noop("Internal name"),
gettext_noop("Size"));
if (verbose && pset.sversion >= 80300)
appendPQExpBuffer(&buf,
" pg_catalog.array_to_string(\n"
" ARRAY(\n"
" SELECT e.enumlabel\n"
" FROM pg_catalog.pg_enum e\n"
! " WHERE e.enumtypid = t.oid\n"
! " ORDER BY e.oid\n"
" ),\n"
" E'\\n'\n"
" ) AS \"%s\",\n",
gettext_noop("Elements"));
appendPQExpBuffer(&buf,
" pg_catalog.obj_description(t.oid, 'pg_type') as \"%s\"\n",
--- 473,499 ----
gettext_noop("Internal name"),
gettext_noop("Size"));
if (verbose && pset.sversion >= 80300)
+ {
appendPQExpBuffer(&buf,
" pg_catalog.array_to_string(\n"
" ARRAY(\n"
" SELECT e.enumlabel\n"
" FROM pg_catalog.pg_enum e\n"
! " WHERE e.enumtypid = t.oid\n");
!
! if (pset.sversion >= 90100 )
! appendPQExpBuffer(&buf,
! " ORDER BY e.enumsortorder\n");
! else
! appendPQExpBuffer(&buf,
! " ORDER BY e.oid\n");
!
! appendPQExpBuffer(&buf,
" ),\n"
" E'\\n'\n"
" ) AS \"%s\",\n",
gettext_noop("Elements"));
+ }
appendPQExpBuffer(&buf,
" pg_catalog.obj_description(t.oid, 'pg_type') as \"%s\"\n",
*** a/src/include/catalog/indexing.h
--- b/src/include/catalog/indexing.h
***************
*** 147,152 **** DECLARE_UNIQUE_INDEX(pg_enum_oid_index, 3502, on pg_enum using btree(oid oid_ops
--- 147,154 ----
#define EnumOidIndexId 3502
DECLARE_UNIQUE_INDEX(pg_enum_typid_label_index, 3503, on pg_enum using btree(enumtypid oid_ops, enumlabel name_ops));
#define EnumTypIdLabelIndexId 3503
+ DECLARE_UNIQUE_INDEX(pg_enum_typid_sortorder_index, 3539, on pg_enum using btree(enumtypid oid_ops, enumsortorder int4_ops));
+ #define EnumTypIdSortOrderIndexId 3539
/* This following index is not used for a cache and is not unique */
DECLARE_INDEX(pg_index_indrelid_index, 2678, on pg_index using btree(indrelid oid_ops));
*** a/src/include/catalog/pg_enum.h
--- b/src/include/catalog/pg_enum.h
***************
*** 35,40 **** CATALOG(pg_enum,3501)
--- 35,41 ----
{
Oid enumtypid; /* OID of owning enum type */
NameData enumlabel; /* text representation of enum value */
+ int4 enumsortorder; /* sort order for this enum label */
} FormData_pg_enum;
/* ----------------
***************
*** 48,56 **** typedef FormData_pg_enum *Form_pg_enum;
* compiler constants for pg_enum
* ----------------
*/
! #define Natts_pg_enum 2
#define Anum_pg_enum_enumtypid 1
#define Anum_pg_enum_enumlabel 2
/* ----------------
* pg_enum has no initial contents
--- 49,58 ----
* compiler constants for pg_enum
* ----------------
*/
! #define Natts_pg_enum 3
#define Anum_pg_enum_enumtypid 1
#define Anum_pg_enum_enumlabel 2
+ #define Anum_pg_enum_enumsortorder 3
/* ----------------
* pg_enum has no initial contents
***************
*** 60,67 **** typedef FormData_pg_enum *Form_pg_enum;
/*
* prototypes for functions in pg_enum.c
*/
! extern void EnumValuesCreate(Oid enumTypeOid, List *vals,
! Oid binary_upgrade_next_pg_enum_oid);
extern void EnumValuesDelete(Oid enumTypeOid);
#endif /* PG_ENUM_H */
--- 62,70 ----
/*
* prototypes for functions in pg_enum.c
*/
! extern void EnumValuesCreate(Oid enumTypeOid, List *vals);
extern void EnumValuesDelete(Oid enumTypeOid);
+ extern void AddEnumLabel(Oid enumTypeOid, char *newVal, char *neighbour,
+ bool newValIsAfter);
#endif /* PG_ENUM_H */
*** a/src/include/catalog/pg_type_fn.h
--- b/src/include/catalog/pg_type_fn.h
***************
*** 50,56 **** extern Oid TypeCreate(Oid newTypeOid,
char storage,
int32 typeMod,
int32 typNDims,
! bool typeNotNull);
extern void GenerateTypeDependencies(Oid typeNamespace,
Oid typeObjectId,
--- 50,56 ----
char storage,
int32 typeMod,
int32 typNDims,
! bool typeNotNull);
extern void GenerateTypeDependencies(Oid typeNamespace,
Oid typeObjectId,
*** a/src/include/commands/typecmds.h
--- b/src/include/commands/typecmds.h
***************
*** 24,29 **** extern void RemoveTypes(DropStmt *drop);
--- 24,30 ----
extern void RemoveTypeById(Oid typeOid);
extern void DefineDomain(CreateDomainStmt *stmt);
extern void DefineEnum(CreateEnumStmt *stmt);
+ extern void AlterEnum (AlterEnumStmt *stmt);
extern Oid DefineCompositeType(const RangeVar *typevar, List *coldeflist);
extern Oid AssignTypeArrayOid(void);
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
***************
*** 351,357 **** typedef enum NodeTag
T_DropUserMappingStmt,
T_AlterTableSpaceOptionsStmt,
T_SecLabelStmt,
!
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
*/
--- 351,357 ----
T_DropUserMappingStmt,
T_AlterTableSpaceOptionsStmt,
T_SecLabelStmt,
! T_AlterEnumStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
*/
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 2195,2200 **** typedef struct CreateEnumStmt
--- 2195,2214 ----
/* ----------------------
+ * Alter Type Statement, enum types
+ * ----------------------
+ */
+ typedef struct AlterEnumStmt
+ {
+ NodeTag type;
+ List *typeName; /* qualified name (list of Value strings) */
+ char *newVal; /* new enum value */
+ char *newValNeighbour;/* neighbouring enum value */
+ bool newValIsAfter; /* new enum value is after neighbour? */
+ } AlterEnumStmt;
+
+
+ /* ----------------------
* Create View Statement
* ----------------------
*/
*** a/src/test/regress/expected/enum.out
--- b/src/test/regress/expected/enum.out
***************
*** 25,30 **** ERROR: invalid input value for enum rainbow: "mauve"
--- 25,92 ----
LINE 1: SELECT 'mauve'::rainbow;
^
--
+ -- adding new values
+ --
+ CREATE TYPE planets AS ENUM ( 'venus', 'earth', 'mars' );
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+ enumlabel | enumsortorder
+ -----------+---------------
+ venus | 1
+ earth | 2
+ mars | 3
+ (3 rows)
+
+ ALTER TYPE planets ADD 'uranus';
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+ enumlabel | enumsortorder
+ -----------+---------------
+ venus | 1
+ earth | 2
+ mars | 3
+ uranus | 4
+ (4 rows)
+
+ ALTER TYPE planets ADD 'mercury' BEFORE 'venus';
+ ALTER TYPE planets ADD 'saturn' BEFORE 'uranus';
+ ALTER TYPE planets ADD 'jupiter' AFTER 'mars';
+ ALTER TYPE planets ADD 'neptune' AFTER 'uranus';
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+ enumlabel | enumsortorder
+ -----------+---------------
+ mercury | 1
+ venus | 2
+ earth | 3
+ mars | 4
+ jupiter | 5
+ saturn | 6
+ uranus | 7
+ neptune | 8
+ (8 rows)
+
+ select 'mars'::planets > 'mercury' as using_sortorder;
+ using_sortorder
+ -----------------
+ t
+ (1 row)
+
+ -- errors for adding labels
+ ALTER TYPE planets ADD
+ 'plutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutopluto';
+ ERROR: invalid enum label "plutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutopluto"
+ DETAIL: Labels must be 63 characters or less.
+ ALTER TYPE planets ADD 'pluto' AFTER 'zeus';
+ ERROR: "zeus" is not an existing label.
+ DROP TYPE planets;
+ --
-- Basic table creation, row selection
--
CREATE TABLE enumtest (col rainbow);
***************
*** 403,409 **** SELECT COUNT(*) FROM pg_type WHERE typname = 'rainbow';
SELECT * FROM pg_enum WHERE NOT EXISTS
(SELECT 1 FROM pg_type WHERE pg_type.oid = enumtypid);
! enumtypid | enumlabel
! -----------+-----------
(0 rows)
--- 465,471 ----
SELECT * FROM pg_enum WHERE NOT EXISTS
(SELECT 1 FROM pg_type WHERE pg_type.oid = enumtypid);
! enumtypid | enumlabel | enumsortorder
! -----------+-----------+---------------
(0 rows)
*** a/src/test/regress/sql/enum.sql
--- b/src/test/regress/sql/enum.sql
***************
*** 16,21 **** SELECT 'red'::rainbow;
--- 16,61 ----
SELECT 'mauve'::rainbow;
--
+ -- adding new values
+ --
+
+ CREATE TYPE planets AS ENUM ( 'venus', 'earth', 'mars' );
+
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+
+ ALTER TYPE planets ADD 'uranus';
+
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+
+ ALTER TYPE planets ADD 'mercury' BEFORE 'venus';
+
+ ALTER TYPE planets ADD 'saturn' BEFORE 'uranus';
+
+ ALTER TYPE planets ADD 'jupiter' AFTER 'mars';
+
+ ALTER TYPE planets ADD 'neptune' AFTER 'uranus';
+
+ SELECT enumlabel, enumsortorder
+ FROM pg_enum
+ WHERE enumtypid = 'planets'::regtype
+ ORDER by 2;
+
+ select 'mars'::planets > 'mercury' as using_sortorder;
+
+ -- errors for adding labels
+ ALTER TYPE planets ADD
+ 'plutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutopluto';
+
+ ALTER TYPE planets ADD 'pluto' AFTER 'zeus';
+
+ DROP TYPE planets;
+ --
-- Basic table creation, row selection
--
CREATE TABLE enumtest (col rainbow);
On Tue, Oct 19, 2010 at 5:42 PM, Andrew Dunstan <andrew@dunslane.net> wrote:
Well a bit more testing shows some benefit. I've sorted out a few kinks, so
this seems to work. In particular, with the above tables, the version
imported from 9.0 can create have an index created in about the same time as
on the fresh table (identical data, but all even numbered Oids).Of course, with lots of odd numbered Oids, if a label gets added the
imported version will degrade in performance much more quickly.
I'm quite impressed by the amount of time and thought being put into
optimizing this. I didn't realize people cared so much about enum
performance; but it's good that they do.
I hope to see more such efforts in other parts of the system.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On 10/19/2010 08:51 PM, Robert Haas wrote:
On Tue, Oct 19, 2010 at 5:42 PM, Andrew Dunstan<andrew@dunslane.net> wrote:
Well a bit more testing shows some benefit. I've sorted out a few kinks, so
this seems to work. In particular, with the above tables, the version
imported from 9.0 can create have an index created in about the same time as
on the fresh table (identical data, but all even numbered Oids).Of course, with lots of odd numbered Oids, if a label gets added the
imported version will degrade in performance much more quickly.I'm quite impressed by the amount of time and thought being put into
optimizing this. I didn't realize people cared so much about enum
performance; but it's good that they do.I hope to see more such efforts in other parts of the system.
:-)
Efficiency has always been one of the major reasons for using enums, so
it's important that we make them extensible without badly affecting
performance.
cheers
andrew
On Tue, Oct 19, 2010 at 08:51:16PM -0400, Robert Haas wrote:
On Tue, Oct 19, 2010 at 5:42 PM, Andrew Dunstan <andrew@dunslane.net> wrote:
Well a bit more testing shows some benefit. I've sorted out a few kinks, so
this seems to work. In particular, with the above tables, the version
imported from 9.0 can create have an index created in about the same time as
on the fresh table (identical data, but all even numbered Oids).Of course, with lots of odd numbered Oids, if a label gets added the
imported version will degrade in performance much more quickly.I'm quite impressed by the amount of time and thought being put into
optimizing this. I didn't realize people cared so much about enum
performance; but it's good that they do.I hope to see more such efforts in other parts of the system.
Which parts of the system, in particular, do you have in mind? Other
people from EDB have mentioned that slimming down the on-disk
representation was one such target. What other ones would you see as
needing such attention?
Cheers,
David.
--
David Fetter <david@fetter.org> http://fetter.org/
Phone: +1 415 235 3778 AIM: dfetter666 Yahoo!: dfetter
Skype: davidfetter XMPP: david.fetter@gmail.com
iCal: webcal://www.tripit.com/feed/ical/people/david74/tripit.ics
Remember to vote!
Consider donating to Postgres: http://www.postgresql.org/about/donate
On Wed, Oct 20, 2010 at 3:16 PM, David Fetter <david@fetter.org> wrote:
On Tue, Oct 19, 2010 at 08:51:16PM -0400, Robert Haas wrote:
On Tue, Oct 19, 2010 at 5:42 PM, Andrew Dunstan <andrew@dunslane.net> wrote:
Well a bit more testing shows some benefit. I've sorted out a few kinks, so
this seems to work. In particular, with the above tables, the version
imported from 9.0 can create have an index created in about the same time as
on the fresh table (identical data, but all even numbered Oids).Of course, with lots of odd numbered Oids, if a label gets added the
imported version will degrade in performance much more quickly.I'm quite impressed by the amount of time and thought being put into
optimizing this. I didn't realize people cared so much about enum
performance; but it's good that they do.I hope to see more such efforts in other parts of the system.
Which parts of the system, in particular, do you have in mind? Other
people from EDB have mentioned that slimming down the on-disk
representation was one such target. What other ones would you see as
needing such attention?
On-disk footprint.
WAL volume.
COPY speed.
Checkpoint I/O.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Tue, Oct 19, 2010 at 9:15 PM, Andrew Dunstan <andrew@dunslane.net> wrote:
Efficiency has always been one of the major reasons for using enums, so
it's important that we make them extensible without badly affecting
performance.
on that note is it worthwhile backpatching recent versions to allocate
enums with even numbered oids? That way people binary upgrading can
get the benefit of the optimization they should qualify for...
merlin
On Wed, Oct 20, 2010 at 6:54 PM, Merlin Moncure <mmoncure@gmail.com> wrote:
On Tue, Oct 19, 2010 at 9:15 PM, Andrew Dunstan <andrew@dunslane.net> wrote:
Efficiency has always been one of the major reasons for using enums, so
it's important that we make them extensible without badly affecting
performance.on that note is it worthwhile backpatching recent versions to allocate
enums with even numbered oids? That way people binary upgrading can
get the benefit of the optimization they should qualify for...
Uh, -1 from me. This is not a bug fix, and it will only help people
who create new enums between the time they upgrade to the relevant
minor release and the time they upgrade to 9.1. We are not into the
business of back-patching marginal peformance enhancements. If we
want to have a 9.0R2 release, or whatever, then so be it, but let's
not be modifying behavior in stable branches unless there's a *bug*.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Andrew Dunstan <andrew@dunslane.net> writes:
Latest patch attached.
I've been working through this patch. It occurs to me that there's a
fairly serious problem with the current implementation of insertion of
new values within the bounds of the current sort ordering. Namely, that
it does that by reassigning the enumsortorder values of pre-existing
rows. That creates a race condition: suppose that our backend is doing
that while another process is busy loading its internal cache of values
of the enum. If we commit partway through the other process's loading
of its cache, it may end up with a cache containing some pre-commit
entries and some post-commit entries. In the worst case it might even
have two images of the same enum label, with different enumsortorder
values. Needless to say, this is catastrophic for correctness of
subsequent comparisons in the other process.
We could try to avoid the race condition, but it's not going to be easy.
I think a better idea is to avoid having to issue updates against
pg_enum rows once they are inserted. To do that, I propose that instead
of integer enumsortorder values, we use float8 values. The initial
entries for a type would still have numbers 1..n, but when we need to
insert a value between two existing entries, we assign it a value
halfway between their enumsortorder values. Then we never have to alter
pre-existing entries, and there's no race condition: at worst, a
process's cache entry might be missing some just-committed rows, and we
know how to deal with that.
The disadvantage of this scheme is that if you repeatedly insert entries
in the "same place" in the sort order, you halve the available range
each time, so you'd run out of room after order-of-fifty halvings.
The values would eventually differ by only one unit in the last place,
so it'd not be possible to insert another value that would still be
distinguishable in the sort order. Is that an acceptable restriction?
(If you did run into this, you could manually reassign enumsortorder
values to get out of it, without having to dump-and-reload; but you'd
have to beware of the same race condition as above.) Of course adding
new values at the start or end of the enum's list wouldn't have that
restriction.
Thoughts?
regards, tom lane
The disadvantage of this scheme is that if you repeatedly insert entries
in the "same place" in the sort order, you halve the available range
each time, so you'd run out of room after order-of-fifty halvings.
This is not a real issue. If anyone is using an ENUM with 1000 values
in it, they're doing it wrong. However, we'd have to present an
intelligible error message in that case.
The float method would also have a couple other issues:
(1) larger value for the enum order, so more RAM. Do we care?
(2) would need to create a view which hid the floats from admins who
just want to look at the enum ordering.
--
-- Josh Berkus
PostgreSQL Experts Inc.
http://www.pgexperts.com
Josh Berkus <josh@agliodbs.com> writes:
The disadvantage of this scheme is that if you repeatedly insert entries
in the "same place" in the sort order, you halve the available range
each time, so you'd run out of room after order-of-fifty halvings.
This is not a real issue. If anyone is using an ENUM with 1000 values
in it, they're doing it wrong. However, we'd have to present an
intelligible error message in that case.
You wouldn't need 1000 values to get into trouble. If you did
CREATE TYPE e AS ENUM ('a', 'b');
ALTER TYPE e ADD 'c1' BEFORE 'b';
ALTER TYPE e ADD 'c2' BEFORE 'b';
ALTER TYPE e ADD 'c3' BEFORE 'b';
ALTER TYPE e ADD 'c4' BEFORE 'b';
...
you'd hit the failure somewhere around c50, assuming IEEE-style floats.
I think an "intelligible error message" wouldn't be hard; it'd say
something like "no more room to insert another enum label between enum
labels c49 and b".
The float method would also have a couple other issues:
(1) larger value for the enum order, so more RAM. Do we care?
The in-memory representation wouldn't be any bigger, because we don't
actually need to keep the enumorder values in the cache entries.
pg_enum rows would get wider, but not materially so considering they
already contain a 64-byte name column.
(2) would need to create a view which hid the floats from admins who
just want to look at the enum ordering.
Why? We weren't going to hide the enumorder values before, why would we
do it with this?
regards, tom lane
On Oct 23, 2010, at 7:12 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Andrew Dunstan <andrew@dunslane.net> writes:
Latest patch attached.
I've been working through this patch. It occurs to me that there's a
fairly serious problem with the current implementation of insertion of
new values within the bounds of the current sort ordering. Namely, that
it does that by reassigning the enumsortorder values of pre-existing
rows. That creates a race condition:
It strikes me that this is merely one facet of our failure to do proper locking on DDL objects other than relations, and that this would be as good a time as any to start fixing it. ISTM that ALTER TYPE should grab a self-excluding lock just as ALTER TABLE already does.
...Robert
Robert Haas <robertmhaas@gmail.com> writes:
On Oct 23, 2010, at 7:12 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
I've been working through this patch. It occurs to me that there's a
fairly serious problem with the current implementation of insertion of
new values within the bounds of the current sort ordering. Namely, that
it does that by reassigning the enumsortorder values of pre-existing
rows. That creates a race condition:
It strikes me that this is merely one facet of our failure to do proper locking on DDL objects other than relations, and that this would be as good a time as any to start fixing it. ISTM that ALTER TYPE should grab a self-excluding lock just as ALTER TABLE already does.
The point of all the design thrashing we've been doing here is to
*avoid* taking locks while comparing enum OIDs. So I'm not impressed
with this proposal. (A self-exclusive lock to prevent concurrent
ALTER TYPEs might be a good idea, but I don't want to block enum
comparisons too.)
I did just think of a possible solution that would work with the
updating implementation: readers of pg_enum could use an MVCC snapshot
instead of SnapshotNow while loading their caches. I'm not certain
offhand how unpleasant this'd be at the C-code level, but it should
be possible. I still prefer the idea of not changing rows once they're
inserted, though --- doing so could possibly also cause transient
failures in, eg, enum_in/enum_out, since those depend on syscaches that
are loaded with SnapshotNow.
regards, tom lane
On Oct 23, 2010, at 7:52 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
I still prefer the idea of not changing rows once they're
inserted, though
Me too. But I really dislike the idea of having a failure mode where we can't insert for no reason that the user can understand. So I'm trying to think of a better option.
Why would you need to lock out type comparisons? Locking out concurrent DDL seems sufficient.
...Robert
Robert Haas <robertmhaas@gmail.com> writes:
Why would you need to lock out type comparisons?
Didn't you get the point? The hazard is to a concurrent process that is
merely trying to load up its enum-values cache so that it can perform an
enum comparison. I don't want such an operation to have to block,
especially not against something that's trying to acquire a more or less
exclusive lock.
regards, tom lane
On 10/23/2010 07:12 PM, Tom Lane wrote:
Andrew Dunstan<andrew@dunslane.net> writes:
Latest patch attached.
I've been working through this patch.
Cool.
[snip: reallocating enum sortorder on existing values has race
conditions. Suggestion to use a float8 instead and add new value half
way between neighbours, so no reassignment is necessary]
The disadvantage of this scheme is that if you repeatedly insert entries
in the "same place" in the sort order, you halve the available range
each time, so you'd run out of room after order-of-fifty halvings.
The values would eventually differ by only one unit in the last place,
so it'd not be possible to insert another value that would still be
distinguishable in the sort order. Is that an acceptable restriction?
(If you did run into this, you could manually reassign enumsortorder
values to get out of it, without having to dump-and-reload; but you'd
have to beware of the same race condition as above.) Of course adding
new values at the start or end of the enum's list wouldn't have that
restriction.
Well, it's a tiny bit like my initial proposal for enum Oid ranges with
gaps, but with a level of indirection :-)
Seriously, I think it might be OK. Could we provide some safe way of
resetting the sortorder values? Or even a not-entirely-safe
superuser-only function might be useful. Binary upgrade could probably
call it safely, for example.
We've sure expended quite a lot of neurons on this feature :-)
cheers
andrew
Andrew Dunstan <andrew@dunslane.net> writes:
Seriously, I think it might be OK. Could we provide some safe way of
resetting the sortorder values? Or even a not-entirely-safe
superuser-only function might be useful. Binary upgrade could probably
call it safely, for example.
You could do it with plain SQL, as long as you weren't concerned about
confusing processes that were concurrently loading their enum caches.
Another thought here is that the split-in-half rule might be
unnecessarily dumb. It leaves equal amounts of code space on both sides
of the new value, even though the odds of subsequent insertions on both
sides are probably unequal. But I'm not sure if we can predict the
usage pattern well enough to know which side is more likely.
regards, tom lane
On Sat, Oct 23, 2010 at 8:11 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
Why would you need to lock out type comparisons?
Didn't you get the point? The hazard is to a concurrent process that is
merely trying to load up its enum-values cache so that it can perform an
enum comparison. I don't want such an operation to have to block,
especially not against something that's trying to acquire a more or less
exclusive lock.
Hmm, yeah, I missed the point. Sorry.
I suppose you could fix this by always updating every row, and storing
in each row the total count of elements (or a random number). Then
it'd be obvious if you'd read an inconsistent view of the world.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On 10/23/2010 08:54 PM, Tom Lane wrote:
Another thought here is that the split-in-half rule might be
unnecessarily dumb. It leaves equal amounts of code space on both sides
of the new value, even though the odds of subsequent insertions on both
sides are probably unequal. But I'm not sure if we can predict the
usage pattern well enough to know which side is more likely.
We can't. In particular, we can't rely on the label to tell us, so we
have no information at all to go on, really. Let's just go with the
simple midpoint.
Are you going to try doing this?
cheers
andrew
Robert Haas <robertmhaas@gmail.com> writes:
I suppose you could fix this by always updating every row, and storing
in each row the total count of elements (or a random number). Then
it'd be obvious if you'd read an inconsistent view of the world.
Well, the easy way to read a consistent view of the world is to load the
cache using an MVCC snapshot instead of SnapshotNow. The current code
structure isn't amenable to that because it's relying on a syscache to
fetch the data for it, but that seems pretty inefficient anyway. I'm
thinking of changing it around so that the enum cache gets loaded with
a regular systable_beginscan() scan, and then we could load with an
MVCC snapshot.
I'm kind of inclined to go to the float-based representation anyway,
though, just because not having to update other rows to do an insert
seems like a good thing. But we could combine that with an MVCC
snapshot on the read side, which would make renumbering safe, which
would mean that we could auto-renumber when we ran out of code space
and not otherwise. Is that getting too complicated?
regards, tom lane
On Sun, Oct 24, 2010 at 12:26 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
I suppose you could fix this by always updating every row, and storing
in each row the total count of elements (or a random number). Then
it'd be obvious if you'd read an inconsistent view of the world.Well, the easy way to read a consistent view of the world is to load the
cache using an MVCC snapshot instead of SnapshotNow.
Yeah, I have to admit I had never given much thought to how evil
SnapshotNow is. I wonder if we have latent bugs because of this.
Seems like anything where an object is represented by more than one
table row is suspect.
The current code
structure isn't amenable to that because it's relying on a syscache to
fetch the data for it, but that seems pretty inefficient anyway. I'm
thinking of changing it around so that the enum cache gets loaded with
a regular systable_beginscan() scan, and then we could load with an
MVCC snapshot.
That sounds reasonable.
I'm kind of inclined to go to the float-based representation anyway,
though, just because not having to update other rows to do an insert
seems like a good thing. But we could combine that with an MVCC
snapshot on the read side, which would make renumbering safe, which
would mean that we could auto-renumber when we ran out of code space
and not otherwise. Is that getting too complicated?
The complexity doesn't bother me, but I'd probably just renumber every
time. It's worth optimizing enums for speed, but micro-optimizing the
enum DDL for speed may not be worth it, especially because it will
ensure that the renumbering code is very rarely tested.
If you are going to only renumber when necessary, I'd probably make
the ordering position an integer rather than a float, and just set the
values for the initial labels to something like 256 * 10^16, 257 *
10^16, 258 * 10^16, 259 * 10^16; and then some regression tests that
add labels a, z, y, x, .... I dunno if floats have completely
consistent representations on all the platforms we support, but with
integers it should be quite easy to predict the exact point when
you're going to run out of space and what the contents of pg_enum
should be just before and just after that point.
--
Robert Haas
President, Floating-Point Haters Anonymous
On Sat, Oct 23, 2010 at 10:11 PM, Robert Haas <robertmhaas@gmail.com> wrote:
I dunno if floats have completely
consistent representations on all the platforms we support, but with
integers it should be quite easy to predict the exact point when
you're going to run out of space and what the contents of pg_enum
should be just before and just after that point.
There's nothing magic about the integral types here. If you use a
string then you could always split by making the string longer. It
would get weird after you get to 256 (you can still handle it but
there would be weird special case code) but an array of integers would
be just as flexible and wouldn't have the same problem.
--
greg
On 24 October 2010 05:26, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Well, the easy way to read a consistent view of the world is to load the
cache using an MVCC snapshot instead of SnapshotNow. The current code
structure isn't amenable to that because it's relying on a syscache to
fetch the data for it, but that seems pretty inefficient anyway. I'm
thinking of changing it around so that the enum cache gets loaded with
a regular systable_beginscan() scan, and then we could load with an
MVCC snapshot.I'm kind of inclined to go to the float-based representation anyway,
though, just because not having to update other rows to do an insert
seems like a good thing. But we could combine that with an MVCC
snapshot on the read side, which would make renumbering safe, which
would mean that we could auto-renumber when we ran out of code space
and not otherwise. Is that getting too complicated?
As an alternative, how about storing the sort order as an array of
OIDs, in a single row in pg_type, or a new table, rather than across
multiple rows in pg_enum.
Code that read it would be guaranteed a consistent view of the data,
and at worst, it might be missing recently added elements, which the
comparison code can already deal with by re-reading the array.
Regards,
Dean
Show quoted text
regards, tom lane
Greg Stark <gsstark@mit.edu> writes:
There's nothing magic about the integral types here. If you use a
string then you could always split by making the string longer.
The entire objective here is to make enum comparisons fast. Somehow,
going to a non-primitive data type to represent the sort values does
not seem like a win from that perspective.
(Likewise for Dean's suggestion for an array of another kind.)
What I'm currently thinking is that we should actually use float4 not
float8. That eliminates any concerns about making the representation
larger than it was before. We'll definitely have to have the logic
to do renumbering, but it'll still be an exceedingly rare thing in
practice. With float4 the implementation would fail at somewhere
around 2^24 elements in an enum (since even with renumbering, there
wouldn't be enough bits to give each element a distinguishable value).
I don't see that as a real objection, and anyway if you were trying
to have an enum with many elements, you'd want the in-memory
representation to be compact.
regards, tom lane
On 10/24/2010 12:20 PM, Tom Lane wrote:
With float4 the implementation would fail at somewhere
around 2^24 elements in an enum (since even with renumbering, there
wouldn't be enough bits to give each element a distinguishable value).
I don't see that as a real objection, and anyway if you were trying
to have an enum with many elements, you'd want the in-memory
representation to be compact.
Anything beyond the square root of this is getting pretty insane,
IMNSHO, so I'm really not that bothered by that number.
Assuming we renumber the sortorder as even positive integers, that
number comes down a couple of bits, but even so that gives us lots of
head room I think.
cheers
andrew
On 24 October 2010 17:20, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Greg Stark <gsstark@mit.edu> writes:
There's nothing magic about the integral types here. If you use a
string then you could always split by making the string longer.The entire objective here is to make enum comparisons fast. Somehow,
going to a non-primitive data type to represent the sort values does
not seem like a win from that perspective.(Likewise for Dean's suggestion for an array of another kind.)
The point with an OID array is that you wouldn't need to store the
enumsortorder values at all. The sort order would just be the index of
the OID in the array. So the comparison code would read the OID array,
traverse it building an array of enum_sort structs {oid, idx}, sort
that by OID and cache it. Then do binary searches to efficiently find
the index (i.e., sort order) for any given OID. That's pretty much
what the comparison code is doing now, except without explicitly
stored sort positions.
The reason I thought this might help is that it would be a single
atomic operation to read the whole enum sort order, so you'd never get
a mix of old and new sort positions, if another transaction was
altering the enum.
What I'm currently thinking is that we should actually use float4 not
float8. That eliminates any concerns about making the representation
larger than it was before. We'll definitely have to have the logic
to do renumbering, but it'll still be an exceedingly rare thing in
practice. With float4 the implementation would fail at somewhere
around 2^24 elements in an enum (since even with renumbering, there
wouldn't be enough bits to give each element a distinguishable value).
I don't see that as a real objection, and anyway if you were trying
to have an enum with many elements, you'd want the in-memory
representation to be compact.
That seems like a lot of additional complexity to do the renumbering,
and you'd have to make the comparison code safe against a concurrent
renumbering.
Regards,
Dean
Show quoted text
regards, tom lane
Andrew Dunstan <andrew@dunslane.net> writes:
On 10/24/2010 12:20 PM, Tom Lane wrote:
With float4 the implementation would fail at somewhere
around 2^24 elements in an enum (since even with renumbering, there
wouldn't be enough bits to give each element a distinguishable value).
I don't see that as a real objection, and anyway if you were trying
to have an enum with many elements, you'd want the in-memory
representation to be compact.
Anything beyond the square root of this is getting pretty insane,
IMNSHO, so I'm really not that bothered by that number.
Here's a WIP patch that incorporates most of what's been discussed here.
The critical part of it is summed up in the comments for RenumberEnumType:
/*
* RenumberEnumType
* Renumber existing enum elements to have sort positions 1..n.
*
* We avoid doing this unless absolutely necessary; in most installations
* it will never happen. The reason is that updating existing pg_enum
* entries creates hazards for other backends that are concurrently reading
* pg_enum with SnapshotNow semantics. A concurrent SnapshotNow scan could
* see both old and new versions of an updated row as valid, or neither of
* them, if the commit happens between scanning the two versions. It's
* also quite likely for a concurrent scan to see an inconsistent set of
* rows (some members updated, some not).
*
* We can avoid these risks by reading pg_enum with an MVCC snapshot
* instead of SnapshotNow, but that forecloses use of the syscaches.
* We therefore make the following choices:
*
* 1. Any code that is interested in the enumsortorder values MUST read
* pg_enum with an MVCC snapshot, or else acquire lock on the enum type
* to prevent concurrent execution of AddEnumLabel(). The risk of
* seeing inconsistent values of enumsortorder is too high otherwise.
*
* 2. Code that is not examining enumsortorder can use a syscache
* (for example, enum_in and enum_out do so). The worst that can happen
* is a transient failure to find any valid value of the row. This is
* judged acceptable in view of the infrequency of use of RenumberEnumType.
*/
This patch isn't committable as-is because I haven't made enum_first,
enum_last, enum_range follow these coding rules: they need to stop
using the syscache and instead use an indexscan on
pg_enum_typid_sortorder_index to locate the relevant rows. That should
be just a small fix though, and it seems likely to be a net win for
performance anyway. There are a couple of other loose ends
too, in particular I still think we need to prevent ALTER TYPE ADD
inside a transaction block because of the risk of finding undefined
enum OIDs in indexes.
Anybody really unhappy with this approach? If not, I'll finish this
up and commit it.
regards, tom lane
Dean Rasheed <dean.a.rasheed@gmail.com> writes:
The point with an OID array is that you wouldn't need to store the
enumsortorder values at all. The sort order would just be the index of
the OID in the array. So the comparison code would read the OID array,
traverse it building an array of enum_sort structs {oid, idx}, sort
that by OID and cache it.
Hmm. But I guess we'd have to keep that array in the pg_type row,
and it'd be a huge PITA to work with at the SQL level. For instance,
psql and pg_dump can easily be adapted to use enumsortorder instead
of pg_enum.oid when they want to read out the labels in sorted order.
Doing the same with an array representation would be a very different
and much uglier query. I'm not eager to contort the catalog
representation that much.
regards, tom lane
On 10/24/2010 03:33 PM, Tom Lane wrote:
Dean Rasheed<dean.a.rasheed@gmail.com> writes:
The point with an OID array is that you wouldn't need to store the
enumsortorder values at all. The sort order would just be the index of
the OID in the array. So the comparison code would read the OID array,
traverse it building an array of enum_sort structs {oid, idx}, sort
that by OID and cache it.Hmm. But I guess we'd have to keep that array in the pg_type row,
and it'd be a huge PITA to work with at the SQL level. For instance,
psql and pg_dump can easily be adapted to use enumsortorder instead
of pg_enum.oid when they want to read out the labels in sorted order.
Doing the same with an array representation would be a very different
and much uglier query. I'm not eager to contort the catalog
representation that much.
If that's the only objection I don't know that it's terribly serious.
psql and pg_dump could sanely use something like:
select enum_oid, row_number() over () as sort_order
from unnest(null::myenum) as enum_oid
That said, I'm generally wary of array fields, especially in the catalog.
cheers
andrew
On 10/24/2010 03:28 PM, Tom Lane wrote:
This patch isn't committable as-is because I haven't made enum_first,
enum_last, enum_range follow these coding rules: they need to stop
using the syscache and instead use an indexscan on
pg_enum_typid_sortorder_index to locate the relevant rows. That should
be just a small fix though, and it seems likely to be a net win for
performance anyway. There are a couple of other loose ends too, in
particular I still think we need to prevent ALTER TYPE ADD inside a
transaction block because of the risk of finding undefined enum OIDs
in indexes. Anybody really unhappy with this approach? If not, I'll
finish this up and commit it.
I'll look at it tonight. At first glance it looks OK.
(BTW, this would be a good case for publishing your development branch
somewhere (e.g. git.postgresql.org). That way I could import it and
easily do a diff between your patch and mine, in about three simple
commands. There are other ways of getting at it, and I'll go and do
that, but we should start to look at using the nice facilities git
provides.)
cheers
andrew
On 10/24/2010 05:09 PM, Andrew Dunstan wrote:
select enum_oid, row_number() over () as sort_order
from unnest(null::myenum) as enum_oid
Of course, I meant:
select enum_label, row_number() over () as sort_order
from unnest(enum_range(null::myenum)) as enum_label
cheers
andrew
BTW, I've forgotten --- did anyone publish a test case for checking
performance of enum comparisons? Or should I just cons up something
privately?
regards, tom lane
On 10/24/2010 05:34 PM, Tom Lane wrote:
BTW, I've forgotten --- did anyone publish a test case for checking
performance of enum comparisons? Or should I just cons up something
privately?
I have been running tests with
<http://developer.postgresql.org/~adunstan/enumtest.dmp>
The table "mydata" contains 100k rows with the column "label" containing
random values of an enum type with 500 labels. Basically, my main test
is to set up an index on that column. The alter the enum type, and test
again. Then alter some of the rows, and test again.
cheers
andrew
Andrew Dunstan <andrew@dunslane.net> writes:
On 10/24/2010 05:34 PM, Tom Lane wrote:
BTW, I've forgotten --- did anyone publish a test case for checking
performance of enum comparisons? Or should I just cons up something
privately?
I have been running tests with
<http://developer.postgresql.org/~adunstan/enumtest.dmp>
The table "mydata" contains 100k rows with the column "label" containing
random values of an enum type with 500 labels. Basically, my main test
is to set up an index on that column. The alter the enum type, and test
again. Then alter some of the rows, and test again.
OK, I did some timing consisting of building a btree index with
maintenance_work_mem set reasonably high. This is on a debug-enabled
build, so it's not representative of production performance, but it will
do for seeing what we're doing to enum comparison performance. Here's
what I tried:
Stock 9.0.1 24.9 sec
patch, all OIDs even 25.2 sec (~ 1% hit)
patch, half of OIDs odd 27.2 sec (~ 9% hit)
same, bitmapset forced null 64.9 sec (~ 160% hit)
(Note that the noise level in these measurements is about 1%;
I'm not entirely convinced that the all-even case is really measurably
slower than 9.0.)
The "half of OIDs odd" case is what you'd get for a binary upgrade
from a 9.0 database. The last case shows what happens if the
intermediate bitmapset-test optimization is disabled, forcing all
comparisons to do binary searches in the sorted-by-OID array
(except for the one-quarter of cases where both OIDs are even
by chance). It's pretty grim but it represents a worst case that
you'd be very unlikely to hit in practice.
This shows that the bitmapset optimization really is quite effective,
at least for cases where all the enum labels are sorted by OID after
all. That motivated me to change the bitmapset setup code to what's
attached. This is potentially a little slower at initializing the
cache, but it makes up for that by still marking most enum members
as sorted even when a few out-of-order members have been inserted.
The key point is that an out-of-order member in the middle of the
array doesn't prevent us from considering following members as
properly sorted, as long as they are correctly ordered with respect to
the other properly-sorted members.
With this approach we can honestly say that inserting an out-of-order
enum value doesn't impact comparison performance for pre-existing
enum members, only for comparisons involving the out-of-order value
itself; even when the existing members were binary-upgraded and thus
weren't all even. I think that's a worthwhile advantage.
IMHO this level of performance is good enough. Anyone unhappy?
regards, tom lane
/*
* Here, we create a bitmap listing a subset of the enum's OIDs that are
* known to be in order and can thus be compared with just OID comparison.
*
* The point of this is that the enum's initial OIDs were certainly in
* order, so there is some subset that can be compared via OID comparison;
* and we'd rather not do binary searches unnecessarily.
*
* This is somewhat heuristic, and might identify a subset of OIDs that
* isn't exactly what the type started with. That's okay as long as
* the subset is correctly sorted.
*/
bitmap_base = InvalidOid;
bitmap = NULL;
bm_size = 1; /* only save sets of at least 2 OIDs */
for (start_pos = 0; start_pos < numitems - 1; start_pos++)
{
/*
* Identify longest sorted subsequence starting at start_pos
*/
Bitmapset *this_bitmap = bms_make_singleton(0);
int this_bm_size = 1;
Oid start_oid = items[start_pos].enum_oid;
float4 prev_order = items[start_pos].sort_order;
int i;
for (i = start_pos + 1; i < numitems; i++)
{
Oid offset;
offset = items[i].enum_oid - start_oid;
/* quit if bitmap would be too large; cutoff is arbitrary */
if (offset >= 1024)
break;
/* include the item if it's in-order */
if (items[i].sort_order > prev_order)
{
prev_order = items[i].sort_order;
this_bitmap = bms_add_member(this_bitmap, (int) offset);
this_bm_size++;
}
}
/* Remember it if larger than previous best */
if (this_bm_size > bm_size)
{
bms_free(bitmap);
bitmap_base = start_oid;
bitmap = this_bitmap;
bm_size = this_bm_size;
}
else
bms_free(this_bitmap);
/*
* Done if it's not possible to find a longer sequence in the rest
* of the list. In typical cases this will happen on the first
* iteration, which is why we create the bitmaps on the fly instead
* of doing a second pass over the list.
*/
if (bm_size >= (numitems - start_pos - 1))
break;
}
On 10/24/2010 08:12 PM, Tom Lane wrote:
OK, I did some timing consisting of building a btree index with
maintenance_work_mem set reasonably high. This is on a debug-enabled
build, so it's not representative of production performance, but it will
do for seeing what we're doing to enum comparison performance. Here's
what I tried:Stock 9.0.1 24.9 sec
patch, all OIDs even 25.2 sec (~ 1% hit)
patch, half of OIDs odd 27.2 sec (~ 9% hit)
same, bitmapset forced null 64.9 sec (~ 160% hit)
(Note that the noise level in these measurements is about 1%;
I'm not entirely convinced that the all-even case is really measurably
slower than 9.0.)
Yeah, that was my conclusion. I tested with debug/cassert turned off,
but my results were similar.
The "half of OIDs odd" case is what you'd get for a binary upgrade
from a 9.0 database. The last case shows what happens if the
intermediate bitmapset-test optimization is disabled, forcing all
comparisons to do binary searches in the sorted-by-OID array
(except for the one-quarter of cases where both OIDs are even
by chance). It's pretty grim but it represents a worst case that
you'd be very unlikely to hit in practice.This shows that the bitmapset optimization really is quite effective,
at least for cases where all the enum labels are sorted by OID after
all. That motivated me to change the bitmapset setup code to what's
attached. This is potentially a little slower at initializing the
cache, but it makes up for that by still marking most enum members
as sorted even when a few out-of-order members have been inserted.
The key point is that an out-of-order member in the middle of the
array doesn't prevent us from considering following members as
properly sorted, as long as they are correctly ordered with respect to
the other properly-sorted members.
That's nice. It's a tradeoff though. Bumping up the cost of setting up
the cache won't have much effect on a creating a large index, but could
affect to performance of retail comparisons significantly. But this is
probably worth it. You'd have to work hard to create the perverse case
that could result in seriously worse cache setup cost.
With this approach we can honestly say that inserting an out-of-order
enum value doesn't impact comparison performance for pre-existing
enum members, only for comparisons involving the out-of-order value
itself; even when the existing members were binary-upgraded and thus
weren't all even. I think that's a worthwhile advantage.
Yeah, that's nice.
IMHO this level of performance is good enough. Anyone unhappy?
No, seems good.
cheers
andrew
Andrew Dunstan <andrew@dunslane.net> writes:
On 10/24/2010 08:12 PM, Tom Lane wrote:
This shows that the bitmapset optimization really is quite effective,
at least for cases where all the enum labels are sorted by OID after
all. That motivated me to change the bitmapset setup code to what's
attached. This is potentially a little slower at initializing the
cache, but it makes up for that by still marking most enum members
as sorted even when a few out-of-order members have been inserted.
That's nice. It's a tradeoff though. Bumping up the cost of setting up
the cache won't have much effect on a creating a large index, but could
affect to performance of retail comparisons significantly. But this is
probably worth it. You'd have to work hard to create the perverse case
that could result in seriously worse cache setup cost.
Well, notice that I moved the caching into typcache.c, rather than
having it be associated with query startup. So unless you're actively
frobbing the enum definition, that's going to be paid only once per
session.
regards, tom lane
On 10/24/2010 09:20 PM, Tom Lane wrote:
Andrew Dunstan<andrew@dunslane.net> writes:
On 10/24/2010 08:12 PM, Tom Lane wrote:
This shows that the bitmapset optimization really is quite effective,
at least for cases where all the enum labels are sorted by OID after
all. That motivated me to change the bitmapset setup code to what's
attached. This is potentially a little slower at initializing the
cache, but it makes up for that by still marking most enum members
as sorted even when a few out-of-order members have been inserted.That's nice. It's a tradeoff though. Bumping up the cost of setting up
the cache won't have much effect on a creating a large index, but could
affect to performance of retail comparisons significantly. But this is
probably worth it. You'd have to work hard to create the perverse case
that could result in seriously worse cache setup cost.Well, notice that I moved the caching into typcache.c, rather than
having it be associated with query startup. So unless you're actively
frobbing the enum definition, that's going to be paid only once per
session.
Oh, yes. Good. I'm just starting to look at this in detail.
cheers
andrew
Tom Lane wrote:
Andrew Dunstan <andrew@dunslane.net> writes:
On 10/24/2010 08:12 PM, Tom Lane wrote:
This shows that the bitmapset optimization really is quite effective,
at least for cases where all the enum labels are sorted by OID after
all. That motivated me to change the bitmapset setup code to what's
attached. This is potentially a little slower at initializing the
cache, but it makes up for that by still marking most enum members
as sorted even when a few out-of-order members have been inserted.That's nice. It's a tradeoff though. Bumping up the cost of setting up
the cache won't have much effect on a creating a large index, but could
affect to performance of retail comparisons significantly. But this is
probably worth it. You'd have to work hard to create the perverse case
that could result in seriously worse cache setup cost.Well, notice that I moved the caching into typcache.c, rather than
having it be associated with query startup. So unless you're actively
frobbing the enum definition, that's going to be paid only once per
session.
Thanks for modifying pg_upgrade so it works with this new format. The
changes look good and cleaner than what I had to do for 9.0.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ It's impossible for everything to be true. +
Tom Lane wrote:
Andrew Dunstan <andrew@dunslane.net> writes:
On 10/24/2010 08:12 PM, Tom Lane wrote:
This shows that the bitmapset optimization really is quite effective,
at least for cases where all the enum labels are sorted by OID after
all. That motivated me to change the bitmapset setup code to what's
attached. This is potentially a little slower at initializing the
cache, but it makes up for that by still marking most enum members
as sorted even when a few out-of-order members have been inserted.That's nice. It's a tradeoff though. Bumping up the cost of setting up
the cache won't have much effect on a creating a large index, but could
affect to performance of retail comparisons significantly. But this is
probably worth it. You'd have to work hard to create the perverse case
that could result in seriously worse cache setup cost.Well, notice that I moved the caching into typcache.c, rather than
having it be associated with query startup. So unless you're actively
frobbing the enum definition, that's going to be paid only once per
session.
FYI, I marked the TODO item for adding enums as completed. The TODO
item used to also mention renaming or removing enums, but I have seen
few requests for that so I removed that suggestion. We can always
re-add it if there is demand.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ It's impossible for everything to be true. +
On 11/12/2010 01:40 PM, Bruce Momjian wrote:
FYI, I marked the TODO item for adding enums as completed. The TODO
item used to also mention renaming or removing enums, but I have seen
few requests for that so I removed that suggestion. We can always
re-add it if there is demand.
Renaming an item would not be terribly hard. Removing one is that nasty
case. There are all sorts of places the old value could be referred to:
table data, view definitions, check constraints, functions etc.
cheers
andrew
Andrew Dunstan <andrew@dunslane.net> writes:
On 11/12/2010 01:40 PM, Bruce Momjian wrote:
FYI, I marked the TODO item for adding enums as completed. The TODO
item used to also mention renaming or removing enums, but I have seen
few requests for that so I removed that suggestion. We can always
re-add it if there is demand.
Renaming an item would not be terribly hard. Removing one is that nasty
case. There are all sorts of places the old value could be referred to:
table data, view definitions, check constraints, functions etc.
Well, you can rename an item today if you don't mind doing a direct
UPDATE on pg_enum. I think that's probably sufficient if the demand
only amounts to one or two requests a year. I'd say leave it off the
TODO list till we see if there's more demand than that.
regards, tom lane
On Fri, Nov 12, 2010 at 1:55 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Andrew Dunstan <andrew@dunslane.net> writes:
On 11/12/2010 01:40 PM, Bruce Momjian wrote:
FYI, I marked the TODO item for adding enums as completed. The TODO
item used to also mention renaming or removing enums, but I have seen
few requests for that so I removed that suggestion. We can always
re-add it if there is demand.Renaming an item would not be terribly hard. Removing one is that nasty
case. There are all sorts of places the old value could be referred to:
table data, view definitions, check constraints, functions etc.Well, you can rename an item today if you don't mind doing a direct
UPDATE on pg_enum. I think that's probably sufficient if the demand
only amounts to one or two requests a year. I'd say leave it off the
TODO list till we see if there's more demand than that.
I'd say put it on and mark it with an [E]. We could use some more
[E]asy items for that list.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Robert Haas <robertmhaas@gmail.com> writes:
On Fri, Nov 12, 2010 at 1:55 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Well, you can rename an item today if you don't mind doing a direct
UPDATE on pg_enum. �I think that's probably sufficient if the demand
only amounts to one or two requests a year. �I'd say leave it off the
TODO list till we see if there's more demand than that.
I'd say put it on and mark it with an [E]. We could use some more
[E]asy items for that list.
We don't need to add marginally-useful features just because they're
easy. If it doesn't have a real use-case, the incremental maintenance
cost of more code is a good reason to reject it.
regards, tom lane
Excerpts from Bruce Momjian's message of vie nov 12 15:40:28 -0300 2010:
FYI, I marked the TODO item for adding enums as completed. The TODO
item used to also mention renaming or removing enums, but I have seen
few requests for that so I removed that suggestion. We can always
re-add it if there is demand.
I'm sure there's going to be more demand for ENUM features, now that
they are more usable.
--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support
On Nov 12, 2010, at 2:20 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
On Fri, Nov 12, 2010 at 1:55 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Well, you can rename an item today if you don't mind doing a direct
UPDATE on pg_enum. I think that's probably sufficient if the demand
only amounts to one or two requests a year. I'd say leave it off the
TODO list till we see if there's more demand than that.I'd say put it on and mark it with an [E]. We could use some more
[E]asy items for that list.We don't need to add marginally-useful features just because they're
easy. If it doesn't have a real use-case, the incremental maintenance
cost of more code is a good reason to reject it.
If we allow users to name objects, we ought to make every effort to also allow renaming them. In my mind, the only way renaming is too marginal to be useful is if the feature itself is too marginal to be useful.
...Robert
On Fri, 2010-11-12 at 14:20 -0500, Tom Lane wrote:
Robert Haas <robertmhaas@gmail.com> writes:
On Fri, Nov 12, 2010 at 1:55 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Well, you can rename an item today if you don't mind doing a direct
UPDATE on pg_enum. I think that's probably sufficient if the demand
only amounts to one or two requests a year. I'd say leave it off the
TODO list till we see if there's more demand than that.I'd say put it on and mark it with an [E]. We could use some more
[E]asy items for that list.We don't need to add marginally-useful features just because they're
easy. If it doesn't have a real use-case, the incremental maintenance
cost of more code is a good reason to reject it.
Perhaps we should remove the ability to rename tables and databases too.
It would certainly lighten the code path.
JD
regards, tom lane
--
PostgreSQL.org Major Contributor
Command Prompt, Inc: http://www.commandprompt.com/ - 509.416.6579
Consulting, Training, Support, Custom Development, Engineering
http://twitter.com/cmdpromptinc | http://identi.ca/commandprompt
Joshua D. Drake wrote:
On Fri, 2010-11-12 at 14:20 -0500, Tom Lane wrote:
Robert Haas <robertmhaas@gmail.com> writes:
On Fri, Nov 12, 2010 at 1:55 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Well, you can rename an item today if you don't mind doing a direct
UPDATE on pg_enum. I think that's probably sufficient if the demand
only amounts to one or two requests a year. I'd say leave it off the
TODO list till we see if there's more demand than that.I'd say put it on and mark it with an [E]. We could use some more
[E]asy items for that list.We don't need to add marginally-useful features just because they're
easy. If it doesn't have a real use-case, the incremental maintenance
cost of more code is a good reason to reject it.Perhaps we should remove the ability to rename tables and databases too.
It would certainly lighten the code path.
OK, got it. Added incomplete TODO item:
Allow renaming and deleting enumerated values from an existing
enumerated data type
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ It's impossible for everything to be true. +
On 11/12/2010 09:18 PM, Bruce Momjian wrote:
OK, got it. Added incomplete TODO item:
Allow renaming and deleting enumerated values from an existing
enumerated data type
I have serious doubts that deleting will ever be sanely doable.
cheers
andrew
Andrew Dunstan wrote:
On 11/12/2010 09:18 PM, Bruce Momjian wrote:
OK, got it. Added incomplete TODO item:
Allow renaming and deleting enumerated values from an existing
enumerated data typeI have serious doubts that deleting will ever be sanely doable.
True. Should we not mention it then? I can't think of many objects we
can't delete.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ It's impossible for everything to be true. +
On fre, 2010-11-12 at 17:19 -0500, Robert Haas wrote:
If we allow users to name objects, we ought to make every effort to
also allow renaming them. In my mind, the only way renaming is too
marginal to be useful is if the feature itself is too marginal to be
useful.
The bottom line is, any kind of database object needs to be changeable
and removable, otherwise there will always be hesitations about its use.
And when there are hesitations about the use, it's often easiest not to
bother.
I remember ten years ago or so we used to send people away who requested
the ability to drop columns, claiming they didn't plan their database
properly, or they should load it from scratch. Nowadays that is
ludicrous; databases live forever, development is agile, everything
needs to be changeable.
On Sat, Nov 13, 2010 at 12:30 PM, Peter Eisentraut <peter_e@gmx.net> wrote:
On fre, 2010-11-12 at 17:19 -0500, Robert Haas wrote:
If we allow users to name objects, we ought to make every effort to
also allow renaming them. In my mind, the only way renaming is too
marginal to be useful is if the feature itself is too marginal to be
useful.The bottom line is, any kind of database object needs to be changeable
and removable, otherwise there will always be hesitations about its use.
And when there are hesitations about the use, it's often easiest not to
bother.I remember ten years ago or so we used to send people away who requested
the ability to drop columns, claiming they didn't plan their database
properly, or they should load it from scratch. Nowadays that is
ludicrous; databases live forever, development is agile, everything
needs to be changeable.
It was ludicrous then, too. I picked MySQL for several projects early
on for precisely the lack of the ability to drop columns in
PostgreSQL.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
I'd say put it on and mark it with an [E]. We could use some more
[E]asy items for that list.We don't need to add marginally-useful features just because they're
easy. If it doesn't have a real use-case, the incremental maintenance
cost of more code is a good reason to reject it.
I'll bite.
Use-case:
1) DBA adds "Department Role" enum, with set
{'Director','Secretary','Staff','Support','Temporary','Liason'}.
2) 3-person data entry team updates all employee records with those roles.
3) First summary report is run.
4) Manager points out that "Liason" is misspelled.
--
-- Josh Berkus
PostgreSQL Experts Inc.
http://www.pgexperts.com