I wrote:
> 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.
I see the patch tester is complaining that this broke, due to commit
4bd199465. The fix is trivial (s/GETARG_ANY_ARRAY/GETARG_ANY_ARRAY_P/)
but for convenience here's an updated patch set.
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_P(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)
*** 1816,1825 ****
{
for (;;)
{
! if (node && IsA(node, ArrayCoerceExpr) &&
! ((ArrayCoerceExpr *) node)->elemfuncid == InvalidOid)
{
! node = (Node *) ((ArrayCoerceExpr *) node)->arg;
}
else if (node && IsA(node, RelabelType))
{
--- 1816,1834 ----
{
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 ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers