1 /*-------------------------------------------------------------------------
4 * transform the parse tree into a query tree
6 * Copyright (c) 1994, Regents of the University of California
10 * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.89 1998/10/28 16:06:54 momjian Exp $
12 *-------------------------------------------------------------------------
21 #include "access/heapam.h"
22 #include "nodes/makefuncs.h"
23 #include "nodes/memnodes.h"
24 #include "nodes/pg_list.h"
25 #include "parser/analyze.h"
26 #include "parser/parse_agg.h"
27 #include "parser/parse_clause.h"
28 #include "parser/parse_node.h"
29 #include "parser/parse_relation.h"
30 #include "parser/parse_target.h"
31 #include "utils/builtins.h"
32 #include "utils/mcxt.h"
34 static Query *transformStmt(ParseState *pstate, Node *stmt);
35 static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
36 static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt);
37 static Query *transformIndexStmt(ParseState *pstate, IndexStmt *stmt);
38 static Query *transformExtendStmt(ParseState *pstate, ExtendStmt *stmt);
39 static Query *transformRuleStmt(ParseState *query, RuleStmt *stmt);
40 static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
41 static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
42 static Query *transformCursorStmt(ParseState *pstate, SelectStmt *stmt);
43 static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt);
45 List *extras_before = NIL;
46 List *extras_after = NIL;
50 * analyze a list of parse trees and transform them if necessary.
52 * Returns a list of transformed parse trees. Optimizable statements are
53 * all transformed to Query while the rest stays the same.
57 parse_analyze(List *pl, ParseState *parentParseState)
59 QueryTreeList *result;
64 result = malloc(sizeof(QueryTreeList));
65 result->len = length(pl);
66 result->qtrees = (Query **) malloc(result->len * sizeof(Query *));
70 pstate = make_parsestate(parentParseState);
71 parsetree = transformStmt(pstate, lfirst(pl));
72 if (pstate->p_target_relation != NULL)
73 heap_close(pstate->p_target_relation);
75 if (extras_before != NIL)
77 result->len += length(extras_before);
78 result->qtrees = (Query **) realloc(result->qtrees, result->len * sizeof(Query *));
79 while (extras_before != NIL)
81 result->qtrees[i++] = transformStmt(pstate, lfirst(extras_before));
82 if (pstate->p_target_relation != NULL)
83 heap_close(pstate->p_target_relation);
84 extras_before = lnext(extras_before);
89 result->qtrees[i++] = parsetree;
91 if (extras_after != NIL)
93 result->len += length(extras_after);
94 result->qtrees = (Query **) realloc(result->qtrees, result->len * sizeof(Query *));
95 while (extras_after != NIL)
97 result->qtrees[i++] = transformStmt(pstate, lfirst(extras_after));
98 if (pstate->p_target_relation != NULL)
99 heap_close(pstate->p_target_relation);
100 extras_after = lnext(extras_after);
114 * transform a Parse tree. If it is an optimizable statement, turn it
118 transformStmt(ParseState *pstate, Node *parseTree)
120 Query *result = NULL;
122 switch (nodeTag(parseTree))
124 /*------------------------
125 * Non-optimizable statements
126 *------------------------
129 result = transformCreateStmt(pstate, (CreateStmt *) parseTree);
133 result = transformIndexStmt(pstate, (IndexStmt *) parseTree);
137 result = transformExtendStmt(pstate, (ExtendStmt *) parseTree);
141 result = transformRuleStmt(pstate, (RuleStmt *) parseTree);
146 ViewStmt *n = (ViewStmt *) parseTree;
148 n->query = (Query *) transformStmt(pstate, (Node *) n->query);
149 result = makeNode(Query);
150 result->commandType = CMD_UTILITY;
151 result->utilityStmt = (Node *) n;
157 MemoryContext oldcontext;
160 * make sure that this Query is allocated in TopMemory
161 * context because vacuum spans transactions and we don't
162 * want to lose the vacuum Query due to end-of-transaction
165 oldcontext = MemoryContextSwitchTo(TopMemoryContext);
166 result = makeNode(Query);
167 result->commandType = CMD_UTILITY;
168 result->utilityStmt = (Node *) parseTree;
169 MemoryContextSwitchTo(oldcontext);
175 ExplainStmt *n = (ExplainStmt *) parseTree;
177 result = makeNode(Query);
178 result->commandType = CMD_UTILITY;
179 n->query = transformStmt(pstate, (Node *) n->query);
180 result->utilityStmt = (Node *) parseTree;
184 /*------------------------
185 * Optimizable statements
186 *------------------------
189 result = transformInsertStmt(pstate, (InsertStmt *) parseTree);
193 result = transformDeleteStmt(pstate, (DeleteStmt *) parseTree);
197 result = transformUpdateStmt(pstate, (UpdateStmt *) parseTree);
201 if (!((SelectStmt *) parseTree)->portalname)
202 result = transformSelectStmt(pstate, (SelectStmt *) parseTree);
204 result = transformCursorStmt(pstate, (SelectStmt *) parseTree);
210 * other statments don't require any transformation-- just
211 * return the original parsetree, yea!
213 result = makeNode(Query);
214 result->commandType = CMD_UTILITY;
215 result->utilityStmt = (Node *) parseTree;
222 * transformDeleteStmt -
223 * transforms a Delete Statement
226 transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
228 Query *qry = makeNode(Query);
230 qry->commandType = CMD_DELETE;
232 /* set up a range table */
233 makeRangeTable(pstate, stmt->relname, NULL);
235 qry->uniqueFlag = NULL;
237 /* fix where clause */
238 qry->qual = transformWhereClause(pstate, stmt->whereClause);
239 qry->hasSubLinks = pstate->p_hasSubLinks;
241 qry->rtable = pstate->p_rtable;
242 qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
244 qry->hasAggs = pstate->p_hasAggs;
245 if (pstate->p_hasAggs)
246 parseCheckAggregates(pstate, qry);
248 return (Query *) qry;
252 * transformInsertStmt -
253 * transform an Insert Statement
256 transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
258 Query *qry = makeNode(Query); /* make a new query tree */
261 qry->commandType = CMD_INSERT;
262 pstate->p_is_insert = true;
264 /* set up a range table */
265 makeRangeTable(pstate, stmt->relname, stmt->fromClause);
267 qry->uniqueFlag = stmt->unique;
269 /* fix the target list */
270 icolumns = pstate->p_insert_columns = makeTargetNames(pstate, stmt->cols);
272 qry->targetList = transformTargetList(pstate, stmt->targetList);
274 /* DEFAULT handling */
275 if (length(qry->targetList) < pstate->p_target_relation->rd_att->natts &&
276 pstate->p_target_relation->rd_att->constr &&
277 pstate->p_target_relation->rd_att->constr->num_defval > 0)
279 Form_pg_attribute *att = pstate->p_target_relation->rd_att->attrs;
280 AttrDefault *defval = pstate->p_target_relation->rd_att->constr->defval;
281 int ndef = pstate->p_target_relation->rd_att->constr->num_defval;
284 * if stmt->cols == NIL then makeTargetNames returns list of all attrs.
285 * May have to shorten icolumns list...
287 if (stmt->cols == NIL)
290 int i = length(qry->targetList);
292 foreach(extrl, icolumns)
295 * decrements first, so if we started with zero items
296 * it will now be negative
302 * this an index into the targetList,
303 * so make sure we had one to start...
307 freeList(lnext(extrl));
322 foreach(tl, icolumns)
324 id = (Ident *) lfirst(tl);
325 if (namestrcmp(&(att[defval[ndef].adnum - 1]->attname), id->name) == 0)
328 if (tl != NIL) /* something given for this attr */
332 * Nothing given for this attr with DEFAULT expr, so add new
333 * TargetEntry to qry->targetList. Note, that we set resno to
334 * defval[ndef].adnum: it's what
335 * transformTargetList()->make_targetlist_expr() does for
336 * INSERT ... SELECT. But for INSERT ... VALUES
337 * pstate->p_last_resno is used. It doesn't matter for
338 * "normal" using (planner creates proper target list in
339 * preptlist.c), but may break RULEs in some way. It seems
340 * better to create proper target list here...
342 te = makeTargetEntry(makeResdom(defval[ndef].adnum,
343 att[defval[ndef].adnum - 1]->atttypid,
344 att[defval[ndef].adnum - 1]->atttypmod,
345 pstrdup(nameout(&(att[defval[ndef].adnum - 1]->attname))),
347 (Node *) stringToNode(defval[ndef].adbin));
348 qry->targetList = lappend(qry->targetList, te);
352 /* fix where clause */
353 qry->qual = transformWhereClause(pstate, stmt->whereClause);
356 * The havingQual has a similar meaning as "qual" in the where
357 * statement. So we can easily use the code from the "where clause"
358 * with some additional traversals done in
359 * .../optimizer/plan/planner.c
361 qry->havingQual = transformWhereClause(pstate, stmt->havingClause);
363 qry->hasSubLinks = pstate->p_hasSubLinks;
365 /* now the range table will not change */
366 qry->rtable = pstate->p_rtable;
367 qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
369 qry->groupClause = transformGroupClause(pstate,
373 /* fix order clause */
374 qry->sortClause = transformSortClause(pstate,
380 qry->hasAggs = pstate->p_hasAggs;
381 if (pstate->p_hasAggs)
382 parseCheckAggregates(pstate, qry);
385 * The INSERT INTO ... SELECT ... could have a UNION in child, so
386 * unionClause may be false
388 qry->unionall = stmt->unionall;
389 qry->unionClause = transformUnionClause(stmt->unionClause, qry->targetList);
392 * If there is a havingQual but there are no aggregates, then there is
393 * something wrong with the query because having must contain
394 * aggregates in its expressions! Otherwise the query could have been
395 * formulated using the where clause.
397 if ((qry->hasAggs == false) && (qry->havingQual != NULL))
399 elog(ERROR, "This is not a valid having query!");
400 return (Query *) NIL;
403 return (Query *) qry;
408 * Create a table name from a list of fields.
411 makeTableName(void *elem,...)
416 char buf[NAMEDATALEN + 1];
420 va_start(args, elem);
425 /* not enough room for next part? then return nothing */
426 if ((strlen(buf) + strlen(name)) >= (sizeof(buf) - 1))
433 name = va_arg(args, void *);
438 name = palloc(strlen(buf) + 1);
445 CreateIndexName(char *table_name, char *column_name, char *label, List *indices)
451 char name2[NAMEDATALEN + 1];
453 /* use working storage, since we might be trying several possibilities */
454 strcpy(name2, column_name);
455 while (iname == NULL)
457 iname = makeTableName(table_name, name2, label, NULL);
458 /* unable to make a name at all? then quit */
465 index = lfirst(ilist);
466 if (strcasecmp(iname, index->idxname) == 0)
469 ilist = lnext(ilist);
471 /* ran through entire list? then no name conflict found so done */
475 /* the last one conflicted, so try a new name component */
479 sprintf(name2, "%s_%d", column_name, (pass + 1));
486 * transformCreateStmt -
487 * transforms the "create table" statement
488 * SQL92 allows constraints to be scattered all over, so thumb through
489 * the columns and collect all constraints into one place.
490 * If there are any implied indices (e.g. UNIQUE or PRIMARY KEY)
491 * then expand those into multiple IndexStmt blocks.
492 * - thomas 1997-12-02
495 transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
498 int have_pkey = FALSE;
506 Constraint *constraint;
515 q->commandType = CMD_UTILITY;
517 elements = stmt->tableElts;
518 constraints = stmt->constraints;
522 while (elements != NIL)
524 element = lfirst(elements);
525 switch (nodeTag(element))
528 column = (ColumnDef *) element;
529 columns = lappend(columns, column);
531 if (column->is_sequence)
535 CreateSeqStmt *sequence;
537 sname = makeTableName(stmt->relname, column->colname, "seq", NULL);
539 constraint = makeNode(Constraint);
540 constraint->contype = CONSTR_DEFAULT;
541 constraint->name = sname;
542 cstring = palloc(9 + strlen(constraint->name) + 2 + 1);
543 strcpy(cstring, "nextval('");
544 strcat(cstring, constraint->name);
545 strcat(cstring, "')");
546 constraint->def = cstring;
547 constraint->keys = NULL;
549 /* The parser only allows PRIMARY KEY as a constraint for the SERIAL type.
550 * So, if there is a constraint of any kind, assume it is that.
551 * If PRIMARY KEY is specified, then don't need to gin up a UNIQUE constraint
552 * since that will be covered already.
553 * - thomas 1998-09-15
555 if (column->constraints != NIL)
557 column->constraints = lappend(column->constraints, constraint);
561 column->constraints = lcons(constraint, NIL);
563 constraint = makeNode(Constraint);
564 constraint->contype = CONSTR_UNIQUE;
565 constraint->name = makeTableName(stmt->relname, column->colname, "key", NULL);
566 column->constraints = lappend(column->constraints, constraint);
569 sequence = makeNode(CreateSeqStmt);
570 sequence->seqname = pstrdup(sname);
571 sequence->options = NIL;
573 elog(NOTICE, "CREATE TABLE will create implicit sequence %s for SERIAL column %s.%s",
574 sequence->seqname, stmt->relname, column->colname);
576 blist = lcons(sequence, NIL);
579 if (column->constraints != NIL)
581 clist = column->constraints;
584 constraint = lfirst(clist);
585 switch (constraint->contype)
588 if (column->is_not_null)
589 elog(ERROR, "CREATE TABLE/NOT NULL already specified"
590 " for %s.%s", stmt->relname, column->colname);
591 column->is_not_null = TRUE;
595 if (column->defval != NULL)
596 elog(ERROR, "CREATE TABLE/DEFAULT multiple values specified"
597 " for %s.%s", stmt->relname, column->colname);
598 column->defval = constraint->def;
602 if (constraint->name == NULL)
603 constraint->name = makeTableName(stmt->relname, "pkey", NULL);
604 if (constraint->keys == NIL)
605 constraint->keys = lappend(constraint->keys, column);
606 dlist = lappend(dlist, constraint);
610 if (constraint->name == NULL)
611 constraint->name = makeTableName(stmt->relname, column->colname, "key", NULL);
612 if (constraint->keys == NIL)
613 constraint->keys = lappend(constraint->keys, column);
614 dlist = lappend(dlist, constraint);
618 constraints = lappend(constraints, constraint);
619 if (constraint->name == NULL)
620 constraint->name = makeTableName(stmt->relname, column->colname, NULL);
624 elog(ERROR, "parser: internal error; unrecognized constraint", NULL);
627 clist = lnext(clist);
633 constraint = (Constraint *) element;
634 switch (constraint->contype)
637 if (constraint->name == NULL)
638 constraint->name = makeTableName(stmt->relname, "pkey", NULL);
639 dlist = lappend(dlist, constraint);
644 if (constraint->name == NULL)
645 constraint->name = makeTableName(stmt->relname, "key", NULL);
647 dlist = lappend(dlist, constraint);
651 constraints = lappend(constraints, constraint);
656 elog(ERROR, "parser: internal error; illegal context for constraint", NULL);
659 elog(ERROR, "parser: internal error; unrecognized constraint", NULL);
665 elog(ERROR, "parser: internal error; unrecognized node", NULL);
668 elements = lnext(elements);
671 stmt->tableElts = columns;
672 stmt->constraints = constraints;
674 /* Now run through the "deferred list" to complete the query transformation.
675 * For PRIMARY KEYs, mark each column as NOT NULL and create an index.
676 * For UNIQUE, create an index as for PRIMARY KEYS, but do not insist on NOT NULL.
678 * Note that this code does not currently look for all possible redundant cases
679 * and either ignore or stop with warning. The create might fail later when
680 * names for indices turn out to be redundant, or a user might have specified
681 * extra useless indices which might hurt performance. - thomas 1997-12-08
685 constraint = lfirst(dlist);
686 if (nodeTag(constraint) != T_Constraint)
687 elog(ERROR, "parser: internal error; unrecognized deferred node", NULL);
689 if (constraint->contype == CONSTR_PRIMARY)
692 elog(ERROR, "CREATE TABLE/PRIMARY KEY multiple primary keys"
693 " for table %s are not legal", stmt->relname);
697 else if (constraint->contype != CONSTR_UNIQUE)
698 elog(ERROR, "parser: internal error; unrecognized deferred constraint", NULL);
700 index = makeNode(IndexStmt);
702 index->unique = TRUE;
703 if (constraint->name != NULL)
704 index->idxname = constraint->name;
705 else if (constraint->contype == CONSTR_PRIMARY)
708 elog(ERROR, "CREATE TABLE/PRIMARY KEY multiple keys for table %s are not legal", stmt->relname);
711 index->idxname = makeTableName(stmt->relname, "pkey", NULL);
714 index->idxname = NULL;
716 index->relname = stmt->relname;
717 index->accessMethod = "btree";
718 index->indexParams = NIL;
719 index->withClause = NIL;
720 index->whereClause = NULL;
722 keys = constraint->keys;
726 columns = stmt->tableElts;
728 while (columns != NIL)
730 column = lfirst(columns);
731 if (strcasecmp(column->colname, key->name) == 0)
735 columns = lnext(columns);
738 elog(ERROR, "parser: column '%s' in key does not exist", key->name);
740 if (constraint->contype == CONSTR_PRIMARY)
741 column->is_not_null = TRUE;
742 iparam = makeNode(IndexElem);
743 iparam->name = strcpy(palloc(strlen(column->colname) + 1), column->colname);
745 iparam->class = NULL;
746 iparam->typename = NULL;
747 index->indexParams = lappend(index->indexParams, iparam);
749 if (index->idxname == NULL)
750 index->idxname = CreateIndexName(stmt->relname, iparam->name, "key", ilist);
755 if (index->idxname == NULL)
756 elog(ERROR, "parser: unable to construct implicit index for table %s"
757 "; name too long", stmt->relname);
759 elog(NOTICE, "CREATE TABLE/%s will create implicit index %s for table %s",
760 ((constraint->contype == CONSTR_PRIMARY) ? "PRIMARY KEY" : "UNIQUE"),
761 index->idxname, stmt->relname);
763 ilist = lappend(ilist, index);
764 dlist = lnext(dlist);
767 q->utilityStmt = (Node *) stmt;
768 extras_before = blist;
769 extras_after = ilist;
775 * transformIndexStmt -
776 * transforms the qualification of the index statement
779 transformIndexStmt(ParseState *pstate, IndexStmt *stmt)
783 qry = makeNode(Query);
784 qry->commandType = CMD_UTILITY;
786 /* take care of the where clause */
787 stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
788 qry->hasSubLinks = pstate->p_hasSubLinks;
790 stmt->rangetable = pstate->p_rtable;
792 qry->utilityStmt = (Node *) stmt;
798 * transformExtendStmt -
799 * transform the qualifications of the Extend Index Statement
803 transformExtendStmt(ParseState *pstate, ExtendStmt *stmt)
807 qry = makeNode(Query);
808 qry->commandType = CMD_UTILITY;
810 /* take care of the where clause */
811 stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
812 qry->hasSubLinks = pstate->p_hasSubLinks;
814 stmt->rangetable = pstate->p_rtable;
816 qry->utilityStmt = (Node *) stmt;
821 * transformRuleStmt -
822 * transform a Create Rule Statement. The actions is a list of parse
823 * trees which is transformed into a list of query trees.
826 transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
832 qry = makeNode(Query);
833 qry->commandType = CMD_UTILITY;
836 * 'instead nothing' rules with a qualification need a query a
837 * rangetable so the rewrite handler can add the negated rule
838 * qualification to the original query. We create a query with the new
839 * command type CMD_NOTHING here that is treated special by the
842 if (stmt->actions == NIL)
844 Query *nothing_qry = makeNode(Query);
846 nothing_qry->commandType = CMD_NOTHING;
848 addRangeTableEntry(pstate, stmt->object->relname, "*CURRENT*",
850 addRangeTableEntry(pstate, stmt->object->relname, "*NEW*",
853 nothing_qry->rtable = pstate->p_rtable;
855 stmt->actions = lappend(NIL, nothing_qry);
858 actions = stmt->actions;
861 * transform each statment, like parse_analyze()
863 while (actions != NIL)
867 * NOTE: 'CURRENT' must always have a varno equal to 1 and 'NEW'
870 addRangeTableEntry(pstate, stmt->object->relname, "*CURRENT*",
872 addRangeTableEntry(pstate, stmt->object->relname, "*NEW*",
875 pstate->p_last_resno = 1;
876 pstate->p_is_rule = true; /* for expand all */
877 pstate->p_hasAggs = false;
879 action = (Query *) lfirst(actions);
880 if (action->commandType != CMD_NOTHING)
881 lfirst(actions) = transformStmt(pstate, lfirst(actions));
882 actions = lnext(actions);
885 /* take care of the where clause */
886 stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
887 qry->hasSubLinks = pstate->p_hasSubLinks;
889 qry->utilityStmt = (Node *) stmt;
895 * transformSelectStmt -
896 * transforms a Select Statement
900 transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
902 Query *qry = makeNode(Query);
904 qry->commandType = CMD_SELECT;
906 /* set up a range table */
907 makeRangeTable(pstate, NULL, stmt->fromClause);
909 qry->uniqueFlag = stmt->unique;
911 qry->into = stmt->into;
912 qry->isPortal = FALSE;
914 qry->targetList = transformTargetList(pstate, stmt->targetList);
916 qry->qual = transformWhereClause(pstate, stmt->whereClause);
919 * The havingQual has a similar meaning as "qual" in the where
920 * statement. So we can easily use the code from the "where clause"
921 * with some additional traversals done in
922 * .../optimizer/plan/planner.c
924 qry->havingQual = transformWhereClause(pstate, stmt->havingClause);
926 qry->hasSubLinks = pstate->p_hasSubLinks;
928 qry->sortClause = transformSortClause(pstate,
934 qry->groupClause = transformGroupClause(pstate,
937 qry->rtable = pstate->p_rtable;
939 qry->hasAggs = pstate->p_hasAggs;
940 if (pstate->p_hasAggs)
941 parseCheckAggregates(pstate, qry);
944 * The INSERT INTO ... SELECT ... could have a UNION in child, so
945 * unionClause may be false
947 qry->unionall = stmt->unionall;
948 qry->unionClause = transformUnionClause(stmt->unionClause, qry->targetList);
951 * If there is a havingQual but there are no aggregates, then there is
952 * something wrong with the query because having must contain
953 * aggregates in its expressions! Otherwise the query could have been
954 * formulated using the where clause.
956 if ((qry->hasAggs == false) && (qry->havingQual != NULL))
958 elog(ERROR, "This is not a valid having query!");
959 return (Query *) NIL;
962 return (Query *) qry;
966 * transformUpdateStmt -
967 * transforms an update statement
971 transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
973 Query *qry = makeNode(Query);
975 qry->commandType = CMD_UPDATE;
976 pstate->p_is_update = true;
979 * the FROM clause is non-standard SQL syntax. We used to be able to
980 * do this with REPLACE in POSTQUEL so we keep the feature.
982 makeRangeTable(pstate, stmt->relname, stmt->fromClause);
984 qry->targetList = transformTargetList(pstate, stmt->targetList);
986 qry->qual = transformWhereClause(pstate, stmt->whereClause);
987 qry->hasSubLinks = pstate->p_hasSubLinks;
989 qry->rtable = pstate->p_rtable;
991 qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
993 qry->hasAggs = pstate->p_hasAggs;
994 if (pstate->p_hasAggs)
995 parseCheckAggregates(pstate, qry);
997 return (Query *) qry;
1001 * transformCursorStmt -
1002 * transform a Create Cursor Statement
1006 transformCursorStmt(ParseState *pstate, SelectStmt *stmt)
1010 qry = transformSelectStmt(pstate, stmt);
1012 qry->into = stmt->portalname;
1013 qry->isPortal = TRUE;
1014 qry->isBinary = stmt->binary; /* internal portal */