diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2002-04-25 02:56:56 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2002-04-25 02:56:56 +0000 |
commit | 52200befd04b9fa71da83231c808764867079226 (patch) | |
tree | dff69a6e149b5e8309f235942ab39c58c8bfc95d /src | |
parent | 4eac3919ddf8556e1b59b55472fb6de42ba77e86 (diff) | |
download | postgresql-52200befd04b9fa71da83231c808764867079226.tar.gz postgresql-52200befd04b9fa71da83231c808764867079226.zip |
Implement types regprocedure, regoper, regoperator, regclass, regtype
per pghackers discussion. Add some more typsanity tests, and clean
up some problems exposed thereby (broken or missing array types for
some built-in types). Also, clean up loose ends from unknownin/out
patch.
Diffstat (limited to 'src')
24 files changed, 1463 insertions, 210 deletions
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index a4ab6fce614..07d013232c7 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/bootstrap/bootstrap.c,v 1.125 2002/03/26 19:15:16 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/bootstrap/bootstrap.c,v 1.126 2002/04/25 02:56:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -109,12 +109,14 @@ static struct typinfo Procid[] = { {"int2vector", INT2VECTOROID, 0, INDEX_MAX_KEYS * 2, F_INT2VECTORIN, F_INT2VECTOROUT}, {"int4", INT4OID, 0, 4, F_INT4IN, F_INT4OUT}, {"regproc", REGPROCOID, 0, 4, F_REGPROCIN, F_REGPROCOUT}, + {"regclass", REGCLASSOID, 0, 4, F_REGCLASSIN, F_REGCLASSOUT}, + {"regtype", REGTYPEOID, 0, 4, F_REGTYPEIN, F_REGTYPEOUT}, {"text", TEXTOID, 0, -1, F_TEXTIN, F_TEXTOUT}, {"oid", OIDOID, 0, 4, F_OIDIN, F_OIDOUT}, {"tid", TIDOID, 0, 6, F_TIDIN, F_TIDOUT}, {"xid", XIDOID, 0, 4, F_XIDIN, F_XIDOUT}, {"cid", CIDOID, 0, 4, F_CIDIN, F_CIDOUT}, - {"oidvector", 30, 0, INDEX_MAX_KEYS * 4, F_OIDVECTORIN, F_OIDVECTOROUT}, + {"oidvector", OIDVECTOROID, 0, INDEX_MAX_KEYS * 4, F_OIDVECTORIN, F_OIDVECTOROUT}, {"smgr", 210, 0, 2, F_SMGRIN, F_SMGROUT}, {"_int4", 1007, INT4OID, -1, F_ARRAY_IN, F_ARRAY_OUT}, {"_aclitem", 1034, 1033, -1, F_ARRAY_IN, F_ARRAY_OUT} @@ -600,7 +602,7 @@ DefineAttr(char *name, char *type, int attnum) attrtypes[attnum]->attnum = 1 + attnum; /* fillatt */ attlen = attrtypes[attnum]->attlen = Ap->am_typ.typlen; attrtypes[attnum]->attbyval = Ap->am_typ.typbyval; - attrtypes[attnum]->attstorage = Ap->am_typ.typstorage;; + attrtypes[attnum]->attstorage = Ap->am_typ.typstorage; attrtypes[attnum]->attalign = Ap->am_typ.typalign; } else @@ -610,28 +612,37 @@ DefineAttr(char *name, char *type, int attnum) elog(DEBUG3, "column %s %s", NameStr(attrtypes[attnum]->attname), type); attrtypes[attnum]->attnum = 1 + attnum; /* fillatt */ attlen = attrtypes[attnum]->attlen = Procid[typeoid].len; - attrtypes[attnum]->attstorage = 'p'; /* * Cheat like mad to fill in these items from the length only. - * This only has to work for types used in the system catalogs... + * This only has to work for types that appear in Procid[]. */ switch (attlen) { case 1: attrtypes[attnum]->attbyval = true; + attrtypes[attnum]->attstorage = 'p'; attrtypes[attnum]->attalign = 'c'; break; case 2: attrtypes[attnum]->attbyval = true; + attrtypes[attnum]->attstorage = 'p'; attrtypes[attnum]->attalign = 's'; break; case 4: attrtypes[attnum]->attbyval = true; + attrtypes[attnum]->attstorage = 'p'; + attrtypes[attnum]->attalign = 'i'; + break; + case -1: + attrtypes[attnum]->attbyval = false; + attrtypes[attnum]->attstorage = 'x'; attrtypes[attnum]->attalign = 'i'; break; default: + /* TID and fixed-length arrays, such as oidvector */ attrtypes[attnum]->attbyval = false; + attrtypes[attnum]->attstorage = 'p'; attrtypes[attnum]->attalign = 'i'; break; } @@ -803,6 +814,13 @@ cleanup() /* ---------------- * gettype + * + * NB: this is really ugly; it will return an integer index into Procid[], + * and not an OID at all, until the first reference to a type not known in + * Procid[]. At that point it will read and cache pg_type in the Typ array, + * and subsequently return a real OID (and set the global pointer Ap to + * point at the found row in Typ). So caller must check whether Typ is + * still NULL to determine what the return value is! * ---------------- */ static Oid @@ -827,7 +845,7 @@ gettype(char *type) } else { - for (i = 0; i <= n_types; i++) + for (i = 0; i < n_types; i++) { if (strncmp(type, Procid[i].name, NAMEDATALEN) == 0) return i; diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c index 0a3aefeaf0f..c81b990b4e6 100644 --- a/src/backend/catalog/namespace.c +++ b/src/backend/catalog/namespace.c @@ -13,7 +13,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.11 2002/04/17 20:57:56 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.12 2002/04/25 02:56:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -365,9 +365,12 @@ OpclassnameGetOpcid(Oid amid, const char *opcname) * Given a possibly-qualified function name and argument count, * retrieve a list of the possible matches. * + * If nargs is -1, we return all functions matching the given name, + * regardless of argument count. + * * We search a single namespace if the function name is qualified, else * all namespaces in the search path. The return list will never contain - * multiple entries with identical argument types --- in the multiple- + * multiple entries with identical argument lists --- in the multiple- * namespace case, we arrange for entries in earlier namespaces to mask * identical entries in later namespaces. */ @@ -423,11 +426,16 @@ FuncnameGetCandidates(List *names, int nargs) namespaceId = InvalidOid; } - /* Search syscache by name and nargs only */ - catlist = SearchSysCacheList(PROCNAMENSP, 2, - CStringGetDatum(funcname), - Int16GetDatum(nargs), - 0, 0); + /* Search syscache by name and (optionally) nargs only */ + if (nargs >= 0) + catlist = SearchSysCacheList(PROCNAMENSP, 2, + CStringGetDatum(funcname), + Int16GetDatum(nargs), + 0, 0); + else + catlist = SearchSysCacheList(PROCNAMENSP, 1, + CStringGetDatum(funcname), + 0, 0, 0); for (i = 0; i < catlist->n_members; i++) { @@ -436,6 +444,8 @@ FuncnameGetCandidates(List *names, int nargs) int pathpos = 0; FuncCandidateList newResult; + nargs = procform->pronargs; + if (OidIsValid(namespaceId)) { /* Consider only procs in specified namespace */ @@ -478,7 +488,8 @@ FuncnameGetCandidates(List *names, int nargs) if (catlist->ordered) { - if (memcmp(procform->proargtypes, resultList->args, + if (nargs == resultList->nargs && + memcmp(procform->proargtypes, resultList->args, nargs * sizeof(Oid)) == 0) prevResult = resultList; else @@ -490,7 +501,8 @@ FuncnameGetCandidates(List *names, int nargs) prevResult; prevResult = prevResult->next) { - if (memcmp(procform->proargtypes, prevResult->args, + if (nargs == prevResult->nargs && + memcmp(procform->proargtypes, prevResult->args, nargs * sizeof(Oid)) == 0) break; } @@ -517,6 +529,7 @@ FuncnameGetCandidates(List *names, int nargs) + nargs * sizeof(Oid)); newResult->pathpos = pathpos; newResult->oid = proctup->t_data->t_oid; + newResult->nargs = nargs; memcpy(newResult->args, procform->proargtypes, nargs * sizeof(Oid)); newResult->next = resultList; @@ -533,14 +546,17 @@ FuncnameGetCandidates(List *names, int nargs) * Given a possibly-qualified operator name and operator kind, * retrieve a list of the possible matches. * + * If oprkind is '\0', we return all operators matching the given name, + * regardless of arguments. + * * We search a single namespace if the operator name is qualified, else * all namespaces in the search path. The return list will never contain - * multiple entries with identical argument types --- in the multiple- + * multiple entries with identical argument lists --- in the multiple- * namespace case, we arrange for entries in earlier namespaces to mask * identical entries in later namespaces. * * The returned items always have two args[] entries --- one or the other - * will be InvalidOid for a prefix or postfix oprkind. + * will be InvalidOid for a prefix or postfix oprkind. nargs is 2, too. */ FuncCandidateList OpernameGetCandidates(List *names, char oprkind) @@ -606,8 +622,8 @@ OpernameGetCandidates(List *names, char oprkind) int pathpos = 0; FuncCandidateList newResult; - /* Ignore operators of wrong kind */ - if (operform->oprkind != oprkind) + /* Ignore operators of wrong kind, if specific kind requested */ + if (oprkind && operform->oprkind != oprkind) continue; if (OidIsValid(namespaceId)) @@ -690,6 +706,7 @@ OpernameGetCandidates(List *names, char oprkind) palloc(sizeof(struct _FuncCandidateList) + sizeof(Oid)); newResult->pathpos = pathpos; newResult->oid = opertup->t_data->t_oid; + newResult->nargs = 2; newResult->args[0] = operform->oprleft; newResult->args[1] = operform->oprright; newResult->next = resultList; diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c index 3a4bb1bba34..52cb26ee6a5 100644 --- a/src/backend/catalog/pg_operator.c +++ b/src/backend/catalog/pg_operator.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/pg_operator.c,v 1.66 2002/04/16 23:08:10 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/pg_operator.c,v 1.67 2002/04/25 02:56:55 tgl Exp $ * * NOTES * these routines moved here from commands/define.c and somewhat cleaned up. @@ -138,7 +138,7 @@ OperatorGet(const char *operatorName, ObjectIdGetDatum(operatorNamespace)); if (HeapTupleIsValid(tup)) { - regproc oprcode = ((Form_pg_operator) GETSTRUCT(tup))->oprcode; + RegProcedure oprcode = ((Form_pg_operator) GETSTRUCT(tup))->oprcode; operatorObjectId = tup->t_data->t_oid; *defined = RegProcedureIsValid(oprcode); @@ -168,7 +168,7 @@ OperatorLookup(List *operatorName, bool *defined) { Oid operatorObjectId; - regproc oprcode; + RegProcedure oprcode; operatorObjectId = LookupOperName(operatorName, leftObjectId, rightObjectId); diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index aca5afc1243..57aad4dbf58 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.70 2002/04/11 20:00:00 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.71 2002/04/25 02:56:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -81,8 +81,7 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, if (!con->constisnull) { - /* We know the source constant is really of type 'text' */ - char *val = DatumGetCString(DirectFunctionCall1(textout, + char *val = DatumGetCString(DirectFunctionCall1(unknownout, con->constvalue)); newcon->constvalue = stringTypeDatum(targetType, val, atttypmod); @@ -477,6 +476,11 @@ TypeCategory(Oid inType) case (OIDOID): case (REGPROCOID): + case (REGPROCEDUREOID): + case (REGOPEROID): + case (REGOPERATOROID): + case (REGCLASSOID): + case (REGTYPEOID): case (INT2OID): case (INT4OID): case (INT8OID): @@ -540,38 +544,72 @@ TypeCategory(Oid inType) * to allow for better type extensibility. */ +#define TypeIsTextGroup(t) \ + ((t) == TEXTOID || \ + (t) == BPCHAROID || \ + (t) == VARCHAROID) + +/* Notice OidGroup is a subset of Int4GroupA */ +#define TypeIsOidGroup(t) \ + ((t) == OIDOID || \ + (t) == REGPROCOID || \ + (t) == REGPROCEDUREOID || \ + (t) == REGOPEROID || \ + (t) == REGOPERATOROID || \ + (t) == REGCLASSOID || \ + (t) == REGTYPEOID) + /* - * This macro describes hard-coded knowledge of binary compatibility - * for built-in types. + * INT4 is binary-compatible with many types, but we don't want to allow + * implicit coercion directly between, say, OID and AbsTime. So we subdivide + * the categories. */ -#define IS_BINARY_COMPATIBLE(a,b) \ - (((a) == BPCHAROID && (b) == TEXTOID) \ - || ((a) == BPCHAROID && (b) == VARCHAROID) \ - || ((a) == VARCHAROID && (b) == TEXTOID) \ - || ((a) == VARCHAROID && (b) == BPCHAROID) \ - || ((a) == TEXTOID && (b) == BPCHAROID) \ - || ((a) == TEXTOID && (b) == VARCHAROID) \ - || ((a) == OIDOID && (b) == INT4OID) \ - || ((a) == OIDOID && (b) == REGPROCOID) \ - || ((a) == INT4OID && (b) == OIDOID) \ - || ((a) == INT4OID && (b) == REGPROCOID) \ - || ((a) == REGPROCOID && (b) == OIDOID) \ - || ((a) == REGPROCOID && (b) == INT4OID) \ - || ((a) == ABSTIMEOID && (b) == INT4OID) \ - || ((a) == INT4OID && (b) == ABSTIMEOID) \ - || ((a) == RELTIMEOID && (b) == INT4OID) \ - || ((a) == INT4OID && (b) == RELTIMEOID) \ - || ((a) == INETOID && (b) == CIDROID) \ - || ((a) == CIDROID && (b) == INETOID) \ - || ((a) == BITOID && (b) == VARBITOID) \ - || ((a) == VARBITOID && (b) == BITOID)) +#define TypeIsInt4GroupA(t) \ + ((t) == INT4OID || \ + TypeIsOidGroup(t)) -bool -IsBinaryCompatible(Oid type1, Oid type2) +#define TypeIsInt4GroupB(t) \ + ((t) == INT4OID || \ + (t) == ABSTIMEOID) + +#define TypeIsInt4GroupC(t) \ + ((t) == INT4OID || \ + (t) == RELTIMEOID) + +#define TypeIsInetGroup(t) \ + ((t) == INETOID || \ + (t) == CIDROID) + +#define TypeIsBitGroup(t) \ + ((t) == BITOID || \ + (t) == VARBITOID) + + +static bool +DirectlyBinaryCompatible(Oid type1, Oid type2) { if (type1 == type2) return true; - if (IS_BINARY_COMPATIBLE(type1, type2)) + if (TypeIsTextGroup(type1) && TypeIsTextGroup(type2)) + return true; + if (TypeIsInt4GroupA(type1) && TypeIsInt4GroupA(type2)) + return true; + if (TypeIsInt4GroupB(type1) && TypeIsInt4GroupB(type2)) + return true; + if (TypeIsInt4GroupC(type1) && TypeIsInt4GroupC(type2)) + return true; + if (TypeIsInetGroup(type1) && TypeIsInetGroup(type2)) + return true; + if (TypeIsBitGroup(type1) && TypeIsBitGroup(type2)) + return true; + return false; +} + + +bool +IsBinaryCompatible(Oid type1, Oid type2) +{ + if (DirectlyBinaryCompatible(type1, type2)) return true; /* * Perhaps the types are domains; if so, look at their base types @@ -580,9 +618,7 @@ IsBinaryCompatible(Oid type1, Oid type2) type1 = getBaseType(type1); if (OidIsValid(type2)) type2 = getBaseType(type2); - if (type1 == type2) - return true; - if (IS_BINARY_COMPATIBLE(type1, type2)) + if (DirectlyBinaryCompatible(type1, type2)) return true; return false; } @@ -627,7 +663,7 @@ PreferredType(CATEGORY category, Oid type) break; case (NUMERIC_TYPE): - if (type == OIDOID) + if (TypeIsOidGroup(type)) result = OIDOID; else if (type == NUMERICOID) result = NUMERICOID; diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c index 8a3dc4d5573..5c02e9b4bbb 100644 --- a/src/backend/parser/parse_node.c +++ b/src/backend/parser/parse_node.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.62 2002/04/16 23:08:11 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.63 2002/04/25 02:56:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -424,7 +424,8 @@ make_const(Value *value) break; case T_String: - val = DirectFunctionCall1(textin, CStringGetDatum(strVal(value))); + val = DirectFunctionCall1(unknownin, + CStringGetDatum(strVal(value))); typeid = UNKNOWNOID; /* will be coerced later */ typelen = -1; /* variable len */ diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c index fd28c8fae77..89be1bac96b 100644 --- a/src/backend/utils/adt/regproc.c +++ b/src/backend/utils/adt/regproc.c @@ -1,51 +1,74 @@ - /*------------------------------------------------------------------------- +/*------------------------------------------------------------------------- * * regproc.c - * Functions for the built-in type "RegProcedure". + * Functions for the built-in types regproc, regclass, regtype, etc. + * + * These types are all binary-compatible with type Oid, and rely on Oid + * for comparison and so forth. Their only interesting behavior is in + * special I/O conversion routines. + * * * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/regproc.c,v 1.65 2002/04/05 00:31:29 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/regproc.c,v 1.66 2002/04/25 02:56:55 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" +#include <ctype.h> + #include "access/genam.h" #include "access/heapam.h" #include "catalog/catname.h" #include "catalog/indexing.h" +#include "catalog/namespace.h" +#include "catalog/pg_operator.h" #include "catalog/pg_proc.h" +#include "catalog/pg_type.h" +#include "lib/stringinfo.h" #include "miscadmin.h" +#include "parser/parse_type.h" #include "utils/builtins.h" #include "utils/fmgroids.h" +#include "utils/lsyscache.h" #include "utils/syscache.h" +static List *stringToQualifiedNameList(const char *string, const char *caller); +static void parseNameAndArgTypes(const char *string, const char *caller, + bool allow_none, + List **names, int *nargs, Oid *argtypes); + + /***************************************************************************** * USER I/O ROUTINES * *****************************************************************************/ /* - * regprocin - converts "proname" or "proid" to proid + * regprocin - converts "proname" to proc OID * - * We need to accept an OID for cases where the name is ambiguous. + * We also accept a numeric OID, mostly for historical reasons. * - * proid of '-' signifies unknown, for consistency with regprocout + * '-' signifies unknown (OID 0). In all other cases, the input must + * match an existing pg_proc entry. */ Datum regprocin(PG_FUNCTION_ARGS) { char *pro_name_or_oid = PG_GETARG_CSTRING(0); RegProcedure result = InvalidOid; - int matches = 0; + List *names; + FuncCandidateList clist; - if (pro_name_or_oid[0] == '-' && pro_name_or_oid[1] == '\0') + /* '-' ? */ + if (strcmp(pro_name_or_oid, "-") == 0) PG_RETURN_OID(InvalidOid); + /* Numeric OID? */ if (pro_name_or_oid[0] >= '0' && pro_name_or_oid[0] <= '9' && strspn(pro_name_or_oid, "0123456789") == strlen(pro_name_or_oid)) @@ -59,13 +82,23 @@ regprocin(PG_FUNCTION_ARGS) 0, 0, 0); if (!RegProcedureIsValid(result)) elog(ERROR, "No procedure with oid %s", pro_name_or_oid); - matches = 1; + PG_RETURN_OID(result); } - else + + /* Else it's a name, possibly schema-qualified */ + + /* + * In bootstrap mode we assume the given name is not schema-qualified, + * and just search pg_proc for a unique match. This is needed for + * initializing other system catalogs (pg_namespace may not exist yet, + * and certainly there are no schemas other than pg_catalog). + */ + if (IsBootstrapProcessingMode()) { + int matches = 0; Relation hdesc; ScanKeyData skey[1]; - SysScanDesc funcscan; + SysScanDesc sysscan; HeapTuple tuple; ScanKeyEntryInitialize(&skey[0], 0x0, @@ -74,46 +107,192 @@ regprocin(PG_FUNCTION_ARGS) CStringGetDatum(pro_name_or_oid)); hdesc = heap_openr(ProcedureRelationName, AccessShareLock); + sysscan = systable_beginscan(hdesc, ProcedureNameNspIndex, true, + SnapshotNow, 1, skey); - funcscan = systable_beginscan(hdesc, ProcedureNameNspIndex, true, - SnapshotNow, 1, skey); - - while (HeapTupleIsValid(tuple = systable_getnext(funcscan))) + while (HeapTupleIsValid(tuple = systable_getnext(sysscan))) { result = (RegProcedure) tuple->t_data->t_oid; if (++matches > 1) break; } - systable_endscan(funcscan); - + systable_endscan(sysscan); heap_close(hdesc, AccessShareLock); + + if (matches == 0) + elog(ERROR, "No procedure with name %s", pro_name_or_oid); + else if (matches > 1) + elog(ERROR, "There is more than one procedure named %s", + pro_name_or_oid); + PG_RETURN_OID(result); } - if (matches > 1) - elog(ERROR, "There is more than one procedure named %s.\n\tSupply the pg_proc oid inside single quotes.", pro_name_or_oid); - else if (matches == 0) + /* + * Normal case: parse the name into components and see if it + * matches any pg_proc entries in the current search path. + */ + names = stringToQualifiedNameList(pro_name_or_oid, "regprocin"); + clist = FuncnameGetCandidates(names, -1); + + if (clist == NULL) elog(ERROR, "No procedure with name %s", pro_name_or_oid); + else if (clist->next != NULL) + elog(ERROR, "There is more than one procedure named %s", + pro_name_or_oid); + + result = clist->oid; PG_RETURN_OID(result); } /* - * regprocout - converts proid to "pro_name" + * regprocout - converts proc OID to "pro_name" */ Datum regprocout(PG_FUNCTION_ARGS) { RegProcedure proid = PG_GETARG_OID(0); - HeapTuple proctup; char *result; + HeapTuple proctup; + + if (proid == InvalidOid) + { + result = pstrdup("-"); + PG_RETURN_CSTRING(result); + } + + proctup = SearchSysCache(PROCOID, + ObjectIdGetDatum(proid), + 0, 0, 0); + + if (HeapTupleIsValid(proctup)) + { + Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup); + char *proname = NameStr(procform->proname); + + /* + * In bootstrap mode, skip the fancy namespace stuff and just + * return the proc name. (This path is only needed for debugging + * output anyway.) + */ + if (IsBootstrapProcessingMode()) + { + result = pstrdup(proname); + } + else + { + char *nspname; + FuncCandidateList clist; + + /* + * Would this proc be found (uniquely!) by regprocin? + * If not, qualify it. + */ + clist = FuncnameGetCandidates(makeList1(makeString(proname)), -1); + if (clist != NULL && clist->next == NULL && + clist->oid == proid) + nspname = NULL; + else + nspname = get_namespace_name(procform->pronamespace); + + result = quote_qualified_identifier(nspname, proname); + } + + ReleaseSysCache(proctup); + } + else + { + /* If OID doesn't match any pg_proc entry, return it numerically */ + result = (char *) palloc(NAMEDATALEN); + snprintf(result, NAMEDATALEN, "%u", proid); + } + + PG_RETURN_CSTRING(result); +} - result = (char *) palloc(NAMEDATALEN); + +/* + * regprocedurein - converts "proname(args)" to proc OID + * + * We also accept a numeric OID, mostly for historical reasons. + * + * '-' signifies unknown (OID 0). In all other cases, the input must + * match an existing pg_proc entry. + */ +Datum +regprocedurein(PG_FUNCTION_ARGS) +{ + char *pro_name_or_oid = PG_GETARG_CSTRING(0); + RegProcedure result = InvalidOid; + List *names; + int nargs; + Oid argtypes[FUNC_MAX_ARGS]; + FuncCandidateList clist; + + /* '-' ? */ + if (strcmp(pro_name_or_oid, "-") == 0) + PG_RETURN_OID(InvalidOid); + + /* Numeric OID? */ + if (pro_name_or_oid[0] >= '0' && + pro_name_or_oid[0] <= '9' && + strspn(pro_name_or_oid, "0123456789") == strlen(pro_name_or_oid)) + { + Oid searchOid; + + searchOid = DatumGetObjectId(DirectFunctionCall1(oidin, + CStringGetDatum(pro_name_or_oid))); + result = (RegProcedure) GetSysCacheOid(PROCOID, + ObjectIdGetDatum(searchOid), + 0, 0, 0); + if (!RegProcedureIsValid(result)) + elog(ERROR, "No procedure with oid %s", pro_name_or_oid); + PG_RETURN_OID(result); + } + + /* + * Else it's a name and arguments. Parse the name and arguments, + * look up potential matches in the current namespace search list, + * and scan to see which one exactly matches the given argument + * types. (There will not be more than one match.) + * + * XXX at present, this code will not work in bootstrap mode, hence this + * datatype cannot be used for any system column that needs to receive + * data during bootstrap. + */ + parseNameAndArgTypes(pro_name_or_oid, "regprocedurein", false, + &names, &nargs, argtypes); + + clist = FuncnameGetCandidates(names, nargs); + + for (; clist; clist = clist->next) + { + if (memcmp(clist->args, argtypes, nargs * sizeof(Oid)) == 0) + break; + } + + if (clist == NULL) + elog(ERROR, "No procedure with name %s", pro_name_or_oid); + + result = clist->oid; + + PG_RETURN_OID(result); +} + +/* + * regprocedureout - converts proc OID to "pro_name(args)" + */ +Datum +regprocedureout(PG_FUNCTION_ARGS) +{ + RegProcedure proid = PG_GETARG_OID(0); + char *result; + HeapTuple proctup; if (proid == InvalidOid) { - result[0] = '-'; - result[1] = '\0'; + result = pstrdup("-"); PG_RETURN_CSTRING(result); } @@ -123,38 +302,862 @@ regprocout(PG_FUNCTION_ARGS) if (HeapTupleIsValid(proctup)) { - char *s; + Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup); + char *proname = NameStr(procform->proname); + int nargs = procform->pronargs; + int i; + char *nspname; + FuncCandidateList clist; + StringInfoData buf; + + /* XXX no support here for bootstrap mode */ + + /* + * Would this proc be found (given the right args) by regprocedurein? + * If not, we need to qualify it. + */ + clist = FuncnameGetCandidates(makeList1(makeString(proname)), nargs); + + for (; clist; clist = clist->next) + { + if (memcmp(clist->args, procform->proargtypes, + nargs * sizeof(Oid)) == 0) + break; + } + + if (clist != NULL && clist->oid == proid) + nspname = NULL; + else + nspname = get_namespace_name(procform->pronamespace); + + initStringInfo(&buf); + + appendStringInfo(&buf, "%s(", + quote_qualified_identifier(nspname, proname)); + for (i = 0; i < nargs; i++) + { + appendStringInfo(&buf, "%s%s", + (i > 0) ? "," : "", + format_type_be(procform->proargtypes[i])); + } + + appendStringInfo(&buf, ")"); + + result = buf.data; - s = NameStr(((Form_pg_proc) GETSTRUCT(proctup))->proname); - StrNCpy(result, s, NAMEDATALEN); ReleaseSysCache(proctup); } else { - result[0] = '-'; - result[1] = '\0'; + /* If OID doesn't match any pg_proc entry, return it numerically */ + result = (char *) palloc(NAMEDATALEN); + snprintf(result, NAMEDATALEN, "%u", proid); + } + + PG_RETURN_CSTRING(result); +} + + +/* + * regoperin - converts "oprname" to operator OID + * + * We also accept a numeric OID, mostly for historical reasons. + * + * '0' signifies unknown (OID 0). In all other cases, the input must + * match an existing pg_operator entry. + */ +Datum +regoperin(PG_FUNCTION_ARGS) +{ + char *opr_name_or_oid = PG_GETARG_CSTRING(0); + Oid result = InvalidOid; + List *names; + FuncCandidateList clist; + + /* '0' ? */ + if (strcmp(opr_name_or_oid, "0") == 0) + PG_RETURN_OID(InvalidOid); + + /* Numeric OID? */ + if (opr_name_or_oid[0] >= '0' && + opr_name_or_oid[0] <= '9' && + strspn(opr_name_or_oid, "0123456789") == strlen(opr_name_or_oid)) + { + Oid searchOid; + + searchOid = DatumGetObjectId(DirectFunctionCall1(oidin, + CStringGetDatum(opr_name_or_oid))); + result = GetSysCacheOid(OPEROID, + ObjectIdGetDatum(searchOid), + 0, 0, 0); + if (!OidIsValid(result)) + elog(ERROR, "No operator with oid %s", opr_name_or_oid); + PG_RETURN_OID(result); + } + + /* Else it's a name, possibly schema-qualified */ + + /* + * In bootstrap mode we assume the given name is not schema-qualified, + * and just search pg_operator for a unique match. This is needed for + * initializing other system catalogs (pg_namespace may not exist yet, + * and certainly there are no schemas other than pg_catalog). + */ + if (IsBootstrapProcessingMode()) + { + int matches = 0; + Relation hdesc; + ScanKeyData skey[1]; + SysScanDesc sysscan; + HeapTuple tuple; + + ScanKeyEntryInitialize(&skey[0], 0x0, + (AttrNumber) Anum_pg_operator_oprname, + (RegProcedure) F_NAMEEQ, + CStringGetDatum(opr_name_or_oid)); + + hdesc = heap_openr(OperatorRelationName, AccessShareLock); + sysscan = systable_beginscan(hdesc, OperatorNameNspIndex, true, + SnapshotNow, 1, skey); + + while (HeapTupleIsValid(tuple = systable_getnext(sysscan))) + { + result = tuple->t_data->t_oid; + if (++matches > 1) + break; + } + + systable_endscan(sysscan); + heap_close(hdesc, AccessShareLock); + + if (matches == 0) + elog(ERROR, "No operator with name %s", opr_name_or_oid); + else if (matches > 1) + elog(ERROR, "There is more than one operator named %s", + opr_name_or_oid); + PG_RETURN_OID(result); + } + + /* + * Normal case: parse the name into components and see if it + * matches any pg_operator entries in the current search path. + */ + names = stringToQualifiedNameList(opr_name_or_oid, "regoperin"); + clist = OpernameGetCandidates(names, '\0'); + + if (clist == NULL) + elog(ERROR, "No operator with name %s", opr_name_or_oid); + else if (clist->next != NULL) + elog(ERROR, "There is more than one operator named %s", + opr_name_or_oid); + + result = clist->oid; + + PG_RETURN_OID(result); +} + +/* + * regoperout - converts operator OID to "opr_name" + */ +Datum +regoperout(PG_FUNCTION_ARGS) +{ + Oid oprid = PG_GETARG_OID(0); + char *result; + HeapTuple opertup; + + if (oprid == InvalidOid) + { + result = pstrdup("0"); + PG_RETURN_CSTRING(result); + } + + opertup = SearchSysCache(OPEROID, + ObjectIdGetDatum(oprid), + 0, 0, 0); + + if (HeapTupleIsValid(opertup)) + { + Form_pg_operator operform = (Form_pg_operator) GETSTRUCT(opertup); + char *oprname = NameStr(operform->oprname); + + /* + * In bootstrap mode, skip the fancy namespace stuff and just + * return the oper name. (This path is only needed for debugging + * output anyway.) + */ + if (IsBootstrapProcessingMode()) + { + result = pstrdup(oprname); + } + else + { + FuncCandidateList clist; + + /* + * Would this oper be found (uniquely!) by regoperin? + * If not, qualify it. + */ + clist = OpernameGetCandidates(makeList1(makeString(oprname)), + '\0'); + if (clist != NULL && clist->next == NULL && + clist->oid == oprid) + result = pstrdup(oprname); + else + { + const char *nspname; + + nspname = get_namespace_name(operform->oprnamespace); + nspname = quote_identifier(nspname); + result = (char *) palloc(strlen(nspname)+strlen(oprname)+2); + sprintf(result, "%s.%s", nspname, oprname); + } + } + + ReleaseSysCache(opertup); + } + else + { + /* If OID doesn't match any pg_operator entry, return it numerically */ + result = (char *) palloc(NAMEDATALEN); + snprintf(result, NAMEDATALEN, "%u", oprid); + } + + PG_RETURN_CSTRING(result); +} + + +/* + * regoperatorin - converts "oprname(args)" to operator OID + * + * We also accept a numeric OID, mostly for historical reasons. + * + * '0' signifies unknown (OID 0). In all other cases, the input must + * match an existing pg_operator entry. + */ +Datum +regoperatorin(PG_FUNCTION_ARGS) +{ + char *opr_name_or_oid = PG_GETARG_CSTRING(0); + Oid result = InvalidOid; + List *names; + int nargs; + Oid argtypes[FUNC_MAX_ARGS]; + char oprkind; + FuncCandidateList clist; + + /* '0' ? */ + if (strcmp(opr_name_or_oid, "0") == 0) + PG_RETURN_OID(InvalidOid); + + /* Numeric OID? */ + if (opr_name_or_oid[0] >= '0' && + opr_name_or_oid[0] <= '9' && + strspn(opr_name_or_oid, "0123456789") == strlen(opr_name_or_oid)) + { + Oid searchOid; + + searchOid = DatumGetObjectId(DirectFunctionCall1(oidin, + CStringGetDatum(opr_name_or_oid))); + result = GetSysCacheOid(OPEROID, + ObjectIdGetDatum(searchOid), + 0, 0, 0); + if (!OidIsValid(result)) + elog(ERROR, "No operator with oid %s", opr_name_or_oid); + PG_RETURN_OID(result); + } + + /* + * Else it's a name and arguments. Parse the name and arguments, + * look up potential matches in the current namespace search list, + * and scan to see which one exactly matches the given argument + * types. (There will not be more than one match.) + * + * XXX at present, this code will not work in bootstrap mode, hence this + * datatype cannot be used for any system column that needs to receive + * data during bootstrap. + */ + parseNameAndArgTypes(opr_name_or_oid, "regoperatorin", true, + &names, &nargs, argtypes); + if (nargs == 1) + elog(ERROR, "regoperatorin: use NONE to denote the missing argument of a unary operator"); + if (nargs != 2) + elog(ERROR, "regoperatorin: provide two argument types for operator"); + + if (argtypes[0] == InvalidOid) + oprkind = 'l'; + else if (argtypes[1] == InvalidOid) + oprkind = 'r'; + else + oprkind = 'b'; + + clist = OpernameGetCandidates(names, oprkind); + + for (; clist; clist = clist->next) + { + if (memcmp(clist->args, argtypes, 2 * sizeof(Oid)) == 0) + break; + } + + if (clist == NULL) + elog(ERROR, "No operator with name %s", opr_name_or_oid); + + result = clist->oid; + + PG_RETURN_OID(result); +} + +/* + * regoperatorout - converts operator OID to "opr_name(args)" + */ +Datum +regoperatorout(PG_FUNCTION_ARGS) +{ + Oid oprid = PG_GETARG_OID(0); + char *result; + HeapTuple opertup; + + if (oprid == InvalidOid) + { + result = pstrdup("0"); + PG_RETURN_CSTRING(result); + } + + opertup = SearchSysCache(OPEROID, + ObjectIdGetDatum(oprid), + 0, 0, 0); + + if (HeapTupleIsValid(opertup)) + { + Form_pg_operator operform = (Form_pg_operator) GETSTRUCT(opertup); + char *oprname = NameStr(operform->oprname); + char *nspname; + FuncCandidateList clist; + StringInfoData buf; + + /* XXX no support here for bootstrap mode */ + + /* + * Would this oper be found (given the right args) by regoperatorin? + * If not, we need to qualify it. + */ + clist = OpernameGetCandidates(makeList1(makeString(oprname)), + operform->oprkind); + + for (; clist; clist = clist->next) + { + if (clist->args[0] == operform->oprleft && + clist->args[1] == operform->oprright) + break; + } + + initStringInfo(&buf); + + if (clist == NULL || clist->oid != oprid) + { + nspname = get_namespace_name(operform->oprnamespace); + appendStringInfo(&buf, "%s.", + quote_identifier(nspname)); + } + + appendStringInfo(&buf, "%s(", oprname); + + if (operform->oprleft) + appendStringInfo(&buf, "%s,", + format_type_be(operform->oprleft)); + else + appendStringInfo(&buf, "NONE,"); + + if (operform->oprright) + appendStringInfo(&buf, "%s)", + format_type_be(operform->oprright)); + else + appendStringInfo(&buf, "NONE)"); + + result = buf.data; + + ReleaseSysCache(opertup); + } + else + { + /* If OID doesn't match any pg_operator entry, return it numerically */ + result = (char *) palloc(NAMEDATALEN); + snprintf(result, NAMEDATALEN, "%u", oprid); } PG_RETURN_CSTRING(result); } +/* + * regclassin - converts "classname" to class OID + * + * We also accept a numeric OID, mostly for historical reasons. + * + * '-' signifies unknown (OID 0). In all other cases, the input must + * match an existing pg_class entry. + */ +Datum +regclassin(PG_FUNCTION_ARGS) +{ + char *class_name_or_oid = PG_GETARG_CSTRING(0); + Oid result = InvalidOid; + List *names; + + /* '-' ? */ + if (strcmp(class_name_or_oid, "-") == 0) + PG_RETURN_OID(InvalidOid); + + /* Numeric OID? */ + if (class_name_or_oid[0] >= '0' && + class_name_or_oid[0] <= '9' && + strspn(class_name_or_oid, "0123456789") == strlen(class_name_or_oid)) + { + Oid searchOid; + + searchOid = DatumGetObjectId(DirectFunctionCall1(oidin, + CStringGetDatum(class_name_or_oid))); + result = GetSysCacheOid(RELOID, + ObjectIdGetDatum(searchOid), + 0, 0, 0); + if (!OidIsValid(result)) + elog(ERROR, "No class with oid %s", class_name_or_oid); + PG_RETURN_OID(result); + } + + /* Else it's a name, possibly schema-qualified */ + + /* + * In bootstrap mode we assume the given name is not schema-qualified, + * and just search pg_class for a match. This is needed for + * initializing other system catalogs (pg_namespace may not exist yet, + * and certainly there are no schemas other than pg_catalog). + */ + if (IsBootstrapProcessingMode()) + { + Relation hdesc; + ScanKeyData skey[1]; + SysScanDesc sysscan; + HeapTuple tuple; + + ScanKeyEntryInitialize(&skey[0], 0x0, + (AttrNumber) Anum_pg_class_relname, + (RegProcedure) F_NAMEEQ, + CStringGetDatum(class_name_or_oid)); + + hdesc = heap_openr(RelationRelationName, AccessShareLock); + sysscan = systable_beginscan(hdesc, ClassNameNspIndex, true, + SnapshotNow, 1, skey); + + if (HeapTupleIsValid(tuple = systable_getnext(sysscan))) + result = tuple->t_data->t_oid; + else + elog(ERROR, "No class with name %s", class_name_or_oid); + + /* We assume there can be only one match */ + + systable_endscan(sysscan); + heap_close(hdesc, AccessShareLock); + + PG_RETURN_OID(result); + } + + /* + * Normal case: parse the name into components and see if it + * matches any pg_class entries in the current search path. + */ + names = stringToQualifiedNameList(class_name_or_oid, "regclassin"); + + result = RangeVarGetRelid(makeRangeVarFromNameList(names), false); + + PG_RETURN_OID(result); +} + +/* + * regclassout - converts class OID to "class_name" + */ +Datum +regclassout(PG_FUNCTION_ARGS) +{ + Oid classid = PG_GETARG_OID(0); + char *result; + HeapTuple classtup; + + if (classid == InvalidOid) + { + result = pstrdup("-"); + PG_RETURN_CSTRING(result); + } + + classtup = SearchSysCache(RELOID, + ObjectIdGetDatum(classid), + 0, 0, 0); + + if (HeapTupleIsValid(classtup)) + { + Form_pg_class classform = (Form_pg_class) GETSTRUCT(classtup); + char *classname = NameStr(classform->relname); + + /* + * In bootstrap mode, skip the fancy namespace stuff and just + * return the class name. (This path is only needed for debugging + * output anyway.) + */ + if (IsBootstrapProcessingMode()) + { + result = pstrdup(classname); + } + else + { + char *nspname; + + /* + * Would this class be found by regclassin? + * If not, qualify it. + */ + if (RelnameGetRelid(classname) == classid) + nspname = NULL; + else + nspname = get_namespace_name(classform->relnamespace); + + result = quote_qualified_identifier(nspname, classname); + } + + ReleaseSysCache(classtup); + } + else + { + /* If OID doesn't match any pg_class entry, return it numerically */ + result = (char *) palloc(NAMEDATALEN); + snprintf(result, NAMEDATALEN, "%u", classid); + } + + PG_RETURN_CSTRING(result); +} + + +/* + * regtypein - converts "typename" to type OID + * + * We also accept a numeric OID, mostly for historical reasons. + * + * '-' signifies unknown (OID 0). In all other cases, the input must + * match an existing pg_type entry. + * + * In bootstrap mode the name must just equal some existing name in pg_type. + * In normal mode the type name can be specified using the full type syntax + * recognized by the parser; for example, DOUBLE PRECISION and INTEGER[] will + * work and be translated to the correct type names. (We ignore any typmod + * info generated by the parser, however.) + */ +Datum +regtypein(PG_FUNCTION_ARGS) +{ + char *typ_name_or_oid = PG_GETARG_CSTRING(0); + Oid result = InvalidOid; + int32 typmod; + + /* '-' ? */ + if (strcmp(typ_name_or_oid, "-") == 0) + PG_RETURN_OID(InvalidOid); + + /* Numeric OID? */ + if (typ_name_or_oid[0] >= '0' && + typ_name_or_oid[0] <= '9' && + strspn(typ_name_or_oid, "0123456789") == strlen(typ_name_or_oid)) + { + Oid searchOid; + + searchOid = DatumGetObjectId(DirectFunctionCall1(oidin, + CStringGetDatum(typ_name_or_oid))); + result = GetSysCacheOid(TYPEOID, + ObjectIdGetDatum(searchOid), + 0, 0, 0); + if (!OidIsValid(result)) + elog(ERROR, "No type with oid %s", typ_name_or_oid); + PG_RETURN_OID(result); + } + + /* Else it's a type name, possibly schema-qualified or decorated */ + + /* + * In bootstrap mode we assume the given name is not schema-qualified, + * and just search pg_type for a match. This is needed for + * initializing other system catalogs (pg_namespace may not exist yet, + * and certainly there are no schemas other than pg_catalog). + */ + if (IsBootstrapProcessingMode()) + { + Relation hdesc; + ScanKeyData skey[1]; + SysScanDesc sysscan; + HeapTuple tuple; + + ScanKeyEntryInitialize(&skey[0], 0x0, + (AttrNumber) Anum_pg_type_typname, + (RegProcedure) F_NAMEEQ, + CStringGetDatum(typ_name_or_oid)); + + hdesc = heap_openr(TypeRelationName, AccessShareLock); + sysscan = systable_beginscan(hdesc, TypeNameNspIndex, true, + SnapshotNow, 1, skey); + + if (HeapTupleIsValid(tuple = systable_getnext(sysscan))) + result = tuple->t_data->t_oid; + else + elog(ERROR, "No type with name %s", typ_name_or_oid); + + /* We assume there can be only one match */ + + systable_endscan(sysscan); + heap_close(hdesc, AccessShareLock); + + PG_RETURN_OID(result); + } + + /* + * Normal case: invoke the full parser to deal with special cases + * such as array syntax. + */ + parseTypeString(typ_name_or_oid, &result, &typmod); + + PG_RETURN_OID(result); +} + +/* + * regtypeout - converts type OID to "typ_name" + */ +Datum +regtypeout(PG_FUNCTION_ARGS) +{ + Oid typid = PG_GETARG_OID(0); + char *result; + HeapTuple typetup; + + if (typid == InvalidOid) + { + result = pstrdup("-"); + PG_RETURN_CSTRING(result); + } + + typetup = SearchSysCache(TYPEOID, + ObjectIdGetDatum(typid), + 0, 0, 0); + + if (HeapTupleIsValid(typetup)) + { + Form_pg_type typeform = (Form_pg_type) GETSTRUCT(typetup); + char *typname = NameStr(typeform->typname); + + /* + * In bootstrap mode, skip the fancy namespace stuff and just + * return the type name. (This path is only needed for debugging + * output anyway.) + */ + if (IsBootstrapProcessingMode()) + { + result = pstrdup(typname); + } + else + { + char *nspname; + + /* + * Would this type be found by regtypein? + * If not, qualify it. + * + * XXX shouldn't we use format_type instead? + */ + if (TypenameGetTypid(typname) == typid) + nspname = NULL; + else + nspname = get_namespace_name(typeform->typnamespace); + + result = quote_qualified_identifier(nspname, typname); + } + + ReleaseSysCache(typetup); + } + else + { + /* If OID doesn't match any pg_type entry, return it numerically */ + result = (char *) palloc(NAMEDATALEN); + snprintf(result, NAMEDATALEN, "%u", typid); + } + + PG_RETURN_CSTRING(result); +} + /***************************************************************************** - * PUBLIC ROUTINES * + * SUPPORT ROUTINES * *****************************************************************************/ -/* regproctooid() - * Lowercase version of RegprocToOid() to allow case-insensitive SQL. - * Define RegprocToOid() as a macro in builtins.h. - * Referenced in pg_proc.h. - tgl 97/04/26 +/* + * Given a C string, parse it into a qualified-name list. */ -Datum -regproctooid(PG_FUNCTION_ARGS) +static List * +stringToQualifiedNameList(const char *string, const char *caller) { - RegProcedure rp = PG_GETARG_OID(0); + char *rawname; + List *result = NIL; + List *namelist; + List *l; + + /* We need a modifiable copy of the input string. */ + rawname = pstrdup(string); + + if (!SplitIdentifierString(rawname, '.', &namelist)) + elog(ERROR, "%s: invalid name syntax", caller); + + if (namelist == NIL) + elog(ERROR, "%s: invalid name syntax", caller); + + foreach(l, namelist) + { + char *curname = (char *) lfirst(l); - PG_RETURN_OID((Oid) rp); + result = lappend(result, makeString(pstrdup(curname))); + } + + pfree(rawname); + freeList(namelist); + + return result; } -/* (see int.c for comparison/operation routines) */ +/* + * Given a C string, parse it into a qualified function or operator name + * followed by a parenthesized list of type names. Reduce the + * type names to an array of OIDs (returned into *nargs and *argtypes; + * the argtypes array should be of size FUNC_MAX_ARGS). The function or + * operator name is returned to *names as a List of Strings. + * + * NONE is accepted as a placeholder for OID 0 if allow_none is true. + */ +static void +parseNameAndArgTypes(const char *string, const char *caller, bool allow_none, + List **names, int *nargs, Oid *argtypes) +{ + char *rawname; + char *ptr; + char *ptr2; + char *typename; + bool in_quote; + bool had_comma; + int paren_count; + Oid typeid; + int32 typmod; + + /* We need a modifiable copy of the input string. */ + rawname = pstrdup(string); + + /* Scan to find the expected left paren; mustn't be quoted */ + in_quote = false; + for (ptr = rawname; *ptr; ptr++) + { + if (*ptr == '"') + in_quote = !in_quote; + else if (*ptr == '(' && !in_quote) + break; + } + if (*ptr == '\0') + elog(ERROR, "%s: expected a left parenthesis", caller); + + /* Separate the name and parse it into a list */ + *ptr++ = '\0'; + *names = stringToQualifiedNameList(rawname, caller); + + /* Check for the trailing right parenthesis and remove it */ + ptr2 = ptr + strlen(ptr); + while (--ptr2 > ptr) + { + if (!isspace((unsigned char) *ptr2)) + break; + } + if (*ptr2 != ')') + elog(ERROR, "%s: expected a right parenthesis", caller); + *ptr2 = '\0'; + + /* Separate the remaining string into comma-separated type names */ + *nargs = 0; + had_comma = false; + + for (;;) + { + /* allow leading whitespace */ + while (isspace((unsigned char) *ptr)) + ptr++; + if (*ptr == '\0') + { + /* End of string. Okay unless we had a comma before. */ + if (had_comma) + elog(ERROR, "%s: expected a type name", caller); + break; + } + typename = ptr; + /* Find end of type name --- end of string or comma */ + /* ... but not a quoted or parenthesized comma */ + in_quote = false; + paren_count = 0; + for (; *ptr; ptr++) + { + if (*ptr == '"') + in_quote = !in_quote; + else if (*ptr == ',' && !in_quote && paren_count == 0) + break; + else if (!in_quote) + { + switch (*ptr) + { + case '(': + case '[': + paren_count++; + break; + case ')': + case ']': + paren_count--; + break; + } + } + } + if (in_quote || paren_count != 0) + elog(ERROR, "%s: improper type name", caller); + ptr2 = ptr; + if (*ptr == ',') + { + had_comma = true; + *ptr++ = '\0'; + } + else + { + had_comma = false; + Assert(*ptr == '\0'); + } + /* Lop off trailing whitespace */ + while (--ptr2 >= typename) + { + if (!isspace((unsigned char) *ptr2)) + break; + *ptr2 = '\0'; + } + + if (allow_none && strcasecmp(typename, "none") == 0) + { + /* Report NONE as OID 0 */ + typeid = InvalidOid; + typmod = -1; + } + else + { + /* Use full parser to resolve the type name */ + parseTypeString(typename, &typeid, &typmod); + } + if (*nargs >= FUNC_MAX_ARGS) + elog(ERROR, "%s: too many argument datatypes", caller); + argtypes[*nargs] = typeid; + (*nargs)++; + } + + pfree(rawname); +} diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index b99c886d8d1..580abead5d0 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -3,7 +3,7 @@ * back to source text * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.98 2002/04/19 23:13:54 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.99 2002/04/25 02:56:55 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -123,7 +123,7 @@ static void get_delete_query_def(Query *query, deparse_context *context); static void get_utility_query_def(Query *query, deparse_context *context); static void get_basic_select_query(Query *query, deparse_context *context); static void get_setop_query(Node *setOp, Query *query, - deparse_context *context, bool toplevel); + deparse_context *context); static void get_rule_sortgroupclause(SortClause *srt, List *tlist, bool force_colno, deparse_context *context); @@ -142,7 +142,6 @@ static void get_from_clause_item(Node *jtnode, Query *query, static void get_opclass_name(Oid opclass, Oid actual_datatype, StringInfo buf); static bool tleIsArrayAssign(TargetEntry *tle); -static char *quote_identifier(char *ident); static char *get_relid_attribute_name(Oid relid, AttrNumber attnum); #define only_marker(rte) ((rte)->inh ? "" : "ONLY ") @@ -1109,7 +1108,7 @@ get_select_query_def(Query *query, deparse_context *context) */ if (query->setOperations) { - get_setop_query(query->setOperations, query, context, true); + get_setop_query(query->setOperations, query, context); /* ORDER BY clauses must be simple in this case */ force_colno = true; } @@ -1266,8 +1265,7 @@ get_basic_select_query(Query *query, deparse_context *context) } static void -get_setop_query(Node *setOp, Query *query, deparse_context *context, - bool toplevel) +get_setop_query(Node *setOp, Query *query, deparse_context *context) { StringInfo buf = context->buf; @@ -1284,33 +1282,29 @@ get_setop_query(Node *setOp, Query *query, deparse_context *context, { SetOperationStmt *op = (SetOperationStmt *) setOp; - /* - * Must suppress parens at top level of a setop tree because of - * grammar limitations... - */ - if (!toplevel) - appendStringInfo(buf, "("); - get_setop_query(op->larg, query, context, false); + appendStringInfo(buf, "(("); + get_setop_query(op->larg, query, context); switch (op->op) { case SETOP_UNION: - appendStringInfo(buf, " UNION "); + appendStringInfo(buf, ") UNION "); break; case SETOP_INTERSECT: - appendStringInfo(buf, " INTERSECT "); + appendStringInfo(buf, ") INTERSECT "); break; case SETOP_EXCEPT: - appendStringInfo(buf, " EXCEPT "); + appendStringInfo(buf, ") EXCEPT "); break; default: elog(ERROR, "get_setop_query: unexpected set op %d", (int) op->op); } if (op->all) - appendStringInfo(buf, "ALL "); - get_setop_query(op->rarg, query, context, false); - if (!toplevel) - appendStringInfo(buf, ")"); + appendStringInfo(buf, "ALL ("); + else + appendStringInfo(buf, "("); + get_setop_query(op->rarg, query, context); + appendStringInfo(buf, "))"); } else { @@ -2585,8 +2579,8 @@ tleIsArrayAssign(TargetEntry *tle) * space-wasteful but well worth it for notational simplicity. * ---------- */ -static char * -quote_identifier(char *ident) +const char * +quote_identifier(const char *ident) { /* * Can avoid quoting if ident starts with a lowercase letter and @@ -2603,7 +2597,7 @@ quote_identifier(char *ident) safe = (ident[0] >= 'a' && ident[0] <= 'z'); if (safe) { - char *ptr; + const char *ptr; for (ptr = ident + 1; *ptr; ptr++) { @@ -2628,7 +2622,7 @@ quote_identifier(char *ident) * Note: ScanKeywordLookup() does case-insensitive comparison, but * that's fine, since we already know we have all-lower-case. */ - if (ScanKeywordLookup(ident) != NULL) + if (ScanKeywordLookup((char *) ident) != NULL) safe = false; } @@ -2641,6 +2635,26 @@ quote_identifier(char *ident) } /* ---------- + * quote_qualified_identifier - Quote a possibly-qualified identifier + * + * Return a name of the form namespace.ident, or just ident if namespace + * is NULL, quoting each component if necessary. The result is palloc'd. + * ---------- + */ +char * +quote_qualified_identifier(const char *namespace, + const char *ident) +{ + StringInfoData buf; + + initStringInfo(&buf); + if (namespace) + appendStringInfo(&buf, "%s.", quote_identifier(namespace)); + appendStringInfo(&buf, "%s", quote_identifier(ident)); + return buf.data; +} + +/* ---------- * get_relid_attribute_name * Get an attribute name by its relations Oid and its attnum * diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index d045705917d..8294612e137 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -15,7 +15,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.109 2002/04/21 19:48:13 thomas Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.110 2002/04/25 02:56:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1991,6 +1991,11 @@ convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue, case NUMERICOID: case OIDOID: case REGPROCOID: + case REGPROCEDUREOID: + case REGOPEROID: + case REGOPERATOROID: + case REGCLASSOID: + case REGTYPEOID: *scaledvalue = convert_numeric_to_scalar(value, valuetypid); *scaledlobound = convert_numeric_to_scalar(lobound, boundstypid); *scaledhibound = convert_numeric_to_scalar(hibound, boundstypid); @@ -2088,6 +2093,11 @@ convert_numeric_to_scalar(Datum value, Oid typid) value)); case OIDOID: case REGPROCOID: + case REGPROCEDUREOID: + case REGOPEROID: + case REGOPERATOROID: + case REGCLASSOID: + case REGTYPEOID: /* we can treat OIDs as integers... */ return (double) DatumGetObjectId(value); } diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c index e7ac4b30b59..5c7aaf64b05 100644 --- a/src/backend/utils/adt/varlena.c +++ b/src/backend/utils/adt/varlena.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.84 2002/04/24 02:12:53 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.85 2002/04/25 02:56:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -21,6 +21,13 @@ #include "utils/builtins.h" #include "utils/pg_locale.h" + +typedef struct varlena unknown; + +#define DatumGetUnknownP(X) ((unknown *) PG_DETOAST_DATUM(X)) +#define PG_GETARG_UNKNOWN_P(n) DatumGetUnknownP(PG_GETARG_DATUM(n)) +#define PG_RETURN_UNKNOWN_P(x) PG_RETURN_POINTER(x) + static int text_cmp(text *arg1, text *arg2); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index efcb65dbfdf..a028d091c9b 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.94 2002/04/06 06:59:22 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.95 2002/04/25 02:56:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -136,8 +136,13 @@ GetCCHashFunc(Oid keytype) return hashint4; case TEXTOID: return hashvarlena; - case REGPROCOID: case OIDOID: + case REGPROCOID: + case REGPROCEDUREOID: + case REGOPEROID: + case REGOPERATOROID: + case REGCLASSOID: + case REGTYPEOID: return hashoid; case OIDVECTOROID: return hashoidvector; diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 67e602fc5a6..2f163f63762 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -3,7 +3,7 @@ * * Copyright 2000 by PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/bin/psql/describe.c,v 1.52 2002/04/24 06:17:04 petere Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/describe.c,v 1.53 2002/04/25 02:56:56 tgl Exp $ */ #include "postgres_fe.h" #include "describe.h" @@ -361,7 +361,7 @@ objectDescription(const char *object) /* Operator descriptions (must get comment via associated function) */ "UNION ALL\n" - " SELECT RegprocToOid(o.oprcode) as oid,\n" + " SELECT CAST(o.oprcode AS oid) as oid,\n" " (SELECT oid FROM pg_class WHERE relname = 'pg_proc') as tableoid,\n" " CAST(o.oprname AS text) as name, CAST('%s' AS text) as object\n" " FROM pg_operator o\n" diff --git a/src/include/c.h b/src/include/c.h index 40abf99aecf..0a8a129c02c 100644 --- a/src/include/c.h +++ b/src/include/c.h @@ -12,7 +12,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: c.h,v 1.118 2002/04/24 02:12:53 momjian Exp $ + * $Id: c.h,v 1.119 2002/04/25 02:56:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -361,9 +361,12 @@ typedef double float8; /* typedef Oid is in postgres_ext.h */ -/* unfortunately, both regproc and RegProcedure are used */ +/* + * regproc is the type name used in the include/catalog headers, but + * RegProcedure is the preferred name in C code. + */ typedef Oid regproc; -typedef Oid RegProcedure; +typedef regproc RegProcedure; typedef uint32 TransactionId; @@ -404,7 +407,6 @@ struct varlena */ typedef struct varlena bytea; typedef struct varlena text; -typedef struct varlena unknown; typedef struct varlena BpChar; /* blank-padded char, ie SQL char(n) */ typedef struct varlena VarChar; /* var-length char, ie SQL varchar(n) */ diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index e2409137bc0..28893a9f54a 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: catversion.h,v 1.125 2002/04/24 05:23:14 momjian Exp $ + * $Id: catversion.h,v 1.126 2002/04/25 02:56:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200204241 +#define CATALOG_VERSION_NO 200204242 #endif diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h index 148fc210a88..6509e61a736 100644 --- a/src/include/catalog/namespace.h +++ b/src/include/catalog/namespace.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: namespace.h,v 1.9 2002/04/17 20:57:56 tgl Exp $ + * $Id: namespace.h,v 1.10 2002/04/25 02:56:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -22,13 +22,13 @@ * found by namespace lookup. Each function/operator is identified * by OID and by argument types; the list must be pruned by type * resolution rules that are embodied in the parser, not here. - * The number of arguments is assumed to be known a priori. */ typedef struct _FuncCandidateList { struct _FuncCandidateList *next; int pathpos; /* for internal use of namespace lookup */ Oid oid; /* the function or operator's OID */ + int nargs; /* number of arg types returned */ Oid args[1]; /* arg types --- VARIABLE LENGTH ARRAY */ } *FuncCandidateList; /* VARIABLE LENGTH STRUCT */ diff --git a/src/include/catalog/pg_namespace.h b/src/include/catalog/pg_namespace.h index 3b7f7d57b6d..9b1e2e7a926 100644 --- a/src/include/catalog/pg_namespace.h +++ b/src/include/catalog/pg_namespace.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_namespace.h,v 1.5 2002/04/21 00:26:43 tgl Exp $ + * $Id: pg_namespace.h,v 1.6 2002/04/25 02:56:56 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -72,9 +72,9 @@ DESCR("System catalog namespace"); DATA(insert OID = 99 ( "pg_toast" PGUID "{=}" )); DESCR("Reserved namespace for TOAST tables"); #define PG_TOAST_NAMESPACE 99 -DATA(insert OID = 2071 ( "public" PGUID "{=UC}" )); +DATA(insert OID = 2200 ( "public" PGUID "{=UC}" )); DESCR("Standard public namespace"); -#define PG_PUBLIC_NAMESPACE 2071 +#define PG_PUBLIC_NAMESPACE 2200 /* diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h index 90484c95c8b..d64daa321f2 100644 --- a/src/include/catalog/pg_operator.h +++ b/src/include/catalog/pg_operator.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_operator.h,v 1.103 2002/04/21 19:48:23 thomas Exp $ + * $Id: pg_operator.h,v 1.104 2002/04/25 02:56:56 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -847,6 +847,33 @@ DATA(insert OID = 2066 ( "+" PGNSP PGUID 0 b t f 1114 1186 1114 0 0 0 0 0 DATA(insert OID = 2067 ( "-" PGNSP PGUID 0 b t f 1114 1114 1186 0 0 0 0 0 0 timestamp_mi - - )); DATA(insert OID = 2068 ( "-" PGNSP PGUID 0 b t f 1114 1186 1114 0 0 0 0 0 0 timestamp_mi_span - - )); +/* array equality operators */ +DATA(insert OID = 2222 ( "=" PGNSP PGUID 0 b t f 2207 2207 16 2222 0 0 0 0 0 array_eq eqsel eqjoinsel )); +DATA(insert OID = 2223 ( "=" PGNSP PGUID 0 b t f 2208 2208 16 2223 0 0 0 0 0 array_eq eqsel eqjoinsel )); +DATA(insert OID = 2224 ( "=" PGNSP PGUID 0 b t f 2209 2209 16 2224 0 0 0 0 0 array_eq eqsel eqjoinsel )); +DATA(insert OID = 2225 ( "=" PGNSP PGUID 0 b t f 2210 2210 16 2225 0 0 0 0 0 array_eq eqsel eqjoinsel )); +DATA(insert OID = 2226 ( "=" PGNSP PGUID 0 b t f 2211 2211 16 2226 0 0 0 0 0 array_eq eqsel eqjoinsel )); + +DATA(insert OID = 2227 ( "=" PGNSP PGUID 0 b t f 629 629 16 2227 0 0 0 0 0 array_eq eqsel eqjoinsel )); +DATA(insert OID = 2228 ( "=" PGNSP PGUID 0 b t f 651 651 16 2228 0 0 0 0 0 array_eq eqsel eqjoinsel )); +DATA(insert OID = 2229 ( "=" PGNSP PGUID 0 b t f 719 719 16 2229 0 0 0 0 0 array_eq eqsel eqjoinsel )); +DATA(insert OID = 2230 ( "=" PGNSP PGUID 0 b t f 791 791 16 2230 0 0 0 0 0 array_eq eqsel eqjoinsel )); +DATA(insert OID = 2231 ( "=" PGNSP PGUID 0 b t f 1014 1014 16 2231 0 0 0 0 0 array_eq eqsel eqjoinsel )); +DATA(insert OID = 2232 ( "=" PGNSP PGUID 0 b t f 1015 1015 16 2232 0 0 0 0 0 array_eq eqsel eqjoinsel )); +DATA(insert OID = 2233 ( "=" PGNSP PGUID 0 b t f 1016 1016 16 2233 0 0 0 0 0 array_eq eqsel eqjoinsel )); +DATA(insert OID = 2234 ( "=" PGNSP PGUID 0 b t f 1040 1040 16 2234 0 0 0 0 0 array_eq eqsel eqjoinsel )); +DATA(insert OID = 2235 ( "=" PGNSP PGUID 0 b t f 1041 1041 16 2235 0 0 0 0 0 array_eq eqsel eqjoinsel )); +DATA(insert OID = 2236 ( "=" PGNSP PGUID 0 b t f 1115 1115 16 2236 0 0 0 0 0 array_eq eqsel eqjoinsel )); +DATA(insert OID = 2237 ( "=" PGNSP PGUID 0 b t f 1182 1182 16 2237 0 0 0 0 0 array_eq eqsel eqjoinsel )); +DATA(insert OID = 2238 ( "=" PGNSP PGUID 0 b t f 1183 1183 16 2238 0 0 0 0 0 array_eq eqsel eqjoinsel )); +DATA(insert OID = 2239 ( "=" PGNSP PGUID 0 b t f 1185 1185 16 2239 0 0 0 0 0 array_eq eqsel eqjoinsel )); +DATA(insert OID = 2240 ( "=" PGNSP PGUID 0 b t f 1187 1187 16 2240 0 0 0 0 0 array_eq eqsel eqjoinsel )); +DATA(insert OID = 2241 ( "=" PGNSP PGUID 0 b t f 1231 1231 16 2241 0 0 0 0 0 array_eq eqsel eqjoinsel )); +DATA(insert OID = 2242 ( "=" PGNSP PGUID 0 b t f 1270 1270 16 2242 0 0 0 0 0 array_eq eqsel eqjoinsel )); +DATA(insert OID = 2243 ( "=" PGNSP PGUID 0 b t f 1561 1561 16 2243 0 0 0 0 0 array_eq eqsel eqjoinsel )); +DATA(insert OID = 2244 ( "=" PGNSP PGUID 0 b t f 1563 1563 16 2244 0 0 0 0 0 array_eq eqsel eqjoinsel )); +DATA(insert OID = 2245 ( "=" PGNSP PGUID 0 b t f 2201 2201 16 2245 0 0 0 0 0 array_eq eqsel eqjoinsel )); + /* * function prototypes diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 3e8f0e22691..8fffef5af1a 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_proc.h,v 1.232 2002/04/24 05:22:20 momjian Exp $ + * $Id: pg_proc.h,v 1.233 2002/04/25 02:56:56 tgl Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -1216,8 +1216,6 @@ DESCR("closest point to line on box"); DATA(insert OID = 964 ( lo_unlink PGNSP PGUID 12 f t f t f v 1 23 "26" 100 0 0 100 lo_unlink - _null_ )); DESCR("large object unlink(delete)"); -DATA(insert OID = 972 ( regproctooid PGNSP PGUID 12 f t f t f i 1 26 "24" 100 0 0 100 regproctooid - _null_ )); -DESCR("get oid for regproc"); DATA(insert OID = 973 ( path_inter PGNSP PGUID 12 f t f t f i 2 16 "602 602" 100 0 0 100 path_inter - _null_ )); DESCR("paths intersect?"); @@ -2930,6 +2928,28 @@ DATA(insert OID = 2158 ( stddev PGNSP PGUID 12 t t f f f i 1 701 "701" 100 0 DATA(insert OID = 2159 ( stddev PGNSP PGUID 12 t t f f f i 1 1700 "1700" 100 0 0 100 aggregate_dummy - _null_ )); +DATA(insert OID = 2212 ( regprocedurein PGNSP PGUID 12 f t f t f s 1 2202 "0" 100 0 0 100 regprocedurein - _null_ )); +DESCR("(internal)"); +DATA(insert OID = 2213 ( regprocedureout PGNSP PGUID 12 f t f t f s 1 23 "0" 100 0 0 100 regprocedureout - _null_ )); +DESCR("(internal)"); +DATA(insert OID = 2214 ( regoperin PGNSP PGUID 12 f t f t f s 1 2203 "0" 100 0 0 100 regoperin - _null_ )); +DESCR("(internal)"); +DATA(insert OID = 2215 ( regoperout PGNSP PGUID 12 f t f t f s 1 23 "0" 100 0 0 100 regoperout - _null_ )); +DESCR("(internal)"); +DATA(insert OID = 2216 ( regoperatorin PGNSP PGUID 12 f t f t f s 1 2204 "0" 100 0 0 100 regoperatorin - _null_ )); +DESCR("(internal)"); +DATA(insert OID = 2217 ( regoperatorout PGNSP PGUID 12 f t f t f s 1 23 "0" 100 0 0 100 regoperatorout - _null_ )); +DESCR("(internal)"); +DATA(insert OID = 2218 ( regclassin PGNSP PGUID 12 f t f t f s 1 2205 "0" 100 0 0 100 regclassin - _null_ )); +DESCR("(internal)"); +DATA(insert OID = 2219 ( regclassout PGNSP PGUID 12 f t f t f s 1 23 "0" 100 0 0 100 regclassout - _null_ )); +DESCR("(internal)"); +DATA(insert OID = 2220 ( regtypein PGNSP PGUID 12 f t f t f s 1 2206 "0" 100 0 0 100 regtypein - _null_ )); +DESCR("(internal)"); +DATA(insert OID = 2221 ( regtypeout PGNSP PGUID 12 f t f t f s 1 23 "0" 100 0 0 100 regtypeout - _null_ )); +DESCR("(internal)"); + + /* * Symbolic values for provolatile column: these indicate whether the result * of a function is dependent *only* on the values of its explicit arguments, diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h index f6d0de6f1cf..d67e3434692 100644 --- a/src/include/catalog/pg_type.h +++ b/src/include/catalog/pg_type.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_type.h,v 1.121 2002/04/24 02:12:53 momjian Exp $ + * $Id: pg_type.h,v 1.122 2002/04/25 02:56:56 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -274,7 +274,7 @@ DATA(insert OID = 23 ( int4 PGNSP PGUID 4 10 t b t \054 0 0 int4in int4ou DESCR("-2 billion to 2 billion integer, 4-byte storage"); #define INT4OID 23 -DATA(insert OID = 24 ( regproc PGNSP PGUID 4 16 t b t \054 0 0 regprocin regprocout regprocin regprocout i p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 24 ( regproc PGNSP PGUID 4 -1 t b t \054 0 0 regprocin regprocout regprocin regprocout i p f 0 -1 0 _null_ _null_ )); DESCR("registered procedure"); #define REGPROCOID 24 @@ -444,7 +444,7 @@ DESCR("hh:mm:ss, ANSI SQL time"); DATA(insert OID = 1114 ( timestamp PGNSP PGUID 8 47 f b t \054 0 0 timestamp_in timestamp_out timestamp_in timestamp_out d p f 0 -1 0 _null_ _null_ )); DESCR("date and time"); #define TIMESTAMPOID 1114 -DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 -1 f b t \054 0 1184 array_in array_out array_in array_out d x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 -1 f b t \054 0 1114 array_in array_out array_in array_out d x f 0 -1 0 _null_ _null_ )); DATA(insert OID = 1182 ( _date PGNSP PGUID -1 -1 f b t \054 0 1082 array_in array_out array_in array_out i x f 0 -1 0 _null_ _null_ )); DATA(insert OID = 1183 ( _time PGNSP PGUID -1 -1 f b t \054 0 1083 array_in array_out array_in array_out d x f 0 -1 0 _null_ _null_ )); DATA(insert OID = 1184 ( timestamptz PGNSP PGUID 8 47 f b t \054 0 0 timestamptz_in timestamptz_out timestamptz_in timestamptz_out d p f 0 -1 0 _null_ _null_ )); @@ -484,8 +484,34 @@ DATA(insert OID = 1790 ( refcursor PGNSP PGUID -1 -1 f b t \054 0 0 textin DESCR("reference cursor (portal name)"); #define REFCURSOROID 1790 -/* OIDS 2000 - 2099 */ -DATA(insert OID = 2019 ( _refcursor PGNSP PGUID -1 -1 f b t \054 0 1790 array_in array_out array_in array_out i x f 0 -1 0 _null_ _null_ )); +/* OIDS 2200 - 2299 */ +DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 -1 f b t \054 0 1790 array_in array_out array_in array_out i x f 0 -1 0 _null_ _null_ )); + +DATA(insert OID = 2202 ( regprocedure PGNSP PGUID 4 -1 t b t \054 0 0 regprocedurein regprocedureout regprocedurein regprocedureout i p f 0 -1 0 _null_ _null_ )); +DESCR("registered procedure (with args)"); +#define REGPROCEDUREOID 2202 + +DATA(insert OID = 2203 ( regoper PGNSP PGUID 4 -1 t b t \054 0 0 regoperin regoperout regoperin regoperout i p f 0 -1 0 _null_ _null_ )); +DESCR("registered operator"); +#define REGOPEROID 2203 + +DATA(insert OID = 2204 ( regoperator PGNSP PGUID 4 -1 t b t \054 0 0 regoperatorin regoperatorout regoperatorin regoperatorout i p f 0 -1 0 _null_ _null_ )); +DESCR("registered operator (with args)"); +#define REGOPERATOROID 2204 + +DATA(insert OID = 2205 ( regclass PGNSP PGUID 4 -1 t b t \054 0 0 regclassin regclassout regclassin regclassout i p f 0 -1 0 _null_ _null_ )); +DESCR("registered class"); +#define REGCLASSOID 2205 + +DATA(insert OID = 2206 ( regtype PGNSP PGUID 4 -1 t b t \054 0 0 regtypein regtypeout regtypein regtypeout i p f 0 -1 0 _null_ _null_ )); +DESCR("registered type"); +#define REGTYPEOID 2206 + +DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 -1 f b t \054 0 2202 array_in array_out array_in array_out i x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 -1 f b t \054 0 2203 array_in array_out array_in array_out i x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 -1 f b t \054 0 2204 array_in array_out array_in array_out i x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 -1 f b t \054 0 2205 array_in array_out array_in array_out i x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 -1 f b t \054 0 2206 array_in array_out array_in array_out i x f 0 -1 0 _null_ _null_ )); /* diff --git a/src/include/fmgr.h b/src/include/fmgr.h index 6280ff294a7..f5d0b0c1ef3 100644 --- a/src/include/fmgr.h +++ b/src/include/fmgr.h @@ -11,7 +11,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: fmgr.h,v 1.20 2002/04/24 02:12:53 momjian Exp $ + * $Id: fmgr.h,v 1.21 2002/04/25 02:56:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -185,7 +185,6 @@ extern struct varlena *pg_detoast_datum_slice(struct varlena * datum, /* DatumGetFoo macros for varlena types will typically look like this: */ #define DatumGetByteaP(X) ((bytea *) PG_DETOAST_DATUM(X)) #define DatumGetTextP(X) ((text *) PG_DETOAST_DATUM(X)) -#define DatumGetUnknownP(X) ((unknown *) PG_DETOAST_DATUM(X)) #define DatumGetBpCharP(X) ((BpChar *) PG_DETOAST_DATUM(X)) #define DatumGetVarCharP(X) ((VarChar *) PG_DETOAST_DATUM(X)) /* And we also offer variants that return an OK-to-write copy */ @@ -201,7 +200,6 @@ extern struct varlena *pg_detoast_datum_slice(struct varlena * datum, /* GETARG macros for varlena types will typically look like this: */ #define PG_GETARG_BYTEA_P(n) DatumGetByteaP(PG_GETARG_DATUM(n)) #define PG_GETARG_TEXT_P(n) DatumGetTextP(PG_GETARG_DATUM(n)) -#define PG_GETARG_UNKNOWN_P(n) DatumGetUnknownP(PG_GETARG_DATUM(n)) #define PG_GETARG_BPCHAR_P(n) DatumGetBpCharP(PG_GETARG_DATUM(n)) #define PG_GETARG_VARCHAR_P(n) DatumGetVarCharP(PG_GETARG_DATUM(n)) /* And we also offer variants that return an OK-to-write copy */ @@ -241,7 +239,6 @@ extern struct varlena *pg_detoast_datum_slice(struct varlena * datum, /* RETURN macros for other pass-by-ref types will typically look like this: */ #define PG_RETURN_BYTEA_P(x) PG_RETURN_POINTER(x) #define PG_RETURN_TEXT_P(x) PG_RETURN_POINTER(x) -#define PG_RETURN_UNKNOWN_P(x) PG_RETURN_POINTER(x) #define PG_RETURN_BPCHAR_P(x) PG_RETURN_POINTER(x) #define PG_RETURN_VARCHAR_P(x) PG_RETURN_POINTER(x) diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 59b1c9d3592..f386665cd2d 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: builtins.h,v 1.178 2002/04/24 02:12:53 momjian Exp $ + * $Id: builtins.h,v 1.179 2002/04/25 02:56:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -329,10 +329,16 @@ extern Datum texticregexne(PG_FUNCTION_ARGS); /* regproc.c */ extern Datum regprocin(PG_FUNCTION_ARGS); extern Datum regprocout(PG_FUNCTION_ARGS); -extern Datum regproctooid(PG_FUNCTION_ARGS); - -/* define macro to replace mixed-case function call - tgl 97/04/27 */ -#define RegprocToOid(rp) ((Oid) (rp)) +extern Datum regprocedurein(PG_FUNCTION_ARGS); +extern Datum regprocedureout(PG_FUNCTION_ARGS); +extern Datum regoperin(PG_FUNCTION_ARGS); +extern Datum regoperout(PG_FUNCTION_ARGS); +extern Datum regoperatorin(PG_FUNCTION_ARGS); +extern Datum regoperatorout(PG_FUNCTION_ARGS); +extern Datum regclassin(PG_FUNCTION_ARGS); +extern Datum regclassout(PG_FUNCTION_ARGS); +extern Datum regtypein(PG_FUNCTION_ARGS); +extern Datum regtypeout(PG_FUNCTION_ARGS); /* ruleutils.c */ extern Datum pg_get_ruledef(PG_FUNCTION_ARGS); @@ -349,6 +355,9 @@ extern List *deparse_context_for_plan(int outer_varno, Node *outercontext, extern Node *deparse_context_for_relation(const char *aliasname, Oid relid); extern Node *deparse_context_for_subplan(const char *name, List *tlist, List *rtable); +extern const char *quote_identifier(const char *ident); +extern char *quote_qualified_identifier(const char *namespace, + const char *ident); /* tid.c */ extern void setLastTid(const ItemPointer tid); diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index e7712b13bff..87f8a7afa41 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -14,11 +14,6 @@ -- -- NB: run this test earlier than the create_operator test, because -- that test creates some bogus operators... --- --- NOTE hardwired assumptions about standard types: --- type bool has OID 16 --- type float8 has OID 701 --- -- **************** pg_proc **************** -- Look for illegal values in pg_proc fields. -- NOTE: currently there are a few pg_proc entries that have prorettype = 0. @@ -26,7 +21,7 @@ SELECT p1.oid, p1.proname FROM pg_proc as p1 WHERE (p1.prolang = 0 OR p1.prorettype = 0 OR - p1.pronargs < 0 OR p1.pronargs > 9) + p1.pronargs < 0 OR p1.pronargs > 16) AND p1.proname !~ '^pl[^_]+_call_handler$' AND p1.proname !~ '^RI_FKey_' AND p1.proname !~ 'costestimate$' @@ -196,7 +191,7 @@ WHERE p1.proimplicit AND t.typname = p1.proname) OR NOT ((p1.pronargs = 1 AND p1.proargtypes[0] != prorettype) OR (p1.pronargs = 2 AND p1.proargtypes[0] = prorettype AND - p1.proargtypes[1] = 23))); + p1.proargtypes[1] = 'int4'::regtype))); oid | proname -----+--------- (0 rows) @@ -263,8 +258,8 @@ WHERE p1.oprnegate = p2.oid AND (p1.oprkind != p2.oprkind OR p1.oprleft != p2.oprleft OR p1.oprright != p2.oprright OR - p1.oprresult != 16 OR - p2.oprresult != 16 OR + p1.oprresult != 'bool'::regtype OR + p2.oprresult != 'bool'::regtype OR p1.oid != p2.oprnegate OR p1.oid = p2.oid); oid | oprcode | oid | oprcode @@ -282,8 +277,8 @@ WHERE p1.oprlsortop = p2.oid AND p1.oprkind != 'b' OR p2.oprkind != 'b' OR p1.oprleft != p2.oprleft OR p1.oprleft != p2.oprright OR - p1.oprresult != 16 OR - p2.oprresult != 16 OR + p1.oprresult != 'bool'::regtype OR + p2.oprresult != 'bool'::regtype OR p1.oprrsortop = 0); oid | oprcode | oid | oprcode -----+---------+-----+--------- @@ -296,8 +291,8 @@ WHERE p1.oprrsortop = p2.oid AND p1.oprkind != 'b' OR p2.oprkind != 'b' OR p1.oprright != p2.oprleft OR p1.oprright != p2.oprright OR - p1.oprresult != 16 OR - p2.oprresult != 16 OR + p1.oprresult != 'bool'::regtype OR + p2.oprresult != 'bool'::regtype OR p1.oprlsortop = 0); oid | oprcode | oid | oprcode -----+---------+-----+--------- @@ -363,8 +358,8 @@ WHERE p1.oprlsortop != p1.oprrsortop AND SELECT p1.oid, p1.oprname FROM pg_operator AS p1 WHERE p1.oprcanhash AND NOT - (p1.oprkind = 'b' AND p1.oprresult = 16 AND p1.oprleft = p1.oprright AND - p1.oprname = '=' AND p1.oprcom = p1.oid); + (p1.oprkind = 'b' AND p1.oprresult = 'bool'::regtype AND + p1.oprleft = p1.oprright AND p1.oprname = '=' AND p1.oprcom = p1.oid); oid | oprname ------+--------- 353 | = @@ -448,11 +443,11 @@ WHERE p1.oprcode = p2.oid AND SELECT p1.oid, p1.oprname, p2.oid, p2.proname FROM pg_operator AS p1, pg_proc AS p2 WHERE p1.oprrest = p2.oid AND - (p1.oprresult != 16 OR - p2.prorettype != 701 OR p2.proretset OR + (p1.oprresult != 'bool'::regtype OR + p2.prorettype != 'float8'::regtype OR p2.proretset OR p2.pronargs != 4 OR - p2.proargtypes[0] != 0 OR p2.proargtypes[1] != 26 OR - p2.proargtypes[2] != 0 OR p2.proargtypes[3] != 23); + p2.proargtypes[0] != 0 OR p2.proargtypes[1] != 'oid'::regtype OR + p2.proargtypes[2] != 0 OR p2.proargtypes[3] != 'int4'::regtype); oid | oprname | oid | proname -----+---------+-----+--------- (0 rows) @@ -464,10 +459,10 @@ WHERE p1.oprrest = p2.oid AND SELECT p1.oid, p1.oprname, p2.oid, p2.proname FROM pg_operator AS p1, pg_proc AS p2 WHERE p1.oprjoin = p2.oid AND - (p1.oprkind != 'b' OR p1.oprresult != 16 OR - p2.prorettype != 701 OR p2.proretset OR + (p1.oprkind != 'b' OR p1.oprresult != 'bool'::regtype OR + p2.prorettype != 'float8'::regtype OR p2.proretset OR p2.pronargs != 3 OR - p2.proargtypes[0] != 0 OR p2.proargtypes[1] != 26 OR + p2.proargtypes[0] != 0 OR p2.proargtypes[1] != 'oid'::regtype OR p2.proargtypes[2] != 0); oid | oprname | oid | proname -----+---------+-----+--------- @@ -611,7 +606,8 @@ WHERE p2.opcamid = p1.oid AND SELECT p1.amopclaid, p1.amopopr, p2.oid, p2.oprname FROM pg_amop AS p1, pg_operator AS p2 WHERE p1.amopopr = p2.oid AND - (p2.oprkind != 'b' OR p2.oprresult != 16 OR p2.oprleft != p2.oprright); + (p2.oprkind != 'b' OR p2.oprresult != 'bool'::regtype OR + p2.oprleft != p2.oprright); amopclaid | amopopr | oid | oprname -----------+---------+-----+--------- (0 rows) diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out index 4713768e851..a625054bad0 100644 --- a/src/test/regress/expected/type_sanity.out +++ b/src/test/regress/expected/type_sanity.out @@ -19,7 +19,9 @@ WHERE (p1.typlen <= 0 AND p1.typlen != -1) OR (p1.typtype != 'b' AND p1.typtype != 'c') OR NOT p1.typisdefined OR (p1.typalign != 'c' AND p1.typalign != 's' AND - p1.typalign != 'i' AND p1.typalign != 'd'); + p1.typalign != 'i' AND p1.typalign != 'd') OR + (p1.typstorage != 'p' AND p1.typstorage != 'x' AND + p1.typstorage != 'e' AND p1.typstorage != 'm'); oid | typname -----+--------- (0 rows) @@ -35,6 +37,15 @@ WHERE p1.typbyval AND -----+--------- (0 rows) +-- Look for "toastable" types that aren't varlena. +SELECT p1.oid, p1.typname +FROM pg_type as p1 +WHERE p1.typstorage != 'p' AND + (p1.typbyval OR p1.typlen != -1); + oid | typname +-----+--------- +(0 rows) + -- Look for complex types that do not have a typrelid entry, -- or basic types that do. SELECT p1.oid, p1.typname @@ -45,6 +56,31 @@ WHERE (p1.typtype = 'c' AND p1.typrelid = 0) OR -----+--------- (0 rows) +-- Look for basic types that don't have an array type. +-- NOTE: as of 7.3, this check finds SET, smgr, and unknown. +SELECT p1.oid, p1.typname +FROM pg_type as p1 +WHERE p1.typtype != 'c' AND p1.typname NOT LIKE '\\_%' AND NOT EXISTS + (SELECT 1 FROM pg_type as p2 + WHERE p2.typname = ('_' || p1.typname)::name AND + p2.typelem = p1.oid); + oid | typname +-----+--------- + 32 | SET + 210 | smgr + 705 | unknown +(3 rows) + +-- Look for array types that don't have an equality operator. +SELECT p1.oid, p1.typname +FROM pg_type as p1 +WHERE p1.typtype != 'c' AND p1.typname LIKE '\\_%' AND NOT EXISTS + (SELECT 1 FROM pg_operator + WHERE oprname = '=' AND oprleft = p1.oid AND oprright = p1.oid); + oid | typname +-----+--------- +(0 rows) + -- Conversion routines must be provided except in 'c' entries. SELECT p1.oid, p1.typname FROM pg_type as p1 @@ -63,7 +99,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typinput = p2.oid AND p1.typtype = 'b' AND (p2.pronargs != 1 OR p2.proretset) AND - (p2.pronargs != 3 OR p2.proretset OR p2.proargtypes[2] != 23); + (p2.pronargs != 3 OR p2.proretset OR p2.proargtypes[2] != 'int4'::regtype); oid | typname | oid | proname -----+---------+-----+--------- (0 rows) @@ -89,7 +125,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typreceive = p2.oid AND p1.typtype = 'b' AND (p2.pronargs != 1 OR p2.proretset) AND - (p2.pronargs != 3 OR p2.proretset OR p2.proargtypes[2] != 23); + (p2.pronargs != 3 OR p2.proretset OR p2.proargtypes[2] != 'int4'::regtype); oid | typname | oid | proname -----+---------+-----+--------- (0 rows) @@ -154,12 +190,15 @@ WHERE p1.relnatts != (SELECT count(*) FROM pg_attribute AS p2 (0 rows) -- Cross-check against pg_type entry +-- NOTE: we allow attstorage to be 'plain' even when typstorage is not; +-- this is mainly for toast tables. SELECT p1.attrelid, p1.attname, p2.oid, p2.typname FROM pg_attribute AS p1, pg_type AS p2 WHERE p1.atttypid = p2.oid AND (p1.attlen != p2.typlen OR p1.attalign != p2.typalign OR - p1.attbyval != p2.typbyval); + p1.attbyval != p2.typbyval OR + (p1.attstorage != p2.typstorage AND p1.attstorage != 'p')); attrelid | attname | oid | typname ----------+---------+-----+--------- (0 rows) diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql index 70565e3252a..c3fd710f449 100644 --- a/src/test/regress/sql/opr_sanity.sql +++ b/src/test/regress/sql/opr_sanity.sql @@ -14,11 +14,6 @@ -- -- NB: run this test earlier than the create_operator test, because -- that test creates some bogus operators... --- --- NOTE hardwired assumptions about standard types: --- type bool has OID 16 --- type float8 has OID 701 --- -- **************** pg_proc **************** @@ -29,7 +24,7 @@ SELECT p1.oid, p1.proname FROM pg_proc as p1 WHERE (p1.prolang = 0 OR p1.prorettype = 0 OR - p1.pronargs < 0 OR p1.pronargs > 9) + p1.pronargs < 0 OR p1.pronargs > 16) AND p1.proname !~ '^pl[^_]+_call_handler$' AND p1.proname !~ '^RI_FKey_' AND p1.proname !~ 'costestimate$' @@ -158,7 +153,7 @@ WHERE p1.proimplicit AND t.typname = p1.proname) OR NOT ((p1.pronargs = 1 AND p1.proargtypes[0] != prorettype) OR (p1.pronargs = 2 AND p1.proargtypes[0] = prorettype AND - p1.proargtypes[1] = 23))); + p1.proargtypes[1] = 'int4'::regtype))); -- **************** pg_operator **************** @@ -216,8 +211,8 @@ WHERE p1.oprnegate = p2.oid AND (p1.oprkind != p2.oprkind OR p1.oprleft != p2.oprleft OR p1.oprright != p2.oprright OR - p1.oprresult != 16 OR - p2.oprresult != 16 OR + p1.oprresult != 'bool'::regtype OR + p2.oprresult != 'bool'::regtype OR p1.oid != p2.oprnegate OR p1.oid = p2.oid); @@ -233,8 +228,8 @@ WHERE p1.oprlsortop = p2.oid AND p1.oprkind != 'b' OR p2.oprkind != 'b' OR p1.oprleft != p2.oprleft OR p1.oprleft != p2.oprright OR - p1.oprresult != 16 OR - p2.oprresult != 16 OR + p1.oprresult != 'bool'::regtype OR + p2.oprresult != 'bool'::regtype OR p1.oprrsortop = 0); SELECT p1.oid, p1.oprcode, p2.oid, p2.oprcode @@ -244,8 +239,8 @@ WHERE p1.oprrsortop = p2.oid AND p1.oprkind != 'b' OR p2.oprkind != 'b' OR p1.oprright != p2.oprleft OR p1.oprright != p2.oprright OR - p1.oprresult != 16 OR - p2.oprresult != 16 OR + p1.oprresult != 'bool'::regtype OR + p2.oprresult != 'bool'::regtype OR p1.oprlsortop = 0); -- A mergejoinable = operator must have a commutator (usually itself) @@ -300,8 +295,8 @@ WHERE p1.oprlsortop != p1.oprrsortop AND SELECT p1.oid, p1.oprname FROM pg_operator AS p1 WHERE p1.oprcanhash AND NOT - (p1.oprkind = 'b' AND p1.oprresult = 16 AND p1.oprleft = p1.oprright AND - p1.oprname = '=' AND p1.oprcom = p1.oid); + (p1.oprkind = 'b' AND p1.oprresult = 'bool'::regtype AND + p1.oprleft = p1.oprright AND p1.oprname = '=' AND p1.oprcom = p1.oid); -- In 6.5 we accepted hashable array equality operators when the array element -- type is hashable. However, what we actually need to make hashjoin work on @@ -372,11 +367,11 @@ WHERE p1.oprcode = p2.oid AND SELECT p1.oid, p1.oprname, p2.oid, p2.proname FROM pg_operator AS p1, pg_proc AS p2 WHERE p1.oprrest = p2.oid AND - (p1.oprresult != 16 OR - p2.prorettype != 701 OR p2.proretset OR + (p1.oprresult != 'bool'::regtype OR + p2.prorettype != 'float8'::regtype OR p2.proretset OR p2.pronargs != 4 OR - p2.proargtypes[0] != 0 OR p2.proargtypes[1] != 26 OR - p2.proargtypes[2] != 0 OR p2.proargtypes[3] != 23); + p2.proargtypes[0] != 0 OR p2.proargtypes[1] != 'oid'::regtype OR + p2.proargtypes[2] != 0 OR p2.proargtypes[3] != 'int4'::regtype); -- If oprjoin is set, the operator must be a binary boolean op, -- and it must link to a proc with the right signature @@ -386,10 +381,10 @@ WHERE p1.oprrest = p2.oid AND SELECT p1.oid, p1.oprname, p2.oid, p2.proname FROM pg_operator AS p1, pg_proc AS p2 WHERE p1.oprjoin = p2.oid AND - (p1.oprkind != 'b' OR p1.oprresult != 16 OR - p2.prorettype != 701 OR p2.proretset OR + (p1.oprkind != 'b' OR p1.oprresult != 'bool'::regtype OR + p2.prorettype != 'float8'::regtype OR p2.proretset OR p2.pronargs != 3 OR - p2.proargtypes[0] != 0 OR p2.proargtypes[1] != 26 OR + p2.proargtypes[0] != 0 OR p2.proargtypes[1] != 'oid'::regtype OR p2.proargtypes[2] != 0); -- **************** pg_aggregate **************** @@ -507,7 +502,8 @@ WHERE p2.opcamid = p1.oid AND SELECT p1.amopclaid, p1.amopopr, p2.oid, p2.oprname FROM pg_amop AS p1, pg_operator AS p2 WHERE p1.amopopr = p2.oid AND - (p2.oprkind != 'b' OR p2.oprresult != 16 OR p2.oprleft != p2.oprright); + (p2.oprkind != 'b' OR p2.oprresult != 'bool'::regtype OR + p2.oprleft != p2.oprright); -- Check that all operators linked to by opclass entries have selectivity -- estimators. This is not absolutely required, but it seems a reasonable diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql index 477696e3217..ca2d24c00a6 100644 --- a/src/test/regress/sql/type_sanity.sql +++ b/src/test/regress/sql/type_sanity.sql @@ -22,7 +22,9 @@ WHERE (p1.typlen <= 0 AND p1.typlen != -1) OR (p1.typtype != 'b' AND p1.typtype != 'c') OR NOT p1.typisdefined OR (p1.typalign != 'c' AND p1.typalign != 's' AND - p1.typalign != 'i' AND p1.typalign != 'd'); + p1.typalign != 'i' AND p1.typalign != 'd') OR + (p1.typstorage != 'p' AND p1.typstorage != 'x' AND + p1.typstorage != 'e' AND p1.typstorage != 'm'); -- Look for "pass by value" types that can't be passed by value. @@ -33,6 +35,13 @@ WHERE p1.typbyval AND (p1.typlen != 2 OR p1.typalign != 's') AND (p1.typlen != 4 OR p1.typalign != 'i'); +-- Look for "toastable" types that aren't varlena. + +SELECT p1.oid, p1.typname +FROM pg_type as p1 +WHERE p1.typstorage != 'p' AND + (p1.typbyval OR p1.typlen != -1); + -- Look for complex types that do not have a typrelid entry, -- or basic types that do. @@ -41,6 +50,24 @@ FROM pg_type as p1 WHERE (p1.typtype = 'c' AND p1.typrelid = 0) OR (p1.typtype != 'c' AND p1.typrelid != 0); +-- Look for basic types that don't have an array type. +-- NOTE: as of 7.3, this check finds SET, smgr, and unknown. + +SELECT p1.oid, p1.typname +FROM pg_type as p1 +WHERE p1.typtype != 'c' AND p1.typname NOT LIKE '\\_%' AND NOT EXISTS + (SELECT 1 FROM pg_type as p2 + WHERE p2.typname = ('_' || p1.typname)::name AND + p2.typelem = p1.oid); + +-- Look for array types that don't have an equality operator. + +SELECT p1.oid, p1.typname +FROM pg_type as p1 +WHERE p1.typtype != 'c' AND p1.typname LIKE '\\_%' AND NOT EXISTS + (SELECT 1 FROM pg_operator + WHERE oprname = '=' AND oprleft = p1.oid AND oprright = p1.oid); + -- Conversion routines must be provided except in 'c' entries. SELECT p1.oid, p1.typname @@ -58,7 +85,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typinput = p2.oid AND p1.typtype = 'b' AND (p2.pronargs != 1 OR p2.proretset) AND - (p2.pronargs != 3 OR p2.proretset OR p2.proargtypes[2] != 23); + (p2.pronargs != 3 OR p2.proretset OR p2.proargtypes[2] != 'int4'::regtype); -- Check for bogus typoutput routines -- The first OR subclause detects bogus non-array cases, @@ -80,7 +107,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typreceive = p2.oid AND p1.typtype = 'b' AND (p2.pronargs != 1 OR p2.proretset) AND - (p2.pronargs != 3 OR p2.proretset OR p2.proargtypes[2] != 23); + (p2.pronargs != 3 OR p2.proretset OR p2.proargtypes[2] != 'int4'::regtype); -- Check for bogus typsend routines -- The first OR subclause detects bogus non-array cases, @@ -132,10 +159,13 @@ WHERE p1.relnatts != (SELECT count(*) FROM pg_attribute AS p2 WHERE p2.attrelid = p1.oid AND p2.attnum > 0); -- Cross-check against pg_type entry +-- NOTE: we allow attstorage to be 'plain' even when typstorage is not; +-- this is mainly for toast tables. SELECT p1.attrelid, p1.attname, p2.oid, p2.typname FROM pg_attribute AS p1, pg_type AS p2 WHERE p1.atttypid = p2.oid AND (p1.attlen != p2.typlen OR p1.attalign != p2.typalign OR - p1.attbyval != p2.typbyval); + p1.attbyval != p2.typbyval OR + (p1.attstorage != p2.typstorage AND p1.attstorage != 'p')); |