diff options
Diffstat (limited to 'src/backend/parser/parse_node.c')
-rw-r--r-- | src/backend/parser/parse_node.c | 245 |
1 files changed, 133 insertions, 112 deletions
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c index 53c756670c3..80a8543d5a5 100644 --- a/src/backend/parser/parse_node.c +++ b/src/backend/parser/parse_node.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.28 1999/07/17 20:17:24 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.29 1999/07/19 00:26:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -24,6 +24,7 @@ #include "parser/parse_node.h" #include "parser/parse_oper.h" #include "parser/parse_relation.h" +#include "parser/parse_target.h" #include "parser/parse_type.h" #include "utils/builtins.h" #include "utils/lsyscache.h" @@ -222,164 +223,184 @@ make_var(ParseState *pstate, Oid relid, char *refname, } /* - * make_array_ref() -- Make an array reference node. + * transformArraySubscripts() + * Transform array subscripting. This is used for both + * array fetch and array assignment. * - * Array references can hang off of arbitrary nested dot (or - * function invocation) expressions. This routine takes a - * tree generated by ParseFunc() and an array index and - * generates a new array reference tree. We do some simple - * typechecking to be sure the dereference is valid in the - * type system, but we don't do any bounds checking here. + * In an array fetch, we are given a source array value and we produce an + * expression that represents the result of extracting a single array element + * or an array slice. * - * indirection is a list of A_Indices + * In an array assignment, we are given a destination array value plus a + * source value that is to be assigned to a single element or a slice of + * that array. We produce an expression that represents the new array value + * with the source data inserted into the right part of the array. + * + * pstate Parse state + * arrayBase Already-transformed expression for the array as a whole + * indirection Untransformed list of subscripts (must not be NIL) + * forceSlice If true, treat subscript as array slice in all cases + * assignFrom NULL for array fetch, else transformed expression for source. */ -ArrayRef * -make_array_ref(Node *expr, - List *indirection) +ArrayRef * +transformArraySubscripts(ParseState *pstate, + Node *arrayBase, + List *indirection, + bool forceSlice, + Node *assignFrom) { - Oid typearray; + Oid typearray, + typeelement, + typeresult; HeapTuple type_tuple; Form_pg_type type_struct_array, type_struct_element; - ArrayRef *aref; - Oid reftype; + bool isSlice = forceSlice; List *upperIndexpr = NIL; List *lowerIndexpr = NIL; + List *idx; + ArrayRef *aref; - typearray = exprType(expr); + /* Get the type tuple for the array */ + typearray = exprType(arrayBase); type_tuple = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(typearray), 0, 0, 0); - if (!HeapTupleIsValid(type_tuple)) - elog(ERROR, "make_array_ref: Cache lookup failed for type %u\n", + elog(ERROR, "transformArraySubscripts: Cache lookup failed for array type %u", typearray); - - /* get the array type struct from the type tuple */ type_struct_array = (Form_pg_type) GETSTRUCT(type_tuple); - if (type_struct_array->typelem == InvalidOid) - elog(ERROR, "make_array_ref: type %s is not an array", + typeelement = type_struct_array->typelem; + if (typeelement == InvalidOid) + elog(ERROR, "transformArraySubscripts: type %s is not an array", type_struct_array->typname); - /* get the type tuple for the element type */ + /* Get the type tuple for the array element type */ type_tuple = SearchSysCacheTuple(TYPOID, - ObjectIdGetDatum(type_struct_array->typelem), + ObjectIdGetDatum(typeelement), 0, 0, 0); if (!HeapTupleIsValid(type_tuple)) - elog(ERROR, "make_array_ref: Cache lookup failed for type %u\n", - typearray); - + elog(ERROR, "transformArraySubscripts: Cache lookup failed for array element type %u", + typeelement); type_struct_element = (Form_pg_type) GETSTRUCT(type_tuple); - while (indirection != NIL) + /* + * A list containing only single subscripts refers to a single array + * element. If any of the items are double subscripts (lower:upper), + * then the subscript expression means an array slice operation. + * In this case, we supply a default lower bound of 1 for any items + * that contain only a single subscript. + * The forceSlice parameter forces us to treat the operation as a + * slice, even if no lower bounds are mentioned. Otherwise, + * we have to prescan the indirection list to see if there are any + * double subscripts. + */ + if (! isSlice) { - A_Indices *ind = lfirst(indirection); - - if (ind->lidx) - - /* - * XXX assumes all lower indices non null in this case - */ - lowerIndexpr = lappend(lowerIndexpr, ind->lidx); - - upperIndexpr = lappend(upperIndexpr, ind->uidx); - indirection = lnext(indirection); + foreach (idx, indirection) + { + A_Indices *ai = (A_Indices *) lfirst(idx); + if (ai->lidx != NULL) + { + isSlice = true; + break; + } + } } - aref = makeNode(ArrayRef); - aref->refattrlength = type_struct_array->typlen; - aref->refelemlength = type_struct_element->typlen; - aref->refelemtype = type_struct_array->typelem; - aref->refelembyval = type_struct_element->typbyval; - aref->refupperindexpr = upperIndexpr; - aref->reflowerindexpr = lowerIndexpr; - aref->refexpr = expr; - aref->refassgnexpr = NULL; - if (lowerIndexpr == NIL) /* accessing a single array element */ - reftype = aref->refelemtype; + /* The type represented by the subscript expression is the element type + * if we are fetching a single element, but it is the same as the array + * type if we are fetching a slice or storing. + */ + if (isSlice || assignFrom != NULL) + typeresult = typearray; else -/* request to clip a part of the array, the result is another array */ - reftype = typearray; + typeresult = typeelement; /* - * we change it to reflect the true type; since the original - * refelemtype doesn't seem to get used anywhere. - ay 10/94 + * Transform the subscript expressions. */ - aref->refelemtype = reftype; - - return aref; -} - - -/* make_array_set() - */ -ArrayRef * -make_array_set(Expr *target_expr, - List *upperIndexpr, - List *lowerIndexpr, - Expr *expr) -{ - Oid typearray; - HeapTuple type_tuple; - Form_pg_type type_struct_array; - Form_pg_type type_struct_element; - ArrayRef *aref; - Oid reftype; - - typearray = exprType((Node *) target_expr); - - type_tuple = SearchSysCacheTuple(TYPOID, - ObjectIdGetDatum(typearray), - 0, 0, 0); - - if (!HeapTupleIsValid(type_tuple)) - elog(ERROR, "make_array_ref: Cache lookup failed for type %u\n", - typearray); - - /* get the array type struct from the type tuple */ - type_struct_array = (Form_pg_type) GETSTRUCT(type_tuple); + foreach (idx, indirection) + { + A_Indices *ai = (A_Indices *) lfirst(idx); + Node *subexpr; - if (type_struct_array->typelem == InvalidOid) - elog(ERROR, "make_array_ref: type %s is not an array", - type_struct_array->typname); - /* get the type tuple for the element type */ - type_tuple = SearchSysCacheTuple(TYPOID, - ObjectIdGetDatum(type_struct_array->typelem), - 0, 0, 0); + if (isSlice) + { + if (ai->lidx) + { + subexpr = transformExpr(pstate, ai->lidx, EXPR_COLUMN_FIRST); + /* If it's not int4 already, try to coerce */ + subexpr = CoerceTargetExpr(pstate, subexpr, + exprType(subexpr), INT4OID); + if (subexpr == NULL) + elog(ERROR, "array index expressions must be integers"); + } + else + { + /* Make a constant 1 */ + subexpr = (Node *) makeConst(INT4OID, + sizeof(int32), + Int32GetDatum(1), + false, + true, /* pass by value */ + false, + false); + } + lowerIndexpr = lappend(lowerIndexpr, subexpr); + } + subexpr = transformExpr(pstate, ai->uidx, EXPR_COLUMN_FIRST); + /* If it's not int4 already, try to coerce */ + subexpr = CoerceTargetExpr(pstate, subexpr, + exprType(subexpr), INT4OID); + if (subexpr == NULL) + elog(ERROR, "array index expressions must be integers"); + upperIndexpr = lappend(upperIndexpr, subexpr); + } - if (!HeapTupleIsValid(type_tuple)) - elog(ERROR, "make_array_ref: Cache lookup failed for type %u\n", - typearray); + /* + * If doing an array store, coerce the source value to the right type. + */ + if (assignFrom != NULL) + { + Oid typesource = exprType(assignFrom); + Oid typeneeded = isSlice ? typearray : typeelement; - type_struct_element = (Form_pg_type) GETSTRUCT(type_tuple); + if (typesource != InvalidOid) + { + if (typesource != typeneeded) + { + assignFrom = CoerceTargetExpr(pstate, assignFrom, + typesource, typeneeded); + if (assignFrom == NULL) + elog(ERROR, "Array assignment requires type '%s'" + " but expression is of type '%s'" + "\n\tYou will need to rewrite or cast the expression", + typeidTypeName(typeneeded), + typeidTypeName(typesource)); + } + } + } + /* + * Ready to build the ArrayRef node. + */ aref = makeNode(ArrayRef); aref->refattrlength = type_struct_array->typlen; aref->refelemlength = type_struct_element->typlen; - aref->refelemtype = type_struct_array->typelem; + aref->refelemtype = typeresult; /* XXX should save element type too */ aref->refelembyval = type_struct_element->typbyval; aref->refupperindexpr = upperIndexpr; aref->reflowerindexpr = lowerIndexpr; - aref->refexpr = (Node *) target_expr; - aref->refassgnexpr = (Node *) expr; - - /* accessing a single array element? */ - if (lowerIndexpr == NIL) - reftype = aref->refelemtype; - - /* otherwise, request to set a part of the array, by another array */ - else - reftype = typearray; - - aref->refelemtype = reftype; + aref->refexpr = arrayBase; + aref->refassgnexpr = assignFrom; return aref; } /* - * * make_const - * * - takes a lispvalue, (as returned to the yacc routine by the lexer) |