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:
@@ -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
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
Reference in New Issue
Block a user