1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-09 06:21:09 +03:00

First phase of work on array improvements. ARRAY[x,y,z] constructor

expressions, ARRAY(sub-SELECT) expressions, some array functions.
Polymorphic functions using ANYARRAY/ANYELEMENT argument and return
types.  Some regression tests in place, documentation is lacking.
Joe Conway, with some kibitzing from Tom Lane.
This commit is contained in:
Tom Lane
2003-04-08 23:20:04 +00:00
parent 6fb5115850
commit 730840c9b6
47 changed files with 2597 additions and 479 deletions

View File

@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.410 2003/04/01 23:42:55 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.411 2003/04/08 23:20:01 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -265,8 +265,8 @@ static void doNegateFloat(Value *v);
%type <defelt> def_elem
%type <node> def_arg columnElem where_clause insert_column_item
a_expr b_expr c_expr r_expr AexprConst
in_expr having_clause func_table
%type <list> row row_descriptor type_list
in_expr having_clause func_table array_expr
%type <list> row row_descriptor type_list array_expr_list
%type <node> case_expr case_arg when_clause case_default
%type <list> when_clause_list
%type <ival> sub_type
@@ -323,7 +323,7 @@ static void doNegateFloat(Value *v);
/* ordinary key words in alphabetical order */
%token <keyword> ABORT_P ABSOLUTE ACCESS ACTION ADD AFTER
AGGREGATE ALL ALTER ANALYSE ANALYZE AND ANY AS ASC
AGGREGATE ALL ALTER ANALYSE ANALYZE AND ANY ARRAY AS ASC
ASSERTION ASSIGNMENT AT AUTHORIZATION
BACKWARD BEFORE BEGIN_P BETWEEN BIGINT BINARY BIT
@@ -4986,6 +4986,19 @@ Typename: SimpleTypename opt_array_bounds
$$->arrayBounds = $3;
$$->setof = TRUE;
}
| SimpleTypename ARRAY '[' Iconst ']'
{
/* SQL99's redundant syntax */
$$ = $1;
$$->arrayBounds = makeList1(makeInteger($4));
}
| SETOF SimpleTypename ARRAY '[' Iconst ']'
{
/* SQL99's redundant syntax */
$$ = $2;
$$->arrayBounds = makeList1(makeInteger($5));
$$->setof = TRUE;
}
;
opt_array_bounds:
@@ -6057,7 +6070,6 @@ c_expr: columnref { $$ = (Node *) $1; }
n->indirection = $3;
$$ = (Node *)n;
}
| '(' a_expr ')' { $$ = $2; }
| '(' a_expr ')' attrs opt_indirection
{
ExprFieldSelect *n = makeNode(ExprFieldSelect);
@@ -6066,6 +6078,19 @@ c_expr: columnref { $$ = (Node *) $1; }
n->indirection = $5;
$$ = (Node *)n;
}
| '(' a_expr ')' opt_indirection
{
if ($4)
{
ExprFieldSelect *n = makeNode(ExprFieldSelect);
n->arg = $2;
n->fields = NIL;
n->indirection = $4;
$$ = (Node *)n;
}
else
$$ = $2;
}
| case_expr
{ $$ = $1; }
| func_name '(' ')'
@@ -6509,6 +6534,17 @@ c_expr: columnref { $$ = (Node *) $1; }
n->subselect = $2;
$$ = (Node *)n;
}
| ARRAY select_with_parens
{
SubLink *n = makeNode(SubLink);
n->subLinkType = ARRAY_SUBLINK;
n->lefthand = NIL;
n->operName = NIL;
n->subselect = $2;
$$ = (Node *)n;
}
| ARRAY array_expr
{ $$ = $2; }
;
/*
@@ -6559,6 +6595,26 @@ type_list: type_list ',' Typename
}
;
array_expr_list: array_expr
{ $$ = makeList1($1); }
| array_expr_list ',' array_expr
{ $$ = lappend($1, $3); }
;
array_expr: '[' expr_list ']'
{
ArrayExpr *n = makeNode(ArrayExpr);
n->elements = $2;
$$ = (Node *)n;
}
| '[' array_expr_list ']'
{
ArrayExpr *n = makeNode(ArrayExpr);
n->elements = $2;
$$ = (Node *)n;
}
;
/* Allow delimited string SCONST in extract_arg as an SQL extension.
* - thomas 2001-04-12
*/
@@ -7346,6 +7402,7 @@ reserved_keyword:
| ANALYZE
| AND
| ANY
| ARRAY
| AS
| ASC
| BOTH

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.137 2003/03/27 16:51:28 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.138 2003/04/08 23:20:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -43,6 +43,7 @@ static const ScanKeyword ScanKeywords[] = {
{"analyze", ANALYZE},
{"and", AND},
{"any", ANY},
{"array", ARRAY},
{"as", AS},
{"asc", ASC},
{"assertion", ASSERTION},

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.93 2003/02/09 06:56:28 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.94 2003/04/08 23:20:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -189,7 +189,8 @@ coerce_type(Node *node, Oid inputTypeId, Oid targetTypeId,
ReleaseSysCache(targetType);
}
else if (targetTypeId == ANYOID ||
targetTypeId == ANYARRAYOID)
targetTypeId == ANYARRAYOID ||
targetTypeId == ANYELEMENTOID)
{
/* assume can_coerce_type verified that implicit coercion is okay */
/* NB: we do NOT want a RelabelType here */
@@ -295,6 +296,7 @@ bool
can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids,
CoercionContext ccontext)
{
bool have_generics = false;
int i;
/* run through argument list... */
@@ -329,29 +331,12 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids,
if (targetTypeId == ANYOID)
continue;
/*
* if target is ANYARRAY and source is a varlena array type,
* accept
*/
if (targetTypeId == ANYARRAYOID)
/* accept if target is ANYARRAY or ANYELEMENT, for now */
if (targetTypeId == ANYARRAYOID ||
targetTypeId == ANYELEMENTOID)
{
Oid typOutput;
Oid typElem;
bool typIsVarlena;
if (getTypeOutputInfo(inputTypeId, &typOutput, &typElem,
&typIsVarlena))
{
if (OidIsValid(typElem) && typIsVarlena)
continue;
}
/*
* Otherwise reject; this assumes there are no explicit
* coercion paths to ANYARRAY. If we don't reject then
* parse_coerce would have to repeat the above test.
*/
return false;
have_generics = true; /* do more checking later */
continue;
}
/*
@@ -374,6 +359,14 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids,
return false;
}
/* If we found any generic argument types, cross-check them */
if (have_generics)
{
if (!check_generic_type_consistency(input_typeids, target_typeids,
nargs))
return false;
}
return true;
}
@@ -644,6 +637,260 @@ coerce_to_common_type(Node *node, Oid targetTypeId, const char *context)
return node;
}
/*
* check_generic_type_consistency()
* Are the actual arguments potentially compatible with a
* polymorphic function?
*
* The argument consistency rules are:
*
* 1) All arguments declared ANYARRAY must have matching datatypes,
* and must in fact be varlena arrays.
* 2) All arguments declared ANYELEMENT must have matching datatypes.
* 3) If there are arguments of both ANYELEMENT and ANYARRAY, make sure
* the actual ANYELEMENT datatype is in fact the element type for
* the actual ANYARRAY datatype.
*
* If we have UNKNOWN input (ie, an untyped literal) for any ANYELEMENT
* or ANYARRAY argument, assume it is okay.
*
* We do not elog here, but just return FALSE if a rule is violated.
*/
bool
check_generic_type_consistency(Oid *actual_arg_types,
Oid *declared_arg_types,
int nargs)
{
int j;
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
Oid array_typelem;
/*
* Loop through the arguments to see if we have any that are
* ANYARRAY or ANYELEMENT. If so, require the actual types to be
* self-consistent
*/
for (j = 0; j < nargs; j++)
{
Oid actual_type = actual_arg_types[j];
if (declared_arg_types[j] == ANYELEMENTOID)
{
if (actual_type == UNKNOWNOID)
continue;
if (OidIsValid(elem_typeid) && actual_type != elem_typeid)
return false;
elem_typeid = actual_type;
}
else if (declared_arg_types[j] == ANYARRAYOID)
{
if (actual_type == UNKNOWNOID)
continue;
if (OidIsValid(array_typeid) && actual_type != array_typeid)
return false;
array_typeid = actual_type;
}
}
/* Get the element type based on the array type, if we have one */
if (OidIsValid(array_typeid))
{
array_typelem = get_element_type(array_typeid);
if (!OidIsValid(array_typelem))
return false; /* should be an array, but isn't */
if (!OidIsValid(elem_typeid))
{
/* if we don't have an element type yet, use the one we just got */
elem_typeid = array_typelem;
}
else if (array_typelem != elem_typeid)
{
/* otherwise, they better match */
return false;
}
}
/* Looks valid */
return true;
}
/*
* enforce_generic_type_consistency()
* Make sure a polymorphic function is legally callable, and
* deduce actual argument and result types.
*
* If ANYARRAY or ANYELEMENT is used for a function's arguments or
* return type, we make sure the actual data types are consistent with
* each other. The argument consistency rules are shown above for
* check_generic_type_consistency().
*
* If we have UNKNOWN input (ie, an untyped literal) for any ANYELEMENT
* or ANYARRAY argument, we attempt to deduce the actual type it should
* have. If successful, we alter that position of declared_arg_types[]
* so that make_fn_arguments will coerce the literal to the right thing.
*
* Rules are applied to the function's return type (possibly altering it)
* if it is declared ANYARRAY or ANYELEMENT:
*
* 1) If return type is ANYARRAY, and any argument is ANYARRAY, use the
* argument's actual type as the function's return type.
* 2) If return type is ANYARRAY, no argument is ANYARRAY, but any argument
* is ANYELEMENT, use the actual type of the argument to determine
* the function's return type, i.e. the element type's corresponding
* array type.
* 3) If return type is ANYARRAY, no argument is ANYARRAY or ANYELEMENT,
* generate an ERROR. This condition is prevented by CREATE FUNCTION
* and is therefore not expected here.
* 4) If return type is ANYELEMENT, and any argument is ANYELEMENT, use the
* argument's actual type as the function's return type.
* 5) If return type is ANYELEMENT, no argument is ANYELEMENT, but any
* argument is ANYARRAY, use the actual type of the argument to determine
* the function's return type, i.e. the array type's corresponding
* element type.
* 6) If return type is ANYELEMENT, no argument is ANYARRAY or ANYELEMENT,
* generate an ERROR. This condition is prevented by CREATE FUNCTION
* and is therefore not expected here.
*/
Oid
enforce_generic_type_consistency(Oid *actual_arg_types,
Oid *declared_arg_types,
int nargs,
Oid rettype)
{
int j;
bool have_generics = false;
bool have_unknowns = false;
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
Oid array_typelem = InvalidOid;
/*
* Loop through the arguments to see if we have any that are
* ANYARRAY or ANYELEMENT. If so, require the actual types to be
* self-consistent
*/
for (j = 0; j < nargs; j++)
{
Oid actual_type = actual_arg_types[j];
if (declared_arg_types[j] == ANYELEMENTOID)
{
have_generics = true;
if (actual_type == UNKNOWNOID)
{
have_unknowns = true;
continue;
}
if (OidIsValid(elem_typeid) && actual_type != elem_typeid)
elog(ERROR, "Arguments declared ANYELEMENT are not all alike: %s vs %s",
format_type_be(elem_typeid),
format_type_be(actual_type));
elem_typeid = actual_type;
}
else if (declared_arg_types[j] == ANYARRAYOID)
{
have_generics = true;
if (actual_type == UNKNOWNOID)
{
have_unknowns = true;
continue;
}
if (OidIsValid(array_typeid) && actual_type != array_typeid)
elog(ERROR, "Arguments declared ANYARRAY are not all alike: %s vs %s",
format_type_be(array_typeid),
format_type_be(actual_type));
array_typeid = actual_type;
}
}
/*
* Fast Track: if none of the arguments are ANYARRAY or ANYELEMENT,
* return the unmodified rettype.
*/
if (!have_generics)
return rettype;
/* Get the element type based on the array type, if we have one */
if (OidIsValid(array_typeid))
{
array_typelem = get_element_type(array_typeid);
if (!OidIsValid(array_typelem))
elog(ERROR, "Argument declared ANYARRAY is not an array: %s",
format_type_be(array_typeid));
if (!OidIsValid(elem_typeid))
{
/* if we don't have an element type yet, use the one we just got */
elem_typeid = array_typelem;
}
else if (array_typelem != elem_typeid)
{
/* otherwise, they better match */
elog(ERROR, "Argument declared ANYARRAY is not consistent with "
"argument declared ANYELEMENT: %s vs %s",
format_type_be(array_typeid),
format_type_be(elem_typeid));
}
}
else if (!OidIsValid(elem_typeid))
{
/* Only way to get here is if all the generic args are UNKNOWN */
elog(ERROR, "Cannot determine ANYARRAY/ANYELEMENT type because input is UNKNOWN");
}
/*
* If we had any unknown inputs, re-scan to assign correct types
*/
if (have_unknowns)
{
for (j = 0; j < nargs; j++)
{
Oid actual_type = actual_arg_types[j];
if (actual_type != UNKNOWNOID)
continue;
if (declared_arg_types[j] == ANYELEMENTOID)
{
declared_arg_types[j] = elem_typeid;
}
else if (declared_arg_types[j] == ANYARRAYOID)
{
if (!OidIsValid(array_typeid))
{
array_typeid = get_array_type(elem_typeid);
if (!OidIsValid(array_typeid))
elog(ERROR, "Cannot find array type for datatype %s",
format_type_be(elem_typeid));
}
declared_arg_types[j] = array_typeid;
}
}
}
/* if we return ANYARRAYOID use the appropriate argument type */
if (rettype == ANYARRAYOID)
{
if (!OidIsValid(array_typeid))
{
array_typeid = get_array_type(elem_typeid);
if (!OidIsValid(array_typeid))
elog(ERROR, "Cannot find array type for datatype %s",
format_type_be(elem_typeid));
}
return array_typeid;
}
/* if we return ANYELEMENTOID use the appropriate argument type */
if (rettype == ANYELEMENTOID)
return elem_typeid;
/* we don't return a generic type; send back the original return type */
return rettype;
}
/* TypeCategory()
* Assign a category to the specified OID.
@@ -727,6 +974,19 @@ TypeCategory(Oid inType)
result = UNKNOWN_TYPE;
break;
case (RECORDOID):
case (CSTRINGOID):
case (ANYOID):
case (ANYARRAYOID):
case (VOIDOID):
case (TRIGGEROID):
case (LANGUAGE_HANDLEROID):
case (INTERNALOID):
case (OPAQUEOID):
case (ANYELEMENTOID):
result = GENERIC_TYPE;
break;
default:
result = USER_TYPE;
break;
@@ -761,6 +1021,12 @@ PreferredType(CATEGORY category, Oid type)
switch (category)
{
case (INVALID_TYPE):
case (UNKNOWN_TYPE):
case (GENERIC_TYPE):
result = UNKNOWNOID;
break;
case (BOOLEAN_TYPE):
result = BOOLOID;
break;
@@ -797,16 +1063,20 @@ PreferredType(CATEGORY category, Oid type)
result = INTERVALOID;
break;
case (GEOMETRIC_TYPE):
result = type;
break;
case (NETWORK_TYPE):
result = INETOID;
break;
case (GEOMETRIC_TYPE):
case (USER_TYPE):
result = type;
break;
default:
elog(ERROR, "PreferredType: unknown category");
result = UNKNOWNOID;
break;
}
@@ -897,7 +1167,7 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
if (sourceTypeId == targetTypeId)
return true;
/* Else look in pg_cast */
/* Look in pg_cast */
tuple = SearchSysCache(CASTSOURCETARGET,
ObjectIdGetDatum(sourceTypeId),
ObjectIdGetDatum(targetTypeId),
@@ -936,6 +1206,28 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
ReleaseSysCache(tuple);
}
else
{
/*
* 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, use array_type_coerce().
*/
Oid targetElemType;
Oid sourceElemType;
Oid elemfuncid;
if ((targetElemType = get_element_type(targetTypeId)) != InvalidOid &&
(sourceElemType = get_element_type(sourceTypeId)) != InvalidOid)
{
if (find_coercion_pathway(targetElemType, sourceElemType,
ccontext, &elemfuncid))
{
*funcid = F_ARRAY_TYPE_COERCE;
result = true;
}
}
}
return result;
}

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.146 2003/02/16 02:30:38 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.147 2003/04/08 23:20:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -395,7 +395,8 @@ transformExpr(ParseState *pstate, Node *expr)
sublink->operOids = NIL;
sublink->useOr = FALSE;
}
else if (sublink->subLinkType == EXPR_SUBLINK)
else if (sublink->subLinkType == EXPR_SUBLINK ||
sublink->subLinkType == ARRAY_SUBLINK)
{
List *tlist = qtree->targetList;
@@ -413,8 +414,8 @@ transformExpr(ParseState *pstate, Node *expr)
}
/*
* EXPR needs no lefthand or combining operator. These
* fields should be NIL already, but make sure.
* EXPR and ARRAY need no lefthand or combining operator.
* These fields should be NIL already, but make sure.
*/
sublink->lefthand = NIL;
sublink->operName = NIL;
@@ -633,6 +634,98 @@ transformExpr(ParseState *pstate, Node *expr)
break;
}
case T_ArrayExpr:
{
ArrayExpr *a = (ArrayExpr *) expr;
ArrayExpr *newa = makeNode(ArrayExpr);
List *newelems = NIL;
List *newcoercedelems = NIL;
List *typeids = NIL;
List *element;
Oid array_type;
Oid element_type;
int ndims;
/* Transform the element expressions */
foreach(element, a->elements)
{
Node *e = (Node *) lfirst(element);
Node *newe;
newe = transformExpr(pstate, e);
newelems = lappend(newelems, newe);
typeids = lappendo(typeids, exprType(newe));
}
/* Select a common type for the elements */
element_type = select_common_type(typeids, "ARRAY");
/* Coerce arguments to common type if necessary */
foreach(element, newelems)
{
Node *e = (Node *) lfirst(element);
Node *newe;
newe = coerce_to_common_type(e, element_type, "ARRAY");
newcoercedelems = lappend(newcoercedelems, newe);
}
/* Do we have an array type to use? */
array_type = get_array_type(element_type);
if (array_type != InvalidOid)
{
/* Elements are presumably of scalar type */
ndims = 1;
}
else
{
/* Must be nested array expressions */
array_type = element_type;
element_type = get_element_type(array_type);
if (!OidIsValid(element_type))
elog(ERROR, "Cannot find array type for datatype %s",
format_type_be(array_type));
/*
* make sure the element expressions all have the same
* number of dimensions
*/
ndims = 0;
foreach(element, newcoercedelems)
{
ArrayExpr *e = (ArrayExpr *) lfirst(element);
if (!IsA(e, ArrayExpr))
elog(ERROR, "Multi-dimensional ARRAY[] must be built from nested array expressions");
if (ndims == 0)
ndims = e->ndims;
else if (e->ndims != ndims)
elog(ERROR, "Nested array expressions must have "
"common number of dimensions");
if (e->element_typeid != element_type)
elog(ERROR, "Nested array expressions must have "
"common element type");
}
/* increment the number of dimensions */
ndims++;
/* make sure we don't have too many dimensions now */
if (ndims > MAXDIM)
elog(ERROR, "Number of array dimensions, %d, "
"exceeds the maximum allowed %d",
ndims, MAXDIM);
}
newa->array_typeid = array_type;
newa->element_typeid = element_type;
newa->elements = newcoercedelems;
newa->ndims = ndims;
result = (Node *) newa;
break;
}
case T_CoalesceExpr:
{
CoalesceExpr *c = (CoalesceExpr *) expr;
@@ -1018,7 +1111,8 @@ exprType(Node *expr)
{
SubLink *sublink = (SubLink *) expr;
if (sublink->subLinkType == EXPR_SUBLINK)
if (sublink->subLinkType == EXPR_SUBLINK ||
sublink->subLinkType == ARRAY_SUBLINK)
{
/* get the type of the subselect's first target column */
Query *qtree = (Query *) sublink->subselect;
@@ -1029,7 +1123,15 @@ exprType(Node *expr)
tent = (TargetEntry *) lfirst(qtree->targetList);
Assert(IsA(tent, TargetEntry));
Assert(!tent->resdom->resjunk);
type = tent->resdom->restype;
if (sublink->subLinkType == EXPR_SUBLINK)
type = tent->resdom->restype;
else /* ARRAY_SUBLINK */
{
type = get_array_type(tent->resdom->restype);
if (!OidIsValid(type))
elog(ERROR, "Cannot find array type for datatype %s",
format_type_be(tent->resdom->restype));
}
}
else
{
@@ -1047,7 +1149,8 @@ exprType(Node *expr)
*/
SubPlan *subplan = (SubPlan *) expr;
if (subplan->subLinkType == EXPR_SUBLINK)
if (subplan->subLinkType == EXPR_SUBLINK ||
subplan->subLinkType == ARRAY_SUBLINK)
{
/* get the type of the subselect's first target column */
TargetEntry *tent;
@@ -1055,7 +1158,15 @@ exprType(Node *expr)
tent = (TargetEntry *) lfirst(subplan->plan->targetlist);
Assert(IsA(tent, TargetEntry));
Assert(!tent->resdom->resjunk);
type = tent->resdom->restype;
if (subplan->subLinkType == EXPR_SUBLINK)
type = tent->resdom->restype;
else /* ARRAY_SUBLINK */
{
type = get_array_type(tent->resdom->restype);
if (!OidIsValid(type))
elog(ERROR, "Cannot find array type for datatype %s",
format_type_be(tent->resdom->restype));
}
}
else
{
@@ -1076,6 +1187,9 @@ exprType(Node *expr)
case T_CaseWhen:
type = exprType((Node *) ((CaseWhen *) expr)->result);
break;
case T_ArrayExpr:
type = ((ArrayExpr *) expr)->array_typeid;
break;
case T_CoalesceExpr:
type = ((CoalesceExpr *) expr)->coalescetype;
break;

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.144 2003/02/09 06:56:28 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.145 2003/04/08 23:20:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -37,10 +37,6 @@ static Oid **argtype_inherit(int nargs, Oid *argtypes);
static int find_inheritors(Oid relid, Oid **supervec);
static Oid **gen_cross_product(InhPaths *arginh, int nargs);
static void make_arguments(int nargs,
List *fargs,
Oid *input_typeids,
Oid *function_typeids);
static int match_argtypes(int nargs,
Oid *input_typeids,
FuncCandidateList function_typeids,
@@ -81,8 +77,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
Node *first_arg = NULL;
int nargs = length(fargs);
int argn;
Oid oid_array[FUNC_MAX_ARGS];
Oid *true_oid_array;
Oid actual_arg_types[FUNC_MAX_ARGS];
Oid *declared_arg_types;
Node *retval;
bool retset;
FuncDetailCode fdresult;
@@ -145,7 +141,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
* function. Extract arg type info and transform RangeVar arguments
* into varnodes of the appropriate form.
*/
MemSet(oid_array, 0, FUNC_MAX_ARGS * sizeof(Oid));
MemSet(actual_arg_types, 0, FUNC_MAX_ARGS * sizeof(Oid));
argn = 0;
foreach(i, fargs)
@@ -238,7 +234,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
else
toid = exprType(arg);
oid_array[argn++] = toid;
actual_arg_types[argn++] = toid;
}
/*
@@ -248,16 +244,16 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
* function's return value. it also returns the true argument types
* to the function.
*/
fdresult = func_get_detail(funcname, fargs, nargs, oid_array,
fdresult = func_get_detail(funcname, fargs, nargs, actual_arg_types,
&funcid, &rettype, &retset,
&true_oid_array);
&declared_arg_types);
if (fdresult == FUNCDETAIL_COERCION)
{
/*
* We can do it as a trivial coercion. coerce_type can handle
* these cases, so why duplicate code...
*/
return coerce_type(lfirst(fargs), oid_array[0], rettype,
return coerce_type(lfirst(fargs), actual_arg_types[0], rettype,
COERCION_EXPLICIT, COERCE_EXPLICIT_CALL);
}
else if (fdresult == FUNCDETAIL_NORMAL)
@@ -303,14 +299,24 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
/*
* Else generate a detailed complaint for a function
*/
func_error(NULL, funcname, nargs, oid_array,
func_error(NULL, funcname, nargs, actual_arg_types,
"Unable to identify a function that satisfies the "
"given argument types"
"\n\tYou may need to add explicit typecasts");
}
/*
* enforce consistency with ANYARRAY and ANYELEMENT argument and
* return types, possibly adjusting return type or declared_arg_types
* (which will be used as the cast destination by make_fn_arguments)
*/
rettype = enforce_generic_type_consistency(actual_arg_types,
declared_arg_types,
nargs,
rettype);
/* perform the necessary typecasting of arguments */
make_arguments(nargs, fargs, oid_array, true_oid_array);
make_fn_arguments(fargs, actual_arg_types, declared_arg_types);
/* build the appropriate output structure */
if (fdresult == FUNCDETAIL_NORMAL)
@@ -1130,32 +1136,36 @@ typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId)
}
/* make_arguments()
* Given the number and types of arguments to a function, and the
* actual arguments and argument types, do the necessary typecasting.
/*
* make_fn_arguments()
*
* Given the actual argument expressions for a function, and the desired
* input types for the function, add any necessary typecasting to the
* expression tree. Caller should already have verified that casting is
* allowed.
*
* Caution: given argument list is modified in-place.
*/
static void
make_arguments(int nargs,
List *fargs,
Oid *input_typeids,
Oid *function_typeids)
void
make_fn_arguments(List *fargs,
Oid *actual_arg_types,
Oid *declared_arg_types)
{
List *current_fargs;
int i;
int i = 0;
for (i = 0, current_fargs = fargs;
i < nargs;
i++, current_fargs = lnext(current_fargs))
foreach(current_fargs, fargs)
{
/* types don't match? then force coercion using a function call... */
if (input_typeids[i] != function_typeids[i])
if (actual_arg_types[i] != declared_arg_types[i])
{
lfirst(current_fargs) = coerce_type(lfirst(current_fargs),
input_typeids[i],
function_typeids[i],
actual_arg_types[i],
declared_arg_types[i],
COERCION_IMPLICIT,
COERCE_IMPLICIT_CAST);
}
i++;
}
}

View File

@@ -8,25 +8,21 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.76 2002/12/12 20:35:13 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.77 2003/04/08 23:20:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/heapam.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
#include "parser/parsetree.h"
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
#include "parser/parse_node.h"
#include "parser/parse_oper.h"
#include "parser/parse_relation.h"
#include "utils/builtins.h"
#include "utils/int8.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "utils/varbit.h"
@@ -49,99 +45,6 @@ make_parsestate(ParseState *parentParseState)
}
/* make_operand()
* Ensure argument type match by forcing conversion of constants.
*/
Node *
make_operand(Node *tree, Oid orig_typeId, Oid target_typeId)
{
Node *result;
if (tree != NULL)
{
/* must coerce? */
if (target_typeId != orig_typeId)
result = coerce_type(tree, orig_typeId, target_typeId,
COERCION_IMPLICIT, COERCE_IMPLICIT_CAST);
else
result = tree;
}
else
{
/* otherwise, this is a NULL value */
result = (Node *) makeNullConst(target_typeId);
}
return result;
} /* make_operand() */
/* make_op()
* Operator construction.
*
* Transform operator expression ensuring type compatibility.
* This is where some type conversion happens.
*/
Expr *
make_op(List *opname, Node *ltree, Node *rtree)
{
Oid ltypeId,
rtypeId;
Operator tup;
Form_pg_operator opform;
Node *left,
*right;
OpExpr *result;
ltypeId = (ltree == NULL) ? UNKNOWNOID : exprType(ltree);
rtypeId = (rtree == NULL) ? UNKNOWNOID : exprType(rtree);
/* right operator? */
if (rtree == NULL)
{
tup = right_oper(opname, ltypeId, false);
opform = (Form_pg_operator) GETSTRUCT(tup);
left = make_operand(ltree, ltypeId, opform->oprleft);
right = NULL;
}
/* left operator? */
else if (ltree == NULL)
{
tup = left_oper(opname, rtypeId, false);
opform = (Form_pg_operator) GETSTRUCT(tup);
right = make_operand(rtree, rtypeId, opform->oprright);
left = NULL;
}
/* otherwise, binary operator */
else
{
tup = oper(opname, ltypeId, rtypeId, false);
opform = (Form_pg_operator) GETSTRUCT(tup);
left = make_operand(ltree, ltypeId, opform->oprleft);
right = make_operand(rtree, rtypeId, opform->oprright);
}
result = makeNode(OpExpr);
result->opno = oprid(tup);
result->opfuncid = InvalidOid;
result->opresulttype = opform->oprresult;
result->opretset = get_func_retset(opform->oprcode);
if (!left)
result->args = makeList1(right);
else if (!right)
result->args = makeList1(left);
else
result->args = makeList2(left, right);
ReleaseSysCache(tup);
return (Expr *) result;
} /* make_op() */
/*
* make_var
* Build a Var node for an attribute identified by RTE and attrno
@@ -193,10 +96,8 @@ transformArraySubscripts(ParseState *pstate,
{
Oid elementType,
resultType;
HeapTuple type_tuple_array,
type_tuple_element;
Form_pg_type type_struct_array,
type_struct_element;
HeapTuple type_tuple_array;
Form_pg_type type_struct_array;
bool isSlice = forceSlice;
List *upperIndexpr = NIL;
List *lowerIndexpr = NIL;
@@ -217,15 +118,6 @@ transformArraySubscripts(ParseState *pstate,
elog(ERROR, "transformArraySubscripts: type %s is not an array",
NameStr(type_struct_array->typname));
/* Get the type tuple for the array element type */
type_tuple_element = SearchSysCache(TYPEOID,
ObjectIdGetDatum(elementType),
0, 0, 0);
if (!HeapTupleIsValid(type_tuple_element))
elog(ERROR, "transformArraySubscripts: Cache lookup failed for array element type %u",
elementType);
type_struct_element = (Form_pg_type) GETSTRUCT(type_tuple_element);
/*
* A list containing only single subscripts refers to a single array
* element. If any of the items are double subscripts (lower:upper),
@@ -330,19 +222,15 @@ transformArraySubscripts(ParseState *pstate,
* Ready to build the ArrayRef node.
*/
aref = makeNode(ArrayRef);
aref->refrestype = resultType; /* XXX should save element type
* OID too */
aref->refattrlength = type_struct_array->typlen;
aref->refelemlength = type_struct_element->typlen;
aref->refelembyval = type_struct_element->typbyval;
aref->refelemalign = type_struct_element->typalign;
aref->refrestype = resultType;
aref->refarraytype = arrayType;
aref->refelemtype = elementType;
aref->refupperindexpr = upperIndexpr;
aref->reflowerindexpr = lowerIndexpr;
aref->refexpr = (Expr *) arrayBase;
aref->refassgnexpr = (Expr *) assignFrom;
ReleaseSysCache(type_tuple_array);
ReleaseSysCache(type_tuple_element);
return aref;
}

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.61 2002/11/29 21:39:11 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.62 2003/04/08 23:20:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -22,13 +22,16 @@
#include "catalog/namespace.h"
#include "catalog/pg_operator.h"
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
#include "parser/parse_func.h"
#include "parser/parse_oper.h"
#include "parser/parse_type.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
static Oid binary_oper_exact(Oid arg1, Oid arg2,
FuncCandidateList candidates);
static Oid oper_select_candidate(int nargs, Oid *input_typeids,
@@ -1008,3 +1011,118 @@ unary_op_error(List *op, Oid arg, bool is_left_op)
NameListToString(op), format_type_be(arg));
}
}
/*
* make_op()
* Operator expression construction.
*
* Transform operator expression ensuring type compatibility.
* This is where some type conversion happens.
*/
Expr *
make_op(List *opname, Node *ltree, Node *rtree)
{
Oid ltypeId,
rtypeId;
Operator tup;
Expr *result;
/* Select the operator */
if (rtree == NULL)
{
/* right operator */
ltypeId = exprType(ltree);
rtypeId = InvalidOid;
tup = right_oper(opname, ltypeId, false);
}
else if (ltree == NULL)
{
/* left operator */
rtypeId = exprType(rtree);
ltypeId = InvalidOid;
tup = left_oper(opname, rtypeId, false);
}
else
{
/* otherwise, binary operator */
ltypeId = exprType(ltree);
rtypeId = exprType(rtree);
tup = oper(opname, ltypeId, rtypeId, false);
}
/* Do typecasting and build the expression tree */
result = make_op_expr(tup, ltree, rtree, ltypeId, rtypeId);
ReleaseSysCache(tup);
return result;
}
/*
* make_op_expr()
* Build operator expression using an already-looked-up operator.
*/
Expr *
make_op_expr(Operator op, Node *ltree, Node *rtree,
Oid ltypeId, Oid rtypeId)
{
Form_pg_operator opform = (Form_pg_operator) GETSTRUCT(op);
Oid actual_arg_types[2];
Oid declared_arg_types[2];
int nargs;
List *args;
Oid rettype;
OpExpr *result;
if (rtree == NULL)
{
/* right operator */
args = makeList1(ltree);
actual_arg_types[0] = ltypeId;
declared_arg_types[0] = opform->oprleft;
nargs = 1;
}
else if (ltree == NULL)
{
/* left operator */
args = makeList1(rtree);
actual_arg_types[0] = rtypeId;
declared_arg_types[0] = opform->oprright;
nargs = 1;
}
else
{
/* otherwise, binary operator */
args = makeList2(ltree, rtree);
actual_arg_types[0] = ltypeId;
actual_arg_types[1] = rtypeId;
declared_arg_types[0] = opform->oprleft;
declared_arg_types[1] = opform->oprright;
nargs = 2;
}
/*
* enforce consistency with ANYARRAY and ANYELEMENT argument and
* return types, possibly adjusting return type or declared_arg_types
* (which will be used as the cast destination by make_fn_arguments)
*/
rettype = enforce_generic_type_consistency(actual_arg_types,
declared_arg_types,
nargs,
opform->oprresult);
/* perform the necessary typecasting of arguments */
make_fn_arguments(args, actual_arg_types, declared_arg_types);
/* and build the expression node */
result = makeNode(OpExpr);
result->opno = oprid(op);
result->opfuncid = InvalidOid;
result->opresulttype = rettype;
result->opretset = get_func_retset(opform->oprcode);
result->args = args;
return (Expr *) result;
}

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.98 2003/02/16 02:30:38 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.99 2003/04/08 23:20:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -470,13 +470,19 @@ FigureColnameInternal(Node *node, char **name)
break;
case T_ExprFieldSelect:
{
char *fname = strVal(llast(((ExprFieldSelect *) node)->fields));
ExprFieldSelect *efs = (ExprFieldSelect *) node;
if (strcmp(fname, "*") != 0)
if (efs->fields)
{
*name = fname;
return 2;
char *fname = strVal(llast(efs->fields));
if (strcmp(fname, "*") != 0)
{
*name = fname;
return 2;
}
}
return FigureColnameInternal(efs->arg, name);
}
break;
case T_FuncCall:
@@ -518,6 +524,10 @@ FigureColnameInternal(Node *node, char **name)
return 1;
}
break;
case T_ArrayExpr:
/* make ARRAY[] act like a function */
*name = "array";
return 2;
case T_CoalesceExpr:
/* make coalesce() act like a regular function */
*name = "coalesce";