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 */