array support phase 3 patch (was Re: array support patch phase 1 patch) - Mailing list pgsql-patches
From | Joe Conway |
---|---|
Subject | array support phase 3 patch (was Re: array support patch phase 1 patch) |
Date | |
Msg-id | 3ED9213F.8050607@joeconway.com Whole thread Raw |
In response to | Re: array support patch phase 1 patch (Tom Lane <tgl@sss.pgh.pa.us>) |
List | pgsql-patches |
Tom Lane wrote: > Joe Conway <mail@joeconway.com> writes: >>Maybe this takes us >>back to Peter's suggestion: >> expression IN (array) >> expression NOT IN (array) >> expression operator ANY (array) >> expression operator SOME (array) >> (expression) operator (array) >> (expression) operator ALL (array) > > There's a lot to be said for that, if we can think of a way to do it. > I believe this way would force us to integrate the operations into the > parser, though, rather than just throwing a few polymorphic functions > at the problem. It's probably a lot more work :-( The attached implements the middle 2 examples above. The IN and NOT IN examples would be ambiguous due to the fact that we already accept "IN (list_of_expressions)" and "NOT IN (list_of_expressions)". Specifically implemented: expression1 operator ANY (expression2) expression1 operator SOME (expression2) expression1 operator ALL (expression2) If expression2 is scalar, it simplifies down to the same as doing: expression1 operator ANY | SOME | ALL (select expression2) If expression2 is an array, the array elements are tested as if you did this: expression1 operator ANY | SOME | ALL (select element_a union all select element_b... union all select element_n) Here are a few examples: create table tse(f1 int, f2 int[], f3 text[]); insert into tse values(1,array[69,42,54], array['g','d','e']); insert into tse values(2,array[1,2,3], array['x','y','z']); insert into tse values(3,array[2,99,0], array['Tom','Bruce']); insert into tse values(4,array[1,1,1], array['a','a','a']); insert into tse values(5,array[5,6,7], array['a','b','c']); regression=# select * from tse where 1 = any (f2); f1 | f2 | f3 ----+---------+--------- 2 | {1,2,3} | {x,y,z} 4 | {1,1,1} | {a,a,a} (2 rows) regression=# select * from tse where 1 = all (f2); f1 | f2 | f3 ----+---------+--------- 4 | {1,1,1} | {a,a,a} (1 row) regression=# select * from tse where 'a' != any (f3); f1 | f2 | f3 ----+------------+------------- 1 | {69,42,54} | {g,d,e} 2 | {1,2,3} | {x,y,z} 3 | {2,99,0} | {Tom,Bruce} 5 | {5,6,7} | {a,b,c} (4 rows) regression=# select * from tse where 'a' != all (f3); f1 | f2 | f3 ----+------------+------------- 1 | {69,42,54} | {g,d,e} 2 | {1,2,3} | {x,y,z} 3 | {2,99,0} | {Tom,Bruce} (3 rows) regression=# select * from tse where 'y' < some (f3); f1 | f2 | f3 ----+---------+--------- 2 | {1,2,3} | {x,y,z} (1 row) The patch passes all regression tests. It is independent of the phase2 patch submitted earlier this week. I am already planning to update the array related docs one more time after the feature freeze, so I'd like to wait until then to document this. If there are no objections, please apply. Thanks, Joe Index: src/backend/executor/nodeSubplan.c =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeSubplan.c,v retrieving revision 1.45 diff -c -r1.45 nodeSubplan.c *** src/backend/executor/nodeSubplan.c 8 Apr 2003 23:20:01 -0000 1.45 --- src/backend/executor/nodeSubplan.c 31 May 2003 20:38:27 -0000 *************** *** 224,229 **** --- 224,230 ---- PlanState *planstate = node->planstate; SubLinkType subLinkType = subplan->subLinkType; bool useOr = subplan->useOr; + bool isExpr = subplan->isExpr; MemoryContext oldcontext; TupleTableSlot *slot; Datum result; *************** *** 292,297 **** --- 293,303 ---- bool rownull = false; int col = 1; List *plst; + int numelems; + int elemnum; + Datum dvalue; + Datum *dvalues = NULL; + bool disnull; if (subLinkType == EXISTS_SUBLINK) { *************** *** 329,337 **** if (subLinkType == ARRAY_SUBLINK) { - Datum dvalue; - bool disnull; - found = true; /* stash away current value */ dvalue = heap_getattr(tup, 1, tdesc, &disnull); --- 335,340 ---- *************** *** 349,446 **** found = true; /* ! * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining ! * operators for columns of tuple. */ ! plst = subplan->paramIds; ! foreach(lst, node->exprs) { ! ExprState *exprstate = (ExprState *) lfirst(lst); ! int paramid = lfirsti(plst); ! ParamExecData *prmdata; ! Datum expresult; ! bool expnull; ! ! /* ! * Load up the Param representing this column of the sub-select. ! */ ! prmdata = &(econtext->ecxt_param_exec_vals[paramid]); ! Assert(prmdata->execPlan == NULL); ! prmdata->value = heap_getattr(tup, col, tdesc, ! &(prmdata->isnull)); ! ! /* ! * Now we can eval the combining operator for this column. ! */ ! expresult = ExecEvalExprSwitchContext(exprstate, econtext, ! &expnull, NULL); ! /* ! * Combine the result into the row result as appropriate. ! */ ! if (col == 1) { ! rowresult = expresult; ! rownull = expnull; } ! else if (useOr) { ! /* combine within row per OR semantics */ ! if (expnull) ! rownull = true; ! else if (DatumGetBool(expresult)) { ! rowresult = BoolGetDatum(true); ! rownull = false; ! break; /* needn't look at any more columns */ } } else { ! /* combine within row per AND semantics */ ! if (expnull) ! rownull = true; ! else if (!DatumGetBool(expresult)) ! { ! rowresult = BoolGetDatum(false); ! rownull = false; ! break; /* needn't look at any more columns */ ! } } - plst = lnext(plst); - col++; } ! if (subLinkType == ANY_SUBLINK) { ! /* combine across rows per OR semantics */ ! if (rownull) ! *isNull = true; ! else if (DatumGetBool(rowresult)) ! { ! result = BoolGetDatum(true); ! *isNull = false; ! break; /* needn't look at any more rows */ } ! } ! else if (subLinkType == ALL_SUBLINK) ! { ! /* combine across rows per AND semantics */ ! if (rownull) ! *isNull = true; ! else if (!DatumGetBool(rowresult)) ! { ! result = BoolGetDatum(false); ! *isNull = false; ! break; /* needn't look at any more rows */ } - } - else - { - /* must be MULTIEXPR_SUBLINK */ - result = rowresult; - *isNull = rownull; } } --- 352,514 ---- found = true; /* ! * When isExpr is true, we have either a scalar expression or an ! * array. In the former case, this is no different than the !isExpr ! * case. In the latter case, iterate over the elements as if they ! * were from multiple input tuples. */ ! if (!isExpr) ! numelems = 1; ! else { ! Oid expr_typeid = tdesc->attrs[0]->atttypid; ! if (expr_typeid != subplan->exprtype) { ! subplan->exprtype = expr_typeid; ! subplan->elemtype = get_element_type(expr_typeid); ! ! if (subplan->elemtype != InvalidOid) ! get_typlenbyvalalign(subplan->elemtype, ! &subplan->elmlen, ! &subplan->elmbyval, ! &subplan->elmalign); } ! ! /* get current value */ ! dvalue = heap_getattr(tup, 1, tdesc, &disnull); ! ! /* XXX this will need work if/when arrays support NULL elements */ ! if (!disnull) { ! if (subplan->elemtype != InvalidOid) ! { ! ArrayType *v = DatumGetArrayTypeP(dvalue); ! ! deconstruct_array(v, subplan->elemtype, subplan->elmlen, ! subplan->elmbyval, subplan->elmalign, ! &dvalues, &numelems); ! } ! else { ! numelems = 1; ! dvalues = (Datum *) palloc(numelems * sizeof(Datum)); ! dvalues[0] = dvalue; } } else { ! numelems = 1; ! dvalues = (Datum *) palloc(numelems * sizeof(Datum)); ! dvalues[0] = (Datum) 0; } } ! for (elemnum = 0; elemnum < numelems; elemnum++) { ! /* ! * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining ! * operators for columns of tuple. ! */ ! col = 1; ! plst = subplan->paramIds; ! foreach(lst, node->exprs) ! { ! ExprState *exprstate = (ExprState *) lfirst(lst); ! int paramid = lfirsti(plst); ! ParamExecData *prmdata; ! Datum expresult; ! bool expnull; ! ! /* ! * Load up the Param representing this column of the sub-select. ! */ ! prmdata = &(econtext->ecxt_param_exec_vals[paramid]); ! Assert(prmdata->execPlan == NULL); ! ! if (!isExpr) ! prmdata->value = heap_getattr(tup, col, tdesc, ! &(prmdata->isnull)); ! else ! { ! prmdata->value = dvalues[elemnum]; ! prmdata->isnull = disnull; ! } ! ! /* ! * Now we can eval the combining operator for this column. ! */ ! expresult = ExecEvalExprSwitchContext(exprstate, econtext, ! &expnull, NULL); ! ! /* ! * Combine the result into the row result as appropriate. ! */ ! if (col == 1) ! { ! rowresult = expresult; ! rownull = expnull; ! } ! else if (useOr) ! { ! /* combine within row per OR semantics */ ! if (expnull) ! rownull = true; ! else if (DatumGetBool(expresult)) ! { ! rowresult = BoolGetDatum(true); ! rownull = false; ! break; /* needn't look at any more columns */ ! } ! } ! else ! { ! /* combine within row per AND semantics */ ! if (expnull) ! rownull = true; ! else if (!DatumGetBool(expresult)) ! { ! rowresult = BoolGetDatum(false); ! rownull = false; ! break; /* needn't look at any more columns */ ! } ! } ! ! plst = lnext(plst); ! col++; } ! ! if (subLinkType == ANY_SUBLINK) ! { ! /* combine across rows per OR semantics */ ! if (rownull) ! *isNull = true; ! else if (DatumGetBool(rowresult)) ! { ! result = BoolGetDatum(true); ! *isNull = false; ! break; /* needn't look at any more rows */ ! } ! } ! else if (subLinkType == ALL_SUBLINK) ! { ! /* combine across rows per AND semantics */ ! if (rownull) ! *isNull = true; ! else if (!DatumGetBool(rowresult)) ! { ! result = BoolGetDatum(false); ! *isNull = false; ! break; /* needn't look at any more rows */ ! } ! } ! else ! { ! /* must be MULTIEXPR_SUBLINK */ ! result = rowresult; ! *isNull = rownull; } } } *************** *** 478,483 **** --- 546,552 ---- buildSubPlanHash(SubPlanState *node) { SubPlan *subplan = (SubPlan *) node->xprstate.expr; + bool isExpr = subplan->isExpr; PlanState *planstate = node->planstate; int ncols = length(node->exprs); ExprContext *innerecontext = node->innerecontext; *************** *** 485,490 **** --- 554,560 ---- MemoryContext oldcontext; int nbuckets; TupleTableSlot *slot; + TupleTableSlot *arrslot = NULL; Assert(subplan->subLinkType == ANY_SUBLINK); Assert(!subplan->useOr); *************** *** 562,604 **** { HeapTuple tup = slot->val; TupleDesc tdesc = slot->ttc_tupleDescriptor; ! int col = 1; List *plst; bool isnew; /* ! * Load up the Params representing the raw sub-select outputs, ! * then form the projection tuple to store in the hashtable. */ ! foreach(plst, subplan->paramIds) { ! int paramid = lfirsti(plst); ! ParamExecData *prmdata; ! prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]); ! Assert(prmdata->execPlan == NULL); ! prmdata->value = heap_getattr(tup, col, tdesc, ! &(prmdata->isnull)); ! col++; ! } ! slot = ExecProject(node->projRight, NULL); ! tup = slot->val; - /* - * If result contains any nulls, store separately or not at all. - * (Since we know the projection tuple has no junk columns, we - * can just look at the overall hasnull info bit, instead of - * groveling through the columns.) - */ - if (HeapTupleNoNulls(tup)) - { - (void) LookupTupleHashEntry(node->hashtable, slot, &isnew); - node->havehashrows = true; } ! else if (node->hashnulls) { ! (void) LookupTupleHashEntry(node->hashnulls, slot, &isnew); ! node->havenullrows = true; } /* --- 632,770 ---- { HeapTuple tup = slot->val; TupleDesc tdesc = slot->ttc_tupleDescriptor; ! TupleDesc arrtdesc = NULL; List *plst; bool isnew; + int numelems; + int elemnum; + Datum dvalue; + Datum *dvalues = NULL; + bool disnull; /* ! * When isExpr is true, we have either a scalar expression or an ! * array. In the former case, this is no different than the !isExpr ! * case. In the latter case, iterate over the elements as if they ! * were from multiple input tuples. */ ! if (!isExpr) ! numelems = 1; ! else { ! Oid expr_typeid = tdesc->attrs[0]->atttypid; ! if (expr_typeid != subplan->exprtype) ! { ! subplan->exprtype = expr_typeid; ! subplan->elemtype = get_element_type(expr_typeid); ! ! if (subplan->elemtype != InvalidOid) ! get_typlenbyvalalign(subplan->elemtype, ! &subplan->elmlen, ! &subplan->elmbyval, ! &subplan->elmalign); ! } ! ! /* get current value */ ! dvalue = heap_getattr(tup, 1, tdesc, &disnull); ! ! if (subplan->elemtype != InvalidOid) ! { ! TupleTable tupleTable; ! ArrayType *v = DatumGetArrayTypeP(dvalue); ! ! arrtdesc = CreateTemplateTupleDesc(1, false); ! TupleDescInitEntry(arrtdesc, 1, "elem", subplan->elemtype, ! -1, 0, false); ! ! tupleTable = ExecCreateTupleTable(1); ! arrslot = ExecAllocTableSlot(tupleTable); ! ExecSetSlotDescriptor(arrslot, arrtdesc, true); ! ! /* XXX this will need work if/when arrays support NULL elements */ ! if (!disnull) ! { ! deconstruct_array(v, subplan->elemtype, subplan->elmlen, ! subplan->elmbyval, subplan->elmalign, ! &dvalues, &numelems); ! } ! else ! { ! numelems = 1; ! dvalues = (Datum *) palloc(numelems * sizeof(Datum)); ! dvalues[0] = (Datum) 0; ! } ! } ! else ! { ! numelems = 1; ! dvalues = (Datum *) palloc(numelems * sizeof(Datum)); ! dvalues[0] = dvalue; ! } } ! ! for (elemnum = 0; elemnum < numelems; elemnum++) { ! int col = 1; ! ! if (!isExpr || subplan->elemtype == InvalidOid) ! { ! /* ! * Load up the Params representing the raw sub-select outputs, ! * then form the projection tuple to store in the hashtable. ! */ ! foreach(plst, subplan->paramIds) ! { ! int paramid = lfirsti(plst); ! ParamExecData *prmdata; ! ! prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]); ! Assert(prmdata->execPlan == NULL); ! ! prmdata->value = heap_getattr(tup, col, tdesc, ! &(prmdata->isnull)); ! ! col++; ! } ! slot = ExecProject(node->projRight, NULL); ! tup = slot->val; ! } ! else ! { ! /* ! * For array type expressions, we need to build up our own ! * tuple and slot ! */ ! char nullflag; ! ! nullflag = disnull ? 'n' : ' '; ! tup = heap_formtuple(arrtdesc, &dvalues[elemnum], &nullflag); ! arrslot = ExecStoreTuple(tup, arrslot, InvalidBuffer, true); ! } ! ! /* ! * If result contains any nulls, store separately or not at all. ! * (Since we know the projection tuple has no junk columns, we ! * can just look at the overall hasnull info bit, instead of ! * groveling through the columns.) ! */ ! if (HeapTupleNoNulls(tup)) ! { ! if (!isExpr) ! (void) LookupTupleHashEntry(node->hashtable, slot, &isnew); ! else ! (void) LookupTupleHashEntry(node->hashtable, arrslot, &isnew); ! node->havehashrows = true; ! } ! else if (node->hashnulls) ! { ! if (!isExpr) ! (void) LookupTupleHashEntry(node->hashnulls, slot, &isnew); ! else ! (void) LookupTupleHashEntry(node->hashnulls, arrslot, &isnew); ! node->havenullrows = true; ! } } /* *************** *** 615,620 **** --- 781,788 ---- * have the potential for a double free attempt. */ ExecClearTuple(node->projRight->pi_slot); + if (arrslot) + ExecClearTuple(arrslot); MemoryContextSwitchTo(oldcontext); } Index: src/backend/nodes/copyfuncs.c =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/copyfuncs.c,v retrieving revision 1.251 diff -c -r1.251 copyfuncs.c *** src/backend/nodes/copyfuncs.c 28 May 2003 16:03:56 -0000 1.251 --- src/backend/nodes/copyfuncs.c 31 May 2003 20:38:27 -0000 *************** *** 825,830 **** --- 825,831 ---- COPY_SCALAR_FIELD(subLinkType); COPY_SCALAR_FIELD(useOr); + COPY_SCALAR_FIELD(isExpr); COPY_NODE_FIELD(lefthand); COPY_NODE_FIELD(operName); COPY_OIDLIST_FIELD(operOids); *************** *** 843,848 **** --- 844,855 ---- COPY_SCALAR_FIELD(subLinkType); COPY_SCALAR_FIELD(useOr); + COPY_SCALAR_FIELD(isExpr); + COPY_SCALAR_FIELD(exprtype); + COPY_SCALAR_FIELD(elemtype); + COPY_SCALAR_FIELD(elmlen); + COPY_SCALAR_FIELD(elmbyval); + COPY_SCALAR_FIELD(elmalign); COPY_NODE_FIELD(exprs); COPY_INTLIST_FIELD(paramIds); COPY_NODE_FIELD(plan); Index: src/backend/nodes/equalfuncs.c =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/equalfuncs.c,v retrieving revision 1.194 diff -c -r1.194 equalfuncs.c *** src/backend/nodes/equalfuncs.c 28 May 2003 16:03:56 -0000 1.194 --- src/backend/nodes/equalfuncs.c 31 May 2003 20:38:27 -0000 *************** *** 300,305 **** --- 300,306 ---- { COMPARE_SCALAR_FIELD(subLinkType); COMPARE_SCALAR_FIELD(useOr); + COMPARE_SCALAR_FIELD(isExpr); COMPARE_NODE_FIELD(lefthand); COMPARE_NODE_FIELD(operName); COMPARE_OIDLIST_FIELD(operOids); *************** *** 313,318 **** --- 314,325 ---- { COMPARE_SCALAR_FIELD(subLinkType); COMPARE_SCALAR_FIELD(useOr); + COMPARE_SCALAR_FIELD(isExpr); + COMPARE_SCALAR_FIELD(exprtype); + COMPARE_SCALAR_FIELD(elemtype); + COMPARE_SCALAR_FIELD(elmlen); + COMPARE_SCALAR_FIELD(elmbyval); + COMPARE_SCALAR_FIELD(elmalign); COMPARE_NODE_FIELD(exprs); COMPARE_INTLIST_FIELD(paramIds); /* should compare plans, but have to settle for comparing plan IDs */ Index: src/backend/nodes/outfuncs.c =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/outfuncs.c,v retrieving revision 1.206 diff -c -r1.206 outfuncs.c *** src/backend/nodes/outfuncs.c 28 May 2003 16:03:56 -0000 1.206 --- src/backend/nodes/outfuncs.c 31 May 2003 20:38:27 -0000 *************** *** 700,705 **** --- 700,706 ---- WRITE_ENUM_FIELD(subLinkType, SubLinkType); WRITE_BOOL_FIELD(useOr); + WRITE_BOOL_FIELD(isExpr); WRITE_NODE_FIELD(lefthand); WRITE_NODE_FIELD(operName); WRITE_OIDLIST_FIELD(operOids); *************** *** 713,718 **** --- 714,725 ---- WRITE_ENUM_FIELD(subLinkType, SubLinkType); WRITE_BOOL_FIELD(useOr); + WRITE_BOOL_FIELD(isExpr); + WRITE_OID_FIELD(exprtype); + WRITE_OID_FIELD(elemtype); + WRITE_INT_FIELD(elmlen); + WRITE_BOOL_FIELD(elmbyval); + WRITE_CHAR_FIELD(elmalign); WRITE_NODE_FIELD(exprs); WRITE_INTLIST_FIELD(paramIds); WRITE_NODE_FIELD(plan); Index: src/backend/nodes/readfuncs.c =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/readfuncs.c,v retrieving revision 1.153 diff -c -r1.153 readfuncs.c *** src/backend/nodes/readfuncs.c 6 May 2003 00:20:32 -0000 1.153 --- src/backend/nodes/readfuncs.c 31 May 2003 20:38:27 -0000 *************** *** 544,549 **** --- 544,550 ---- READ_ENUM_FIELD(subLinkType, SubLinkType); READ_BOOL_FIELD(useOr); + READ_BOOL_FIELD(isExpr); READ_NODE_FIELD(lefthand); READ_NODE_FIELD(operName); READ_OIDLIST_FIELD(operOids); Index: src/backend/optimizer/plan/subselect.c =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/backend/optimizer/plan/subselect.c,v retrieving revision 1.75 diff -c -r1.75 subselect.c *** src/backend/optimizer/plan/subselect.c 29 Apr 2003 22:13:09 -0000 1.75 --- src/backend/optimizer/plan/subselect.c 31 May 2003 20:38:27 -0000 *************** *** 67,73 **** static List *convert_sublink_opers(List *lefthand, List *operOids, List *targetlist, int rtindex, ! List **righthandIds); static bool subplan_is_hashable(SubLink *slink, SubPlan *node); static Node *replace_correlation_vars_mutator(Node *node, void *context); static Node *process_sublinks_mutator(Node *node, bool *isTopQual); --- 67,73 ---- static List *convert_sublink_opers(List *lefthand, List *operOids, List *targetlist, int rtindex, ! bool isExpr, List **righthandIds); static bool subplan_is_hashable(SubLink *slink, SubPlan *node); static Node *replace_correlation_vars_mutator(Node *node, void *context); static Node *process_sublinks_mutator(Node *node, bool *isTopQual); *************** *** 240,245 **** --- 240,251 ---- */ node->subLinkType = slink->subLinkType; node->useOr = slink->useOr; + node->isExpr = slink->isExpr; + node->exprtype = InvalidOid; + node->elemtype = InvalidOid; + node->elmlen = 0; + node->elmbyval = false; + node->elmalign = '\0'; node->exprs = NIL; node->paramIds = NIL; node->useHashTable = false; *************** *** 316,322 **** exprs = convert_sublink_opers(lefthand, slink->operOids, plan->targetlist, ! 0, &node->paramIds); node->setParam = listCopy(node->paramIds); PlannerInitPlan = lappend(PlannerInitPlan, node); --- 322,328 ---- exprs = convert_sublink_opers(lefthand, slink->operOids, plan->targetlist, ! 0, node->isExpr, &node->paramIds); node->setParam = listCopy(node->paramIds); PlannerInitPlan = lappend(PlannerInitPlan, node); *************** *** 399,405 **** node->exprs = convert_sublink_opers(lefthand, slink->operOids, plan->targetlist, ! 0, &node->paramIds); /* --- 405,411 ---- node->exprs = convert_sublink_opers(lefthand, slink->operOids, plan->targetlist, ! 0, node->isExpr, &node->paramIds); /* *************** *** 444,450 **** static List * convert_sublink_opers(List *lefthand, List *operOids, List *targetlist, int rtindex, ! List **righthandIds) { List *result = NIL; List *lst; --- 450,456 ---- static List * convert_sublink_opers(List *lefthand, List *operOids, List *targetlist, int rtindex, ! bool isExpr, List **righthandIds) { List *result = NIL; List *lst; *************** *** 499,511 **** * are not expecting to have to resolve unknown Params, so * it's okay to pass a null pstate.) */ ! result = lappend(result, ! make_op_expr(NULL, ! tup, ! leftop, ! rightop, ! exprType(leftop), ! te->resdom->restype)); ReleaseSysCache(tup); --- 505,542 ---- * are not expecting to have to resolve unknown Params, so * it's okay to pass a null pstate.) */ ! if (!isExpr) ! { ! result = lappend(result, ! make_op_expr(NULL, ! tup, ! leftop, ! rightop, ! exprType(leftop), ! te->resdom->restype)); ! } ! else ! { ! Oid exprtype = te->resdom->restype; ! Oid elemtype = get_element_type(exprtype); ! ! if (elemtype != InvalidOid) ! result = lappend(result, ! make_op_expr(NULL, ! tup, ! leftop, ! rightop, ! exprType(leftop), ! elemtype)); ! else ! result = lappend(result, ! make_op_expr(NULL, ! tup, ! leftop, ! rightop, ! exprType(leftop), ! exprtype)); ! } ReleaseSysCache(tup); *************** *** 616,628 **** /* * The sublink type must be "= ANY" --- that is, an IN operator. * (We require the operator name to be unqualified, which may be ! * overly paranoid, or may not be.) */ if (sublink->subLinkType != ANY_SUBLINK) return NULL; if (length(sublink->operName) != 1 || strcmp(strVal(lfirst(sublink->operName)), "=") != 0) return NULL; /* * The sub-select must not refer to any Vars of the parent query. * (Vars of higher levels should be okay, though.) --- 647,663 ---- /* * The sublink type must be "= ANY" --- that is, an IN operator. * (We require the operator name to be unqualified, which may be ! * overly paranoid, or may not be.) It must not be an Expression ! * sublink. */ if (sublink->subLinkType != ANY_SUBLINK) return NULL; if (length(sublink->operName) != 1 || strcmp(strVal(lfirst(sublink->operName)), "=") != 0) return NULL; + if (sublink->isExpr) + return NULL; + /* * The sub-select must not refer to any Vars of the parent query. * (Vars of higher levels should be okay, though.) *************** *** 675,681 **** exprs = convert_sublink_opers(sublink->lefthand, sublink->operOids, subselect->targetList, ! rtindex, &ininfo->sub_targetlist); return (Node *) make_ands_explicit(exprs); } --- 710,716 ---- exprs = convert_sublink_opers(sublink->lefthand, sublink->operOids, subselect->targetList, ! rtindex, sublink->isExpr, &ininfo->sub_targetlist); return (Node *) make_ands_explicit(exprs); } Index: src/backend/parser/gram.y =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/gram.y,v retrieving revision 2.416 diff -c -r2.416 gram.y *** src/backend/parser/gram.y 29 May 2003 20:40:36 -0000 2.416 --- src/backend/parser/gram.y 31 May 2003 20:38:27 -0000 *************** *** 5490,5495 **** --- 5490,5496 ---- { SubLink *n = makeNode(SubLink); n->subLinkType = ANY_SUBLINK; + n->isExpr = false; n->lefthand = $1; n->operName = makeList1(makeString("=")); n->subselect = $3; *************** *** 5500,5505 **** --- 5501,5507 ---- /* Make an IN node */ SubLink *n = makeNode(SubLink); n->subLinkType = ANY_SUBLINK; + n->isExpr = false; n->lefthand = $1; n->operName = makeList1(makeString("=")); n->subselect = $4; *************** *** 5511,5516 **** --- 5513,5519 ---- { SubLink *n = makeNode(SubLink); n->subLinkType = $3; + n->isExpr = false; n->lefthand = $1; n->operName = $2; n->subselect = $4; *************** *** 5521,5526 **** --- 5524,5530 ---- { SubLink *n = makeNode(SubLink); n->subLinkType = MULTIEXPR_SUBLINK; + n->isExpr = false; n->lefthand = $1; n->operName = $2; n->subselect = $3; *************** *** 5904,5909 **** --- 5908,5914 ---- { SubLink *n = (SubLink *)$3; n->subLinkType = ANY_SUBLINK; + n->isExpr = false; n->lefthand = makeList1($1); n->operName = makeList1(makeString("=")); $$ = (Node *)n; *************** *** 5931,5936 **** --- 5936,5942 ---- { /* Make an IN node */ SubLink *n = (SubLink *)$4; + n->isExpr = false; n->subLinkType = ANY_SUBLINK; n->lefthand = makeList1($1); n->operName = makeList1(makeString("=")); *************** *** 5957,5967 **** --- 5963,6000 ---- { SubLink *n = makeNode(SubLink); n->subLinkType = $3; + n->isExpr = false; n->lefthand = makeList1($1); n->operName = $2; n->subselect = $4; $$ = (Node *)n; } + | a_expr qual_all_Op sub_type '(' a_expr ')' %prec Op + { + SubLink *n = makeNode(SubLink); + SelectStmt *s = makeNode(SelectStmt); + ResTarget *r = makeNode(ResTarget); + + r->name = NULL; + r->indirection = NIL; + r->val = (Node *)$5; + + s->distinctClause = NIL; + s->targetList = makeList1(r); + s->into = NULL; + s->intoColNames = NIL; + s->fromClause = NIL; + s->whereClause = NULL; + s->groupClause = NIL; + s->havingClause = NULL; + + n->subLinkType = $3; + n->isExpr = true; + n->lefthand = makeList1($1); + n->operName = $2; + n->subselect = (Node *) s; + $$ = (Node *)n; + } | UNIQUE select_with_parens %prec Op { /* Not sure how to get rid of the parentheses *************** *** 6538,6543 **** --- 6571,6577 ---- { SubLink *n = makeNode(SubLink); n->subLinkType = EXPR_SUBLINK; + n->isExpr = false; n->lefthand = NIL; n->operName = NIL; n->subselect = $1; *************** *** 6547,6552 **** --- 6581,6587 ---- { SubLink *n = makeNode(SubLink); n->subLinkType = EXISTS_SUBLINK; + n->isExpr = false; n->lefthand = NIL; n->operName = NIL; n->subselect = $2; *************** *** 6556,6561 **** --- 6591,6597 ---- { SubLink *n = makeNode(SubLink); n->subLinkType = ARRAY_SUBLINK; + n->isExpr = false; n->lefthand = NIL; n->operName = NIL; n->subselect = $2; *************** *** 6730,6735 **** --- 6766,6772 ---- in_expr: select_with_parens { SubLink *n = makeNode(SubLink); + n->isExpr = false; n->subselect = $1; /* other fields will be filled later */ $$ = (Node *)n; Index: src/backend/parser/parse_expr.c =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_expr.c,v retrieving revision 1.148 diff -c -r1.148 parse_expr.c *** src/backend/parser/parse_expr.c 29 Apr 2003 22:13:10 -0000 1.148 --- src/backend/parser/parse_expr.c 31 May 2003 20:38:27 -0000 *************** *** 436,441 **** --- 436,442 ---- sublink->operName = NIL; sublink->operOids = NIL; sublink->useOr = FALSE; + sublink->isExpr = FALSE; } else if (sublink->subLinkType == EXPR_SUBLINK || sublink->subLinkType == ARRAY_SUBLINK) *************** *** 463,468 **** --- 464,470 ---- sublink->operName = NIL; sublink->operOids = NIL; sublink->useOr = FALSE; + sublink->isExpr = FALSE; } else { *************** *** 538,547 **** * here, because make_subplan() will insert type * coercion calls if needed. */ ! optup = oper(op, ! exprType(lexpr), ! exprType((Node *) tent->expr), ! false); opform = (Form_pg_operator) GETSTRUCT(optup); if (opform->oprresult != BOOLOID) --- 540,569 ---- * here, because make_subplan() will insert type * coercion calls if needed. */ ! if (!sublink->isExpr) ! { ! optup = oper(op, ! exprType(lexpr), ! exprType((Node *) tent->expr), ! false); ! } ! else ! { ! Oid exprtype = exprType((Node *) tent->expr); ! Oid elemtype = get_element_type(exprtype); ! ! if (elemtype != InvalidOid) ! optup = oper(op, ! exprType(lexpr), ! elemtype, ! false); ! else ! optup = oper(op, ! exprType(lexpr), ! exprtype, ! false); ! } ! opform = (Form_pg_operator) GETSTRUCT(optup); if (opform->oprresult != BOOLOID) Index: src/include/nodes/primnodes.h =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/include/nodes/primnodes.h,v retrieving revision 1.82 diff -c -r1.82 primnodes.h *** src/include/nodes/primnodes.h 6 May 2003 00:20:33 -0000 1.82 --- src/include/nodes/primnodes.h 31 May 2003 20:38:27 -0000 *************** *** 357,371 **** /* ---------------- * SubLink * ! * A SubLink represents a subselect appearing in an expression, and in some ! * cases also the combining operator(s) just above it. The subLinkType ! * indicates the form of the expression represented: * EXISTS_SUBLINK EXISTS(SELECT ...) * ALL_SUBLINK (lefthand) op ALL (SELECT ...) * ANY_SUBLINK (lefthand) op ANY (SELECT ...) * MULTIEXPR_SUBLINK (lefthand) op (SELECT ...) * EXPR_SUBLINK (SELECT with single targetlist item ...) * ARRAY_SUBLINK ARRAY(SELECT with single targetlist item ...) * For ALL, ANY, and MULTIEXPR, the lefthand is a list of expressions of the * same length as the subselect's targetlist. MULTIEXPR will *always* have * a list with more than one entry; if the subselect has just one target --- 357,375 ---- /* ---------------- * SubLink * ! * A SubLink represents a subselect, or an expression, appearing in an ! * expression, and in some cases also the combining operator(s) just above ! * it. The subLinkType indicates the form of the expression represented: * EXISTS_SUBLINK EXISTS(SELECT ...) * ALL_SUBLINK (lefthand) op ALL (SELECT ...) * ANY_SUBLINK (lefthand) op ANY (SELECT ...) * MULTIEXPR_SUBLINK (lefthand) op (SELECT ...) * EXPR_SUBLINK (SELECT with single targetlist item ...) * ARRAY_SUBLINK ARRAY(SELECT with single targetlist item ...) + * If an expression is used in place of the subselect, it is transformed + * into a simple "(SELECT expr)" in gram.y. This is to allow arrays to be + * used as if they were the result of a single column subselect. If the + * expression is scalar, it is treated as a one element array. * For ALL, ANY, and MULTIEXPR, the lefthand is a list of expressions of the * same length as the subselect's targetlist. MULTIEXPR will *always* have * a list with more than one entry; if the subselect has just one target *************** *** 414,419 **** --- 418,425 ---- SubLinkType subLinkType; /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */ bool useOr; /* TRUE to combine column results with * "OR" not "AND" */ + bool isExpr; /* TRUE if the subselect is really derived + * from a single expression */ List *lefthand; /* list of outer-query expressions on the * left */ List *operName; /* originally specified operator name */ *************** *** 455,460 **** --- 461,475 ---- SubLinkType subLinkType; /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */ bool useOr; /* TRUE to combine column results with * "OR" not "AND" */ + bool isExpr; /* TRUE if the subselect is really derived + * from a single expression */ + /* runtime cache for single array expressions */ + Oid exprtype; /* array and element type, and other info + * needed deconstruct the array */ + Oid elemtype; + int16 elmlen; + bool elmbyval; + char elmalign; /* The combining operators, transformed to executable expressions: */ List *exprs; /* list of OpExpr expression trees */ List *paramIds; /* IDs of Params embedded in the above */
pgsql-patches by date: