From a7964b86a42b8def193e25566226fe66bd4ecc61 Mon Sep 17 00:00:00 2001 From: Dmitrii Dolgov Date: Fri, 20 Jul 2018 22:21:36 +0200 Subject: [PATCH 2/5] Base implementation of subscripting mechanism --- contrib/pg_stat_statements/pg_stat_statements.c | 1 + src/backend/catalog/heap.c | 6 +- src/backend/catalog/pg_type.c | 16 +- src/backend/commands/typecmds.c | 78 ++++++- src/backend/executor/execExpr.c | 40 ++-- src/backend/executor/execExprInterp.c | 119 ++--------- src/backend/nodes/copyfuncs.c | 2 + src/backend/nodes/equalfuncs.c | 2 + src/backend/nodes/outfuncs.c | 2 + src/backend/nodes/readfuncs.c | 2 + src/backend/parser/parse_expr.c | 54 +++-- src/backend/parser/parse_node.c | 259 ++++++++---------------- src/backend/parser/parse_target.c | 101 ++++----- src/backend/utils/adt/ruleutils.c | 20 +- src/backend/utils/cache/lsyscache.c | 23 +++ src/include/c.h | 2 + src/include/catalog/pg_class.dat | 2 +- src/include/catalog/pg_type.dat | 233 +++++++++++++-------- src/include/catalog/pg_type.h | 10 +- src/include/executor/execExpr.h | 27 +-- src/include/nodes/primnodes.h | 65 +++--- src/include/nodes/subscripting.h | 42 ++++ src/include/parser/parse_node.h | 19 +- src/include/utils/lsyscache.h | 1 + 24 files changed, 608 insertions(+), 518 deletions(-) create mode 100644 src/include/nodes/subscripting.h diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c index c734e04..01a67ce 100644 --- a/contrib/pg_stat_statements/pg_stat_statements.c +++ b/contrib/pg_stat_statements/pg_stat_statements.c @@ -2513,6 +2513,7 @@ JumbleExpr(pgssJumbleState *jstate, Node *node) JumbleExpr(jstate, (Node *) sbsref->reflowerindexpr); JumbleExpr(jstate, (Node *) sbsref->refexpr); JumbleExpr(jstate, (Node *) sbsref->refassgnexpr); + APP_JUMB(sbsref->refnestedfunc); } break; case T_FuncExpr: diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index d223ba8..c5e29cf 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -986,7 +986,8 @@ AddNewRelationType(const char *typeName, -1, /* typmod */ 0, /* array dimensions for typBaseType */ false, /* Type NOT NULL */ - InvalidOid); /* rowtypes never have a collation */ + InvalidOid, /* rowtypes never have a collation */ + InvalidOid); /* typsubshandler - none */ } /* -------------------------------- @@ -1259,7 +1260,8 @@ heap_create_with_catalog(const char *relname, -1, /* typmod */ 0, /* array dimensions for typBaseType */ false, /* Type NOT NULL */ - InvalidOid); /* rowtypes never have a collation */ + InvalidOid, /* rowtypes never have a collation */ + F_ARRAY_SUBSCRIPT_HANDLER); /* array implementation */ pfree(relarrayname); } diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c index 2ddd46d..4bae288 100644 --- a/src/backend/catalog/pg_type.c +++ b/src/backend/catalog/pg_type.c @@ -117,6 +117,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId) values[Anum_pg_type_typtypmod - 1] = Int32GetDatum(-1); values[Anum_pg_type_typndims - 1] = Int32GetDatum(0); values[Anum_pg_type_typcollation - 1] = ObjectIdGetDatum(InvalidOid); + values[Anum_pg_type_typsubshandler - 1] = ObjectIdGetDatum(InvalidOid); nulls[Anum_pg_type_typdefaultbin - 1] = true; nulls[Anum_pg_type_typdefault - 1] = true; nulls[Anum_pg_type_typacl - 1] = true; @@ -163,6 +164,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId) false, InvalidOid, InvalidOid, + InvalidOid, NULL, false); @@ -221,7 +223,8 @@ TypeCreate(Oid newTypeOid, int32 typeMod, int32 typNDims, /* Array dimensions for baseType */ bool typeNotNull, - Oid typeCollation) + Oid typeCollation, + Oid subscriptingHandlerProcedure) { Relation pg_type_desc; Oid typeObjectId; @@ -361,6 +364,7 @@ TypeCreate(Oid newTypeOid, values[Anum_pg_type_typtypmod - 1] = Int32GetDatum(typeMod); values[Anum_pg_type_typndims - 1] = Int32GetDatum(typNDims); values[Anum_pg_type_typcollation - 1] = ObjectIdGetDatum(typeCollation); + values[Anum_pg_type_typsubshandler - 1] = ObjectIdGetDatum(subscriptingHandlerProcedure); /* * initialize the default binary value for this type. Check for nulls of @@ -478,6 +482,7 @@ TypeCreate(Oid newTypeOid, isImplicitArray, baseType, typeCollation, + subscriptingHandlerProcedure, (defaultTypeBin ? stringToNode(defaultTypeBin) : NULL), @@ -524,6 +529,7 @@ GenerateTypeDependencies(Oid typeNamespace, bool isImplicitArray, Oid baseType, Oid typeCollation, + Oid subscriptingHandlerProcedure, Node *defaultExpr, bool rebuild) { @@ -676,6 +682,14 @@ GenerateTypeDependencies(Oid typeNamespace, /* Normal dependency on the default expression. */ if (defaultExpr) recordDependencyOnExpr(&myself, defaultExpr, NIL, DEPENDENCY_NORMAL); + + if (OidIsValid(subscriptingHandlerProcedure)) + { + referenced.classId = ProcedureRelationId; + referenced.objectId = subscriptingHandlerProcedure; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } } /* diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 175ecc8..6b992c7 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -91,6 +91,7 @@ static Oid findTypeSendFunction(List *procname, Oid typeOid); static Oid findTypeTypmodinFunction(List *procname); static Oid findTypeTypmodoutFunction(List *procname); static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid); +static Oid findTypeSubscriptingFunction(List *procname, Oid typeOid, bool parseFunc); static Oid findRangeSubOpclass(List *opcname, Oid subtype); static Oid findRangeCanonicalFunction(List *procname, Oid typeOid); static Oid findRangeSubtypeDiffFunction(List *procname, Oid subtype); @@ -122,6 +123,7 @@ DefineType(ParseState *pstate, List *names, List *parameters) List *typmodinName = NIL; List *typmodoutName = NIL; List *analyzeName = NIL; + List *subscriptingParseName = NIL; char category = TYPCATEGORY_USER; bool preferred = false; char delimiter = DEFAULT_TYPDELIM; @@ -140,6 +142,7 @@ DefineType(ParseState *pstate, List *names, List *parameters) DefElem *typmodinNameEl = NULL; DefElem *typmodoutNameEl = NULL; DefElem *analyzeNameEl = NULL; + DefElem *subscriptingParseNameEl = NULL; DefElem *categoryEl = NULL; DefElem *preferredEl = NULL; DefElem *delimiterEl = NULL; @@ -162,6 +165,7 @@ DefineType(ParseState *pstate, List *names, List *parameters) Oid resulttype; ListCell *pl; ObjectAddress address; + Oid subscriptingParseOid = InvalidOid; /* * As of Postgres 8.4, we require superuser privilege to create a base @@ -261,6 +265,8 @@ DefineType(ParseState *pstate, List *names, List *parameters) else if (strcmp(defel->defname, "analyze") == 0 || strcmp(defel->defname, "analyse") == 0) defelp = &analyzeNameEl; + else if (strcmp(defel->defname, "subscripting_handler") == 0) + defelp = &subscriptingParseNameEl; else if (strcmp(defel->defname, "category") == 0) defelp = &categoryEl; else if (strcmp(defel->defname, "preferred") == 0) @@ -331,6 +337,8 @@ DefineType(ParseState *pstate, List *names, List *parameters) typmodoutName = defGetQualifiedName(typmodoutNameEl); if (analyzeNameEl) analyzeName = defGetQualifiedName(analyzeNameEl); + if (subscriptingParseNameEl) + subscriptingParseName = defGetQualifiedName(subscriptingParseNameEl); if (categoryEl) { char *p = defGetString(categoryEl); @@ -512,6 +520,10 @@ DefineType(ParseState *pstate, List *names, List *parameters) if (analyzeName) analyzeOid = findTypeAnalyzeFunction(analyzeName, typoid); + if (subscriptingParseName) + subscriptingParseOid = findTypeSubscriptingFunction(subscriptingParseName, + typoid, true); + /* * Check permissions on functions. We choose to require the creator/owner * of a type to also own the underlying functions. Since creating a type @@ -631,7 +643,8 @@ DefineType(ParseState *pstate, List *names, List *parameters) -1, /* typMod (Domains only) */ 0, /* Array Dimensions of typbasetype */ false, /* Type NOT NULL */ - collation); /* type's collation */ + collation, /* type's collation */ + subscriptingParseOid); /* subscripting procedure */ Assert(typoid == address.objectId); /* @@ -672,7 +685,8 @@ DefineType(ParseState *pstate, List *names, List *parameters) -1, /* typMod (Domains only) */ 0, /* Array dimensions of typbasetype */ false, /* Type NOT NULL */ - collation); /* type's collation */ + collation, /* type's collation */ + F_ARRAY_SUBSCRIPT_HANDLER); pfree(array_type); @@ -735,6 +749,7 @@ DefineDomain(CreateDomainStmt *stmt) Oid receiveProcedure; Oid sendProcedure; Oid analyzeProcedure; + Oid subscriptingHandlerProcedure; bool byValue; char category; char delimiter; @@ -862,6 +877,9 @@ DefineDomain(CreateDomainStmt *stmt) /* Analysis function */ analyzeProcedure = baseType->typanalyze; + /* Subscripting functions */ + subscriptingHandlerProcedure = baseType->typsubshandler; + /* Inherited default value */ datum = SysCacheGetAttr(TYPEOID, typeTup, Anum_pg_type_typdefault, &isnull); @@ -1066,7 +1084,8 @@ DefineDomain(CreateDomainStmt *stmt) basetypeMod, /* typeMod value */ typNDims, /* Array dimensions for base type */ typNotNull, /* Type NOT NULL */ - domaincoll); /* type's collation */ + domaincoll, /* type's collation */ + subscriptingHandlerProcedure); /* subscripting procedure */ /* * Create the array type that goes with it. @@ -1106,7 +1125,8 @@ DefineDomain(CreateDomainStmt *stmt) -1, /* typMod (Domains only) */ 0, /* Array dimensions of typbasetype */ false, /* Type NOT NULL */ - domaincoll); /* type's collation */ + domaincoll, /* type's collation */ + F_ARRAY_SUBSCRIPT_HANDLER); /* array subscripting implementation */ pfree(domainArrayName); @@ -1221,7 +1241,8 @@ DefineEnum(CreateEnumStmt *stmt) -1, /* typMod (Domains only) */ 0, /* Array dimensions of typbasetype */ false, /* Type NOT NULL */ - InvalidOid); /* type's collation */ + InvalidOid, /* type's collation */ + InvalidOid); /* typsubshandler - none */ /* Enter the enum's values into pg_enum */ EnumValuesCreate(enumTypeAddr.objectId, stmt->vals); @@ -1261,7 +1282,8 @@ DefineEnum(CreateEnumStmt *stmt) -1, /* typMod (Domains only) */ 0, /* Array dimensions of typbasetype */ false, /* Type NOT NULL */ - InvalidOid); /* type's collation */ + InvalidOid, /* type's collation */ + F_ARRAY_SUBSCRIPT_HANDLER); /* array subscripting implementation */ pfree(enumArrayName); @@ -1570,7 +1592,8 @@ DefineRange(CreateRangeStmt *stmt) -1, /* typMod (Domains only) */ 0, /* Array dimensions of typbasetype */ false, /* Type NOT NULL */ - InvalidOid); /* type's collation (ranges never have one) */ + InvalidOid, /* type's collation (ranges never have one) */ + InvalidOid); /* typsubshandler - none */ Assert(typoid == address.objectId); /* Create the entry in pg_range */ @@ -1612,7 +1635,8 @@ DefineRange(CreateRangeStmt *stmt) -1, /* typMod (Domains only) */ 0, /* Array dimensions of typbasetype */ false, /* Type NOT NULL */ - InvalidOid); /* typcollation */ + InvalidOid, /* typcollation */ + F_ARRAY_SUBSCRIPT_HANDLER); /* array subscripting implementation */ pfree(rangeArrayName); @@ -1955,6 +1979,43 @@ findTypeAnalyzeFunction(List *procname, Oid typeOid) return procOid; } +static Oid +findTypeSubscriptingFunction(List *procname, Oid typeOid, bool parseFunc) +{ + Oid argList[2]; + Oid procOid; + int nargs; + + if (parseFunc) + { + /* + * Subscripting function parse always take two INTERNAL argument and + * return INTERNAL. + */ + argList[0] = INTERNALOID; + nargs = 1; + } + else + { + /* + * Subscripting functions fetch/assign always take one typeOid + * argument, one INTERNAL argument and return typeOid. + */ + argList[0] = typeOid; + argList[1] = INTERNALOID; + nargs = 2; + } + + procOid = LookupFuncName(procname, nargs, argList, true); + if (!OidIsValid(procOid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("function %s does not exist", + func_signature_string(procname, nargs, NIL, argList)))); + + return procOid; +} + /* * Find suitable support functions and opclasses for a range type. */ @@ -2308,6 +2369,7 @@ AlterDomainDefault(List *names, Node *defaultRaw) false, /* a domain isn't an implicit array */ typTup->typbasetype, typTup->typcollation, + typTup->typsubshandler, defaultExpr, true); /* Rebuild is true */ diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c index 3d8d05b..08a41cb 100644 --- a/src/backend/executor/execExpr.c +++ b/src/backend/executor/execExpr.c @@ -2436,20 +2436,18 @@ static void ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref, ExprState *state, Datum *resv, bool *resnull) { - bool isAssignment = (aref->refassgnexpr != NULL); - ArrayRefState *arefstate = palloc0(sizeof(ArrayRefState)); - List *adjust_jumps = NIL; - ListCell *lc; - int i; - - /* Fill constant fields of ArrayRefState */ - arefstate->isassignment = isAssignment; - arefstate->refelemtype = aref->refelemtype; - arefstate->refattrlength = get_typlen(aref->refarraytype); - get_typlenbyvalalign(aref->refelemtype, - &arefstate->refelemlength, - &arefstate->refelembyval, - &arefstate->refelemalign); + bool isAssignment = (sbsref->refassgnexpr != NULL); + SubscriptingRefState *sbsrefstate = palloc0(sizeof(SubscriptingRefState)); + List *adjust_jumps = NIL; + ListCell *lc; + int i; + RegProcedure typsubshandler = get_typsubsprocs(sbsref->refcontainertype); + + /* Fill constant fields of SubscriptingRefState */ + sbsrefstate->isassignment = isAssignment; + sbsrefstate->refelemtype = sbsref->refelemtype; + sbsrefstate->refattrlength = get_typlen(sbsref->refcontainertype); + sbsrefstate->sbsroutines = (SubscriptRoutines *) OidFunctionCall0(typsubshandler); /* * Evaluate array input. It's safe to do so into resv/resnull, because we @@ -2469,23 +2467,11 @@ ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref, scratch->opcode = EEOP_JUMP_IF_NULL; scratch->d.jump.jumpdone = -1; /* adjust later */ ExprEvalPushStep(state, scratch); + adjust_jumps = lappend_int(adjust_jumps, state->steps_len - 1); } - /* Verify subscript list lengths are within limit */ - if (list_length(aref->refupperindexpr) > MAXDIM) - ereport(ERROR, - (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), - errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)", - list_length(aref->refupperindexpr), MAXDIM))); - - if (list_length(aref->reflowerindexpr) > MAXDIM) - ereport(ERROR, - (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), - errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)", - list_length(aref->reflowerindexpr), MAXDIM))); - /* Evaluate upper subscripts */ i = 0; foreach(lc, sbsref->refupperindexpr) diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index 3299fe5..1d1d85a 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -3078,7 +3078,7 @@ ExecEvalSubscriptingRef(ExprState *state, ExprEvalStep *op) indexes = sbsrefstate->lowerindex; off = op->d.sbsref_subscript.off; - indexes[off] = DatumGetInt32(arefstate->subscriptvalue); + indexes[off] = sbsrefstate->subscriptvalue; return true; } @@ -3091,37 +3091,14 @@ ExecEvalSubscriptingRef(ExprState *state, ExprEvalStep *op) void ExecEvalSubscriptingRefFetch(ExprState *state, ExprEvalStep *op) { - ArrayRefState *arefstate = op->d.arrayref.state; + SubscriptingRefState *sbsrefstate = op->d.sbsref.state; + SubscriptRoutines *sbsroutines = sbsrefstate->sbsroutines; /* Should not get here if source container (or any subscript) is null */ Assert(!(*op->resnull)); - if (arefstate->numlower == 0) - { - /* Scalar case */ - *op->resvalue = array_get_element(*op->resvalue, - arefstate->numupper, - arefstate->upperindex, - arefstate->refattrlength, - arefstate->refelemlength, - arefstate->refelembyval, - arefstate->refelemalign, - op->resnull); - } - else - { - /* Slice case */ - *op->resvalue = array_get_slice(*op->resvalue, - arefstate->numupper, - arefstate->upperindex, - arefstate->lowerindex, - arefstate->upperprovided, - arefstate->lowerprovided, - arefstate->refattrlength, - arefstate->refelemlength, - arefstate->refelembyval, - arefstate->refelemalign); - } + *op->resvalue = sbsroutines->fetch(*op->resvalue, sbsrefstate); + *op->resnull = sbsrefstate->resnull; } /* @@ -3133,41 +3110,22 @@ ExecEvalSubscriptingRefFetch(ExprState *state, ExprEvalStep *op) void ExecEvalSubscriptingRefOld(ExprState *state, ExprEvalStep *op) { - ArrayRefState *arefstate = op->d.arrayref.state; + SubscriptingRefState *sbsrefstate = op->d.sbsref.state; + SubscriptRoutines *sbsroutines = sbsrefstate->sbsroutines; if (*op->resnull) { - /* whole array is null, so any element or slice is too */ - arefstate->prevvalue = (Datum) 0; - arefstate->prevnull = true; - } - else if (arefstate->numlower == 0) - { - /* Scalar case */ - arefstate->prevvalue = array_get_element(*op->resvalue, - arefstate->numupper, - arefstate->upperindex, - arefstate->refattrlength, - arefstate->refelemlength, - arefstate->refelembyval, - arefstate->refelemalign, - &arefstate->prevnull); + /* whole container is null, so any element or slice is too */ + sbsrefstate->prevvalue = (Datum) 0; + sbsrefstate->prevnull = true; } else { - /* Slice case */ - /* this is currently unreachable */ - arefstate->prevvalue = array_get_slice(*op->resvalue, - arefstate->numupper, - arefstate->upperindex, - arefstate->lowerindex, - arefstate->upperprovided, - arefstate->lowerprovided, - arefstate->refattrlength, - arefstate->refelemlength, - arefstate->refelembyval, - arefstate->refelemalign); - arefstate->prevnull = false; + sbsrefstate->prevvalue = sbsroutines->fetch(*op->resvalue, sbsrefstate); + + if (sbsrefstate->numlower != 0) + sbsrefstate->prevnull = false; + } } @@ -3180,7 +3138,8 @@ ExecEvalSubscriptingRefOld(ExprState *state, ExprEvalStep *op) void ExecEvalSubscriptingRefAssign(ExprState *state, ExprEvalStep *op) { - ArrayRefState *arefstate = op->d.arrayref.state; + SubscriptingRefState *sbsrefstate = op->d.sbsref.state; + SubscriptRoutines *sbsroutines = sbsrefstate->sbsroutines; /* * For an assignment to a fixed-length container type, both the original @@ -3193,47 +3152,9 @@ ExecEvalSubscriptingRefAssign(ExprState *state, ExprEvalStep *op) return; } - /* - * For assignment to varlena arrays, we handle a NULL original array by - * substituting an empty (zero-dimensional) array; insertion of the new - * element will result in a singleton array value. It does not matter - * whether the new element is NULL. - */ - if (*op->resnull) - { - *op->resvalue = PointerGetDatum(construct_empty_array(arefstate->refelemtype)); - *op->resnull = false; - } - - if (arefstate->numlower == 0) - { - /* Scalar case */ - *op->resvalue = array_set_element(*op->resvalue, - arefstate->numupper, - arefstate->upperindex, - arefstate->replacevalue, - arefstate->replacenull, - arefstate->refattrlength, - arefstate->refelemlength, - arefstate->refelembyval, - arefstate->refelemalign); - } - else - { - /* Slice case */ - *op->resvalue = array_set_slice(*op->resvalue, - arefstate->numupper, - arefstate->upperindex, - arefstate->lowerindex, - arefstate->upperprovided, - arefstate->lowerprovided, - arefstate->replacevalue, - arefstate->replacenull, - arefstate->refattrlength, - arefstate->refelemlength, - arefstate->refelembyval, - arefstate->refelemalign); - } + sbsrefstate->resnull = *op->resnull; + *op->resvalue = sbsroutines->assign(*op->resvalue, sbsrefstate); + *op->resnull = sbsrefstate->resnull; } /* diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index f67f14e..96e45c9 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -1486,8 +1486,10 @@ _copySubscriptingRef(const SubscriptingRef *from) COPY_SCALAR_FIELD(refcontainertype); COPY_SCALAR_FIELD(refelemtype); + COPY_SCALAR_FIELD(refassgntype); COPY_SCALAR_FIELD(reftypmod); COPY_SCALAR_FIELD(refcollid); + COPY_SCALAR_FIELD(refnestedfunc); COPY_NODE_FIELD(refupperindexpr); COPY_NODE_FIELD(reflowerindexpr); COPY_NODE_FIELD(refexpr); diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 73c91bc..c7a9c2c 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -268,8 +268,10 @@ _equalSubscriptingRef(const SubscriptingRef *a, const SubscriptingRef *b) { COMPARE_SCALAR_FIELD(refcontainertype); COMPARE_SCALAR_FIELD(refelemtype); + COMPARE_SCALAR_FIELD(refassgntype); COMPARE_SCALAR_FIELD(reftypmod); COMPARE_SCALAR_FIELD(refcollid); + COMPARE_SCALAR_FIELD(refnestedfunc); COMPARE_NODE_FIELD(refupperindexpr); COMPARE_NODE_FIELD(reflowerindexpr); COMPARE_NODE_FIELD(refexpr); diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 70a48d6..b0c8baa 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -1246,8 +1246,10 @@ _outSubscriptingRef(StringInfo str, const SubscriptingRef *node) WRITE_OID_FIELD(refcontainertype); WRITE_OID_FIELD(refelemtype); + WRITE_OID_FIELD(refassgntype); WRITE_INT_FIELD(reftypmod); WRITE_OID_FIELD(refcollid); + WRITE_OID_FIELD(refnestedfunc); WRITE_NODE_FIELD(refupperindexpr); WRITE_NODE_FIELD(reflowerindexpr); WRITE_NODE_FIELD(refexpr); diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 167932c..a4f53e6 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -654,8 +654,10 @@ _readSubscriptingRef(void) READ_OID_FIELD(refcontainertype); READ_OID_FIELD(refelemtype); + READ_OID_FIELD(refassgntype); READ_INT_FIELD(reftypmod); READ_OID_FIELD(refcollid); + READ_OID_FIELD(refnestedfunc); READ_NODE_FIELD(refupperindexpr); READ_NODE_FIELD(reflowerindexpr); READ_NODE_FIELD(refexpr); diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 385e54a..5ef606b 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -434,11 +434,13 @@ unknown_attribute(ParseState *pstate, Node *relref, const char *attname, static Node * transformIndirection(ParseState *pstate, A_Indirection *ind) { - Node *last_srf = pstate->p_last_srf; - Node *result = transformExprRecurse(pstate, ind->arg); - List *subscripts = NIL; - int location = exprLocation(result); - ListCell *i; + Node *last_srf = pstate->p_last_srf; + Node *result = transformExprRecurse(pstate, ind->arg); + SubscriptRoutines *sbsroutines; + SubscriptingRef *sbsref; + List *subscripts = NIL; + int location = exprLocation(result); + ListCell *i; /* * We have to split any field-selection operations apart from @@ -466,13 +468,20 @@ transformIndirection(ParseState *pstate, A_Indirection *ind) /* process subscripts before this field selection */ if (subscripts) - result = (Node *) transformArraySubscripts(pstate, - result, - exprType(result), - InvalidOid, - exprTypmod(result), - subscripts, - NULL); + { + sbsref = transformContainerSubscripts(pstate, + result, + exprType(result), + InvalidOid, + exprTypmod(result), + subscripts, + NULL); + + sbsroutines = getSubscriptingRoutines(sbsref->refcontainertype); + sbsref = sbsroutines->prepare(false, sbsref); + sbsroutines->validate(false, sbsref, pstate); + result = (Node *) sbsref; + } subscripts = NIL; newresult = ParseFuncOrColumn(pstate, @@ -489,13 +498,20 @@ transformIndirection(ParseState *pstate, A_Indirection *ind) } /* process trailing subscripts, if any */ if (subscripts) - result = (Node *) transformArraySubscripts(pstate, - result, - exprType(result), - InvalidOid, - exprTypmod(result), - subscripts, - NULL); + { + sbsref = transformContainerSubscripts(pstate, + result, + exprType(result), + InvalidOid, + exprTypmod(result), + subscripts, + NULL); + + sbsroutines = getSubscriptingRoutines(sbsref->refcontainertype); + sbsref = sbsroutines->prepare(false, sbsref); + sbsroutines->validate(false, sbsref, pstate); + result = (Node *) sbsref; + } return result; } diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c index d267288..f032f8e 100644 --- a/src/backend/parser/parse_node.c +++ b/src/backend/parser/parse_node.c @@ -203,31 +203,23 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno, int location) } /* - * transformArrayType() - * Identify the types involved in a subscripting operation + * transformContainerType() + * Identify the types involved in a subscripting operation for container * - * On entry, arrayType/arrayTypmod identify the type of the input value - * to be subscripted (which could be a domain type). These are modified - * if necessary to identify the actual array type and typmod, and the - * array's element type is returned. An error is thrown if the input isn't - * an array type. + * On entry, containerType/containerTypmod are modified if necessary to + * identify the actual container type and typmod. */ -Oid -transformArrayType(Oid *arrayType, int32 *arrayTypmod) +void +transformContainerType(Oid *containerType, int32 *containerTypmod) { - Oid origArrayType = *arrayType; - Oid elementType; - HeapTuple type_tuple_array; - Form_pg_type type_struct_array; - /* * If the input is a domain, smash to base type, and extract the actual - * typmod to be applied to the base type. Subscripting a domain is an - * operation that necessarily works on the base array type, not the domain - * itself. (Note that we provide no method whereby the creator of a - * domain over an array type could hide its ability to be subscripted.) + * typmod to be applied to the base type. Subscripting a domain is an + * operation that necessarily works on the base container type, not the + * domain itself. (Note that we provide no method whereby the creator of a + * domain over a container type could hide its ability to be subscripted.) */ - *arrayType = getBaseTypeAndTypmod(*arrayType, arrayTypmod); + *containerType = getBaseTypeAndTypmod(*containerType, containerTypmod); /* * We treat int2vector and oidvector as though they were domains over @@ -236,87 +228,70 @@ transformArrayType(Oid *arrayType, int32 *arrayTypmod) * xxxvector type; so we want the result of a slice operation to be * considered to be of the more general type. */ - if (*arrayType == INT2VECTOROID) - *arrayType = INT2ARRAYOID; - else if (*arrayType == OIDVECTOROID) - *arrayType = OIDARRAYOID; - - /* Get the type tuple for the array */ - type_tuple_array = SearchSysCache1(TYPEOID, ObjectIdGetDatum(*arrayType)); - if (!HeapTupleIsValid(type_tuple_array)) - elog(ERROR, "cache lookup failed for type %u", *arrayType); - type_struct_array = (Form_pg_type) GETSTRUCT(type_tuple_array); - - /* needn't check typisdefined since this will fail anyway */ - - elementType = type_struct_array->typelem; - if (elementType == InvalidOid) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("cannot subscript type %s because it is not an array", - format_type_be(origArrayType)))); - - ReleaseSysCache(type_tuple_array); - - return elementType; + if (*containerType == INT2VECTOROID) + *containerType = INT2ARRAYOID; + else if (*containerType == OIDVECTOROID) + *containerType = OIDARRAYOID; } /* - * transformArraySubscripts() - * Transform array subscripting. This is used for both - * array fetch and array assignment. + * transformContainerSubscripts() + * Transform container subscripting. This is used for both + * container fetch and container assignment. * - * In an array fetch, we are given a source array value and we produce an - * expression that represents the result of extracting a single array element - * or an array slice. + * In a container fetch, we are given a source container value and we produce + * an expression that represents the result of extracting a single container + * element or a container slice. * - * In an array assignment, we are given a destination array value plus a - * source value that is to be assigned to a single element or a slice of - * that array. We produce an expression that represents the new array value - * with the source data inserted into the right part of the array. + * In a container assignment, we are given a destination container value plus a + * source value that is to be assigned to a single element or a slice of that + * container. We produce an expression that represents the new container value + * with the source data inserted into the right part of the container. * - * For both cases, if the source array is of a domain-over-array type, - * the result is of the base array type or its element type; essentially, - * we must fold a domain to its base type before applying subscripting. - * (Note that int2vector and oidvector are treated as domains here.) + * For both cases, this function contains only general subscripting logic while + * type-specific logic (e.g. type verifications and coersion) is placen in + * separate procedure indicated by typsubshandler. There is only one exception + * for now about domain-over-container, if the source container is of a + * domain-over-container type, the result is of the base container type or its + * element type; essentially, we must fold a domain to its base type before + * applying subscripting. (Note that int2vector and oidvector are treated as + * domains here.) An error will appear in case if current container type + * doesn't have a subscripting procedure. * - * pstate Parse state - * arrayBase Already-transformed expression for the array as a whole - * arrayType OID of array's datatype (should match type of arrayBase, - * or be the base type of arrayBase's domain type) - * elementType OID of array's element type (fetch with transformArrayType, - * or pass InvalidOid to do it here) - * arrayTypMod typmod for the array (which is also typmod for the elements) - * indirection Untransformed list of subscripts (must not be NIL) - * assignFrom NULL for array fetch, else transformed expression for source. + * pstate Parse state + * containerBase Already-transformed expression for the container as a whole + * containerType OID of container's datatype (should match type of containerBase, + * or be the base type of containerBase's domain type) + * elementType OID of container's element type (fetch with + * transformContainerType, or pass InvalidOid to do it here) + * containerTypMod typmod for the container (which is also typmod for the elements) + * indirection Untransformed list of subscripts (must not be NIL) + * assignFrom NULL for container fetch, else transformed expression for source. */ -ArrayRef * -transformArraySubscripts(ParseState *pstate, - Node *arrayBase, - Oid arrayType, - Oid elementType, - int32 arrayTypMod, - List *indirection, - Node *assignFrom) + +SubscriptingRef * +transformContainerSubscripts(ParseState *pstate, + Node *containerBase, + Oid containerType, + Oid elementType, + int32 containerTypMod, + List *indirection, + Node *assignFrom) { - bool isSlice = false; - List *upperIndexpr = NIL; - List *lowerIndexpr = NIL; - ListCell *idx; - ArrayRef *aref; + bool isSlice = false; + List *upperIndexpr = NIL; + List *lowerIndexpr = NIL; + List *indexprSlice = NIL; + ListCell *idx; + SubscriptingRef *sbsref; - /* - * Caller may or may not have bothered to determine elementType. Note - * that if the caller did do so, arrayType/arrayTypMod must be as modified - * by transformArrayType, ie, smash domain to base type. - */ - if (!OidIsValid(elementType)) - elementType = transformArrayType(&arrayType, &arrayTypMod); + /* Identify the actual container type and element type involved */ + transformContainerType(&containerType, &containerTypMod); /* - * A list containing only simple subscripts refers to a single array + * A list containing only simple subscripts refers to a single container * element. If any of the items are slice specifiers (lower:upper), then - * the subscript expression means an array slice operation. In this case, + * the subscript expression means an container slice operation. In this case, * we convert any non-slice items to slices by treating the single * subscript as the upper bound and supplying an assumed lower bound of 1. * We have to prescan the list to see if there are any slice items. @@ -345,29 +320,6 @@ transformArraySubscripts(ParseState *pstate, if (ai->lidx) { subexpr = transformExpr(pstate, ai->lidx, pstate->p_expr_kind); - /* If it's not int4 already, try to coerce */ - subexpr = coerce_to_target_type(pstate, - subexpr, exprType(subexpr), - INT4OID, -1, - COERCION_ASSIGNMENT, - COERCE_IMPLICIT_CAST, - -1); - if (subexpr == NULL) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("array subscript must have type integer"), - parser_errposition(pstate, exprLocation(ai->lidx)))); - } - else if (!ai->is_slice) - { - /* Make a constant 1 */ - subexpr = (Node *) makeConst(INT4OID, - -1, - InvalidOid, - sizeof(int32), - Int32GetDatum(1), - false, - true); /* pass by value */ } else { @@ -375,77 +327,42 @@ transformArraySubscripts(ParseState *pstate, subexpr = NULL; } lowerIndexpr = lappend(lowerIndexpr, subexpr); + indexprSlice = lappend(indexprSlice, ai); } - else - Assert(ai->lidx == NULL && !ai->is_slice); - - if (ai->uidx) - { - subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind); - /* If it's not int4 already, try to coerce */ - subexpr = coerce_to_target_type(pstate, - subexpr, exprType(subexpr), - INT4OID, -1, - COERCION_ASSIGNMENT, - COERCE_IMPLICIT_CAST, - -1); - if (subexpr == NULL) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("array subscript must have type integer"), - parser_errposition(pstate, exprLocation(ai->uidx)))); - } - else - { - /* Slice with omitted upper bound, put NULL into the list */ - Assert(isSlice && ai->is_slice); - subexpr = NULL; - } + subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind); upperIndexpr = lappend(upperIndexpr, subexpr); } /* - * If doing an array store, coerce the source value to the right type. - * (This should agree with the coercion done by transformAssignedExpr.) + * Ready to build the SubscriptingRef node. */ + sbsref = (SubscriptingRef *) makeNode(SubscriptingRef); if (assignFrom != NULL) - { - Oid typesource = exprType(assignFrom); - Oid typeneeded = isSlice ? arrayType : elementType; - Node *newFrom; - - newFrom = coerce_to_target_type(pstate, - assignFrom, typesource, - typeneeded, arrayTypMod, - COERCION_ASSIGNMENT, - COERCE_IMPLICIT_CAST, - -1); - if (newFrom == NULL) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("array assignment requires type %s" - " but expression is of type %s", - format_type_be(typeneeded), - format_type_be(typesource)), - errhint("You will need to rewrite or cast the expression."), - parser_errposition(pstate, exprLocation(assignFrom)))); - assignFrom = newFrom; - } + sbsref->refassgnexpr = (Expr *) assignFrom; - /* - * Ready to build the ArrayRef node. - */ - aref = makeNode(ArrayRef); - aref->refarraytype = arrayType; - aref->refelemtype = elementType; - aref->reftypmod = arrayTypMod; + sbsref->refcontainertype = containerType; + sbsref->reftypmod = containerTypMod; /* refcollid will be set by parse_collate.c */ - aref->refupperindexpr = upperIndexpr; - aref->reflowerindexpr = lowerIndexpr; - aref->refexpr = (Expr *) arrayBase; - aref->refassgnexpr = (Expr *) assignFrom; + sbsref->refupperindexpr = upperIndexpr; + sbsref->reflowerindexpr = lowerIndexpr; + sbsref->refindexprslice = indexprSlice; + sbsref->refexpr = (Expr *) containerBase; + + return sbsref; +} + +SubscriptRoutines* +getSubscriptingRoutines(Oid containerType) +{ + RegProcedure typsubshandler = get_typsubsprocs(containerType); + + if (!OidIsValid(typsubshandler)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("cannot subscript type %s because it does not support subscripting", + format_type_be(containerType)))); - return aref; + return (SubscriptRoutines *) OidFunctionCall0(typsubshandler); } /* diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index d78376c..32efd18 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -838,27 +838,22 @@ transformAssignmentIndirection(ParseState *pstate, location); } - /* base case: just coerce RHS to match target type ID */ - - result = coerce_to_target_type(pstate, - rhs, exprType(rhs), - targetTypeId, targetTypMod, - COERCION_ASSIGNMENT, - COERCE_IMPLICIT_CAST, - -1); - if (result == NULL) + /* + * Base case: just coerce RHS to match target type ID. + * It's necessary only for field selection, since for + * subscripting it's custom code who should define types. + */ + if (!targetIsSubscripting) { - if (targetIsArray) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("array assignment to \"%s\" requires type %s" - " but expression is of type %s", - targetName, - format_type_be(targetTypeId), - format_type_be(exprType(rhs))), - errhint("You will need to rewrite or cast the expression."), - parser_errposition(pstate, location))); - else + result = coerce_to_target_type(pstate, + rhs, exprType(rhs), + targetTypeId, targetTypMod, + COERCION_ASSIGNMENT, + COERCE_IMPLICIT_CAST, + -1); + + if (result == NULL) + { ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("subfield \"%s\" is of type %s" @@ -892,58 +887,68 @@ transformAssignmentSubscripts(ParseState *pstate, Node *rhs, int location) { - Node *result; - Oid arrayType; - int32 arrayTypMod; - Oid elementTypeId; - Oid typeNeeded; - Oid collationNeeded; + Node *result; + Oid containerType; + int32 containerTypMod; + Oid collationNeeded; + SubscriptingRef *sbsref; + SubscriptRoutines *sbsroutines; Assert(subscripts != NIL); - /* Identify the actual array type and element type involved */ - arrayType = targetTypeId; - arrayTypMod = targetTypMod; - elementTypeId = transformArrayType(&arrayType, &arrayTypMod); + /* Identify the actual container type and element type involved */ + containerType = targetTypeId; + containerTypMod = targetTypMod; + + /* process subscripts */ + sbsref = transformContainerSubscripts(pstate, + basenode, + containerType, + exprType(rhs), + containerTypMod, + subscripts, + rhs); + + sbsroutines = getSubscriptingRoutines(sbsref->refcontainertype); - /* Identify type that RHS must provide */ - typeNeeded = isSlice ? arrayType : elementTypeId; + /* + * Let custom code provide necessary information about required types: + * refelemtype and refassgntype + */ + sbsref = sbsroutines->prepare(rhs != NULL, sbsref); /* * container normally has same collation as elements, but there's an * exception: we might be subscripting a domain over an container type. In * that case use collation of the base type. */ - if (arrayType == targetTypeId) + if (sbsref->refcontainertype == containerType) collationNeeded = targetCollation; else - collationNeeded = get_typcollation(arrayType); + collationNeeded = get_typcollation(containerType); /* recurse to create appropriate RHS for container assign */ rhs = transformAssignmentIndirection(pstate, NULL, targetName, true, - typeNeeded, - arrayTypMod, + sbsref->refassgntype, + sbsref->reftypmod, collationNeeded, next_indirection, rhs, location); - /* process subscripts */ - result = (Node *) transformArraySubscripts(pstate, - basenode, - arrayType, - elementTypeId, - arrayTypMod, - subscripts, - rhs); - - /* If target was a domain over array, need to coerce up to the domain */ - if (arrayType != targetTypeId) + /* Provide fully prepared subscriptinng information for custom validation */ + sbsref->refassgnexpr = (Expr *) rhs; + sbsroutines->validate(rhs != NULL, sbsref, pstate); + + result = (Node *) sbsref; + + /* If target was a domain over container, need to coerce up to the domain */ + if (sbsref->refcontainertype != targetTypeId) { - Oid resulttype = exprType(result); + Oid resulttype = exprType(result); result = coerce_to_target_type(pstate, result, resulttype, diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index a1de680..6c98e48 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -7772,17 +7772,17 @@ get_rule_expr(Node *node, deparse_context *context, if (need_parens) appendStringInfoChar(buf, ')'); - /* - * If there's a refassgnexpr, we want to print the node in the - * format "array[subscripts] := refassgnexpr". This is not - * legal SQL, so decompilation of INSERT or UPDATE statements - * should always use processIndirection as part of the - * statement-level syntax. We should only see this when - * EXPLAIN tries to print the targetlist of a plan resulting - * from such a statement. - */ - if (aref->refassgnexpr) + if (IsAssignment(sbsref)) { + /* + * If there's a refassgnexpr, we want to print the node in the + * format "container[subscripts] := refassgnexpr". This is not + * legal SQL, so decompilation of INSERT or UPDATE statements + * should always use processIndirection as part of the + * statement-level syntax. We should only see this when + * EXPLAIN tries to print the targetlist of a plan resulting + * from such a statement. + */ Node *refassgnexpr; /* diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index bba595a..99c3a9e 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -3106,3 +3106,26 @@ get_range_subtype(Oid rangeOid) else return InvalidOid; } + +/* + * get_typsubshandler + * + * Given the type OID, return the type's subscripting procedures, if any, + * through pointers in arguments. + */ +RegProcedure +get_typsubsprocs(Oid typid) +{ + HeapTuple tp; + + tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid)); + if (HeapTupleIsValid(tp)) + { + RegProcedure handler = ((Form_pg_type) GETSTRUCT(tp))->typsubshandler; + ReleaseSysCache(tp); + + return handler; + } + else + return InvalidOid; +} diff --git a/src/include/c.h b/src/include/c.h index 1e50103..2ae0cdc 100644 --- a/src/include/c.h +++ b/src/include/c.h @@ -499,6 +499,8 @@ typedef struct int indx[MAXDIM]; } IntArray; +#define MAX_SUBSCRIPT_DEPTH 12 + /* ---------------- * Variable-length datatypes all share the 'struct varlena' header. * diff --git a/src/include/catalog/pg_class.dat b/src/include/catalog/pg_class.dat index 9fffdef..f42cbfe 100644 --- a/src/include/catalog/pg_class.dat +++ b/src/include/catalog/pg_class.dat @@ -25,7 +25,7 @@ reloftype => '0', relowner => 'PGUID', relam => '0', relfilenode => '0', reltablespace => '0', relpages => '0', reltuples => '0', relallvisible => '0', reltoastrelid => '0', relhasindex => 'f', relisshared => 'f', - relpersistence => 'p', relkind => 'r', relnatts => '30', relchecks => '0', + relpersistence => 'p', relkind => 'r', relnatts => '31', relchecks => '0', relhasoids => 't', relhasrules => 'f', relhastriggers => 'f', relhassubclass => 'f', relrowsecurity => 'f', relforcerowsecurity => 'f', relispopulated => 't', relreplident => 'n', relispartition => 'f', diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat index 48e01cd..68f6d79 100644 --- a/src/include/catalog/pg_type.dat +++ b/src/include/catalog/pg_type.dat @@ -48,7 +48,8 @@ typname => 'name', typlen => 'NAMEDATALEN', typbyval => 'f', typcategory => 'S', typelem => 'char', typarray => '_name', typinput => 'namein', typoutput => 'nameout', typreceive => 'namerecv', - typsend => 'namesend', typalign => 'c' }, + typsend => 'namesend', typalign => 'c', + typsubshandler => 'array_subscript_handler' }, { oid => '20', descr => '~18 digit integer, 8-byte storage', typname => 'int8', typlen => '8', typbyval => 'FLOAT8PASSBYVAL', typcategory => 'N', typarray => '_int8', typinput => 'int8in', @@ -62,7 +63,8 @@ typname => 'int2vector', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'int2', typarray => '_int2vector', typinput => 'int2vectorin', typoutput => 'int2vectorout', typreceive => 'int2vectorrecv', - typsend => 'int2vectorsend', typalign => 'i' }, + typsend => 'int2vectorsend', typalign => 'i', + typsubshandler => 'array_subscript_handler' }, { oid => '23', descr => '-2 billion to 2 billion integer, 4-byte storage', typname => 'int4', typlen => '4', typbyval => 't', typcategory => 'N', typarray => '_int4', typinput => 'int4in', typoutput => 'int4out', @@ -97,7 +99,8 @@ typname => 'oidvector', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'oid', typarray => '_oidvector', typinput => 'oidvectorin', typoutput => 'oidvectorout', typreceive => 'oidvectorrecv', - typsend => 'oidvectorsend', typalign => 'i' }, + typsend => 'oidvectorsend', typalign => 'i', + typsubshandler => 'array_subscript_handler' }, # hand-built rowtype entries for bootstrapped catalogs # NB: OIDs assigned here must match the BKI_ROWTYPE_OID declarations @@ -138,12 +141,14 @@ typname => '_xml', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'xml', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '199', typname => '_json', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'json', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '194', oid_symbol => 'PGNODETREEOID', descr => 'string representing an internal node tree', typname => 'pg_node_tree', typlen => '-1', typbyval => 'f', @@ -185,37 +190,38 @@ typname => 'point', typlen => '16', typbyval => 'f', typcategory => 'G', typelem => 'float8', typarray => '_point', typinput => 'point_in', typoutput => 'point_out', typreceive => 'point_recv', typsend => 'point_send', - typalign => 'd' }, + typalign => 'd', typsubshandler => 'array_subscript_handler' }, { oid => '601', descr => 'geometric line segment \'(pt1,pt2)\'', typname => 'lseg', typlen => '32', typbyval => 'f', typcategory => 'G', typelem => 'point', typarray => '_lseg', typinput => 'lseg_in', typoutput => 'lseg_out', typreceive => 'lseg_recv', typsend => 'lseg_send', - typalign => 'd' }, + typalign => 'd', typsubshandler => 'array_subscript_handler' }, { oid => '602', descr => 'geometric path \'(pt1,...)\'', typname => 'path', typlen => '-1', typbyval => 'f', typcategory => 'G', typarray => '_path', typinput => 'path_in', typoutput => 'path_out', typreceive => 'path_recv', typsend => 'path_send', typalign => 'd', - typstorage => 'x' }, + typstorage => 'x', typsubshandler => 'array_subscript_handler' }, { oid => '603', descr => 'geometric box \'(lower left,upper right)\'', typname => 'box', typlen => '32', typbyval => 'f', typcategory => 'G', typdelim => ';', typelem => 'point', typarray => '_box', typinput => 'box_in', typoutput => 'box_out', typreceive => 'box_recv', typsend => 'box_send', - typalign => 'd' }, + typalign => 'd', typsubshandler => 'array_subscript_handler' }, { oid => '604', descr => 'geometric polygon \'(pt1,...)\'', typname => 'polygon', typlen => '-1', typbyval => 'f', typcategory => 'G', typarray => '_polygon', typinput => 'poly_in', typoutput => 'poly_out', typreceive => 'poly_recv', typsend => 'poly_send', typalign => 'd', - typstorage => 'x' }, + typstorage => 'x', typsubshandler => 'array_subscript_handler' }, { oid => '628', descr => 'geometric line', typname => 'line', typlen => '24', typbyval => 'f', typcategory => 'G', typelem => 'float8', typarray => '_line', typinput => 'line_in', typoutput => 'line_out', typreceive => 'line_recv', typsend => 'line_send', - typalign => 'd' }, + typalign => 'd', typsubshandler => 'array_subscript_handler' }, { oid => '629', typname => '_line', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'line', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, # OIDS 700 - 799 @@ -258,7 +264,8 @@ typname => '_circle', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'circle', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '790', oid_symbol => 'CASHOID', descr => 'monetary amounts, $d,ddd.cc', typname => 'money', typlen => '8', typbyval => 'FLOAT8PASSBYVAL', @@ -269,7 +276,8 @@ typname => '_money', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'money', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, # OIDS 800 - 899 @@ -299,142 +307,166 @@ typname => '_bool', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'bool', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '1001', typname => '_bytea', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'bytea', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '1002', typname => '_char', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'char', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '1003', typname => '_name', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'name', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '1005', typname => '_int2', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'int2', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '1006', typname => '_int2vector', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'int2vector', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '1007', typname => '_int4', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'int4', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '1008', typname => '_regproc', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'regproc', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '1009', typname => '_text', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'text', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', - typcollation => '100' }, + typcollation => '100', typsubshandler => 'array_subscript_handler' }, { oid => '1028', typname => '_oid', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'oid', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '1010', typname => '_tid', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'tid', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '1011', typname => '_xid', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'xid', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '1012', typname => '_cid', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'cid', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '1013', typname => '_oidvector', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'oidvector', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '1014', typname => '_bpchar', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'bpchar', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', typmodin => 'bpchartypmodin', typmodout => 'bpchartypmodout', typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', - typcollation => '100' }, + typcollation => '100', typsubshandler => 'array_subscript_handler' }, { oid => '1015', typname => '_varchar', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'varchar', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', typmodin => 'varchartypmodin', typmodout => 'varchartypmodout', typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', - typcollation => '100' }, + typcollation => '100', typsubshandler => 'array_subscript_handler' }, { oid => '1016', typname => '_int8', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'int8', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '1017', typname => '_point', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'point', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '1018', typname => '_lseg', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'lseg', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '1019', typname => '_path', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'path', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '1020', typname => '_box', typlen => '-1', typbyval => 'f', typcategory => 'A', typdelim => ';', typelem => 'box', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '1021', typname => '_float4', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'float4', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '1022', typname => '_float8', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'float8', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '1023', typname => '_abstime', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'abstime', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '1024', typname => '_reltime', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'reltime', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '1025', typname => '_tinterval', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'tinterval', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '1027', typname => '_polygon', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'polygon', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '1033', descr => 'access control list', typname => 'aclitem', typlen => '12', typbyval => 'f', typcategory => 'U', typarray => '_aclitem', typinput => 'aclitemin', typoutput => 'aclitemout', @@ -443,32 +475,38 @@ typname => '_aclitem', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'aclitem', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '1040', typname => '_macaddr', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'macaddr', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '775', typname => '_macaddr8', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'macaddr8', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '1041', typname => '_inet', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'inet', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '651', typname => '_cidr', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'cidr', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '1263', typname => '_cstring', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'cstring', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '1042', descr => 'char(length), blank-padded string, fixed storage length', typname => 'bpchar', typlen => '-1', typbyval => 'f', typcategory => 'S', @@ -506,18 +544,21 @@ typelem => 'timestamp', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', typmodin => 'timestamptypmodin', typmodout => 'timestamptypmodout', - typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '1182', typname => '_date', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'date', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '1183', typname => '_time', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'time', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', typmodin => 'timetypmodin', typmodout => 'timetypmodout', - typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '1184', descr => 'date and time with time zone', typname => 'timestamptz', typlen => '8', typbyval => 'FLOAT8PASSBYVAL', typcategory => 'D', typispreferred => 't', typarray => '_timestamptz', @@ -530,7 +571,8 @@ typcategory => 'A', typelem => 'timestamptz', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', typmodin => 'timestamptztypmodin', typmodout => 'timestamptztypmodout', - typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '1186', descr => '@ , time interval', typname => 'interval', typlen => '16', typbyval => 'f', typcategory => 'T', typispreferred => 't', typarray => '_interval', typinput => 'interval_in', @@ -542,7 +584,8 @@ typelem => 'interval', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', typmodin => 'intervaltypmodin', typmodout => 'intervaltypmodout', - typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, # OIDS 1200 - 1299 @@ -551,7 +594,8 @@ typelem => 'numeric', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', typmodin => 'numerictypmodin', typmodout => 'numerictypmodout', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '1266', descr => 'time of day with time zone', typname => 'timetz', typlen => '12', typbyval => 'f', typcategory => 'D', typarray => '_timetz', typinput => 'timetz_in', typoutput => 'timetz_out', @@ -563,7 +607,8 @@ typelem => 'timetz', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', typmodin => 'timetztypmodin', typmodout => 'timetztypmodout', - typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, # OIDS 1500 - 1599 @@ -577,7 +622,8 @@ typelem => 'bit', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', typmodin => 'bittypmodin', typmodout => 'bittypmodout', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '1562', descr => 'variable-length bit string', typname => 'varbit', typlen => '-1', typbyval => 'f', typcategory => 'V', typispreferred => 't', typarray => '_varbit', typinput => 'varbit_in', @@ -589,7 +635,8 @@ typelem => 'varbit', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', typmodin => 'varbittypmodin', typmodout => 'varbittypmodout', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, # OIDS 1700 - 1799 @@ -613,7 +660,8 @@ typname => '_refcursor', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'refcursor', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '2202', descr => 'registered procedure (with args)', typname => 'regprocedure', typlen => '4', typbyval => 't', typcategory => 'N', @@ -650,37 +698,44 @@ typname => '_regprocedure', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'regprocedure', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '2208', typname => '_regoper', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'regoper', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '2209', typname => '_regoperator', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'regoperator', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '2210', typname => '_regclass', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'regclass', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '2211', typname => '_regtype', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'regtype', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '4097', typname => '_regrole', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'regrole', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '4090', typname => '_regnamespace', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'regnamespace', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, # uuid { oid => '2950', descr => 'UUID datatype', @@ -691,7 +746,8 @@ typname => '_uuid', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'uuid', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, # pg_lsn { oid => '3220', oid_symbol => 'LSNOID', descr => 'PostgreSQL LSN datatype', @@ -703,7 +759,8 @@ typname => '_pg_lsn', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'pg_lsn', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, # text search { oid => '3614', descr => 'text representation for text search', @@ -737,39 +794,45 @@ typname => '_tsvector', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'tsvector', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '3644', typname => '_gtsvector', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'gtsvector', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '3645', typname => '_tsquery', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'tsquery', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '3735', typname => '_regconfig', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'regconfig', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '3770', typname => '_regdictionary', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'regdictionary', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, # jsonb { oid => '3802', descr => 'Binary JSON', typname => 'jsonb', typlen => '-1', typbyval => 'f', typcategory => 'U', typarray => '_jsonb', typinput => 'jsonb_in', typoutput => 'jsonb_out', typreceive => 'jsonb_recv', typsend => 'jsonb_send', typalign => 'i', - typstorage => 'x' }, + typstorage => 'x', typsubshandler => 'jsonb_subscript_handler' }, { oid => '3807', typname => '_jsonb', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'jsonb', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '2970', descr => 'txid snapshot', typname => 'txid_snapshot', typlen => '-1', typbyval => 'f', @@ -781,7 +844,8 @@ typname => '_txid_snapshot', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'txid_snapshot', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, # range types { oid => '3904', descr => 'range of integers', @@ -793,7 +857,8 @@ typname => '_int4range', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'int4range', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '3906', descr => 'range of numerics', typname => 'numrange', typlen => '-1', typbyval => 'f', typtype => 'r', typcategory => 'R', typarray => '_numrange', typinput => 'range_in', @@ -803,7 +868,8 @@ typname => '_numrange', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'numrange', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '3908', descr => 'range of timestamps without time zone', typname => 'tsrange', typlen => '-1', typbyval => 'f', typtype => 'r', typcategory => 'R', typarray => '_tsrange', typinput => 'range_in', @@ -813,7 +879,8 @@ typname => '_tsrange', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'tsrange', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '3910', descr => 'range of timestamps with time zone', typname => 'tstzrange', typlen => '-1', typbyval => 'f', typtype => 'r', typcategory => 'R', typarray => '_tstzrange', typinput => 'range_in', @@ -823,7 +890,8 @@ typname => '_tstzrange', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'tstzrange', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '3912', descr => 'range of dates', typname => 'daterange', typlen => '-1', typbyval => 'f', typtype => 'r', typcategory => 'R', typarray => '_daterange', typinput => 'range_in', @@ -833,7 +901,8 @@ typname => '_daterange', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'daterange', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'i', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '3926', descr => 'range of bigints', typname => 'int8range', typlen => '-1', typbyval => 'f', typtype => 'r', typcategory => 'R', typarray => '_int8range', typinput => 'range_in', @@ -843,7 +912,8 @@ typname => '_int8range', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'int8range', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, # pseudo-types # types with typtype='p' represent various special cases in the type system. @@ -863,7 +933,8 @@ typname => '_record', typlen => '-1', typbyval => 'f', typtype => 'p', typcategory => 'P', typelem => 'record', typinput => 'array_in', typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', - typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x' }, + typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '2275', typname => 'cstring', typlen => '-2', typbyval => 'f', typtype => 'p', typcategory => 'P', typarray => '_cstring', typinput => 'cstring_in', diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h index 1e9cea7..5b442b6 100644 --- a/src/include/catalog/pg_type.h +++ b/src/include/catalog/pg_type.h @@ -214,6 +214,12 @@ CATALOG(pg_type,1247,TypeRelationId) BKI_BOOTSTRAP BKI_ROWTYPE_OID(71,TypeRelati */ Oid typcollation BKI_DEFAULT(0); + /* + * Type specific subscripting logic. If typsubshandler is none, it means + * that this type doesn't support subscripting. + */ + regproc typsubshandler BKI_DEFAULT(-) BKI_LOOKUP(pg_proc); + #ifdef CATALOG_VARLEN /* variable-length fields start here */ /* @@ -321,7 +327,8 @@ extern ObjectAddress TypeCreate(Oid newTypeOid, int32 typeMod, int32 typNDims, bool typeNotNull, - Oid typeCollation); + Oid typeCollation, + Oid subscriptingParseProcedure); extern void GenerateTypeDependencies(Oid typeNamespace, Oid typeObjectId, @@ -339,6 +346,7 @@ extern void GenerateTypeDependencies(Oid typeNamespace, bool isImplicitArray, Oid baseType, Oid typeCollation, + Oid subscriptingParseProcedure, Node *defaultExpr, bool rebuild); diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h index 58ae64b..f643e87 100644 --- a/src/include/executor/execExpr.h +++ b/src/include/executor/execExpr.h @@ -19,7 +19,7 @@ /* forward references to avoid circularity */ struct ExprEvalStep; -struct ArrayRefState; +struct SubscriptRoutines; /* Bits in ExprState->flags (see also execnodes.h for public flag bits): */ /* expression's interpreter has been initialized */ @@ -639,28 +639,27 @@ typedef struct ExprEvalStep } d; } ExprEvalStep; - -/* Non-inline data for array operations */ -typedef struct ArrayRefState +/* Non-inline data for container operations */ +typedef struct SubscriptingRefState { bool isassignment; /* is it assignment, or just fetch? */ - Oid refelemtype; /* OID of the array element type */ - int16 refattrlength; /* typlen of array type */ - int16 refelemlength; /* typlen of the array element type */ + Oid refelemtype; /* OID of the container element type */ + int16 refattrlength; /* typlen of container type */ + int16 refelemlength; /* typlen of the container element type */ bool refelembyval; /* is the element type pass-by-value? */ char refelemalign; /* typalign of the element type */ /* numupper and upperprovided[] are filled at compile time */ /* at runtime, extracted subscript datums get stored in upperindex[] */ int numupper; - bool upperprovided[MAXDIM]; - int upperindex[MAXDIM]; + bool upperprovided[MAX_SUBSCRIPT_DEPTH]; + Datum upperindex[MAX_SUBSCRIPT_DEPTH]; /* similarly for lower indexes, if any */ int numlower; - bool lowerprovided[MAXDIM]; - int lowerindex[MAXDIM]; + bool lowerprovided[MAX_SUBSCRIPT_DEPTH]; + Datum lowerindex[MAX_SUBSCRIPT_DEPTH]; /* subscript expressions get evaluated into here */ Datum subscriptvalue; @@ -670,11 +669,13 @@ typedef struct ArrayRefState Datum replacevalue; bool replacenull; - /* if we have a nested assignment, ARRAYREF_OLD puts old value here */ + /* if we have a nested assignment, SBSREF_OLD puts old value here */ Datum prevvalue; bool prevnull; -} ArrayRefState; + bool resnull; + struct SubscriptRoutines *sbsroutines; +} SubscriptingRefState; /* functions in execExpr.c */ extern void ExprEvalPushStep(ExprState *es, const ExprEvalStep *s); diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 93719ad..22bed11 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -365,18 +365,18 @@ typedef struct WindowFunc } WindowFunc; /* ---------------- - * ArrayRef: describes an array subscripting operation - * - * An ArrayRef can describe fetching a single element from an array, - * fetching a subarray (array slice), storing a single element into - * an array, or storing a slice. The "store" cases work with an - * initial array value and a source value that is inserted into the - * appropriate part of the array; the result of the operation is an - * entire new modified array value. - * - * If reflowerindexpr = NIL, then we are fetching or storing a single array - * element at the subscripts given by refupperindexpr. Otherwise we are - * fetching or storing an array slice, that is a rectangular subarray + * SubscriptingRef: describes a subscripting operation over a container + * + * A SubscriptingRef can describe fetching a single element from a container, + * fetching a part of container (e.g. array slice), storing a single element into + * a container, or storing a slice. The "store" cases work with an + * initial container value and a source value that is inserted into the + * appropriate part of the container; the result of the operation is an + * entire new modified container value. + * + * If reflowerindexpr = NIL, then we are fetching or storing a single container + * element at the subscripts given by refupperindexpr. Otherwise we are + * fetching or storing a container slice, that is a rectangular subcontainer * with lower and upper bounds given by the index expressions. * reflowerindexpr must be the same length as refupperindexpr when it * is not NIL. @@ -388,28 +388,35 @@ typedef struct WindowFunc * element; but it is the array type when doing subarray fetch or either * type of store. * - * Note: for the cases where an array is returned, if refexpr yields a R/W - * expanded array, then the implementation is allowed to modify that object + * Note: for the cases where a container is returned, if refexpr yields a R/W + * expanded container, then the implementation is allowed to modify that object * in-place and return the same object.) * ---------------- */ -typedef struct ArrayRef + +typedef struct SubscriptingRef { Expr xpr; - Oid refarraytype; /* type of the array proper */ - Oid refelemtype; /* type of the array elements */ - int32 reftypmod; /* typmod of the array (and elements too) */ - Oid refcollid; /* OID of collation, or InvalidOid if none */ - List *refupperindexpr; /* expressions that evaluate to upper - * array indexes */ - List *reflowerindexpr; /* expressions that evaluate to lower - * array indexes, or NIL for single array - * element */ - Expr *refexpr; /* the expression that evaluates to an array - * value */ - Expr *refassgnexpr; /* expression for the source value, or NULL if - * fetch */ -} ArrayRef; + Oid refcontainertype; /* type of the container proper */ + Oid refelemtype; /* type of the container elements */ + Oid refassgntype; /* type of assignment expr that is required */ + int32 reftypmod; /* typmod of the container (and elements too) */ + Oid refcollid; /* OID of collation, or InvalidOid if none */ + Oid refnestedfunc; /* OID of type-specific function to handle nested assignment */ + List *refupperindexpr; /* expressions that evaluate to upper container + * indexes */ + List *reflowerindexpr; /* expressions that evaluate to lower container + * indexes, or NIL for single container element */ + List *refindexprslice; /* whether or not related indexpr from + * reflowerindexpr is a slice */ + Expr *refexpr; /* the expression that evaluates to a container + * value */ + + Expr *refassgnexpr; /* expression for the source value, or NULL if + * fetch */ +} SubscriptingRef; + +#define IsAssignment(expr) ( ((SubscriptingRef*) expr)->refassgnexpr != NULL ) /* * CoercionContext - distinguishes the allowed set of type casts diff --git a/src/include/nodes/subscripting.h b/src/include/nodes/subscripting.h new file mode 100644 index 0000000..1800d5e --- /dev/null +++ b/src/include/nodes/subscripting.h @@ -0,0 +1,42 @@ +/*------------------------------------------------------------------------- + * + * subscripting.h + * API for generic type subscripting + * + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/nodes/subscripting.h + * + *------------------------------------------------------------------------- + */ +#ifndef SUBSCRIPTING_H +#define SUBSCRIPTING_H + +#include "parser/parse_node.h" +#include "nodes/primnodes.h" + +struct ParseState; +struct SubscriptingRefState; + +/* Callback function signatures --- see xsubscripting.sgml for more info. */ +typedef SubscriptingRef * (*SubscriptingPrepare) (bool isAssignment, SubscriptingRef *sbsef); + +typedef SubscriptingRef * (*SubscriptingValidate) (bool isAssignment, SubscriptingRef *sbsef, + struct ParseState *pstate); + +typedef Datum (*SubscriptingFetch) (Datum source, struct SubscriptingRefState *sbsrefstate); + +typedef Datum (*SubscriptingAssign) (Datum source, struct SubscriptingRefState *sbrsefstate); + +typedef struct SubscriptRoutines +{ + SubscriptingPrepare prepare; + SubscriptingValidate validate; + SubscriptingFetch fetch; + SubscriptingAssign assign; + +} SubscriptRoutines; + + +#endif /* SUBSCRIPTING_H */ diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index 0230543..62298d9 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -15,6 +15,7 @@ #define PARSE_NODE_H #include "nodes/parsenodes.h" +#include "nodes/subscripting.h" #include "utils/queryenvironment.h" #include "utils/relcache.h" @@ -271,14 +272,16 @@ extern void cancel_parser_errposition_callback(ParseCallbackState *pcbstate); extern Var *make_var(ParseState *pstate, RangeTblEntry *rte, int attrno, int location); -extern Oid transformArrayType(Oid *arrayType, int32 *arrayTypmod); -extern ArrayRef *transformArraySubscripts(ParseState *pstate, - Node *arrayBase, - Oid arrayType, - Oid elementType, - int32 arrayTypMod, - List *indirection, - Node *assignFrom); +extern void transformContainerType(Oid *containerType, int32 *containerTypmod); + +extern SubscriptingRef *transformContainerSubscripts(ParseState *pstate, + Node *containerBase, + Oid containerType, + Oid elementType, + int32 containerTypMod, + List *indirection, + Node *assignFrom); +extern SubscriptRoutines* getSubscriptingRoutines(Oid containerType); extern Const *make_const(ParseState *pstate, Value *value, int location); #endif /* PARSE_NODE_H */ diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h index e55ea40..6e021c4 100644 --- a/src/include/utils/lsyscache.h +++ b/src/include/utils/lsyscache.h @@ -176,6 +176,7 @@ extern void free_attstatsslot(AttStatsSlot *sslot); extern char *get_namespace_name(Oid nspid); extern char *get_namespace_name_or_temp(Oid nspid); extern Oid get_range_subtype(Oid rangeOid); +extern RegProcedure get_typsubsprocs(Oid typid); #define type_is_array(typid) (get_element_type(typid) != InvalidOid) /* type_is_array_domain accepts both plain arrays and domains over arrays */ -- 2.7.4