I wrote: > Attached is a patch series that allows us to create arrays of domain > types.
Here's a rebased-up-to-HEAD version of this patch set. The only actual change is removal of a no-longer-needed hunk in pl_exec.c. regards, tom lane
diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c index 9d75e86..d7db32e 100644 *** a/src/backend/optimizer/prep/preptlist.c --- b/src/backend/optimizer/prep/preptlist.c *************** expand_targetlist(List *tlist, int comma *** 306,314 **** new_expr = coerce_to_domain(new_expr, InvalidOid, -1, atttype, COERCE_IMPLICIT_CAST, -1, - false, false); } else --- 306,314 ---- new_expr = coerce_to_domain(new_expr, InvalidOid, -1, atttype, + COERCION_IMPLICIT, COERCE_IMPLICIT_CAST, -1, false); } else diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index e79ad26..5a241bd 100644 *** a/src/backend/parser/parse_coerce.c --- b/src/backend/parser/parse_coerce.c *************** *** 34,48 **** static Node *coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod, ! CoercionForm cformat, int location, ! bool isExplicit, bool hideInputCoercion); static void hide_coercion_node(Node *node); static Node *build_coercion_expression(Node *node, CoercionPathType pathtype, Oid funcId, Oid targetTypeId, int32 targetTypMod, ! CoercionForm cformat, int location, ! bool isExplicit); static Node *coerce_record_to_complex(ParseState *pstate, Node *node, Oid targetTypeId, CoercionContext ccontext, --- 34,49 ---- static Node *coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod, ! CoercionContext ccontext, CoercionForm cformat, ! int location, ! bool hideInputCoercion); static void hide_coercion_node(Node *node); static Node *build_coercion_expression(Node *node, CoercionPathType pathtype, Oid funcId, Oid targetTypeId, int32 targetTypMod, ! CoercionContext ccontext, CoercionForm cformat, ! int location); static Node *coerce_record_to_complex(ParseState *pstate, Node *node, Oid targetTypeId, CoercionContext ccontext, *************** coerce_to_target_type(ParseState *pstate *** 110,117 **** */ result = coerce_type_typmod(result, targettype, targettypmod, ! cformat, location, ! (cformat != COERCE_IMPLICIT_CAST), (result != expr && !IsA(result, Const))); if (expr != origexpr) --- 111,117 ---- */ result = coerce_type_typmod(result, targettype, targettypmod, ! ccontext, cformat, location, (result != expr && !IsA(result, Const))); if (expr != origexpr) *************** coerce_type(ParseState *pstate, Node *no *** 355,361 **** result = coerce_to_domain(result, baseTypeId, baseTypeMod, targetTypeId, ! cformat, location, false, false); ReleaseSysCache(baseType); --- 355,362 ---- result = coerce_to_domain(result, baseTypeId, baseTypeMod, targetTypeId, ! ccontext, cformat, location, ! false); ReleaseSysCache(baseType); *************** coerce_type(ParseState *pstate, Node *no *** 417,436 **** result = build_coercion_expression(node, pathtype, funcId, baseTypeId, baseTypeMod, ! cformat, location, ! (cformat != COERCE_IMPLICIT_CAST)); /* * If domain, coerce to the domain type and relabel with domain ! * type ID. We can skip the internal length-coercion step if the ! * selected coercion function was a type-and-length coercion. */ if (targetTypeId != baseTypeId) result = coerce_to_domain(result, baseTypeId, baseTypeMod, targetTypeId, ! cformat, location, true, ! exprIsLengthCoercion(result, ! NULL)); } else { --- 418,434 ---- result = build_coercion_expression(node, pathtype, funcId, baseTypeId, baseTypeMod, ! ccontext, cformat, location); /* * If domain, coerce to the domain type and relabel with domain ! * type ID, hiding the previous coercion node. */ if (targetTypeId != baseTypeId) result = coerce_to_domain(result, baseTypeId, baseTypeMod, targetTypeId, ! ccontext, cformat, location, ! true); } else { *************** coerce_type(ParseState *pstate, Node *no *** 444,450 **** * then we won't need a RelabelType node. */ result = coerce_to_domain(node, InvalidOid, -1, targetTypeId, ! cformat, location, false, false); if (result == node) { /* --- 442,449 ---- * then we won't need a RelabelType node. */ result = coerce_to_domain(node, InvalidOid, -1, targetTypeId, ! ccontext, cformat, location, ! false); if (result == node) { /* *************** can_coerce_type(int nargs, Oid *input_ty *** 636,654 **** * 'baseTypeMod': base type typmod of domain, if known (pass -1 if caller * has not bothered to look this up) * 'typeId': target type to coerce to ! * 'cformat': coercion format * 'location': coercion request location * 'hideInputCoercion': if true, hide the input coercion under this one. - * 'lengthCoercionDone': if true, caller already accounted for length, - * ie the input is already of baseTypMod as well as baseTypeId. * * If the target type isn't a domain, the given 'arg' is returned as-is. */ Node * coerce_to_domain(Node *arg, Oid baseTypeId, int32 baseTypeMod, Oid typeId, ! CoercionForm cformat, int location, ! bool hideInputCoercion, ! bool lengthCoercionDone) { CoerceToDomain *result; --- 635,651 ---- * 'baseTypeMod': base type typmod of domain, if known (pass -1 if caller * has not bothered to look this up) * 'typeId': target type to coerce to ! * 'ccontext': context indicator to control coercions ! * 'cformat': coercion display format * 'location': coercion request location * 'hideInputCoercion': if true, hide the input coercion under this one. * * If the target type isn't a domain, the given 'arg' is returned as-is. */ Node * coerce_to_domain(Node *arg, Oid baseTypeId, int32 baseTypeMod, Oid typeId, ! CoercionContext ccontext, CoercionForm cformat, int location, ! bool hideInputCoercion) { CoerceToDomain *result; *************** coerce_to_domain(Node *arg, Oid baseType *** 677,690 **** * would be safe to do anyway, without lots of knowledge about what the * base type thinks the typmod means. */ ! if (!lengthCoercionDone) ! { ! if (baseTypeMod >= 0) ! arg = coerce_type_typmod(arg, baseTypeId, baseTypeMod, ! COERCE_IMPLICIT_CAST, location, ! (cformat != COERCE_IMPLICIT_CAST), ! false); ! } /* * Now build the domain coercion node. This represents run-time checking --- 674,682 ---- * would be safe to do anyway, without lots of knowledge about what the * base type thinks the typmod means. */ ! arg = coerce_type_typmod(arg, baseTypeId, baseTypeMod, ! ccontext, COERCE_IMPLICIT_CAST, location, ! false); /* * Now build the domain coercion node. This represents run-time checking *************** coerce_to_domain(Node *arg, Oid baseType *** 714,724 **** * The caller must have already ensured that the value is of the correct * type, typically by applying coerce_type. * ! * cformat determines the display properties of the generated node (if any), ! * while isExplicit may affect semantics. If hideInputCoercion is true ! * *and* we generate a node, the input node is forced to IMPLICIT display ! * form, so that only the typmod coercion node will be visible when ! * displaying the expression. * * NOTE: this does not need to work on domain types, because any typmod * coercion for a domain is considered to be part of the type coercion --- 706,719 ---- * The caller must have already ensured that the value is of the correct * type, typically by applying coerce_type. * ! * ccontext may affect semantics, depending on whether the length coercion ! * function pays attention to the isExplicit flag it's passed. ! * ! * cformat determines the display properties of the generated node (if any). ! * ! * If hideInputCoercion is true *and* we generate a node, the input node is ! * forced to IMPLICIT display form, so that only the typmod coercion node will ! * be visible when displaying the expression. * * NOTE: this does not need to work on domain types, because any typmod * coercion for a domain is considered to be part of the type coercion *************** coerce_to_domain(Node *arg, Oid baseType *** 726,733 **** */ static Node * coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod, ! CoercionForm cformat, int location, ! bool isExplicit, bool hideInputCoercion) { CoercionPathType pathtype; Oid funcId; --- 721,729 ---- */ static Node * coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod, ! CoercionContext ccontext, CoercionForm cformat, ! int location, ! bool hideInputCoercion) { CoercionPathType pathtype; Oid funcId; *************** coerce_type_typmod(Node *node, Oid targe *** 749,756 **** node = build_coercion_expression(node, pathtype, funcId, targetTypeId, targetTypMod, ! cformat, location, ! isExplicit); } return node; --- 745,751 ---- node = build_coercion_expression(node, pathtype, funcId, targetTypeId, targetTypMod, ! ccontext, cformat, location); } return node; *************** build_coercion_expression(Node *node, *** 799,806 **** CoercionPathType pathtype, Oid funcId, Oid targetTypeId, int32 targetTypMod, ! CoercionForm cformat, int location, ! bool isExplicit) { int nargs = 0; --- 794,801 ---- CoercionPathType pathtype, Oid funcId, Oid targetTypeId, int32 targetTypMod, ! CoercionContext ccontext, CoercionForm cformat, ! int location) { int nargs = 0; *************** build_coercion_expression(Node *node, *** 865,871 **** -1, InvalidOid, sizeof(bool), ! BoolGetDatum(isExplicit), false, true); --- 860,866 ---- -1, InvalidOid, sizeof(bool), ! BoolGetDatum(ccontext == COERCION_EXPLICIT), false, true); *************** build_coercion_expression(Node *node, *** 893,899 **** */ acoerce->resulttypmod = (nargs >= 2) ? targetTypMod : -1; /* resultcollid will be set by parse_collate.c */ ! acoerce->isExplicit = isExplicit; acoerce->coerceformat = cformat; acoerce->location = location; --- 888,894 ---- */ acoerce->resulttypmod = (nargs >= 2) ? targetTypMod : -1; /* resultcollid will be set by parse_collate.c */ ! acoerce->isExplicit = (ccontext == COERCION_EXPLICIT); acoerce->coerceformat = cformat; acoerce->location = location; diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index ef52dd5..7054d4f 100644 *** a/src/backend/rewrite/rewriteHandler.c --- b/src/backend/rewrite/rewriteHandler.c *************** rewriteTargetListIU(List *targetList, *** 875,883 **** new_expr = coerce_to_domain(new_expr, InvalidOid, -1, att_tup->atttypid, COERCE_IMPLICIT_CAST, -1, - false, false); } } --- 875,883 ---- new_expr = coerce_to_domain(new_expr, InvalidOid, -1, att_tup->atttypid, + COERCION_IMPLICIT, COERCE_IMPLICIT_CAST, -1, false); } } *************** rewriteValuesRTE(RangeTblEntry *rte, Rel *** 1271,1279 **** new_expr = coerce_to_domain(new_expr, InvalidOid, -1, att_tup->atttypid, COERCE_IMPLICIT_CAST, -1, - false, false); } newList = lappend(newList, new_expr); --- 1271,1279 ---- new_expr = coerce_to_domain(new_expr, InvalidOid, -1, att_tup->atttypid, + COERCION_IMPLICIT, COERCE_IMPLICIT_CAST, -1, false); } newList = lappend(newList, new_expr); diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c index 5c17213..c5773ef 100644 *** a/src/backend/rewrite/rewriteManip.c --- b/src/backend/rewrite/rewriteManip.c *************** ReplaceVarsFromTargetList_callback(Var * *** 1429,1437 **** var->varcollid), InvalidOid, -1, var->vartype, COERCE_IMPLICIT_CAST, -1, - false, false); } elog(ERROR, "could not find replacement targetlist entry for attno %d", --- 1429,1437 ---- var->varcollid), InvalidOid, -1, var->vartype, + COERCION_IMPLICIT, COERCE_IMPLICIT_CAST, -1, false); } elog(ERROR, "could not find replacement targetlist entry for attno %d", diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h index 06f6529..e560f0c 100644 *** a/src/include/parser/parse_coerce.h --- b/src/include/parser/parse_coerce.h *************** extern Node *coerce_type(ParseState *pst *** 48,56 **** CoercionContext ccontext, CoercionForm cformat, int location); extern Node *coerce_to_domain(Node *arg, Oid baseTypeId, int32 baseTypeMod, Oid typeId, ! CoercionForm cformat, int location, ! bool hideInputCoercion, ! bool lengthCoercionDone); extern Node *coerce_to_boolean(ParseState *pstate, Node *node, const char *constructName); --- 48,55 ---- CoercionContext ccontext, CoercionForm cformat, int location); extern Node *coerce_to_domain(Node *arg, Oid baseTypeId, int32 baseTypeMod, Oid typeId, ! CoercionContext ccontext, CoercionForm cformat, int location, ! bool hideInputCoercion); extern Node *coerce_to_boolean(ParseState *pstate, Node *node, const char *constructName);
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c index fa409d7..1776730 100644 *** a/contrib/pg_stat_statements/pg_stat_statements.c --- b/contrib/pg_stat_statements/pg_stat_statements.c *************** JumbleExpr(pgssJumbleState *jstate, Node *** 2631,2636 **** --- 2631,2637 ---- APP_JUMB(acexpr->resulttype); JumbleExpr(jstate, (Node *) acexpr->arg); + JumbleExpr(jstate, (Node *) acexpr->elemexpr); } break; case T_ConvertRowtypeExpr: diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index 6fffc29..2668650 100644 *** a/src/backend/catalog/dependency.c --- b/src/backend/catalog/dependency.c *************** find_expr_references_walker(Node *node, *** 1738,1748 **** { ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node; ! if (OidIsValid(acoerce->elemfuncid)) ! add_object_address(OCLASS_PROC, acoerce->elemfuncid, 0, ! context->addrs); add_object_address(OCLASS_TYPE, acoerce->resulttype, 0, context->addrs); /* fall through to examine arguments */ } else if (IsA(node, ConvertRowtypeExpr)) --- 1738,1751 ---- { ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node; ! /* as above, depend on type */ add_object_address(OCLASS_TYPE, acoerce->resulttype, 0, context->addrs); + /* the collation might not be referenced anywhere else, either */ + if (OidIsValid(acoerce->resultcollid) && + acoerce->resultcollid != DEFAULT_COLLATION_OID) + add_object_address(OCLASS_COLLATION, acoerce->resultcollid, 0, + context->addrs); /* fall through to examine arguments */ } else if (IsA(node, ConvertRowtypeExpr)) diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c index be9d23b..e083961 100644 *** a/src/backend/executor/execExpr.c --- b/src/backend/executor/execExpr.c *************** ExecInitExprRec(Expr *node, PlanState *p *** 1225,1230 **** --- 1225,1231 ---- { ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node; Oid resultelemtype; + ExprState *elemstate; /* evaluate argument into step's result area */ ExecInitExprRec(acoerce->arg, parent, state, resv, resnull); *************** ExecInitExprRec(Expr *node, PlanState *p *** 1234,1275 **** ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("target type is not an array"))); - /* Arrays over domains aren't supported yet */ - Assert(getBaseType(resultelemtype) == resultelemtype); ! scratch.opcode = EEOP_ARRAYCOERCE; ! scratch.d.arraycoerce.coerceexpr = acoerce; ! scratch.d.arraycoerce.resultelemtype = resultelemtype; ! if (OidIsValid(acoerce->elemfuncid)) ! { ! AclResult aclresult; ! /* Check permission to call function */ ! aclresult = pg_proc_aclcheck(acoerce->elemfuncid, ! GetUserId(), ! ACL_EXECUTE); ! if (aclresult != ACLCHECK_OK) ! aclcheck_error(aclresult, ACL_KIND_PROC, ! get_func_name(acoerce->elemfuncid)); ! InvokeFunctionExecuteHook(acoerce->elemfuncid); ! /* Set up the primary fmgr lookup information */ ! scratch.d.arraycoerce.elemfunc = ! (FmgrInfo *) palloc0(sizeof(FmgrInfo)); ! fmgr_info(acoerce->elemfuncid, ! scratch.d.arraycoerce.elemfunc); ! fmgr_info_set_expr((Node *) acoerce, ! scratch.d.arraycoerce.elemfunc); /* Set up workspace for array_map */ scratch.d.arraycoerce.amstate = (ArrayMapState *) palloc0(sizeof(ArrayMapState)); } else { ! /* Don't need workspace if there's no conversion func */ ! scratch.d.arraycoerce.elemfunc = NULL; scratch.d.arraycoerce.amstate = NULL; } --- 1235,1283 ---- ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("target type is not an array"))); ! /* ! * Construct a sub-expression for the per-element expression; ! * but don't ready it until after we check it for triviality. ! * We assume it hasn't any Var references, but does have a ! * CaseTestExpr representing the source array element values. ! */ ! elemstate = makeNode(ExprState); ! elemstate->expr = acoerce->elemexpr; ! elemstate->innermost_caseval = (Datum *) palloc(sizeof(Datum)); ! elemstate->innermost_casenull = (bool *) palloc(sizeof(bool)); ! ExecInitExprRec(acoerce->elemexpr, parent, elemstate, ! &elemstate->resvalue, &elemstate->resnull); ! if (elemstate->steps_len == 1 && ! elemstate->steps[0].opcode == EEOP_CASE_TESTVAL) ! { ! /* Trivial, so we need no per-element work at runtime */ ! elemstate = NULL; ! } ! else ! { ! /* Not trivial, so append a DONE step */ ! scratch.opcode = EEOP_DONE; ! ExprEvalPushStep(elemstate, &scratch); ! /* and ready the subexpression */ ! ExecReadyExpr(elemstate); ! } ! scratch.opcode = EEOP_ARRAYCOERCE; ! scratch.d.arraycoerce.elemexprstate = elemstate; ! scratch.d.arraycoerce.resultelemtype = resultelemtype; + if (elemstate) + { /* Set up workspace for array_map */ scratch.d.arraycoerce.amstate = (ArrayMapState *) palloc0(sizeof(ArrayMapState)); } else { ! /* Don't need workspace if there's no subexpression */ scratch.d.arraycoerce.amstate = NULL; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index bd8a15d..f9244d8 100644 *** a/src/backend/executor/execExprInterp.c --- b/src/backend/executor/execExprInterp.c *************** ExecInterpExpr(ExprState *state, ExprCon *** 1252,1258 **** EEO_CASE(EEOP_ARRAYCOERCE) { /* too complex for an inline implementation */ ! ExecEvalArrayCoerce(state, op); EEO_NEXT(); } --- 1252,1258 ---- EEO_CASE(EEOP_ARRAYCOERCE) { /* too complex for an inline implementation */ ! ExecEvalArrayCoerce(state, op, econtext); EEO_NEXT(); } *************** ExecEvalArrayExpr(ExprState *state, Expr *** 2328,2338 **** * Source array is in step's result variable. */ void ! ExecEvalArrayCoerce(ExprState *state, ExprEvalStep *op) { - ArrayCoerceExpr *acoerce = op->d.arraycoerce.coerceexpr; Datum arraydatum; - FunctionCallInfoData locfcinfo; /* NULL array -> NULL result */ if (*op->resnull) --- 2328,2336 ---- * Source array is in step's result variable. */ void ! ExecEvalArrayCoerce(ExprState *state, ExprEvalStep *op, ExprContext *econtext) { Datum arraydatum; /* NULL array -> NULL result */ if (*op->resnull) *************** ExecEvalArrayCoerce(ExprState *state, Ex *** 2344,2350 **** * If it's binary-compatible, modify the element type in the array header, * but otherwise leave the array as we received it. */ ! if (!OidIsValid(acoerce->elemfuncid)) { /* Detoast input array if necessary, and copy in any case */ ArrayType *array = DatumGetArrayTypePCopy(arraydatum); --- 2342,2348 ---- * If it's binary-compatible, modify the element type in the array header, * but otherwise leave the array as we received it. */ ! if (op->d.arraycoerce.elemexprstate == NULL) { /* Detoast input array if necessary, and copy in any case */ ArrayType *array = DatumGetArrayTypePCopy(arraydatum); *************** ExecEvalArrayCoerce(ExprState *state, Ex *** 2355,2377 **** } /* ! * Use array_map to apply the function to each array element. ! * ! * We pass on the desttypmod and isExplicit flags whether or not the ! * function wants them. ! * ! * Note: coercion functions are assumed to not use collation. */ ! InitFunctionCallInfoData(locfcinfo, op->d.arraycoerce.elemfunc, 3, ! InvalidOid, NULL, NULL); ! locfcinfo.arg[0] = arraydatum; ! locfcinfo.arg[1] = Int32GetDatum(acoerce->resulttypmod); ! locfcinfo.arg[2] = BoolGetDatum(acoerce->isExplicit); ! locfcinfo.argnull[0] = false; ! locfcinfo.argnull[1] = false; ! locfcinfo.argnull[2] = false; ! ! *op->resvalue = array_map(&locfcinfo, op->d.arraycoerce.resultelemtype, op->d.arraycoerce.amstate); } --- 2353,2364 ---- } /* ! * Use array_map to apply the sub-expression to each array element. */ ! *op->resvalue = array_map(arraydatum, ! op->d.arraycoerce.elemexprstate, ! econtext, ! op->d.arraycoerce.resultelemtype, op->d.arraycoerce.amstate); } diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index f1bed14..b274af2 100644 *** a/src/backend/nodes/copyfuncs.c --- b/src/backend/nodes/copyfuncs.c *************** _copyArrayCoerceExpr(const ArrayCoerceEx *** 1698,1708 **** ArrayCoerceExpr *newnode = makeNode(ArrayCoerceExpr); COPY_NODE_FIELD(arg); ! COPY_SCALAR_FIELD(elemfuncid); COPY_SCALAR_FIELD(resulttype); COPY_SCALAR_FIELD(resulttypmod); COPY_SCALAR_FIELD(resultcollid); - COPY_SCALAR_FIELD(isExplicit); COPY_SCALAR_FIELD(coerceformat); COPY_LOCATION_FIELD(location); --- 1698,1707 ---- ArrayCoerceExpr *newnode = makeNode(ArrayCoerceExpr); COPY_NODE_FIELD(arg); ! COPY_NODE_FIELD(elemexpr); COPY_SCALAR_FIELD(resulttype); COPY_SCALAR_FIELD(resulttypmod); COPY_SCALAR_FIELD(resultcollid); COPY_SCALAR_FIELD(coerceformat); COPY_LOCATION_FIELD(location); diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 8b56b91..5c839f4 100644 *** a/src/backend/nodes/equalfuncs.c --- b/src/backend/nodes/equalfuncs.c *************** static bool *** 513,523 **** _equalArrayCoerceExpr(const ArrayCoerceExpr *a, const ArrayCoerceExpr *b) { COMPARE_NODE_FIELD(arg); ! COMPARE_SCALAR_FIELD(elemfuncid); COMPARE_SCALAR_FIELD(resulttype); COMPARE_SCALAR_FIELD(resulttypmod); COMPARE_SCALAR_FIELD(resultcollid); - COMPARE_SCALAR_FIELD(isExplicit); COMPARE_COERCIONFORM_FIELD(coerceformat); COMPARE_LOCATION_FIELD(location); --- 513,522 ---- _equalArrayCoerceExpr(const ArrayCoerceExpr *a, const ArrayCoerceExpr *b) { COMPARE_NODE_FIELD(arg); ! COMPARE_NODE_FIELD(elemexpr); COMPARE_SCALAR_FIELD(resulttype); COMPARE_SCALAR_FIELD(resulttypmod); COMPARE_SCALAR_FIELD(resultcollid); COMPARE_COERCIONFORM_FIELD(coerceformat); COMPARE_LOCATION_FIELD(location); diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c index e3eb0c5..8e6f27e 100644 *** a/src/backend/nodes/nodeFuncs.c --- b/src/backend/nodes/nodeFuncs.c *************** check_functions_in_node(Node *node, chec *** 1717,1731 **** return true; } break; - case T_ArrayCoerceExpr: - { - ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node; - - if (OidIsValid(expr->elemfuncid) && - checker(expr->elemfuncid, context)) - return true; - } - break; case T_RowCompareExpr: { RowCompareExpr *rcexpr = (RowCompareExpr *) node; --- 1717,1722 ---- *************** expression_tree_walker(Node *node, *** 2023,2029 **** case T_CoerceViaIO: return walker(((CoerceViaIO *) node)->arg, context); case T_ArrayCoerceExpr: ! return walker(((ArrayCoerceExpr *) node)->arg, context); case T_ConvertRowtypeExpr: return walker(((ConvertRowtypeExpr *) node)->arg, context); case T_CollateExpr: --- 2014,2028 ---- case T_CoerceViaIO: return walker(((CoerceViaIO *) node)->arg, context); case T_ArrayCoerceExpr: ! { ! ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node; ! ! if (walker(acoerce->arg, context)) ! return true; ! if (walker(acoerce->elemexpr, context)) ! return true; ! } ! break; case T_ConvertRowtypeExpr: return walker(((ConvertRowtypeExpr *) node)->arg, context); case T_CollateExpr: *************** expression_tree_mutator(Node *node, *** 2705,2710 **** --- 2704,2710 ---- FLATCOPY(newnode, acoerce, ArrayCoerceExpr); MUTATE(newnode->arg, acoerce->arg, Expr *); + MUTATE(newnode->elemexpr, acoerce->elemexpr, Expr *); return (Node *) newnode; } break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index b83d919..2532edc 100644 *** a/src/backend/nodes/outfuncs.c --- b/src/backend/nodes/outfuncs.c *************** _outArrayCoerceExpr(StringInfo str, cons *** 1394,1404 **** WRITE_NODE_TYPE("ARRAYCOERCEEXPR"); WRITE_NODE_FIELD(arg); ! WRITE_OID_FIELD(elemfuncid); WRITE_OID_FIELD(resulttype); WRITE_INT_FIELD(resulttypmod); WRITE_OID_FIELD(resultcollid); - WRITE_BOOL_FIELD(isExplicit); WRITE_ENUM_FIELD(coerceformat, CoercionForm); WRITE_LOCATION_FIELD(location); } --- 1394,1403 ---- WRITE_NODE_TYPE("ARRAYCOERCEEXPR"); WRITE_NODE_FIELD(arg); ! WRITE_NODE_FIELD(elemexpr); WRITE_OID_FIELD(resulttype); WRITE_INT_FIELD(resulttypmod); WRITE_OID_FIELD(resultcollid); WRITE_ENUM_FIELD(coerceformat, CoercionForm); WRITE_LOCATION_FIELD(location); } diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index fbf8330..07ba691 100644 *** a/src/backend/nodes/readfuncs.c --- b/src/backend/nodes/readfuncs.c *************** _readArrayCoerceExpr(void) *** 892,902 **** READ_LOCALS(ArrayCoerceExpr); READ_NODE_FIELD(arg); ! READ_OID_FIELD(elemfuncid); READ_OID_FIELD(resulttype); READ_INT_FIELD(resulttypmod); READ_OID_FIELD(resultcollid); - READ_BOOL_FIELD(isExplicit); READ_ENUM_FIELD(coerceformat, CoercionForm); READ_LOCATION_FIELD(location); --- 892,901 ---- READ_LOCALS(ArrayCoerceExpr); READ_NODE_FIELD(arg); ! READ_NODE_FIELD(elemexpr); READ_OID_FIELD(resulttype); READ_INT_FIELD(resulttypmod); READ_OID_FIELD(resultcollid); READ_ENUM_FIELD(coerceformat, CoercionForm); READ_LOCATION_FIELD(location); diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index 051a854..8fd49d6 100644 *** a/src/backend/optimizer/path/costsize.c --- b/src/backend/optimizer/path/costsize.c *************** cost_qual_eval_walker(Node *node, cost_q *** 3632,3642 **** else if (IsA(node, ArrayCoerceExpr)) { ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node; ! Node *arraynode = (Node *) acoerce->arg; ! if (OidIsValid(acoerce->elemfuncid)) ! context->total.per_tuple += get_func_cost(acoerce->elemfuncid) * ! cpu_operator_cost * estimate_array_length(arraynode); } else if (IsA(node, RowCompareExpr)) { --- 3632,3645 ---- else if (IsA(node, ArrayCoerceExpr)) { ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node; ! QualCost perelemcost; ! cost_qual_eval_node(&perelemcost, (Node *) acoerce->elemexpr, ! context->root); ! context->total.startup += perelemcost.startup; ! if (perelemcost.per_tuple > 0) ! context->total.per_tuple += perelemcost.per_tuple * ! estimate_array_length((Node *) acoerce->arg); } else if (IsA(node, RowCompareExpr)) { diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index b0c9e94..dee4414 100644 *** a/src/backend/optimizer/plan/setrefs.c --- b/src/backend/optimizer/plan/setrefs.c *************** fix_expr_common(PlannerInfo *root, Node *** 1395,1406 **** record_plan_function_dependency(root, ((ScalarArrayOpExpr *) node)->opfuncid); } - else if (IsA(node, ArrayCoerceExpr)) - { - if (OidIsValid(((ArrayCoerceExpr *) node)->elemfuncid)) - record_plan_function_dependency(root, - ((ArrayCoerceExpr *) node)->elemfuncid); - } else if (IsA(node, Const)) { Const *con = (Const *) node; --- 1395,1400 ---- diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 93add27..7961362 100644 *** a/src/backend/optimizer/util/clauses.c --- b/src/backend/optimizer/util/clauses.c *************** contain_nonstrict_functions_walker(Node *** 1361,1366 **** --- 1361,1377 ---- return true; if (IsA(node, FieldStore)) return true; + if (IsA(node, ArrayCoerceExpr)) + { + /* + * ArrayCoerceExpr is strict at the array level, regardless of what + * the per-element expression is; so we should ignore elemexpr and + * recurse only into the arg. + */ + return expression_tree_walker((Node *) ((ArrayCoerceExpr *) node)->arg, + contain_nonstrict_functions_walker, + context); + } if (IsA(node, CaseExpr)) return true; if (IsA(node, ArrayExpr)) *************** contain_nonstrict_functions_walker(Node *** 1380,1393 **** if (IsA(node, BooleanTest)) return true; ! /* ! * Check other function-containing nodes; but ArrayCoerceExpr is strict at ! * the array level, regardless of elemfunc. ! */ ! if (!IsA(node, ArrayCoerceExpr) && ! check_functions_in_node(node, contain_nonstrict_functions_checker, context)) return true; return expression_tree_walker(node, contain_nonstrict_functions_walker, context); } --- 1391,1401 ---- if (IsA(node, BooleanTest)) return true; ! /* Check other function-containing nodes */ ! if (check_functions_in_node(node, contain_nonstrict_functions_checker, context)) return true; + return expression_tree_walker(node, contain_nonstrict_functions_walker, context); } *************** find_nonnullable_rels_walker(Node *node, *** 1757,1763 **** } else if (IsA(node, ArrayCoerceExpr)) { ! /* ArrayCoerceExpr is strict at the array level */ ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node; result = find_nonnullable_rels_walker((Node *) expr->arg, top_level); --- 1765,1771 ---- } else if (IsA(node, ArrayCoerceExpr)) { ! /* ArrayCoerceExpr is strict at the array level; ignore elemexpr */ ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node; result = find_nonnullable_rels_walker((Node *) expr->arg, top_level); *************** find_nonnullable_vars_walker(Node *node, *** 1965,1971 **** } else if (IsA(node, ArrayCoerceExpr)) { ! /* ArrayCoerceExpr is strict at the array level */ ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node; result = find_nonnullable_vars_walker((Node *) expr->arg, top_level); --- 1973,1979 ---- } else if (IsA(node, ArrayCoerceExpr)) { ! /* ArrayCoerceExpr is strict at the array level; ignore elemexpr */ ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node; result = find_nonnullable_vars_walker((Node *) expr->arg, top_level); *************** eval_const_expressions_mutator(Node *nod *** 3005,3036 **** { ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node; Expr *arg; ArrayCoerceExpr *newexpr; /* ! * Reduce constants in the ArrayCoerceExpr's argument, then ! * build a new ArrayCoerceExpr. */ arg = (Expr *) eval_const_expressions_mutator((Node *) expr->arg, context); newexpr = makeNode(ArrayCoerceExpr); newexpr->arg = arg; ! newexpr->elemfuncid = expr->elemfuncid; newexpr->resulttype = expr->resulttype; newexpr->resulttypmod = expr->resulttypmod; newexpr->resultcollid = expr->resultcollid; - newexpr->isExplicit = expr->isExplicit; newexpr->coerceformat = expr->coerceformat; newexpr->location = expr->location; /* ! * If constant argument and it's a binary-coercible or ! * immutable conversion, we can simplify it to a constant. */ if (arg && IsA(arg, Const) && ! (!OidIsValid(newexpr->elemfuncid) || ! func_volatile(newexpr->elemfuncid) == PROVOLATILE_IMMUTABLE)) return (Node *) evaluate_expr((Expr *) newexpr, newexpr->resulttype, newexpr->resulttypmod, --- 3013,3050 ---- { ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node; Expr *arg; + Expr *elemexpr; ArrayCoerceExpr *newexpr; /* ! * Reduce constants in the ArrayCoerceExpr's argument and ! * per-element expressions, then build a new ArrayCoerceExpr. */ arg = (Expr *) eval_const_expressions_mutator((Node *) expr->arg, context); + elemexpr = (Expr *) eval_const_expressions_mutator((Node *) expr->elemexpr, + context); newexpr = makeNode(ArrayCoerceExpr); newexpr->arg = arg; ! newexpr->elemexpr = elemexpr; newexpr->resulttype = expr->resulttype; newexpr->resulttypmod = expr->resulttypmod; newexpr->resultcollid = expr->resultcollid; newexpr->coerceformat = expr->coerceformat; newexpr->location = expr->location; /* ! * If constant argument and per-element expression is ! * immutable, we can simplify the whole thing to a constant. ! * Exception: although contain_mutable_functions considers ! * CoerceToDomain immutable for historical reasons, let's not ! * do so here; this ensures coercion to an array-over-domain ! * does not apply the domain's constraints until runtime. */ if (arg && IsA(arg, Const) && ! elemexpr && !IsA(elemexpr, CoerceToDomain) && ! !contain_mutable_functions((Node *) elemexpr)) return (Node *) evaluate_expr((Expr *) newexpr, newexpr->resulttype, newexpr->resulttypmod, diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index 5a241bd..0355c02 100644 *** a/src/backend/parser/parse_coerce.c --- b/src/backend/parser/parse_coerce.c *************** build_coercion_expression(Node *node, *** 876,894 **** { /* We need to build an ArrayCoerceExpr */ ArrayCoerceExpr *acoerce = makeNode(ArrayCoerceExpr); acoerce->arg = (Expr *) node; ! acoerce->elemfuncid = funcId; acoerce->resulttype = targetTypeId; /* ! * Label the output as having a particular typmod only if we are ! * really invoking a length-coercion function, ie one with more than ! * one argument. */ ! acoerce->resulttypmod = (nargs >= 2) ? targetTypMod : -1; /* resultcollid will be set by parse_collate.c */ - acoerce->isExplicit = (ccontext == COERCION_EXPLICIT); acoerce->coerceformat = cformat; acoerce->location = location; --- 876,927 ---- { /* We need to build an ArrayCoerceExpr */ ArrayCoerceExpr *acoerce = makeNode(ArrayCoerceExpr); + CaseTestExpr *ctest = makeNode(CaseTestExpr); + Oid sourceBaseTypeId; + int32 sourceBaseTypeMod; + Oid targetElementType; + Node *elemexpr; + + /* + * Look through any domain over the source array type. Note we don't + * expect that the target type is a domain; it must be a plain array. + * (To get to a domain target type, we'll do coerce_to_domain later.) + */ + sourceBaseTypeMod = exprTypmod(node); + sourceBaseTypeId = getBaseTypeAndTypmod(exprType(node), + &sourceBaseTypeMod); + + /* Set up CaseTestExpr representing one element of source array */ + ctest->typeId = get_element_type(sourceBaseTypeId); + Assert(OidIsValid(ctest->typeId)); + ctest->typeMod = sourceBaseTypeMod; + ctest->collation = InvalidOid; /* Assume coercions don't care */ + + /* And coerce it to the target element type */ + targetElementType = get_element_type(targetTypeId); + Assert(OidIsValid(targetElementType)); + + elemexpr = coerce_to_target_type(NULL, + (Node *) ctest, + ctest->typeId, + targetElementType, + targetTypMod, + ccontext, + cformat, + location); + if (elemexpr == NULL) /* shouldn't happen */ + elog(ERROR, "failed to coerce array element type as expected"); acoerce->arg = (Expr *) node; ! acoerce->elemexpr = (Expr *) elemexpr; acoerce->resulttype = targetTypeId; /* ! * Label the output as having a particular element typmod only if we ! * ended up with a per-element expression that is labeled that way. */ ! acoerce->resulttypmod = exprTypmod(elemexpr); /* resultcollid will be set by parse_collate.c */ acoerce->coerceformat = cformat; acoerce->location = location; *************** IsBinaryCoercible(Oid srctype, Oid targe *** 2143,2150 **** * COERCION_PATH_RELABELTYPE: binary-compatible cast, no function needed * *funcid is set to InvalidOid * COERCION_PATH_ARRAYCOERCE: need an ArrayCoerceExpr node ! * *funcid is set to the element cast function, or InvalidOid ! * if the array elements are binary-compatible * COERCION_PATH_COERCEVIAIO: need a CoerceViaIO node * *funcid is set to InvalidOid * --- 2176,2182 ---- * COERCION_PATH_RELABELTYPE: binary-compatible cast, no function needed * *funcid is set to InvalidOid * COERCION_PATH_ARRAYCOERCE: need an ArrayCoerceExpr node ! * *funcid is set to InvalidOid * COERCION_PATH_COERCEVIAIO: need a CoerceViaIO node * *funcid is set to InvalidOid * *************** find_coercion_pathway(Oid targetTypeId, *** 2230,2240 **** { /* * If there's no pg_cast entry, perhaps we are dealing with a pair of ! * array types. If so, and if the element types have a suitable cast, ! * report that we can coerce with an ArrayCoerceExpr. ! * ! * Note that the source type can be a domain over array, but not the ! * target, because ArrayCoerceExpr won't check domain constraints. * * Hack: disallow coercions to oidvector and int2vector, which * otherwise tend to capture coercions that should go to "real" array --- 2262,2269 ---- { /* * If there's no pg_cast entry, perhaps we are dealing with a pair of ! * array types. If so, and if their element types have a conversion ! * pathway, report that we can coerce with an ArrayCoerceExpr. * * Hack: disallow coercions to oidvector and int2vector, which * otherwise tend to capture coercions that should go to "real" array *************** find_coercion_pathway(Oid targetTypeId, *** 2249,2255 **** Oid sourceElem; if ((targetElem = get_element_type(targetTypeId)) != InvalidOid && ! (sourceElem = get_base_element_type(sourceTypeId)) != InvalidOid) { CoercionPathType elempathtype; Oid elemfuncid; --- 2278,2284 ---- Oid sourceElem; if ((targetElem = get_element_type(targetTypeId)) != InvalidOid && ! (sourceElem = get_element_type(sourceTypeId)) != InvalidOid) { CoercionPathType elempathtype; Oid elemfuncid; *************** find_coercion_pathway(Oid targetTypeId, *** 2258,2271 **** sourceElem, ccontext, &elemfuncid); ! if (elempathtype != COERCION_PATH_NONE && ! elempathtype != COERCION_PATH_ARRAYCOERCE) { ! *funcid = elemfuncid; ! if (elempathtype == COERCION_PATH_COERCEVIAIO) ! result = COERCION_PATH_COERCEVIAIO; ! else ! result = COERCION_PATH_ARRAYCOERCE; } } } --- 2287,2295 ---- sourceElem, ccontext, &elemfuncid); ! if (elempathtype != COERCION_PATH_NONE) { ! result = COERCION_PATH_ARRAYCOERCE; } } } *************** find_coercion_pathway(Oid targetTypeId, *** 2306,2312 **** * If the given type is a varlena array type, we do not look for a coercion * function associated directly with the array type, but instead look for * one associated with the element type. An ArrayCoerceExpr node must be ! * used to apply such a function. * * We use the same result enum as find_coercion_pathway, but the only possible * result codes are: --- 2330,2338 ---- * If the given type is a varlena array type, we do not look for a coercion * function associated directly with the array type, but instead look for * one associated with the element type. An ArrayCoerceExpr node must be ! * used to apply such a function. (Note: currently, it's pointless to ! * return the funcid in this case, because it'll just get looked up again ! * in the recursive construction of the ArrayCoerceExpr's elemexpr.) * * We use the same result enum as find_coercion_pathway, but the only possible * result codes are: diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c index 2a4de41..4caff0b 100644 *** a/src/backend/utils/adt/arrayfuncs.c --- b/src/backend/utils/adt/arrayfuncs.c *************** array_set(ArrayType *array, int nSubscri *** 3092,3112 **** /* * array_map() * ! * Map an array through an arbitrary function. Return a new array with ! * same dimensions and each source element transformed by fn(). Each ! * source element is passed as the first argument to fn(); additional ! * arguments to be passed to fn() can be specified by the caller. ! * The output array can have a different element type than the input. * * Parameters are: ! * * fcinfo: a function-call data structure pre-constructed by the caller ! * to be ready to call the desired function, with everything except the ! * first argument position filled in. In particular, flinfo identifies ! * the function fn(), and if nargs > 1 then argument positions after the ! * first must be preset to the additional values to be passed. The ! * first argument position initially holds the input array value. * * retType: OID of element type of output array. This must be the same as, ! * or binary-compatible with, the result type of fn(). * * amstate: workspace for array_map. Must be zeroed by caller before * first call, and not touched after that. * --- 3092,3109 ---- /* * array_map() * ! * Map an array through an arbitrary expression. Return a new array with ! * the same dimensions and each source element transformed by the given, ! * already-compiled expression. Each source element is placed in the ! * innermost_caseval/innermost_casenull fields of the ExprState. * * Parameters are: ! * * arrayd: Datum representing array argument. ! * * exprstate: ExprState representing the per-element transformation. ! * * econtext: context for expression evaluation. * * retType: OID of element type of output array. This must be the same as, ! * or binary-compatible with, the result type of the expression. It might ! * be different from the input array's element type. * * amstate: workspace for array_map. Must be zeroed by caller before * first call, and not touched after that. * *************** array_set(ArrayType *array, int nSubscri *** 3116,3126 **** * * NB: caller must assure that input array is not NULL. NULL elements in * the array are OK however. */ Datum ! array_map(FunctionCallInfo fcinfo, Oid retType, ArrayMapState *amstate) { ! AnyArrayType *v; ArrayType *result; Datum *values; bool *nulls; --- 3113,3126 ---- * * NB: caller must assure that input array is not NULL. NULL elements in * the array are OK however. + * NB: caller should be running in econtext's per-tuple memory context. */ Datum ! array_map(Datum arrayd, ! ExprState *exprstate, ExprContext *econtext, ! Oid retType, ArrayMapState *amstate) { ! AnyArrayType *v = DatumGetAnyArray(arrayd); ArrayType *result; Datum *values; bool *nulls; *************** array_map(FunctionCallInfo fcinfo, Oid r *** 3141,3153 **** array_iter iter; ArrayMetaState *inp_extra; ArrayMetaState *ret_extra; ! ! /* Get input array */ ! if (fcinfo->nargs < 1) ! elog(ERROR, "invalid nargs: %d", fcinfo->nargs); ! if (PG_ARGISNULL(0)) ! elog(ERROR, "null input array"); ! v = PG_GETARG_ANY_ARRAY(0); inpType = AARR_ELEMTYPE(v); ndim = AARR_NDIM(v); --- 3141,3148 ---- array_iter iter; ArrayMetaState *inp_extra; ArrayMetaState *ret_extra; ! Datum *transform_source = exprstate->innermost_caseval; ! bool *transform_source_isnull = exprstate->innermost_casenull; inpType = AARR_ELEMTYPE(v); ndim = AARR_NDIM(v); *************** array_map(FunctionCallInfo fcinfo, Oid r *** 3158,3164 **** if (nitems <= 0) { /* Return empty array */ ! PG_RETURN_ARRAYTYPE_P(construct_empty_array(retType)); } /* --- 3153,3159 ---- if (nitems <= 0) { /* Return empty array */ ! return PointerGetDatum(construct_empty_array(retType)); } /* *************** array_map(FunctionCallInfo fcinfo, Oid r *** 3203,3241 **** for (i = 0; i < nitems; i++) { - bool callit = true; - /* Get source element, checking for NULL */ ! fcinfo->arg[0] = array_iter_next(&iter, &fcinfo->argnull[0], i, ! inp_typlen, inp_typbyval, inp_typalign); ! ! /* ! * Apply the given function to source elt and extra args. ! */ ! if (fcinfo->flinfo->fn_strict) ! { ! int j; ! ! for (j = 0; j < fcinfo->nargs; j++) ! { ! if (fcinfo->argnull[j]) ! { ! callit = false; ! break; ! } ! } ! } ! if (callit) ! { ! fcinfo->isnull = false; ! values[i] = FunctionCallInvoke(fcinfo); ! } ! else ! fcinfo->isnull = true; ! nulls[i] = fcinfo->isnull; ! if (fcinfo->isnull) hasnulls = true; else { --- 3198,3212 ---- for (i = 0; i < nitems; i++) { /* Get source element, checking for NULL */ ! *transform_source = ! array_iter_next(&iter, transform_source_isnull, i, ! inp_typlen, inp_typbyval, inp_typalign); ! /* Apply the given expression to source element */ ! values[i] = ExecEvalExpr(exprstate, econtext, &nulls[i]); ! if (nulls[i]) hasnulls = true; else { *************** array_map(FunctionCallInfo fcinfo, Oid r *** 3254,3260 **** } } ! /* Allocate and initialize the result array */ if (hasnulls) { dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems); --- 3225,3231 ---- } } ! /* Allocate and fill the result array */ if (hasnulls) { dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems); *************** array_map(FunctionCallInfo fcinfo, Oid r *** 3273,3290 **** memcpy(ARR_DIMS(result), AARR_DIMS(v), ndim * sizeof(int)); memcpy(ARR_LBOUND(result), AARR_LBOUND(v), ndim * sizeof(int)); - /* - * Note: do not risk trying to pfree the results of the called function - */ CopyArrayEls(result, values, nulls, nitems, typlen, typbyval, typalign, false); pfree(values); pfree(nulls); ! PG_RETURN_ARRAYTYPE_P(result); } /* --- 3244,3261 ---- memcpy(ARR_DIMS(result), AARR_DIMS(v), ndim * sizeof(int)); memcpy(ARR_LBOUND(result), AARR_LBOUND(v), ndim * sizeof(int)); CopyArrayEls(result, values, nulls, nitems, typlen, typbyval, typalign, false); + /* + * Note: do not risk trying to pfree the results of the called expression + */ pfree(values); pfree(nulls); ! return PointerGetDatum(result); } /* diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 81b0bc3..7b3fdc9 100644 *** a/src/backend/utils/adt/selfuncs.c --- b/src/backend/utils/adt/selfuncs.c *************** strip_array_coercion(Node *node) *** 1753,1762 **** { for (;;) { ! if (node && IsA(node, ArrayCoerceExpr) && ! ((ArrayCoerceExpr *) node)->elemfuncid == InvalidOid) { ! node = (Node *) ((ArrayCoerceExpr *) node)->arg; } else if (node && IsA(node, RelabelType)) { --- 1753,1771 ---- { for (;;) { ! if (node && IsA(node, ArrayCoerceExpr)) { ! ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node; ! ! /* ! * If the per-element expression is just a RelabelType on top of ! * CaseTestExpr, then we know it's a binary-compatible relabeling. ! */ ! if (IsA(acoerce->elemexpr, RelabelType) && ! IsA(((RelabelType *) acoerce->elemexpr)->arg, CaseTestExpr)) ! node = (Node *) acoerce->arg; ! else ! break; } else if (node && IsA(node, RelabelType)) { diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c index a7b0782..9197335 100644 *** a/src/backend/utils/fmgr/fmgr.c --- b/src/backend/utils/fmgr/fmgr.c *************** get_call_expr_argtype(Node *expr, int ar *** 1941,1948 **** args = ((DistinctExpr *) expr)->args; else if (IsA(expr, ScalarArrayOpExpr)) args = ((ScalarArrayOpExpr *) expr)->args; - else if (IsA(expr, ArrayCoerceExpr)) - args = list_make1(((ArrayCoerceExpr *) expr)->arg); else if (IsA(expr, NullIfExpr)) args = ((NullIfExpr *) expr)->args; else if (IsA(expr, WindowFunc)) --- 1941,1946 ---- *************** get_call_expr_argtype(Node *expr, int ar *** 1956,1971 **** argtype = exprType((Node *) list_nth(args, argnum)); /* ! * special hack for ScalarArrayOpExpr and ArrayCoerceExpr: what the ! * underlying function will actually get passed is the element type of the ! * array. */ if (IsA(expr, ScalarArrayOpExpr) && argnum == 1) argtype = get_base_element_type(argtype); - else if (IsA(expr, ArrayCoerceExpr) && - argnum == 0) - argtype = get_base_element_type(argtype); return argtype; } --- 1954,1965 ---- argtype = exprType((Node *) list_nth(args, argnum)); /* ! * special hack for ScalarArrayOpExpr: what the underlying function will ! * actually get passed is the element type of the array. */ if (IsA(expr, ScalarArrayOpExpr) && argnum == 1) argtype = get_base_element_type(argtype); return argtype; } *************** get_call_expr_arg_stable(Node *expr, int *** 2012,2019 **** args = ((DistinctExpr *) expr)->args; else if (IsA(expr, ScalarArrayOpExpr)) args = ((ScalarArrayOpExpr *) expr)->args; - else if (IsA(expr, ArrayCoerceExpr)) - args = list_make1(((ArrayCoerceExpr *) expr)->arg); else if (IsA(expr, NullIfExpr)) args = ((NullIfExpr *) expr)->args; else if (IsA(expr, WindowFunc)) --- 2006,2011 ---- diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h index 8ee0496..78d2247 100644 *** a/src/include/executor/execExpr.h --- b/src/include/executor/execExpr.h *************** typedef struct ExprEvalStep *** 385,394 **** /* for EEOP_ARRAYCOERCE */ struct { ! ArrayCoerceExpr *coerceexpr; Oid resultelemtype; /* element type of result array */ - FmgrInfo *elemfunc; /* lookup info for element coercion - * function */ struct ArrayMapState *amstate; /* workspace for array_map */ } arraycoerce; --- 385,392 ---- /* for EEOP_ARRAYCOERCE */ struct { ! ExprState *elemexprstate; /* null if no per-element work */ Oid resultelemtype; /* element type of result array */ struct ArrayMapState *amstate; /* workspace for array_map */ } arraycoerce; *************** extern void ExecEvalRowNull(ExprState *s *** 621,627 **** extern void ExecEvalRowNotNull(ExprState *state, ExprEvalStep *op, ExprContext *econtext); extern void ExecEvalArrayExpr(ExprState *state, ExprEvalStep *op); ! extern void ExecEvalArrayCoerce(ExprState *state, ExprEvalStep *op); extern void ExecEvalRow(ExprState *state, ExprEvalStep *op); extern void ExecEvalMinMax(ExprState *state, ExprEvalStep *op); extern void ExecEvalFieldSelect(ExprState *state, ExprEvalStep *op, --- 619,626 ---- extern void ExecEvalRowNotNull(ExprState *state, ExprEvalStep *op, ExprContext *econtext); extern void ExecEvalArrayExpr(ExprState *state, ExprEvalStep *op); ! extern void ExecEvalArrayCoerce(ExprState *state, ExprEvalStep *op, ! ExprContext *econtext); extern void ExecEvalRow(ExprState *state, ExprEvalStep *op); extern void ExecEvalMinMax(ExprState *state, ExprEvalStep *op); extern void ExecEvalFieldSelect(ExprState *state, ExprEvalStep *op, diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 8c536a8..ccb5123 100644 *** a/src/include/nodes/primnodes.h --- b/src/include/nodes/primnodes.h *************** typedef struct CoerceViaIO *** 820,830 **** * ArrayCoerceExpr * * ArrayCoerceExpr represents a type coercion from one array type to another, ! * which is implemented by applying the indicated element-type coercion ! * function to each element of the source array. If elemfuncid is InvalidOid ! * then the element types are binary-compatible, but the coercion still ! * requires some effort (we have to fix the element type ID stored in the ! * array header). * ---------------- */ --- 820,831 ---- * ArrayCoerceExpr * * ArrayCoerceExpr represents a type coercion from one array type to another, ! * which is implemented by applying the per-element coercion expression ! * "elemexpr" to each element of the source array. Within elemexpr, the ! * source element is represented by a CaseTestExpr node. Note that even if ! * elemexpr is a no-op (that is, just CaseTestExpr + RelabelType), the ! * coercion still requires some effort: we have to fix the element type OID ! * stored in the array header. * ---------------- */ *************** typedef struct ArrayCoerceExpr *** 832,842 **** { Expr xpr; Expr *arg; /* input expression (yields an array) */ ! Oid elemfuncid; /* OID of element coercion function, or 0 */ Oid resulttype; /* output type of coercion (an array type) */ int32 resulttypmod; /* output typmod (also element typmod) */ Oid resultcollid; /* OID of collation, or InvalidOid if none */ - bool isExplicit; /* conversion semantics flag to pass to func */ CoercionForm coerceformat; /* how to display this node */ int location; /* token location, or -1 if unknown */ } ArrayCoerceExpr; --- 833,842 ---- { Expr xpr; Expr *arg; /* input expression (yields an array) */ ! Expr *elemexpr; /* expression representing per-element work */ Oid resulttype; /* output type of coercion (an array type) */ int32 resulttypmod; /* output typmod (also element typmod) */ Oid resultcollid; /* OID of collation, or InvalidOid if none */ CoercionForm coerceformat; /* how to display this node */ int location; /* token location, or -1 if unknown */ } ArrayCoerceExpr; diff --git a/src/include/utils/array.h b/src/include/utils/array.h index 61a67a2..32833f0 100644 *** a/src/include/utils/array.h --- b/src/include/utils/array.h *************** *** 64,69 **** --- 64,73 ---- #include "fmgr.h" #include "utils/expandeddatum.h" + /* avoid including execnodes.h here */ + struct ExprState; + struct ExprContext; + /* * Arrays are varlena objects, so must meet the varlena convention that *************** extern ArrayType *array_set(ArrayType *a *** 360,367 **** Datum dataValue, bool isNull, int arraytyplen, int elmlen, bool elmbyval, char elmalign); ! extern Datum array_map(FunctionCallInfo fcinfo, Oid retType, ! ArrayMapState *amstate); extern void array_bitmap_copy(bits8 *destbitmap, int destoffset, const bits8 *srcbitmap, int srcoffset, --- 364,372 ---- Datum dataValue, bool isNull, int arraytyplen, int elmlen, bool elmbyval, char elmalign); ! extern Datum array_map(Datum arrayd, ! struct ExprState *exprstate, struct ExprContext *econtext, ! Oid retType, ArrayMapState *amstate); extern void array_bitmap_copy(bits8 *destbitmap, int destoffset, const bits8 *srcbitmap, int srcoffset,
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 7ed16ae..12d2cd6 100644 *** a/src/backend/commands/typecmds.c --- b/src/backend/commands/typecmds.c *************** ObjectAddress *** 729,734 **** --- 729,735 ---- DefineDomain(CreateDomainStmt *stmt) { char *domainName; + char *domainArrayName; Oid domainNamespace; AclResult aclresult; int16 internalLength; *************** DefineDomain(CreateDomainStmt *stmt) *** 757,762 **** --- 758,764 ---- Oid basetypeoid; Oid old_type_oid; Oid domaincoll; + Oid domainArrayOid; Form_pg_type baseType; int32 basetypeMod; Oid baseColl; *************** DefineDomain(CreateDomainStmt *stmt) *** 1027,1032 **** --- 1029,1037 ---- } } + /* Allocate OID for array type */ + domainArrayOid = AssignTypeArrayOid(); + /* * Have TypeCreate do all the real work. */ *************** DefineDomain(CreateDomainStmt *stmt) *** 1051,1057 **** analyzeProcedure, /* analyze procedure */ InvalidOid, /* no array element type */ false, /* this isn't an array */ ! InvalidOid, /* no arrays for domains (yet) */ basetypeoid, /* base type ID */ defaultValue, /* default type value (text) */ defaultValueBin, /* default type value (binary) */ --- 1056,1062 ---- analyzeProcedure, /* analyze procedure */ InvalidOid, /* no array element type */ false, /* this isn't an array */ ! domainArrayOid, /* array type we are about to create */ basetypeoid, /* base type ID */ defaultValue, /* default type value (text) */ defaultValueBin, /* default type value (binary) */ *************** DefineDomain(CreateDomainStmt *stmt) *** 1064,1069 **** --- 1069,1116 ---- domaincoll); /* type's collation */ /* + * Create the array type that goes with it. + */ + domainArrayName = makeArrayTypeName(domainName, domainNamespace); + + /* alignment must be 'i' or 'd' for arrays */ + alignment = (alignment == 'd') ? 'd' : 'i'; + + TypeCreate(domainArrayOid, /* force assignment of this type OID */ + domainArrayName, /* type name */ + domainNamespace, /* namespace */ + InvalidOid, /* relation oid (n/a here) */ + 0, /* relation kind (ditto) */ + GetUserId(), /* owner's ID */ + -1, /* internal size (always varlena) */ + TYPTYPE_BASE, /* type-type (base type) */ + TYPCATEGORY_ARRAY, /* type-category (array) */ + false, /* array types are never preferred */ + delimiter, /* array element delimiter */ + F_ARRAY_IN, /* input procedure */ + F_ARRAY_OUT, /* output procedure */ + F_ARRAY_RECV, /* receive procedure */ + F_ARRAY_SEND, /* send procedure */ + InvalidOid, /* typmodin procedure - none */ + InvalidOid, /* typmodout procedure - none */ + F_ARRAY_TYPANALYZE, /* analyze procedure */ + address.objectId, /* element type ID */ + true, /* yes this is an array type */ + InvalidOid, /* no further array type */ + InvalidOid, /* base type ID */ + NULL, /* never a default type value */ + NULL, /* binary default isn't sent either */ + false, /* never passed by value */ + alignment, /* see above */ + 'x', /* ARRAY is always toastable */ + -1, /* typMod (Domains only) */ + 0, /* Array dimensions of typbasetype */ + false, /* Type NOT NULL */ + domaincoll); /* type's collation */ + + pfree(domainArrayName); + + /* * Process constraints which refer to the domain ID returned by TypeCreate */ foreach(listptr, schema) *************** DefineEnum(CreateEnumStmt *stmt) *** 1139,1144 **** --- 1186,1192 ---- errmsg("type \"%s\" already exists", enumName))); } + /* Allocate OID for array type */ enumArrayOid = AssignTypeArrayOid(); /* Create the pg_type entry */ diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out index 3acc696..1e62c57 100644 *** a/src/test/regress/expected/domain.out --- b/src/test/regress/expected/domain.out *************** Rules: *** 310,315 **** --- 310,410 ---- drop table dcomptable; drop type comptype cascade; NOTICE: drop cascades to type dcomptypea + -- Test arrays over domains + create domain posint as int check (value > 0); + create table pitable (f1 posint[]); + insert into pitable values(array[42]); + insert into pitable values(array[-1]); -- fail + ERROR: value for domain posint violates check constraint "posint_check" + insert into pitable values('{0}'); -- fail + ERROR: value for domain posint violates check constraint "posint_check" + LINE 1: insert into pitable values('{0}'); + ^ + update pitable set f1[1] = f1[1] + 1; + update pitable set f1[1] = 0; -- fail + ERROR: value for domain posint violates check constraint "posint_check" + select * from pitable; + f1 + ------ + {43} + (1 row) + + drop table pitable; + create domain vc4 as varchar(4); + create table vc4table (f1 vc4[]); + insert into vc4table values(array['too long']); -- fail + ERROR: value too long for type character varying(4) + insert into vc4table values(array['too long']::vc4[]); -- cast truncates + select * from vc4table; + f1 + ---------- + {"too "} + (1 row) + + drop table vc4table; + drop type vc4; + -- You can sort of fake arrays-of-arrays by putting a domain in between + create domain dposinta as posint[]; + create table dposintatable (f1 dposinta[]); + insert into dposintatable values(array[array[42]]); -- fail + ERROR: column "f1" is of type dposinta[] but expression is of type integer[] + LINE 1: insert into dposintatable values(array[array[42]]); + ^ + HINT: You will need to rewrite or cast the expression. + insert into dposintatable values(array[array[42]::posint[]]); -- still fail + ERROR: column "f1" is of type dposinta[] but expression is of type posint[] + LINE 1: insert into dposintatable values(array[array[42]::posint[]])... + ^ + HINT: You will need to rewrite or cast the expression. + insert into dposintatable values(array[array[42]::dposinta]); -- but this works + select f1, f1[1], (f1[1])[1] from dposintatable; + f1 | f1 | f1 + ----------+------+---- + {"{42}"} | {42} | 42 + (1 row) + + select pg_typeof(f1) from dposintatable; + pg_typeof + ------------ + dposinta[] + (1 row) + + select pg_typeof(f1[1]) from dposintatable; + pg_typeof + ----------- + dposinta + (1 row) + + select pg_typeof(f1[1][1]) from dposintatable; + pg_typeof + ----------- + dposinta + (1 row) + + select pg_typeof((f1[1])[1]) from dposintatable; + pg_typeof + ----------- + posint + (1 row) + + update dposintatable set f1[2] = array[99]; + select f1, f1[1], (f1[2])[1] from dposintatable; + f1 | f1 | f1 + -----------------+------+---- + {"{42}","{99}"} | {42} | 99 + (1 row) + + -- it'd be nice if you could do something like this, but for now you can't: + update dposintatable set f1[2][1] = array[97]; + ERROR: wrong number of array subscripts + -- maybe someday we can make this syntax work: + update dposintatable set (f1[2])[1] = array[98]; + ERROR: syntax error at or near "[" + LINE 1: update dposintatable set (f1[2])[1] = array[98]; + ^ + drop table dposintatable; + drop domain posint cascade; + NOTICE: drop cascades to type dposinta -- Test not-null restrictions create domain dnotnull varchar(15) NOT NULL; create domain dnull varchar(15); diff --git a/src/test/regress/sql/domain.sql b/src/test/regress/sql/domain.sql index 0fd383e..8fb3e20 100644 *** a/src/test/regress/sql/domain.sql --- b/src/test/regress/sql/domain.sql *************** drop table dcomptable; *** 166,171 **** --- 166,214 ---- drop type comptype cascade; + -- Test arrays over domains + + create domain posint as int check (value > 0); + + create table pitable (f1 posint[]); + insert into pitable values(array[42]); + insert into pitable values(array[-1]); -- fail + insert into pitable values('{0}'); -- fail + update pitable set f1[1] = f1[1] + 1; + update pitable set f1[1] = 0; -- fail + select * from pitable; + drop table pitable; + + create domain vc4 as varchar(4); + create table vc4table (f1 vc4[]); + insert into vc4table values(array['too long']); -- fail + insert into vc4table values(array['too long']::vc4[]); -- cast truncates + select * from vc4table; + drop table vc4table; + drop type vc4; + + -- You can sort of fake arrays-of-arrays by putting a domain in between + create domain dposinta as posint[]; + create table dposintatable (f1 dposinta[]); + insert into dposintatable values(array[array[42]]); -- fail + insert into dposintatable values(array[array[42]::posint[]]); -- still fail + insert into dposintatable values(array[array[42]::dposinta]); -- but this works + select f1, f1[1], (f1[1])[1] from dposintatable; + select pg_typeof(f1) from dposintatable; + select pg_typeof(f1[1]) from dposintatable; + select pg_typeof(f1[1][1]) from dposintatable; + select pg_typeof((f1[1])[1]) from dposintatable; + update dposintatable set f1[2] = array[99]; + select f1, f1[1], (f1[2])[1] from dposintatable; + -- it'd be nice if you could do something like this, but for now you can't: + update dposintatable set f1[2][1] = array[97]; + -- maybe someday we can make this syntax work: + update dposintatable set (f1[2])[1] = array[98]; + + drop table dposintatable; + drop domain posint cascade; + + -- Test not-null restrictions create domain dnotnull varchar(15) NOT NULL;
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers