1 /*-------------------------------------------------------------------------
4 * transform the parse tree into a query tree
6 * Copyright (c) 1994, Regents of the University of California
8 * $Id: analyze.c,v 1.91 1998/12/14 06:50:32 scrappy Exp $
10 *-------------------------------------------------------------------------
19 #include "access/heapam.h"
20 #include "nodes/makefuncs.h"
21 #include "nodes/memnodes.h"
22 #include "nodes/pg_list.h"
23 #include "parser/analyze.h"
24 #include "parser/parse_agg.h"
25 #include "parser/parse_clause.h"
26 #include "parser/parse_node.h"
27 #include "parser/parse_relation.h"
28 #include "parser/parse_target.h"
29 #include "utils/builtins.h"
30 #include "utils/mcxt.h"
32 static Query *transformStmt(ParseState *pstate, Node *stmt);
33 static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
34 static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt);
35 static Query *transformIndexStmt(ParseState *pstate, IndexStmt *stmt);
36 static Query *transformExtendStmt(ParseState *pstate, ExtendStmt *stmt);
37 static Query *transformRuleStmt(ParseState *query, RuleStmt *stmt);
38 static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
39 static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
40 static Query *transformCursorStmt(ParseState *pstate, SelectStmt *stmt);
41 static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt);
43 List *extras_before = NIL;
44 List *extras_after = NIL;
48 * analyze a list of parse trees and transform them if necessary.
50 * Returns a list of transformed parse trees. Optimizable statements are
51 * all transformed to Query while the rest stays the same.
55 parse_analyze(List *pl, ParseState *parentParseState)
57 QueryTreeList *result;
62 result = malloc(sizeof(QueryTreeList));
63 result->len = length(pl);
64 result->qtrees = (Query **) malloc(result->len * sizeof(Query *));
68 pstate = make_parsestate(parentParseState);
69 parsetree = transformStmt(pstate, lfirst(pl));
70 if (pstate->p_target_relation != NULL)
71 heap_close(pstate->p_target_relation);
73 if (extras_before != NIL)
75 result->len += length(extras_before);
76 result->qtrees = (Query **) realloc(result->qtrees, result->len * sizeof(Query *));
77 while (extras_before != NIL)
79 result->qtrees[i++] = transformStmt(pstate, lfirst(extras_before));
80 if (pstate->p_target_relation != NULL)
81 heap_close(pstate->p_target_relation);
82 extras_before = lnext(extras_before);
87 result->qtrees[i++] = parsetree;
89 if (extras_after != NIL)
91 result->len += length(extras_after);
92 result->qtrees = (Query **) realloc(result->qtrees, result->len * sizeof(Query *));
93 while (extras_after != NIL)
95 result->qtrees[i++] = transformStmt(pstate, lfirst(extras_after));
96 if (pstate->p_target_relation != NULL)
97 heap_close(pstate->p_target_relation);
98 extras_after = lnext(extras_after);
112 * transform a Parse tree. If it is an optimizable statement, turn it
116 transformStmt(ParseState *pstate, Node *parseTree)
118 Query *result = NULL;
120 switch (nodeTag(parseTree))
122 /*------------------------
123 * Non-optimizable statements
124 *------------------------
127 result = transformCreateStmt(pstate, (CreateStmt *) parseTree);
131 result = transformIndexStmt(pstate, (IndexStmt *) parseTree);
135 result = transformExtendStmt(pstate, (ExtendStmt *) parseTree);
139 result = transformRuleStmt(pstate, (RuleStmt *) parseTree);
144 ViewStmt *n = (ViewStmt *) parseTree;
146 n->query = (Query *) transformStmt(pstate, (Node *) n->query);
147 result = makeNode(Query);
148 result->commandType = CMD_UTILITY;
149 result->utilityStmt = (Node *) n;
155 MemoryContext oldcontext;
158 * make sure that this Query is allocated in TopMemory
159 * context because vacuum spans transactions and we don't
160 * want to lose the vacuum Query due to end-of-transaction
163 oldcontext = MemoryContextSwitchTo(TopMemoryContext);
164 result = makeNode(Query);
165 result->commandType = CMD_UTILITY;
166 result->utilityStmt = (Node *) parseTree;
167 MemoryContextSwitchTo(oldcontext);
173 ExplainStmt *n = (ExplainStmt *) parseTree;
175 result = makeNode(Query);
176 result->commandType = CMD_UTILITY;
177 n->query = transformStmt(pstate, (Node *) n->query);
178 result->utilityStmt = (Node *) parseTree;
182 /*------------------------
183 * Optimizable statements
184 *------------------------
187 result = transformInsertStmt(pstate, (InsertStmt *) parseTree);
191 result = transformDeleteStmt(pstate, (DeleteStmt *) parseTree);
195 result = transformUpdateStmt(pstate, (UpdateStmt *) parseTree);
199 if (!((SelectStmt *) parseTree)->portalname)
200 result = transformSelectStmt(pstate, (SelectStmt *) parseTree);
202 result = transformCursorStmt(pstate, (SelectStmt *) parseTree);
208 * other statments don't require any transformation-- just
209 * return the original parsetree, yea!
211 result = makeNode(Query);
212 result->commandType = CMD_UTILITY;
213 result->utilityStmt = (Node *) parseTree;
220 * transformDeleteStmt -
221 * transforms a Delete Statement
224 transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
226 Query *qry = makeNode(Query);
228 qry->commandType = CMD_DELETE;
230 /* set up a range table */
231 makeRangeTable(pstate, stmt->relname, NULL);
233 qry->uniqueFlag = NULL;
235 /* fix where clause */
236 qry->qual = transformWhereClause(pstate, stmt->whereClause);
237 qry->hasSubLinks = pstate->p_hasSubLinks;
239 qry->rtable = pstate->p_rtable;
240 qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
242 qry->hasAggs = pstate->p_hasAggs;
243 if (pstate->p_hasAggs)
244 parseCheckAggregates(pstate, qry);
246 return (Query *) qry;
250 * transformInsertStmt -
251 * transform an Insert Statement
254 transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
256 Query *qry = makeNode(Query); /* make a new query tree */
259 qry->commandType = CMD_INSERT;
260 pstate->p_is_insert = true;
262 /* set up a range table */
263 makeRangeTable(pstate, stmt->relname, stmt->fromClause);
265 qry->uniqueFlag = stmt->unique;
267 /* fix the target list */
268 icolumns = pstate->p_insert_columns = makeTargetNames(pstate, stmt->cols);
270 qry->targetList = transformTargetList(pstate, stmt->targetList);
272 /* DEFAULT handling */
273 if (length(qry->targetList) < pstate->p_target_relation->rd_att->natts &&
274 pstate->p_target_relation->rd_att->constr &&
275 pstate->p_target_relation->rd_att->constr->num_defval > 0)
277 Form_pg_attribute *att = pstate->p_target_relation->rd_att->attrs;
278 AttrDefault *defval = pstate->p_target_relation->rd_att->constr->defval;
279 int ndef = pstate->p_target_relation->rd_att->constr->num_defval;
282 * if stmt->cols == NIL then makeTargetNames returns list of all attrs.
283 * May have to shorten icolumns list...
285 if (stmt->cols == NIL)
288 int i = length(qry->targetList);
290 foreach(extrl, icolumns)
293 * decrements first, so if we started with zero items
294 * it will now be negative
300 * this an index into the targetList,
301 * so make sure we had one to start...
305 freeList(lnext(extrl));
320 foreach(tl, icolumns)
322 id = (Ident *) lfirst(tl);
323 if (namestrcmp(&(att[defval[ndef].adnum - 1]->attname), id->name) == 0)
326 if (tl != NIL) /* something given for this attr */
330 * Nothing given for this attr with DEFAULT expr, so add new
331 * TargetEntry to qry->targetList. Note, that we set resno to
332 * defval[ndef].adnum: it's what
333 * transformTargetList()->make_targetlist_expr() does for
334 * INSERT ... SELECT. But for INSERT ... VALUES
335 * pstate->p_last_resno is used. It doesn't matter for
336 * "normal" using (planner creates proper target list in
337 * preptlist.c), but may break RULEs in some way. It seems
338 * better to create proper target list here...
340 te = makeTargetEntry(makeResdom(defval[ndef].adnum,
341 att[defval[ndef].adnum - 1]->atttypid,
342 att[defval[ndef].adnum - 1]->atttypmod,
343 pstrdup(nameout(&(att[defval[ndef].adnum - 1]->attname))),
345 (Node *) stringToNode(defval[ndef].adbin));
346 qry->targetList = lappend(qry->targetList, te);
350 /* fix where clause */
351 qry->qual = transformWhereClause(pstate, stmt->whereClause);
354 * The havingQual has a similar meaning as "qual" in the where
355 * statement. So we can easily use the code from the "where clause"
356 * with some additional traversals done in
357 * .../optimizer/plan/planner.c
359 qry->havingQual = transformWhereClause(pstate, stmt->havingClause);
361 qry->hasSubLinks = pstate->p_hasSubLinks;
363 /* now the range table will not change */
364 qry->rtable = pstate->p_rtable;
365 qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
367 qry->groupClause = transformGroupClause(pstate,
371 /* fix order clause */
372 qry->sortClause = transformSortClause(pstate,
378 qry->hasAggs = pstate->p_hasAggs;
379 if (pstate->p_hasAggs)
380 parseCheckAggregates(pstate, qry);
383 * The INSERT INTO ... SELECT ... could have a UNION in child, so
384 * unionClause may be false
386 qry->unionall = stmt->unionall;
387 qry->unionClause = transformUnionClause(stmt->unionClause, qry->targetList);
390 * If there is a havingQual but there are no aggregates, then there is
391 * something wrong with the query because having must contain
392 * aggregates in its expressions! Otherwise the query could have been
393 * formulated using the where clause.
395 if ((qry->hasAggs == false) && (qry->havingQual != NULL))
397 elog(ERROR, "SELECT/HAVING requires aggregates to be valid");
398 return (Query *) NIL;
401 return (Query *) qry;
406 * Create a table name from a list of fields.
409 makeTableName(void *elem,...)
414 char buf[NAMEDATALEN + 1];
418 va_start(args, elem);
423 /* not enough room for next part? then return nothing */
424 if ((strlen(buf) + strlen(name)) >= (sizeof(buf) - 1))
431 name = va_arg(args, void *);
436 name = palloc(strlen(buf) + 1);
443 CreateIndexName(char *table_name, char *column_name, char *label, List *indices)
449 char name2[NAMEDATALEN + 1];
451 /* use working storage, since we might be trying several possibilities */
452 strcpy(name2, column_name);
453 while (iname == NULL)
455 iname = makeTableName(table_name, name2, label, NULL);
456 /* unable to make a name at all? then quit */
463 index = lfirst(ilist);
464 if (strcasecmp(iname, index->idxname) == 0)
467 ilist = lnext(ilist);
469 /* ran through entire list? then no name conflict found so done */
473 /* the last one conflicted, so try a new name component */
477 sprintf(name2, "%s_%d", column_name, (pass + 1));
484 * transformCreateStmt -
485 * transforms the "create table" statement
486 * SQL92 allows constraints to be scattered all over, so thumb through
487 * the columns and collect all constraints into one place.
488 * If there are any implied indices (e.g. UNIQUE or PRIMARY KEY)
489 * then expand those into multiple IndexStmt blocks.
490 * - thomas 1997-12-02
493 transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
496 int have_pkey = FALSE;
504 Constraint *constraint;
513 q->commandType = CMD_UTILITY;
515 elements = stmt->tableElts;
516 constraints = stmt->constraints;
520 while (elements != NIL)
522 element = lfirst(elements);
523 switch (nodeTag(element))
526 column = (ColumnDef *) element;
527 columns = lappend(columns, column);
529 if (column->is_sequence)
533 CreateSeqStmt *sequence;
535 sname = makeTableName(stmt->relname, column->colname, "seq", NULL);
537 constraint = makeNode(Constraint);
538 constraint->contype = CONSTR_DEFAULT;
539 constraint->name = sname;
540 cstring = palloc(9 + strlen(constraint->name) + 2 + 1);
541 strcpy(cstring, "nextval('");
542 strcat(cstring, constraint->name);
543 strcat(cstring, "')");
544 constraint->def = cstring;
545 constraint->keys = NULL;
547 /* The parser only allows PRIMARY KEY as a constraint for the SERIAL type.
548 * So, if there is a constraint of any kind, assume it is that.
549 * If PRIMARY KEY is specified, then don't need to gin up a UNIQUE constraint
550 * since that will be covered already.
551 * - thomas 1998-09-15
553 if (column->constraints != NIL)
555 column->constraints = lappend(column->constraints, constraint);
559 column->constraints = lcons(constraint, NIL);
561 constraint = makeNode(Constraint);
562 constraint->contype = CONSTR_UNIQUE;
563 constraint->name = makeTableName(stmt->relname, column->colname, "key", NULL);
564 column->constraints = lappend(column->constraints, constraint);
567 sequence = makeNode(CreateSeqStmt);
568 sequence->seqname = pstrdup(sname);
569 sequence->options = NIL;
571 elog(NOTICE, "CREATE TABLE will create implicit sequence %s for SERIAL column %s.%s",
572 sequence->seqname, stmt->relname, column->colname);
574 blist = lcons(sequence, NIL);
577 if (column->constraints != NIL)
579 clist = column->constraints;
582 constraint = lfirst(clist);
583 switch (constraint->contype)
586 if (column->is_not_null)
587 elog(ERROR, "CREATE TABLE/NOT NULL already specified"
588 " for %s.%s", stmt->relname, column->colname);
589 column->is_not_null = TRUE;
593 if (column->defval != NULL)
594 elog(ERROR, "CREATE TABLE/DEFAULT multiple values specified"
595 " for %s.%s", stmt->relname, column->colname);
596 column->defval = constraint->def;
600 if (constraint->name == NULL)
601 constraint->name = makeTableName(stmt->relname, "pkey", NULL);
602 if (constraint->keys == NIL)
603 constraint->keys = lappend(constraint->keys, column);
604 dlist = lappend(dlist, constraint);
608 if (constraint->name == NULL)
609 constraint->name = makeTableName(stmt->relname, column->colname, "key", NULL);
610 if (constraint->keys == NIL)
611 constraint->keys = lappend(constraint->keys, column);
612 dlist = lappend(dlist, constraint);
616 constraints = lappend(constraints, constraint);
617 if (constraint->name == NULL)
618 constraint->name = makeTableName(stmt->relname, column->colname, NULL);
622 elog(ERROR, "parser: unrecognized constraint (internal error)", NULL);
625 clist = lnext(clist);
631 constraint = (Constraint *) element;
632 switch (constraint->contype)
635 if (constraint->name == NULL)
636 constraint->name = makeTableName(stmt->relname, "pkey", NULL);
637 dlist = lappend(dlist, constraint);
642 if (constraint->name == NULL)
643 constraint->name = makeTableName(stmt->relname, "key", NULL);
645 dlist = lappend(dlist, constraint);
649 constraints = lappend(constraints, constraint);
654 elog(ERROR, "parser: illegal context for constraint (internal error)", NULL);
657 elog(ERROR, "parser: unrecognized constraint (internal error)", NULL);
663 elog(ERROR, "parser: unrecognized node (internal error)", NULL);
666 elements = lnext(elements);
669 stmt->tableElts = columns;
670 stmt->constraints = constraints;
672 /* Now run through the "deferred list" to complete the query transformation.
673 * For PRIMARY KEYs, mark each column as NOT NULL and create an index.
674 * For UNIQUE, create an index as for PRIMARY KEYS, but do not insist on NOT NULL.
676 * Note that this code does not currently look for all possible redundant cases
677 * and either ignore or stop with warning. The create might fail later when
678 * names for indices turn out to be redundant, or a user might have specified
679 * extra useless indices which might hurt performance. - thomas 1997-12-08
683 constraint = lfirst(dlist);
684 if (nodeTag(constraint) != T_Constraint)
685 elog(ERROR, "parser: unrecognized deferred node (internal error)", NULL);
687 if (constraint->contype == CONSTR_PRIMARY)
690 elog(ERROR, "CREATE TABLE/PRIMARY KEY multiple primary keys"
691 " for table %s are not legal", stmt->relname);
695 else if (constraint->contype != CONSTR_UNIQUE)
696 elog(ERROR, "parser: unrecognized deferred constraint (internal error)", NULL);
698 index = makeNode(IndexStmt);
700 index->unique = TRUE;
701 if (constraint->name != NULL)
702 index->idxname = constraint->name;
703 else if (constraint->contype == CONSTR_PRIMARY)
706 elog(ERROR, "CREATE TABLE/PRIMARY KEY multiple keys for table %s are not legal", stmt->relname);
709 index->idxname = makeTableName(stmt->relname, "pkey", NULL);
712 index->idxname = NULL;
714 index->relname = stmt->relname;
715 index->accessMethod = "btree";
716 index->indexParams = NIL;
717 index->withClause = NIL;
718 index->whereClause = NULL;
720 keys = constraint->keys;
724 columns = stmt->tableElts;
726 while (columns != NIL)
728 column = lfirst(columns);
729 if (strcasecmp(column->colname, key->name) == 0)
733 columns = lnext(columns);
736 elog(ERROR, "CREATE TABLE column '%s' in key does not exist", key->name);
738 if (constraint->contype == CONSTR_PRIMARY)
739 column->is_not_null = TRUE;
740 iparam = makeNode(IndexElem);
741 iparam->name = strcpy(palloc(strlen(column->colname) + 1), column->colname);
743 iparam->class = NULL;
744 iparam->typename = NULL;
745 index->indexParams = lappend(index->indexParams, iparam);
747 if (index->idxname == NULL)
748 index->idxname = CreateIndexName(stmt->relname, iparam->name, "key", ilist);
753 if (index->idxname == NULL)
754 elog(ERROR, "CREATE TABLE unable to construct implicit index for table %s"
755 "; name too long", stmt->relname);
757 elog(NOTICE, "CREATE TABLE/%s will create implicit index %s for table %s",
758 ((constraint->contype == CONSTR_PRIMARY) ? "PRIMARY KEY" : "UNIQUE"),
759 index->idxname, stmt->relname);
761 ilist = lappend(ilist, index);
762 dlist = lnext(dlist);
765 q->utilityStmt = (Node *) stmt;
766 extras_before = blist;
767 extras_after = ilist;
773 * transformIndexStmt -
774 * transforms the qualification of the index statement
777 transformIndexStmt(ParseState *pstate, IndexStmt *stmt)
781 qry = makeNode(Query);
782 qry->commandType = CMD_UTILITY;
784 /* take care of the where clause */
785 stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
786 qry->hasSubLinks = pstate->p_hasSubLinks;
788 stmt->rangetable = pstate->p_rtable;
790 qry->utilityStmt = (Node *) stmt;
796 * transformExtendStmt -
797 * transform the qualifications of the Extend Index Statement
801 transformExtendStmt(ParseState *pstate, ExtendStmt *stmt)
805 qry = makeNode(Query);
806 qry->commandType = CMD_UTILITY;
808 /* take care of the where clause */
809 stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
810 qry->hasSubLinks = pstate->p_hasSubLinks;
812 stmt->rangetable = pstate->p_rtable;
814 qry->utilityStmt = (Node *) stmt;
819 * transformRuleStmt -
820 * transform a Create Rule Statement. The actions is a list of parse
821 * trees which is transformed into a list of query trees.
824 transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
830 qry = makeNode(Query);
831 qry->commandType = CMD_UTILITY;
834 * 'instead nothing' rules with a qualification need a query a
835 * rangetable so the rewrite handler can add the negated rule
836 * qualification to the original query. We create a query with the new
837 * command type CMD_NOTHING here that is treated special by the
840 if (stmt->actions == NIL)
842 Query *nothing_qry = makeNode(Query);
844 nothing_qry->commandType = CMD_NOTHING;
846 addRangeTableEntry(pstate, stmt->object->relname, "*CURRENT*",
848 addRangeTableEntry(pstate, stmt->object->relname, "*NEW*",
851 nothing_qry->rtable = pstate->p_rtable;
853 stmt->actions = lappend(NIL, nothing_qry);
856 actions = stmt->actions;
859 * transform each statment, like parse_analyze()
861 while (actions != NIL)
865 * NOTE: 'CURRENT' must always have a varno equal to 1 and 'NEW'
868 addRangeTableEntry(pstate, stmt->object->relname, "*CURRENT*",
870 addRangeTableEntry(pstate, stmt->object->relname, "*NEW*",
873 pstate->p_last_resno = 1;
874 pstate->p_is_rule = true; /* for expand all */
875 pstate->p_hasAggs = false;
877 action = (Query *) lfirst(actions);
878 if (action->commandType != CMD_NOTHING)
879 lfirst(actions) = transformStmt(pstate, lfirst(actions));
880 actions = lnext(actions);
883 /* take care of the where clause */
884 stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
885 qry->hasSubLinks = pstate->p_hasSubLinks;
887 qry->utilityStmt = (Node *) stmt;
893 * transformSelectStmt -
894 * transforms a Select Statement
898 transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
900 Query *qry = makeNode(Query);
902 qry->commandType = CMD_SELECT;
904 /* set up a range table */
905 makeRangeTable(pstate, NULL, stmt->fromClause);
907 qry->uniqueFlag = stmt->unique;
909 qry->into = stmt->into;
910 qry->isPortal = FALSE;
912 qry->targetList = transformTargetList(pstate, stmt->targetList);
914 qry->qual = transformWhereClause(pstate, stmt->whereClause);
917 * The havingQual has a similar meaning as "qual" in the where
918 * statement. So we can easily use the code from the "where clause"
919 * with some additional traversals done in optimizer/plan/planner.c
921 qry->havingQual = transformWhereClause(pstate, stmt->havingClause);
923 qry->hasSubLinks = pstate->p_hasSubLinks;
925 qry->sortClause = transformSortClause(pstate,
931 qry->groupClause = transformGroupClause(pstate,
934 qry->rtable = pstate->p_rtable;
936 qry->hasAggs = pstate->p_hasAggs;
937 if (pstate->p_hasAggs)
938 parseCheckAggregates(pstate, qry);
941 * The INSERT INTO ... SELECT ... could have a UNION in child, so
942 * unionClause may be false
944 qry->unionall = stmt->unionall;
945 qry->unionClause = transformUnionClause(stmt->unionClause, qry->targetList);
948 * If there is a havingQual but there are no aggregates, then there is
949 * something wrong with the query because having must contain
950 * aggregates in its expressions! Otherwise the query could have been
951 * formulated using the where clause.
953 if ((qry->hasAggs == false) && (qry->havingQual != NULL))
955 elog(ERROR, "SELECT/HAVING requires aggregates to be valid");
956 return (Query *) NIL;
959 return (Query *) qry;
963 * transformUpdateStmt -
964 * transforms an update statement
968 transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
970 Query *qry = makeNode(Query);
972 qry->commandType = CMD_UPDATE;
973 pstate->p_is_update = true;
976 * the FROM clause is non-standard SQL syntax. We used to be able to
977 * do this with REPLACE in POSTQUEL so we keep the feature.
979 makeRangeTable(pstate, stmt->relname, stmt->fromClause);
981 qry->targetList = transformTargetList(pstate, stmt->targetList);
983 qry->qual = transformWhereClause(pstate, stmt->whereClause);
984 qry->hasSubLinks = pstate->p_hasSubLinks;
986 qry->rtable = pstate->p_rtable;
988 qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
990 qry->hasAggs = pstate->p_hasAggs;
991 if (pstate->p_hasAggs)
992 parseCheckAggregates(pstate, qry);
994 return (Query *) qry;
998 * transformCursorStmt -
999 * transform a Create Cursor Statement
1003 transformCursorStmt(ParseState *pstate, SelectStmt *stmt)
1007 qry = transformSelectStmt(pstate, stmt);
1009 qry->into = stmt->portalname;
1010 qry->isPortal = TRUE;
1011 qry->isBinary = stmt->binary; /* internal portal */