diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c index 70cfdb2c9d..f5835e89dd 100644 --- a/contrib/pg_stat_statements/pg_stat_statements.c +++ b/contrib/pg_stat_statements/pg_stat_statements.c @@ -2858,6 +2858,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/Catalog.pm b/src/backend/catalog/Catalog.pm index dd39a086ce..b4dfa26518 100644 --- a/src/backend/catalog/Catalog.pm +++ b/src/backend/catalog/Catalog.pm @@ -384,6 +384,7 @@ sub GenerateArrayTypes # Arrays require INT alignment, unless the element type requires # DOUBLE alignment. $array_type{typalign} = $elem_type->{typalign} eq 'd' ? 'd' : 'i'; + $array_type{typsubshandler} = 'array_subscript_handler'; # Fill in the rest of the array entry's fields. foreach my $column (@$pgtype_schema) diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 4cd7d76938..d157f6f5ac 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -1091,7 +1091,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 */ } /* -------------------------------- @@ -1370,7 +1371,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 aeb4a54f63..bd67512e26 100644 --- a/src/backend/catalog/pg_type.c +++ b/src/backend/catalog/pg_type.c @@ -119,6 +119,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; @@ -159,10 +160,10 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId) GenerateTypeDependencies(tup, pg_type_desc, NULL, - NULL, 0, false, false, + InvalidOid, false); /* Post creation hook for new shell type */ @@ -220,7 +221,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; @@ -373,6 +375,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 diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 483bb65ddc..596c6cf3ca 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -115,6 +115,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); @@ -149,6 +150,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; @@ -167,6 +169,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; @@ -188,6 +191,7 @@ DefineType(ParseState *pstate, List *names, List *parameters) Oid typoid; ListCell *pl; ObjectAddress address; + Oid subscriptingParseOid = InvalidOid; /* * As of Postgres 8.4, we require superuser privilege to create a base @@ -288,6 +292,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) @@ -358,6 +364,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); @@ -482,6 +490,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 @@ -563,7 +575,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); /* @@ -604,7 +617,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); @@ -667,6 +681,7 @@ DefineDomain(CreateDomainStmt *stmt) Oid receiveProcedure; Oid sendProcedure; Oid analyzeProcedure; + Oid subscriptingHandlerProcedure; bool byValue; char category; char delimiter; @@ -800,6 +815,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); @@ -1005,7 +1023,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. @@ -1045,7 +1064,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); @@ -1160,7 +1180,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); @@ -1200,7 +1221,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); @@ -1488,7 +1510,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 == InvalidOid || typoid == address.objectId); typoid = address.objectId; @@ -1531,7 +1554,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); @@ -1904,6 +1928,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 parse functions always take two INTERNAL arguments and + * return INTERNAL. + */ + argList[0] = INTERNALOID; + nargs = 1; + } + else + { + /* + * Subscripting fetch/assign functions 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. */ diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c index c8382e9381..473e595662 100644 --- a/src/backend/executor/execExpr.c +++ b/src/backend/executor/execExpr.c @@ -40,6 +40,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" +#include "nodes/subscripting.h" #include "optimizer/optimizer.h" #include "pgstat.h" #include "utils/acl.h" @@ -2522,6 +2523,7 @@ static void ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref, ExprState *state, Datum *resv, bool *resnull) { + SubscriptRoutines *sbsroutines = getSubscriptingRoutines(sbsref->refcontainertype); bool isAssignment = (sbsref->refassgnexpr != NULL); int nupper = list_length(sbsref->refupperindexpr); int nlower = list_length(sbsref->reflowerindexpr); @@ -2538,12 +2540,6 @@ ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref, /* Fill constant fields of SubscriptingRefState */ sbsrefstate->isassignment = isAssignment; - sbsrefstate->refelemtype = sbsref->refelemtype; - sbsrefstate->refattrlength = get_typlen(sbsref->refcontainertype); - get_typlenbyvalalign(sbsref->refelemtype, - &sbsrefstate->refelemlength, - &sbsrefstate->refelembyval, - &sbsrefstate->refelemalign); sbsrefstate->numupper = nupper; sbsrefstate->numlower = nlower; /* Set up per-subscript arrays */ @@ -2561,6 +2557,14 @@ ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref, sbsrefstate->lowerindexnull = (bool *) ptr; /* ptr += nlower * sizeof(bool); */ + /* + * Let the container-type-specific code have a chance. It must fill in + * the sbs_subscripts, sbs_fetch, sbs_assign, and sbs_fetch_old function + * pointers for us to possibly use in execution steps below; and it can + * optionally set up some data pointed to by the workspace field. + */ + sbsroutines->exec_setup(sbsref, sbsrefstate); + /* * Evaluate array input. It's safe to do so into resv/resnull, because we * won't use that as target for any of the other subexpressions, and it'll @@ -2632,6 +2636,7 @@ ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref, /* SBSREF_SUBSCRIPTS checks and converts all the subscripts at once */ scratch->opcode = EEOP_SBSREF_SUBSCRIPTS; + scratch->d.sbsref_subscript.subscriptfunc = sbsrefstate->sbs_subscripts; scratch->d.sbsref_subscript.state = sbsrefstate; scratch->d.sbsref_subscript.jumpdone = -1; /* adjust later */ ExprEvalPushStep(state, scratch); @@ -2660,6 +2665,7 @@ ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref, if (isAssignmentIndirectionExpr(sbsref->refassgnexpr)) { scratch->opcode = EEOP_SBSREF_OLD; + scratch->d.sbsref.subscriptfunc = sbsrefstate->sbs_fetch_old; scratch->d.sbsref.state = sbsrefstate; ExprEvalPushStep(state, scratch); } @@ -2679,6 +2685,7 @@ ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref, /* and perform the assignment */ scratch->opcode = EEOP_SBSREF_ASSIGN; + scratch->d.sbsref.subscriptfunc = sbsrefstate->sbs_assign; scratch->d.sbsref.state = sbsrefstate; ExprEvalPushStep(state, scratch); } @@ -2686,6 +2693,7 @@ ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref, { /* array fetch is much simpler */ scratch->opcode = EEOP_SBSREF_FETCH; + scratch->d.sbsref.subscriptfunc = sbsrefstate->sbs_fetch; scratch->d.sbsref.state = sbsrefstate; ExprEvalPushStep(state, scratch); } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index 1853405026..a4b71fb554 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -1398,10 +1398,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull) EEO_CASE(EEOP_SBSREF_SUBSCRIPTS) { - /* Process array subscript(s) */ - - /* too complex for an inline implementation */ - if (ExecEvalSubscriptingRef(state, op)) + /* Process container subscript(s) */ + if (op->d.sbsref_subscript.subscriptfunc(state, op, econtext)) { EEO_NEXT(); } @@ -1419,9 +1417,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull) * referenced (via a CaseTestExpr) inside the assignment * expression. */ - - /* too complex for an inline implementation */ - ExecEvalSubscriptingRefOld(state, op); + op->d.sbsref.subscriptfunc(state, op, econtext); EEO_NEXT(); } @@ -1431,19 +1427,17 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull) */ EEO_CASE(EEOP_SBSREF_ASSIGN) { - /* too complex for an inline implementation */ - ExecEvalSubscriptingRefAssign(state, op); + op->d.sbsref.subscriptfunc(state, op, econtext); EEO_NEXT(); } /* - * Fetch subset of an array. + * Perform SubscriptingRef fetch */ EEO_CASE(EEOP_SBSREF_FETCH) { - /* too complex for an inline implementation */ - ExecEvalSubscriptingRefFetch(state, op); + op->d.sbsref.subscriptfunc(state, op, econtext); EEO_NEXT(); } @@ -3122,252 +3116,6 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext *op->resnull = false; } -/* - * Process the subscripts in a SubscriptingRef expression. - * - * If any subscript is NULL, throw error in assignment case, or in fetch case - * set result to NULL and return false (instructing caller to skip the rest - * of the SubscriptingRef sequence). - * - * We convert all the subscripts to plain integers and save them in the - * sbsrefstate->workspace array. - */ -bool -ExecEvalSubscriptingRef(ExprState *state, ExprEvalStep *op) -{ - SubscriptingRefState *sbsrefstate = op->d.sbsref.state; - int *indexes; - - /* - * Allocate workspace if first time through. This is also a good place to - * enforce the implementation limit on number of array subscripts. - */ - if (sbsrefstate->workspace == NULL) - { - if (sbsrefstate->numupper > MAXDIM) - ereport(ERROR, - (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), - errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)", - sbsrefstate->numupper, MAXDIM))); - - /* Should be impossible if parser is sane, but check anyway: */ - if (sbsrefstate->numlower != 0 && - sbsrefstate->numupper != sbsrefstate->numlower) - elog(ERROR, "upper and lower index lists are not same length"); - - /* - * Workspace always has room for MAXDIM subscripts even if we don't - * have that many. This is necessary because array_get/set_slice may - * scribble on the extra entries. - */ - sbsrefstate->workspace = - MemoryContextAlloc(GetMemoryChunkContext(sbsrefstate), - 2 * MAXDIM * sizeof(int)); - } - - /* Process upper subscripts */ - indexes = (int *) sbsrefstate->workspace; - for (int i = 0; i < sbsrefstate->numupper; i++) - { - if (sbsrefstate->upperprovided[i]) - { - /* If any index expr yields NULL, result is NULL or error */ - if (sbsrefstate->upperindexnull[i]) - { - if (sbsrefstate->isassignment) - ereport(ERROR, - (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), - errmsg("array subscript in assignment must not be null"))); - *op->resnull = true; - return false; - } - indexes[i] = DatumGetInt32(sbsrefstate->upperindex[i]); - } - } - - /* Likewise for lower subscripts */ - indexes += MAXDIM; - for (int i = 0; i < sbsrefstate->numlower; i++) - { - if (sbsrefstate->lowerprovided[i]) - { - /* If any index expr yields NULL, result is NULL or error */ - if (sbsrefstate->lowerindexnull[i]) - { - if (sbsrefstate->isassignment) - ereport(ERROR, - (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), - errmsg("array subscript in assignment must not be null"))); - *op->resnull = true; - return false; - } - indexes[i] = DatumGetInt32(sbsrefstate->lowerindex[i]); - } - } - - return true; -} - -/* - * Evaluate SubscriptingRef fetch. - * - * Source container is in step's result variable, - * and indexes have already been evaluated into workspace array. - */ -void -ExecEvalSubscriptingRefFetch(ExprState *state, ExprEvalStep *op) -{ - SubscriptingRefState *sbsrefstate = op->d.sbsref.state; - int *upperindex = (int *) sbsrefstate->workspace; - int *lowerindex = upperindex + MAXDIM; - - /* Should not get here if source container (or any subscript) is null */ - Assert(!(*op->resnull)); - - if (sbsrefstate->numlower == 0) - { - /* Scalar case */ - *op->resvalue = array_get_element(*op->resvalue, - sbsrefstate->numupper, - upperindex, - sbsrefstate->refattrlength, - sbsrefstate->refelemlength, - sbsrefstate->refelembyval, - sbsrefstate->refelemalign, - op->resnull); - } - else - { - /* Slice case */ - *op->resvalue = array_get_slice(*op->resvalue, - sbsrefstate->numupper, - upperindex, - lowerindex, - sbsrefstate->upperprovided, - sbsrefstate->lowerprovided, - sbsrefstate->refattrlength, - sbsrefstate->refelemlength, - sbsrefstate->refelembyval, - sbsrefstate->refelemalign); - } -} - -/* - * Compute old container element/slice value for a SubscriptingRef assignment - * expression. Will only be generated if the new-value subexpression - * contains SubscriptingRef or FieldStore. The value is stored into the - * SubscriptingRefState's prevvalue/prevnull fields. - */ -void -ExecEvalSubscriptingRefOld(ExprState *state, ExprEvalStep *op) -{ - SubscriptingRefState *sbsrefstate = op->d.sbsref.state; - int *upperindex = (int *) sbsrefstate->workspace; - int *lowerindex = upperindex + MAXDIM; - - if (*op->resnull) - { - /* whole array is null, so any element or slice is too */ - sbsrefstate->prevvalue = (Datum) 0; - sbsrefstate->prevnull = true; - } - else if (sbsrefstate->numlower == 0) - { - /* Scalar case */ - sbsrefstate->prevvalue = array_get_element(*op->resvalue, - sbsrefstate->numupper, - upperindex, - sbsrefstate->refattrlength, - sbsrefstate->refelemlength, - sbsrefstate->refelembyval, - sbsrefstate->refelemalign, - &sbsrefstate->prevnull); - } - else - { - /* Slice case */ - /* this is currently unreachable */ - sbsrefstate->prevvalue = array_get_slice(*op->resvalue, - sbsrefstate->numupper, - upperindex, - lowerindex, - sbsrefstate->upperprovided, - sbsrefstate->lowerprovided, - sbsrefstate->refattrlength, - sbsrefstate->refelemlength, - sbsrefstate->refelembyval, - sbsrefstate->refelemalign); - sbsrefstate->prevnull = false; - } -} - -/* - * Evaluate SubscriptingRef assignment. - * - * Input container (possibly null) is in result area, replacement value is in - * SubscriptingRefState's replacevalue/replacenull. - */ -void -ExecEvalSubscriptingRefAssign(ExprState *state, ExprEvalStep *op) -{ - SubscriptingRefState *sbsrefstate = op->d.sbsref.state; - int *upperindex = (int *) sbsrefstate->workspace; - int *lowerindex = upperindex + MAXDIM; - - /* - * For an assignment to a fixed-length container type, both the original - * container and the value to be assigned into it must be non-NULL, else - * we punt and return the original container. - */ - if (sbsrefstate->refattrlength > 0) - { - if (*op->resnull || sbsrefstate->replacenull) - 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(sbsrefstate->refelemtype)); - *op->resnull = false; - } - - if (sbsrefstate->numlower == 0) - { - /* Scalar case */ - *op->resvalue = array_set_element(*op->resvalue, - sbsrefstate->numupper, - upperindex, - sbsrefstate->replacevalue, - sbsrefstate->replacenull, - sbsrefstate->refattrlength, - sbsrefstate->refelemlength, - sbsrefstate->refelembyval, - sbsrefstate->refelemalign); - } - else - { - /* Slice case */ - *op->resvalue = array_set_slice(*op->resvalue, - sbsrefstate->numupper, - upperindex, - lowerindex, - sbsrefstate->upperprovided, - sbsrefstate->lowerprovided, - sbsrefstate->replacevalue, - sbsrefstate->replacenull, - sbsrefstate->refattrlength, - sbsrefstate->refelemlength, - sbsrefstate->refelembyval, - sbsrefstate->refelemalign); - } -} - /* * Evaluate a rowtype coercion operation. * This may require rearranging field positions. diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c index bc9b6771e3..e7f0d84521 100644 --- a/src/backend/jit/llvm/llvmjit_expr.c +++ b/src/backend/jit/llvm/llvmjit_expr.c @@ -1116,22 +1116,35 @@ llvm_compile_expr(ExprState *state) } case EEOP_SBSREF_OLD: - build_EvalXFunc(b, mod, "ExecEvalSubscriptingRefOld", - v_state, op); - LLVMBuildBr(b, opblocks[opno + 1]); - break; - case EEOP_SBSREF_ASSIGN: - build_EvalXFunc(b, mod, "ExecEvalSubscriptingRefAssign", - v_state, op); - LLVMBuildBr(b, opblocks[opno + 1]); - break; - case EEOP_SBSREF_FETCH: - build_EvalXFunc(b, mod, "ExecEvalSubscriptingRefFetch", - v_state, op); - LLVMBuildBr(b, opblocks[opno + 1]); - break; + { + LLVMTypeRef param_types[3]; + LLVMValueRef v_params[3]; + LLVMTypeRef v_functype; + LLVMValueRef v_func; + + param_types[0] = l_ptr(StructExprState); + param_types[1] = l_ptr(TypeSizeT); + param_types[2] = l_ptr(StructExprContext); + + v_functype = LLVMFunctionType(LLVMVoidType(), + param_types, + lengthof(param_types), + false); + v_func = l_ptr_const(op->d.sbsref.subscriptfunc, + l_ptr(v_functype)); + + v_params[0] = v_state; + v_params[1] = l_ptr_const(op, l_ptr(TypeSizeT)); + v_params[2] = v_econtext; + LLVMBuildCall(b, + v_func, + v_params, lengthof(v_params), ""); + + LLVMBuildBr(b, opblocks[opno + 1]); + break; + } case EEOP_CASE_TESTVAL: { @@ -1749,10 +1762,29 @@ llvm_compile_expr(ExprState *state) case EEOP_SBSREF_SUBSCRIPTS: { int jumpdone = op->d.sbsref_subscript.jumpdone; + LLVMTypeRef param_types[3]; + LLVMValueRef v_params[3]; + LLVMTypeRef v_functype; + LLVMValueRef v_func; LLVMValueRef v_ret; - v_ret = build_EvalXFunc(b, mod, "ExecEvalSubscriptingRef", - v_state, op); + param_types[0] = l_ptr(StructExprState); + param_types[1] = l_ptr(TypeSizeT); + param_types[2] = l_ptr(StructExprContext); + + v_functype = LLVMFunctionType(TypeParamBool, + param_types, + lengthof(param_types), + false); + v_func = l_ptr_const(op->d.sbsref_subscript.subscriptfunc, + l_ptr(v_functype)); + + v_params[0] = v_state; + v_params[1] = l_ptr_const(op, l_ptr(TypeSizeT)); + v_params[2] = v_econtext; + v_ret = LLVMBuildCall(b, + v_func, + v_params, lengthof(v_params), ""); v_ret = LLVMBuildZExt(b, v_ret, TypeStorageBool, ""); LLVMBuildCondBr(b, diff --git a/src/backend/jit/llvm/llvmjit_types.c b/src/backend/jit/llvm/llvmjit_types.c index 1ed3cafa2f..ae3c88aad9 100644 --- a/src/backend/jit/llvm/llvmjit_types.c +++ b/src/backend/jit/llvm/llvmjit_types.c @@ -124,10 +124,6 @@ void *referenced_functions[] = ExecEvalSQLValueFunction, ExecEvalScalarArrayOp, ExecEvalSubPlan, - ExecEvalSubscriptingRef, - ExecEvalSubscriptingRefAssign, - ExecEvalSubscriptingRefFetch, - ExecEvalSubscriptingRefOld, ExecEvalSysVar, ExecEvalWholeRowVar, ExecEvalXmlExpr, diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 910906f639..90aebb270b 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -1548,8 +1548,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 687609f59e..9d54830c3d 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -276,8 +276,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/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c index 1dc873ed25..a78d3b634f 100644 --- a/src/backend/nodes/nodeFuncs.c +++ b/src/backend/nodes/nodeFuncs.c @@ -70,7 +70,7 @@ exprType(const Node *expr) const SubscriptingRef *sbsref = (const SubscriptingRef *) expr; /* slice and/or store operations yield the container type */ - if (sbsref->reflowerindexpr || sbsref->refassgnexpr) + if (IsAssignment(sbsref) || sbsref->reflowerindexpr) type = sbsref->refcontainertype; else type = sbsref->refelemtype; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 9c73c605a4..7edaa4c3d2 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -1194,8 +1194,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 169d5581b9..7ece697c75 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -671,8 +671,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 36002f059d..12e11744ec 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -20,6 +20,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" +#include "nodes/subscripting.h" #include "optimizer/optimizer.h" #include "parser/analyze.h" #include "parser/parse_agg.h" @@ -431,6 +432,8 @@ transformIndirection(ParseState *pstate, A_Indirection *ind) { 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; @@ -461,13 +464,20 @@ transformIndirection(ParseState *pstate, A_Indirection *ind) /* process subscripts before this field selection */ if (subscripts) - result = (Node *) transformContainerSubscripts(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, @@ -484,13 +494,20 @@ transformIndirection(ParseState *pstate, A_Indirection *ind) } /* process trailing subscripts, if any */ if (subscripts) - result = (Node *) transformContainerSubscripts(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 6e98fe55fc..31e40acc64 100644 --- a/src/backend/parser/parse_node.c +++ b/src/backend/parser/parse_node.c @@ -184,21 +184,12 @@ pcb_error_callback(void *arg) * transformContainerType() * Identify the types involved in a subscripting operation for container * - * - * On entry, containerType/containerTypmod 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 container type and typmod, and the - * container'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 +void transformContainerType(Oid *containerType, int32 *containerTypmod) { - Oid origContainerType = *containerType; - Oid elementType; - HeapTuple type_tuple_container; - Form_pg_type type_struct_container; - /* * 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 @@ -219,25 +210,6 @@ transformContainerType(Oid *containerType, int32 *containerTypmod) *containerType = INT2ARRAYOID; else if (*containerType == OIDVECTOROID) *containerType = OIDARRAYOID; - - /* Get the type tuple for the container */ - type_tuple_container = SearchSysCache1(TYPEOID, ObjectIdGetDatum(*containerType)); - if (!HeapTupleIsValid(type_tuple_container)) - elog(ERROR, "cache lookup failed for type %u", *containerType); - type_struct_container = (Form_pg_type) GETSTRUCT(type_tuple_container); - - /* needn't check typisdefined since this will fail anyway */ - - elementType = type_struct_container->typelem; - if (elementType == InvalidOid) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("cannot subscript type %s because it is not an array", - format_type_be(origContainerType)))); - - ReleaseSysCache(type_tuple_container); - - return elementType; } /* @@ -254,10 +226,15 @@ transformContainerType(Oid *containerType, int32 *containerTypmod) * 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 container 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 coercion) is placed in + * separate procedures 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 the case the current container type + * doesn't have a subscripting procedure. * * pstate Parse state * containerBase Already-transformed expression for the container as a whole @@ -284,16 +261,12 @@ transformContainerSubscripts(ParseState *pstate, 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, containerType/containerTypMod must be as - * modified by transformContainerType, ie, smash domain to base type. - */ - if (!OidIsValid(elementType)) - elementType = transformContainerType(&containerType, &containerTypMod); + /* Identify the actual container type and element type involved */ + transformContainerType(&containerType, &containerTypMod); /* * A list containing only simple subscripts refers to a single container @@ -327,29 +300,6 @@ transformContainerSubscripts(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 { @@ -357,63 +307,12 @@ transformContainerSubscripts(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.) - */ - if (assignFrom != NULL) - { - Oid typesource = exprType(assignFrom); - Oid typeneeded = isSlice ? containerType : elementType; - Node *newFrom; - - newFrom = coerce_to_target_type(pstate, - assignFrom, typesource, - typeneeded, containerTypMod, - 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; - } - /* * Ready to build the SubscriptingRef node. */ @@ -422,13 +321,12 @@ transformContainerSubscripts(ParseState *pstate, sbsref->refassgnexpr = (Expr *) assignFrom; sbsref->refcontainertype = containerType; - sbsref->refelemtype = elementType; sbsref->reftypmod = containerTypMod; /* refcollid will be set by parse_collate.c */ sbsref->refupperindexpr = upperIndexpr; sbsref->reflowerindexpr = lowerIndexpr; + sbsref->refindexprslice = indexprSlice; sbsref->refexpr = (Expr *) containerBase; - sbsref->refassgnexpr = (Expr *) assignFrom; return sbsref; } diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 9de0cff833..3230661eac 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -20,6 +20,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" +#include "nodes/subscripting.h" #include "parser/parse_coerce.h" #include "parser/parse_expr.h" #include "parser/parse_func.h" @@ -848,27 +849,21 @@ 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 its custom code should + * define types. + */ + if (!targetIsSubscripting) { - if (targetIsSubscripting) - 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" @@ -879,6 +874,8 @@ transformAssignmentIndirection(ParseState *pstate, errhint("You will need to rewrite or cast the expression."), parser_errposition(pstate, location))); } + else + result = rhs; return result; } @@ -903,26 +900,39 @@ transformAssignmentSubscripts(ParseState *pstate, Node *result; Oid containerType; int32 containerTypMod; - Oid elementTypeId; - Oid typeNeeded; Oid collationNeeded; + SubscriptingRef *sbsref; + SubscriptRoutines *sbsroutines; Assert(subscripts != NIL); /* Identify the actual array type and element type involved */ containerType = targetTypeId; containerTypMod = targetTypMod; - elementTypeId = transformContainerType(&containerType, &containerTypMod); - /* Identify type that RHS must provide */ - typeNeeded = isSlice ? containerType : elementTypeId; + /* process subscripts */ + sbsref = transformContainerSubscripts(pstate, + basenode, + containerType, + exprType(rhs), + containerTypMod, + subscripts, + rhs); + + sbsroutines = getSubscriptingRoutines(sbsref->refcontainertype); + + /* + * 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 a container type. In * that case use collation of the base type. */ - if (containerType == targetTypeId) + if (sbsref->refcontainertype == containerType) collationNeeded = targetCollation; else collationNeeded = get_typcollation(containerType); @@ -932,25 +942,22 @@ transformAssignmentSubscripts(ParseState *pstate, NULL, targetName, true, - typeNeeded, - containerTypMod, + sbsref->refassgntype, + sbsref->reftypmod, collationNeeded, indirection, next_indirection, rhs, location); - /* process subscripts */ - result = (Node *) transformContainerSubscripts(pstate, - basenode, - containerType, - elementTypeId, - containerTypMod, - subscripts, - rhs); + /* Provide fully prepared subscripting 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 (containerType != targetTypeId) + if (sbsref->refcontainertype != targetTypeId) { Oid resulttype = exprType(result); diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c index 4c8a739bc4..b7415e8790 100644 --- a/src/backend/utils/adt/arrayfuncs.c +++ b/src/backend/utils/adt/arrayfuncs.c @@ -19,19 +19,25 @@ #include "access/htup_details.h" #include "catalog/pg_type.h" +#include "executor/execExpr.h" #include "funcapi.h" #include "libpq/pqformat.h" +#include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" +#include "nodes/subscripting.h" #include "nodes/supportnodes.h" #include "optimizer/optimizer.h" +#include "parser/parse_coerce.h" #include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/arrayaccess.h" #include "utils/builtins.h" #include "utils/datum.h" +#include "utils/fmgroids.h" #include "utils/lsyscache.h" #include "utils/memutils.h" #include "utils/selfuncs.h" +#include "utils/syscache.h" #include "utils/typcache.h" @@ -88,6 +94,25 @@ typedef struct ArrayIteratorData int current_item; /* the item # we're at in the array */ } ArrayIteratorData; +/* SubscriptingRefState.workspace for array subscripting operations */ +typedef struct ArraySubWorkspace +{ + /* Values determined during expression compilation */ + Oid refelemtype; /* OID of the array element type */ + int16 refattrlength; /* typlen of array type */ + int16 refelemlength; /* typlen of the array element type */ + bool refelembyval; /* is the element type pass-by-value? */ + char refelemalign; /* typalign of the element type */ + + /* + * Subscript values converted to integers. Note that these arrays must be + * of length MAXDIM even when dealing with fewer subscripts, because + * array_get/set_slice may scribble on the extra entries. + */ + int upperindex[MAXDIM]; + int lowerindex[MAXDIM]; +} ArraySubWorkspace; + static bool array_isspace(char ch); static int ArrayCount(const char *str, int *dim, char typdelim); static void ReadArrayStr(char *arrayStr, const char *origStr, @@ -6628,3 +6653,518 @@ width_bucket_array_variable(Datum operand, return left; } + + +/* + * XXX undocumented is unacceptable + */ +static SubscriptingRef * +array_subscript_prepare(bool isAssignment, SubscriptingRef *sbsref) +{ + Oid array_type = sbsref->refcontainertype; + HeapTuple type_tuple_container; + Form_pg_type type_struct_container; + bool is_slice = sbsref->reflowerindexpr != NIL; + + /* Get the type tuple for the container */ + type_tuple_container = SearchSysCache1(TYPEOID, ObjectIdGetDatum(array_type)); + if (!HeapTupleIsValid(type_tuple_container)) + elog(ERROR, "cache lookup failed for type %u", array_type); + type_struct_container = (Form_pg_type) GETSTRUCT(type_tuple_container); + + /* needn't check typisdefined since this will fail anyway */ + sbsref->refelemtype = type_struct_container->typelem; + + /* Identify type that RHS must provide */ + if (isAssignment) + sbsref->refassgntype = is_slice ? sbsref->refcontainertype : sbsref->refelemtype; + + ReleaseSysCache(type_tuple_container); + + return sbsref; +} + +/* + * XXX undocumented is unacceptable + */ +static SubscriptingRef * +array_subscript_validate(bool isAssignment, SubscriptingRef *sbsref, + ParseState *pstate) +{ + bool is_slice = sbsref->reflowerindexpr != NIL; + Oid typeneeded = InvalidOid, + typesource = InvalidOid; + Node *new_from; + Node *subexpr; + List *upperIndexpr = NIL; + List *lowerIndexpr = NIL; + ListCell *u, + *l, + *s; + + foreach(u, sbsref->refupperindexpr) + { + subexpr = (Node *) lfirst(u); + + if (subexpr == NULL) + { + upperIndexpr = lappend(upperIndexpr, subexpr); + continue; + } + + 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(subexpr)))); + + upperIndexpr = lappend(upperIndexpr, subexpr); + } + + sbsref->refupperindexpr = upperIndexpr; + + forboth(l, sbsref->reflowerindexpr, s, sbsref->refindexprslice) + { + A_Indices *ai = (A_Indices *) lfirst(s); + + subexpr = (Node *) lfirst(l); + + if (subexpr == NULL && !ai->is_slice) + { + /* Make a constant 1 */ + subexpr = (Node *) makeConst(INT4OID, + -1, + InvalidOid, + sizeof(int32), + Int32GetDatum(1), + false, + true); /* pass by value */ + } + + if (subexpr == NULL) + { + lowerIndexpr = lappend(lowerIndexpr, subexpr); + continue; + } + + 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(subexpr)))); + + lowerIndexpr = lappend(lowerIndexpr, subexpr); + } + + sbsref->reflowerindexpr = lowerIndexpr; + + if (isAssignment) + { + SubscriptingRef *assignRef = (SubscriptingRef *) sbsref; + Node *assignExpr = (Node *) assignRef->refassgnexpr; + + typesource = exprType(assignExpr); + typeneeded = is_slice ? sbsref->refcontainertype : sbsref->refelemtype; + new_from = coerce_to_target_type(pstate, + assignExpr, typesource, + typeneeded, sbsref->reftypmod, + COERCION_ASSIGNMENT, + COERCE_IMPLICIT_CAST, + -1); + if (new_from == NULL) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("array assignment requires type %s" + " but expression is of type %s", + format_type_be(sbsref->refelemtype), + format_type_be(typesource)), + errhint("You will need to rewrite or cast the expression."), + parser_errposition(pstate, exprLocation(assignExpr)))); + assignRef->refassgnexpr = (Expr *) new_from; + } + + sbsref->refnestedfunc = F_ARRAY_SUBSCRIPT_HANDLER; + + /* Verify subscript list lengths are within limit */ + if (list_length(sbsref->refupperindexpr) > MAXDIM) + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)", + list_length(sbsref->refupperindexpr), MAXDIM))); + + if (list_length(sbsref->reflowerindexpr) > MAXDIM) + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)", + list_length(sbsref->reflowerindexpr), MAXDIM))); + + return sbsref; +} + +/* + * Process the subscripts in a SubscriptingRef expression. + * + * If any subscript is NULL, throw error in assignment case, or in fetch case + * set result to NULL and return false (instructing caller to skip the rest + * of the SubscriptingRef sequence). + * + * We convert all the subscripts to plain integers and save them in the + * sbsrefstate->workspace arrays. + */ +static bool +array_subscript_subscripts(ExprState *state, + ExprEvalStep *op, + ExprContext *econtext) +{ + SubscriptingRefState *sbsrefstate = op->d.sbsref_subscript.state; + ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace; + + /* Process upper subscripts */ + for (int i = 0; i < sbsrefstate->numupper; i++) + { + if (sbsrefstate->upperprovided[i]) + { + /* If any index expr yields NULL, result is NULL or error */ + if (sbsrefstate->upperindexnull[i]) + { + if (sbsrefstate->isassignment) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("array subscript in assignment must not be null"))); + *op->resnull = true; + return false; + } + workspace->upperindex[i] = DatumGetInt32(sbsrefstate->upperindex[i]); + } + } + + /* Likewise for lower subscripts */ + for (int i = 0; i < sbsrefstate->numlower; i++) + { + if (sbsrefstate->lowerprovided[i]) + { + /* If any index expr yields NULL, result is NULL or error */ + if (sbsrefstate->lowerindexnull[i]) + { + if (sbsrefstate->isassignment) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("array subscript in assignment must not be null"))); + *op->resnull = true; + return false; + } + workspace->lowerindex[i] = DatumGetInt32(sbsrefstate->lowerindex[i]); + } + } + + return true; +} + +/* + * Evaluate SubscriptingRef fetch for an array element. + * + * Source container is in step's result variable, + * and indexes have already been evaluated into workspace array. + */ +static void +array_subscript_fetch(ExprState *state, + ExprEvalStep *op, + ExprContext *econtext) +{ + SubscriptingRefState *sbstate = op->d.sbsref.state; + ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbstate->workspace; + + /* Should not get here if source array (or any subscript) is null */ + Assert(!(*op->resnull)); + + *op->resvalue = array_get_element(*op->resvalue, + sbstate->numupper, + workspace->upperindex, + workspace->refattrlength, + workspace->refelemlength, + workspace->refelembyval, + workspace->refelemalign, + op->resnull); +} + +/* + * Evaluate SubscriptingRef fetch for an array slice. + * + * Source container is in step's result variable, + * and indexes have already been evaluated into workspace array. + */ +static void +array_subscript_fetch_slice(ExprState *state, + ExprEvalStep *op, + ExprContext *econtext) +{ + SubscriptingRefState *sbstate = op->d.sbsref.state; + ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbstate->workspace; + + /* Should not get here if source array (or any subscript) is null */ + Assert(!(*op->resnull)); + + *op->resvalue = array_get_slice(*op->resvalue, + sbstate->numupper, + workspace->upperindex, + workspace->lowerindex, + sbstate->upperprovided, + sbstate->lowerprovided, + workspace->refattrlength, + workspace->refelemlength, + workspace->refelembyval, + workspace->refelemalign); +} + +/* + * Evaluate SubscriptingRef assignment for an array element assignment. + * + * Input container (possibly null) is in result area, replacement value is in + * SubscriptingRefState's replacevalue/replacenull. + */ +static void +array_subscript_assign(ExprState *state, + ExprEvalStep *op, + ExprContext *econtext) +{ + SubscriptingRefState *sbstate = op->d.sbsref.state; + ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbstate->workspace; + Datum arraySource = *op->resvalue; + + /* + * For an assignment to a fixed-length array type, both the original array + * and the value to be assigned into it must be non-NULL, else we punt and + * return the original array. + */ + if (workspace->refattrlength > 0) + { + if (*op->resnull || sbstate->replacenull) + 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) + { + arraySource = PointerGetDatum(construct_empty_array(workspace->refelemtype)); + *op->resnull = false; + } + + *op->resvalue = array_set_element(arraySource, + sbstate->numupper, + workspace->upperindex, + sbstate->replacevalue, + sbstate->replacenull, + workspace->refattrlength, + workspace->refelemlength, + workspace->refelembyval, + workspace->refelemalign); +} + +/* + * Evaluate SubscriptingRef assignment for an array slice assignment. + * + * Input container (possibly null) is in result area, replacement value is in + * SubscriptingRefState's replacevalue/replacenull. + */ +static void +array_subscript_assign_slice(ExprState *state, + ExprEvalStep *op, + ExprContext *econtext) +{ + SubscriptingRefState *sbstate = op->d.sbsref.state; + ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbstate->workspace; + Datum arraySource = *op->resvalue; + + /* + * For an assignment to a fixed-length array type, both the original array + * and the value to be assigned into it must be non-NULL, else we punt and + * return the original array. + */ + if (workspace->refattrlength > 0) + { + if (*op->resnull || sbstate->replacenull) + 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) + { + arraySource = PointerGetDatum(construct_empty_array(workspace->refelemtype)); + *op->resnull = false; + } + + *op->resvalue = array_set_slice(arraySource, + sbstate->numupper, + workspace->upperindex, + workspace->lowerindex, + sbstate->upperprovided, + sbstate->lowerprovided, + sbstate->replacevalue, + sbstate->replacenull, + workspace->refattrlength, + workspace->refelemlength, + workspace->refelembyval, + workspace->refelemalign); +} + +/* + * Compute old array element value for a SubscriptingRef assignment + * expression. Will only be called if the new-value subexpression + * contains SubscriptingRef or FieldStore. The value is stored into the + * SubscriptingRefState's prevvalue/prevnull fields. + */ +static void +array_subscript_fetch_old(ExprState *state, + ExprEvalStep *op, + ExprContext *econtext) +{ + SubscriptingRefState *sbstate = op->d.sbsref.state; + ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbstate->workspace; + + if (*op->resnull) + { + /* whole array is null, so any element is too */ + sbstate->prevvalue = (Datum) 0; + sbstate->prevnull = true; + } + else + sbstate->prevvalue = array_get_element(*op->resvalue, + sbstate->numupper, + workspace->upperindex, + workspace->refattrlength, + workspace->refelemlength, + workspace->refelembyval, + workspace->refelemalign, + &sbstate->prevnull); +} + +/* + * Compute old array slice value for a SubscriptingRef assignment + * expression. Will only be called if the new-value subexpression + * contains SubscriptingRef or FieldStore. The value is stored into the + * SubscriptingRefState's prevvalue/prevnull fields. + */ +static void +array_subscript_fetch_old_slice(ExprState *state, + ExprEvalStep *op, + ExprContext *econtext) +{ + SubscriptingRefState *sbstate = op->d.sbsref.state; + ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbstate->workspace; + + if (*op->resnull) + { + /* whole array is null, so any slice is too */ + sbstate->prevvalue = (Datum) 0; + sbstate->prevnull = true; + } + else + { + sbstate->prevvalue = array_get_slice(*op->resvalue, + sbstate->numupper, + workspace->upperindex, + workspace->lowerindex, + sbstate->upperprovided, + sbstate->lowerprovided, + workspace->refattrlength, + workspace->refelemlength, + workspace->refelembyval, + workspace->refelemalign); + /* slices of non-null arrays are never null */ + sbstate->prevnull = false; + } +} + +/* + * Set up execution state for an array subscript operation. + */ +static void +array_exec_setup(SubscriptingRef *sbsref, + SubscriptingRefState *sbsrefstate) +{ + bool is_slice = (sbsrefstate->numlower != 0); + ArraySubWorkspace *workspace; + + /* + * Allocate type-specific workspace. This is also a good place to enforce + * the implementation limit on number of array subscripts. + */ + if (sbsrefstate->numupper > MAXDIM) + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)", + sbsrefstate->numupper, MAXDIM))); + + /* Should be impossible if parser is sane, but check anyway: */ + if (sbsrefstate->numlower != 0 && + sbsrefstate->numupper != sbsrefstate->numlower) + elog(ERROR, "upper and lower index lists are not same length"); + + workspace = (ArraySubWorkspace *) + MemoryContextAlloc(GetMemoryChunkContext(sbsrefstate), + sizeof(ArraySubWorkspace)); + sbsrefstate->workspace = workspace; + + /* + * Collect datatype details we'll need at execution. + */ + workspace->refelemtype = sbsref->refelemtype; + workspace->refattrlength = get_typlen(sbsref->refcontainertype); + get_typlenbyvalalign(sbsref->refelemtype, + &workspace->refelemlength, + &workspace->refelembyval, + &workspace->refelemalign); + + /* Pass back pointers to step execution functions */ + sbsrefstate->sbs_subscripts = array_subscript_subscripts; + if (is_slice) + { + sbsrefstate->sbs_fetch = array_subscript_fetch_slice; + sbsrefstate->sbs_assign = array_subscript_assign_slice; + sbsrefstate->sbs_fetch_old = array_subscript_fetch_old_slice; + } + else + { + sbsrefstate->sbs_fetch = array_subscript_fetch; + sbsrefstate->sbs_assign = array_subscript_assign; + sbsrefstate->sbs_fetch_old = array_subscript_fetch_old; + } +} + +/* + * Handle array-type subscripting logic. + */ +Datum +array_subscript_handler(PG_FUNCTION_ARGS) +{ + SubscriptRoutines *sbsroutines = (SubscriptRoutines *) + palloc(sizeof(SubscriptRoutines)); + + sbsroutines->prepare = array_subscript_prepare; + sbsroutines->validate = array_subscript_validate; + sbsroutines->exec_setup = array_exec_setup; + + PG_RETURN_POINTER(sbsroutines); +} diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index c2c6df2a4f..f516a5011e 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -7999,7 +7999,7 @@ get_rule_expr(Node *node, deparse_context *context, * EXPLAIN tries to print the targetlist of a plan resulting * from such a statement. */ - if (sbsref->refassgnexpr) + if (IsAssignment(sbsref)) { Node *refassgnexpr; diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index ae23299162..1297695692 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -2966,6 +2966,51 @@ type_is_collatable(Oid typid) } +/* + * get_typsubshandler + * + * Given the type OID, return the type's subscripting procedure's OID, + * if it has one. + */ +RegProcedure +get_typsubshandler(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; +} + +/* + * getSubscriptingRoutines + * + * Given the type OID, fetch the type's subscripting functions. + * Fail if type is not subscriptable. + */ +struct SubscriptRoutines * +getSubscriptingRoutines(Oid typid) +{ + RegProcedure typsubshandler = get_typsubshandler(typid); + + if (!OidIsValid(typsubshandler)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("cannot subscript type %s because it does not support subscripting", + format_type_be(typid)))); + + return (struct SubscriptRoutines *) + DatumGetPointer(OidFunctionCall0(typsubshandler)); +} + + /* ---------- STATISTICS CACHE ---------- */ /* diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index fc2202b843..9fa9990efe 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -10936,6 +10936,13 @@ proargnames => '{max_data_alignment,database_block_size,blocks_per_segment,wal_block_size,bytes_per_wal_segment,max_identifier_length,max_index_columns,max_toast_chunk_size,large_object_chunk_size,float8_pass_by_value,data_page_checksum_version}', prosrc => 'pg_control_init' }, +{ oid => '6099', + descr => 'Array subscripting logic', + proname => 'array_subscript_handler', + prorettype => 'internal', + proargtypes => 'internal', + prosrc => 'array_subscript_handler' }, + # collation management functions { oid => '3445', descr => 'import collations from operating system', proname => 'pg_import_system_collations', procost => '100', diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat index 21a467a7a7..9f29461e39 100644 --- a/src/include/catalog/pg_type.dat +++ b/src/include/catalog/pg_type.dat @@ -50,7 +50,8 @@ typname => 'name', typlen => 'NAMEDATALEN', typbyval => 'f', typcategory => 'S', typelem => 'char', typinput => 'namein', typoutput => 'nameout', typreceive => 'namerecv', typsend => 'namesend', - typalign => 'c', typcollation => 'C' }, + typalign => 'c', typcollation => 'C', + typsubshandler => 'array_subscript_handler' }, { oid => '20', array_type_oid => '1016', descr => '~18 digit integer, 8-byte storage', typname => 'int8', typlen => '8', typbyval => 'FLOAT8PASSBYVAL', @@ -66,7 +67,7 @@ typname => 'int2vector', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'int2', typinput => 'int2vectorin', typoutput => 'int2vectorout', typreceive => 'int2vectorrecv', typsend => 'int2vectorsend', - typalign => 'i' }, + typalign => 'i', typsubshandler => 'array_subscript_handler' }, { oid => '23', array_type_oid => '1007', descr => '-2 billion to 2 billion integer, 4-byte storage', typname => 'int4', typlen => '4', typbyval => 't', typcategory => 'N', @@ -105,7 +106,8 @@ descr => 'array of oids, used in system tables', typname => 'oidvector', typlen => '-1', typbyval => 'f', typcategory => 'A', typelem => 'oid', typinput => 'oidvectorin', typoutput => 'oidvectorout', - typreceive => 'oidvectorrecv', typsend => 'oidvectorsend', typalign => 'i' }, + typreceive => 'oidvectorrecv', 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 @@ -179,32 +181,37 @@ descr => 'geometric point \'(x, y)\'', typname => 'point', typlen => '16', typbyval => 'f', typcategory => 'G', typelem => 'float8', typinput => 'point_in', typoutput => 'point_out', - typreceive => 'point_recv', typsend => 'point_send', typalign => 'd' }, + typreceive => 'point_recv', typsend => 'point_send', typalign => 'd', + typsubshandler => 'array_subscript_handler' }, { oid => '601', array_type_oid => '1018', descr => 'geometric line segment \'(pt1,pt2)\'', typname => 'lseg', typlen => '32', typbyval => 'f', typcategory => 'G', typelem => 'point', typinput => 'lseg_in', typoutput => 'lseg_out', - typreceive => 'lseg_recv', typsend => 'lseg_send', typalign => 'd' }, + typreceive => 'lseg_recv', typsend => 'lseg_send', typalign => 'd', + typsubshandler => 'array_subscript_handler' }, { oid => '602', array_type_oid => '1019', descr => 'geometric path \'(pt1,...)\'', typname => 'path', typlen => '-1', typbyval => 'f', typcategory => 'G', typinput => 'path_in', typoutput => 'path_out', typreceive => 'path_recv', - typsend => 'path_send', typalign => 'd', typstorage => 'x' }, + typsend => 'path_send', typalign => 'd', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '603', array_type_oid => '1020', descr => 'geometric box \'(lower left,upper right)\'', typname => 'box', typlen => '32', typbyval => 'f', typcategory => 'G', typdelim => ';', typelem => 'point', typinput => 'box_in', typoutput => 'box_out', typreceive => 'box_recv', typsend => 'box_send', - typalign => 'd' }, + typalign => 'd', typsubshandler => 'array_subscript_handler' }, { oid => '604', array_type_oid => '1027', descr => 'geometric polygon \'(pt1,...)\'', typname => 'polygon', typlen => '-1', typbyval => 'f', typcategory => 'G', typinput => 'poly_in', typoutput => 'poly_out', typreceive => 'poly_recv', - typsend => 'poly_send', typalign => 'd', typstorage => 'x' }, + typsend => 'poly_send', typalign => 'd', typstorage => 'x', + typsubshandler => 'array_subscript_handler' }, { oid => '628', array_type_oid => '629', descr => 'geometric line', typname => 'line', typlen => '24', typbyval => 'f', typcategory => 'G', typelem => 'float8', typinput => 'line_in', typoutput => 'line_out', - typreceive => 'line_recv', typsend => 'line_send', typalign => 'd' }, + typreceive => 'line_recv', typsend => 'line_send', typalign => 'd', + typsubshandler => 'array_subscript_handler' }, # OIDS 700 - 799 @@ -263,7 +270,7 @@ { oid => '1033', array_type_oid => '1034', descr => 'access control list', typname => 'aclitem', typlen => '12', typbyval => 'f', typcategory => 'U', typinput => 'aclitemin', typoutput => 'aclitemout', typreceive => '-', - typsend => '-', typalign => 'i' }, + typsend => '-', typalign => 'i', typsubshandler => 'array_subscript_handler' }, { oid => '1042', array_type_oid => '1014', descr => 'char(length), blank-padded string, fixed storage length', typname => 'bpchar', typlen => '-1', typbyval => 'f', typcategory => 'S', @@ -302,7 +309,8 @@ typcategory => 'D', typispreferred => 't', typinput => 'timestamptz_in', typoutput => 'timestamptz_out', typreceive => 'timestamptz_recv', typsend => 'timestamptz_send', typmodin => 'timestamptztypmodin', - typmodout => 'timestamptztypmodout', typalign => 'd' }, + typmodout => 'timestamptztypmodout', typalign => 'd', + typsubshandler => 'array_subscript_handler' }, { oid => '1186', array_type_oid => '1187', descr => '@ , time interval', typname => 'interval', typlen => '16', typbyval => 'f', typcategory => 'T', diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h index 6099e5f57c..3e10179e1c 100644 --- a/src/include/catalog/pg_type.h +++ b/src/include/catalog/pg_type.h @@ -221,6 +221,12 @@ CATALOG(pg_type,1247,TypeRelationId) BKI_BOOTSTRAP BKI_ROWTYPE_OID(71,TypeRelati */ Oid typcollation BKI_DEFAULT(0) BKI_LOOKUP(pg_collation); + /* + * Type specific subscripting logic. If typsubshandler is NULL, 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 */ /* @@ -363,7 +369,8 @@ extern ObjectAddress TypeCreate(Oid newTypeOid, int32 typeMod, int32 typNDims, bool typeNotNull, - Oid typeCollation); + Oid typeCollation, + Oid subscriptingParseProcedure); extern void GenerateTypeDependencies(HeapTuple typeTuple, Relation typeCatalog, diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h index b768c30b74..baa2363bed 100644 --- a/src/include/executor/execExpr.h +++ b/src/include/executor/execExpr.h @@ -32,6 +32,11 @@ typedef void (*ExecEvalSubroutine) (ExprState *state, struct ExprEvalStep *op, ExprContext *econtext); +/* API for out-of-line evaluation subroutines returning bool */ +typedef bool (*ExecEvalBoolSubroutine) (ExprState *state, + struct ExprEvalStep *op, + ExprContext *econtext); + /* * Discriminator for ExprEvalSteps. * @@ -497,6 +502,7 @@ typedef struct ExprEvalStep /* for EEOP_SBSREF_SUBSCRIPTS */ struct { + ExecEvalBoolSubroutine subscriptfunc; /* evaluation subroutine */ /* too big to have inline */ struct SubscriptingRefState *state; int jumpdone; /* jump here on null */ @@ -505,6 +511,7 @@ typedef struct ExprEvalStep /* for EEOP_SBSREF_OLD / ASSIGN / FETCH */ struct { + ExecEvalSubroutine subscriptfunc; /* evaluation subroutine */ /* too big to have inline */ struct SubscriptingRefState *state; } sbsref; @@ -638,12 +645,6 @@ typedef struct SubscriptingRefState { bool isassignment; /* is it assignment, or just fetch? */ - 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 */ - /* workspace for type-specific subscripting code */ void *workspace; @@ -667,6 +668,17 @@ typedef struct SubscriptingRefState /* if we have a nested assignment, SBSREF_OLD puts old value here */ Datum prevvalue; bool prevnull; + + /* + * Step execution function pointers returned by exec_setup method. These + * are not needed at runtime, only during expression compilation; but it's + * not worth complicating exec_setup's API by making an additional struct + * to hold them. + */ + ExecEvalBoolSubroutine sbs_subscripts; /* process subscripts */ + ExecEvalSubroutine sbs_fetch; /* function to fetch an element */ + ExecEvalSubroutine sbs_assign; /* function to assign an element */ + ExecEvalSubroutine sbs_fetch_old; /* fetch old value for assignment */ } SubscriptingRefState; @@ -711,10 +723,6 @@ extern void ExecEvalFieldStoreDeForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext); extern void ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext); -extern bool ExecEvalSubscriptingRef(ExprState *state, ExprEvalStep *op); -extern void ExecEvalSubscriptingRefFetch(ExprState *state, ExprEvalStep *op); -extern void ExecEvalSubscriptingRefOld(ExprState *state, ExprEvalStep *op); -extern void ExecEvalSubscriptingRefAssign(ExprState *state, ExprEvalStep *op); extern void ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext); extern void ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op); diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index cdbe781c73..36ceac084e 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -425,13 +425,18 @@ typedef struct SubscriptingRef Expr xpr; 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 */ @@ -439,6 +444,8 @@ typedef struct SubscriptingRef * 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 0000000000..1aba2f80bc --- /dev/null +++ b/src/include/nodes/subscripting.h @@ -0,0 +1,38 @@ +/*------------------------------------------------------------------------- + * + * subscripting.h + * API for generic type subscripting + * + * Portions Copyright (c) 1996-2020, 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 void (*SubscriptingExecSetup) (SubscriptingRef *sbsref, + struct SubscriptingRefState *sbsrefstate); + +typedef struct SubscriptRoutines +{ + SubscriptingPrepare prepare; + SubscriptingValidate validate; + SubscriptingExecSetup exec_setup; +} SubscriptRoutines; + +#endif /* SUBSCRIPTING_H */ diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index d25819aa28..fcc6c426e7 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -313,7 +313,7 @@ extern void setup_parser_errposition_callback(ParseCallbackState *pcbstate, ParseState *pstate, int location); extern void cancel_parser_errposition_callback(ParseCallbackState *pcbstate); -extern Oid transformContainerType(Oid *containerType, int32 *containerTypmod); +extern void transformContainerType(Oid *containerType, int32 *containerTypmod); extern SubscriptingRef *transformContainerSubscripts(ParseState *pstate, Node *containerBase, @@ -322,6 +322,7 @@ extern SubscriptingRef *transformContainerSubscripts(ParseState *pstate, int32 containerTypMod, List *indirection, Node *assignFrom); + 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 fecfe1f4f6..38cd2940df 100644 --- a/src/include/utils/lsyscache.h +++ b/src/include/utils/lsyscache.h @@ -17,6 +17,9 @@ #include "access/htup.h" #include "nodes/pg_list.h" +/* avoid including subscripting.h here */ +struct SubscriptRoutines; + /* Result list element for get_op_btree_interpretation */ typedef struct OpBtreeInterpretation { @@ -172,6 +175,8 @@ extern void getTypeBinaryOutputInfo(Oid type, Oid *typSend, bool *typIsVarlena); extern Oid get_typmodin(Oid typid); extern Oid get_typcollation(Oid typid); extern bool type_is_collatable(Oid typid); +extern RegProcedure get_typsubshandler(Oid typid); +extern struct SubscriptRoutines *getSubscriptingRoutines(Oid typid); extern Oid getBaseType(Oid typid); extern Oid getBaseTypeAndTypmod(Oid typid, int32 *typmod); extern int32 get_typavgwidth(Oid typid, int32 typmod);