mirror of
https://github.com/zebrajr/postgres.git
synced 2025-12-07 12:20:31 +01:00
now. Here some tested features, (examples included in the patch):
1.1) Subselects in the having clause 1.2) Double nested subselects
1.3) Subselects used in the where clause and in the having clause
simultaneously 1.4) Union Selects using having 1.5) Indexes
on the base relations are used correctly 1.6) Unallowed Queries
are prevented (e.g. qualifications in the
having clause that belong to the where clause) 1.7) Insert
into as select
2) Queries using the having clause on view relations also work
but there are some restrictions:
2.1) Create View as Select ... Having ...; using base tables in
the select 2.1.1) The Query rewrite system:
2.1.2) Why are only simple queries allowed against a view from 2.1)
? 2.2) Select ... from testview1, testview2, ... having...; 3) Bug
in ExecMergeJoin ??
Regards Stefan
903 lines
23 KiB
C
903 lines
23 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* analyze.c--
|
|
* transform the parse tree into a query tree
|
|
*
|
|
* Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.77 1998/07/19 05:49:17 momjian Exp $
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
|
|
#include "postgres.h"
|
|
#include "access/heapam.h"
|
|
#include "nodes/makefuncs.h"
|
|
#include "nodes/memnodes.h"
|
|
#include "nodes/pg_list.h"
|
|
#include "parser/analyze.h"
|
|
#include "parser/parse_agg.h"
|
|
#include "parser/parse_clause.h"
|
|
#include "parser/parse_node.h"
|
|
#include "parser/parse_relation.h"
|
|
#include "parser/parse_target.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/mcxt.h"
|
|
#ifdef PARSEDEBUG
|
|
#include "nodes/print.h"
|
|
#endif
|
|
|
|
static Query *transformStmt(ParseState *pstate, Node *stmt);
|
|
static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
|
|
static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt);
|
|
static Query *transformIndexStmt(ParseState *pstate, IndexStmt *stmt);
|
|
static Query *transformExtendStmt(ParseState *pstate, ExtendStmt *stmt);
|
|
static Query *transformRuleStmt(ParseState *query, RuleStmt *stmt);
|
|
static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
|
|
static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
|
|
static Query *transformCursorStmt(ParseState *pstate, SelectStmt *stmt);
|
|
static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt);
|
|
|
|
List *extras = NIL;
|
|
|
|
/*
|
|
* parse_analyze -
|
|
* analyze a list of parse trees and transform them if necessary.
|
|
*
|
|
* Returns a list of transformed parse trees. Optimizable statements are
|
|
* all transformed to Query while the rest stays the same.
|
|
*
|
|
*/
|
|
QueryTreeList *
|
|
parse_analyze(List *pl, ParseState *parentParseState)
|
|
{
|
|
QueryTreeList *result;
|
|
ParseState *pstate;
|
|
int i = 0;
|
|
|
|
result = malloc(sizeof(QueryTreeList));
|
|
result->len = length(pl);
|
|
result->qtrees = (Query **) malloc(result->len * sizeof(Query *));
|
|
|
|
while (pl != NIL)
|
|
{
|
|
#ifdef PARSEDEBUG
|
|
elog(DEBUG,"parse tree from yacc:\n---\n%s\n---\n", nodeToString(lfirst(pl)));
|
|
#endif
|
|
|
|
pstate = make_parsestate(parentParseState);
|
|
result->qtrees[i++] = transformStmt(pstate, lfirst(pl));
|
|
if (pstate->p_target_relation != NULL)
|
|
heap_close(pstate->p_target_relation);
|
|
|
|
if (extras != NIL)
|
|
{
|
|
result->len += length(extras);
|
|
result->qtrees = (Query **) realloc(result->qtrees, result->len * sizeof(Query *));
|
|
while (extras != NIL)
|
|
{
|
|
result->qtrees[i++] = transformStmt(pstate, lfirst(extras));
|
|
if (pstate->p_target_relation != NULL)
|
|
heap_close(pstate->p_target_relation);
|
|
extras = lnext(extras);
|
|
}
|
|
}
|
|
extras = NIL;
|
|
pl = lnext(pl);
|
|
pfree(pstate);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* transformStmt -
|
|
* transform a Parse tree. If it is an optimizable statement, turn it
|
|
* into a Query tree.
|
|
*/
|
|
static Query *
|
|
transformStmt(ParseState *pstate, Node *parseTree)
|
|
{
|
|
Query *result = NULL;
|
|
|
|
switch (nodeTag(parseTree))
|
|
{
|
|
/*------------------------
|
|
* Non-optimizable statements
|
|
*------------------------
|
|
*/
|
|
case T_CreateStmt:
|
|
result = transformCreateStmt(pstate, (CreateStmt *) parseTree);
|
|
break;
|
|
|
|
case T_IndexStmt:
|
|
result = transformIndexStmt(pstate, (IndexStmt *) parseTree);
|
|
break;
|
|
|
|
case T_ExtendStmt:
|
|
result = transformExtendStmt(pstate, (ExtendStmt *) parseTree);
|
|
break;
|
|
|
|
case T_RuleStmt:
|
|
result = transformRuleStmt(pstate, (RuleStmt *) parseTree);
|
|
break;
|
|
|
|
case T_ViewStmt:
|
|
{
|
|
ViewStmt *n = (ViewStmt *) parseTree;
|
|
|
|
n->query = (Query *) transformStmt(pstate, (Node *) n->query);
|
|
result = makeNode(Query);
|
|
result->commandType = CMD_UTILITY;
|
|
result->utilityStmt = (Node *) n;
|
|
}
|
|
break;
|
|
|
|
case T_VacuumStmt:
|
|
{
|
|
MemoryContext oldcontext;
|
|
|
|
/*
|
|
* make sure that this Query is allocated in TopMemory
|
|
* context because vacuum spans transactions and we don't
|
|
* want to lose the vacuum Query due to end-of-transaction
|
|
* free'ing
|
|
*/
|
|
oldcontext = MemoryContextSwitchTo(TopMemoryContext);
|
|
result = makeNode(Query);
|
|
result->commandType = CMD_UTILITY;
|
|
result->utilityStmt = (Node *) parseTree;
|
|
MemoryContextSwitchTo(oldcontext);
|
|
break;
|
|
|
|
}
|
|
case T_ExplainStmt:
|
|
{
|
|
ExplainStmt *n = (ExplainStmt *) parseTree;
|
|
|
|
result = makeNode(Query);
|
|
result->commandType = CMD_UTILITY;
|
|
n->query = transformStmt(pstate, (Node *) n->query);
|
|
result->utilityStmt = (Node *) parseTree;
|
|
}
|
|
break;
|
|
|
|
/*------------------------
|
|
* Optimizable statements
|
|
*------------------------
|
|
*/
|
|
case T_InsertStmt:
|
|
result = transformInsertStmt(pstate, (InsertStmt *) parseTree);
|
|
break;
|
|
|
|
case T_DeleteStmt:
|
|
result = transformDeleteStmt(pstate, (DeleteStmt *) parseTree);
|
|
break;
|
|
|
|
case T_UpdateStmt:
|
|
result = transformUpdateStmt(pstate, (UpdateStmt *) parseTree);
|
|
break;
|
|
|
|
case T_SelectStmt:
|
|
if (!((SelectStmt *) parseTree)->portalname)
|
|
result = transformSelectStmt(pstate, (SelectStmt *) parseTree);
|
|
else
|
|
result = transformCursorStmt(pstate, (SelectStmt *) parseTree);
|
|
break;
|
|
|
|
default:
|
|
|
|
/*
|
|
* other statments don't require any transformation-- just
|
|
* return the original parsetree, yea!
|
|
*/
|
|
result = makeNode(Query);
|
|
result->commandType = CMD_UTILITY;
|
|
result->utilityStmt = (Node *) parseTree;
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* transformDeleteStmt -
|
|
* transforms a Delete Statement
|
|
*/
|
|
static Query *
|
|
transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
|
|
{
|
|
Query *qry = makeNode(Query);
|
|
|
|
qry->commandType = CMD_DELETE;
|
|
|
|
/* set up a range table */
|
|
makeRangeTable(pstate, stmt->relname, NULL);
|
|
|
|
qry->uniqueFlag = NULL;
|
|
|
|
/* fix where clause */
|
|
qry->qual = transformWhereClause(pstate, stmt->whereClause);
|
|
qry->hasSubLinks = pstate->p_hasSubLinks;
|
|
|
|
qry->rtable = pstate->p_rtable;
|
|
qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
|
|
|
|
qry->hasAggs = pstate->p_hasAggs;
|
|
if (pstate->p_hasAggs)
|
|
parseCheckAggregates(pstate, qry);
|
|
|
|
return (Query *) qry;
|
|
}
|
|
|
|
/*
|
|
* transformInsertStmt -
|
|
* transform an Insert Statement
|
|
*/
|
|
static Query *
|
|
transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
|
|
{
|
|
Query *qry = makeNode(Query); /* make a new query tree */
|
|
List *icolumns;
|
|
|
|
qry->commandType = CMD_INSERT;
|
|
pstate->p_is_insert = true;
|
|
|
|
/* set up a range table */
|
|
makeRangeTable(pstate, stmt->relname, stmt->fromClause);
|
|
|
|
qry->uniqueFlag = stmt->unique;
|
|
|
|
/* fix the target list */
|
|
icolumns = pstate->p_insert_columns = makeTargetNames(pstate, stmt->cols);
|
|
|
|
qry->targetList = transformTargetList(pstate, stmt->targetList);
|
|
|
|
/* DEFAULT handling */
|
|
if (length(qry->targetList) < pstate->p_target_relation->rd_att->natts &&
|
|
pstate->p_target_relation->rd_att->constr &&
|
|
pstate->p_target_relation->rd_att->constr->num_defval > 0)
|
|
{
|
|
AttributeTupleForm *att = pstate->p_target_relation->rd_att->attrs;
|
|
AttrDefault *defval = pstate->p_target_relation->rd_att->constr->defval;
|
|
int ndef = pstate->p_target_relation->rd_att->constr->num_defval;
|
|
|
|
/*
|
|
* if stmt->cols == NIL then makeTargetNames returns list of all
|
|
* attrs: have to shorter icolumns list...
|
|
*/
|
|
if (stmt->cols == NIL)
|
|
{
|
|
List *extrl;
|
|
int i = length(qry->targetList);
|
|
|
|
foreach(extrl, icolumns)
|
|
{
|
|
if (--i <= 0)
|
|
break;
|
|
}
|
|
freeList(lnext(extrl));
|
|
lnext(extrl) = NIL;
|
|
}
|
|
|
|
while (ndef-- > 0)
|
|
{
|
|
List *tl;
|
|
Ident *id;
|
|
TargetEntry *te;
|
|
|
|
foreach(tl, icolumns)
|
|
{
|
|
id = (Ident *) lfirst(tl);
|
|
if (!namestrcmp(&(att[defval[ndef].adnum - 1]->attname), id->name))
|
|
break;
|
|
}
|
|
if (tl != NIL) /* something given for this attr */
|
|
continue;
|
|
|
|
/*
|
|
* Nothing given for this attr with DEFAULT expr, so add new
|
|
* TargetEntry to qry->targetList. Note, that we set resno to
|
|
* defval[ndef].adnum: it's what
|
|
* transformTargetList()->make_targetlist_expr() does for
|
|
* INSERT ... SELECT. But for INSERT ... VALUES
|
|
* pstate->p_last_resno is used. It doesn't matter for
|
|
* "normal" using (planner creates proper target list in
|
|
* preptlist.c), but may break RULEs in some way. It seems
|
|
* better to create proper target list here...
|
|
*/
|
|
te = makeNode(TargetEntry);
|
|
te->resdom = makeResdom(defval[ndef].adnum,
|
|
att[defval[ndef].adnum - 1]->atttypid,
|
|
att[defval[ndef].adnum - 1]->atttypmod,
|
|
pstrdup(nameout(&(att[defval[ndef].adnum - 1]->attname))),
|
|
0, 0, 0);
|
|
te->fjoin = NULL;
|
|
te->expr = (Node *) stringToNode(defval[ndef].adbin);
|
|
qry->targetList = lappend(qry->targetList, te);
|
|
}
|
|
}
|
|
|
|
/* fix where clause */
|
|
qry->qual = transformWhereClause(pstate, stmt->whereClause);
|
|
|
|
/* The havingQual has a similar meaning as "qual" in the where statement.
|
|
* So we can easily use the code from the "where clause" with some additional
|
|
* traversals done in .../optimizer/plan/planner.c */
|
|
qry->havingQual = transformWhereClause(pstate, stmt->havingClause);
|
|
|
|
qry->hasSubLinks = pstate->p_hasSubLinks;
|
|
|
|
/* now the range table will not change */
|
|
qry->rtable = pstate->p_rtable;
|
|
qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
|
|
|
|
qry->groupClause = transformGroupClause(pstate,
|
|
stmt->groupClause,
|
|
qry->targetList);
|
|
|
|
/* fix order clause */
|
|
qry->sortClause = transformSortClause(pstate,
|
|
NIL,
|
|
NIL,
|
|
qry->targetList,
|
|
qry->uniqueFlag);
|
|
|
|
qry->hasAggs = pstate->p_hasAggs;
|
|
if (pstate->p_hasAggs)
|
|
parseCheckAggregates(pstate, qry);
|
|
|
|
/* The INSERT INTO ... SELECT ... could have a UNION
|
|
* in child, so unionClause may be false
|
|
*/
|
|
qry->unionall = stmt->unionall;
|
|
qry->unionClause = transformUnionClause(stmt->unionClause, qry->targetList);
|
|
|
|
/* If there is a havingQual but there are no aggregates, then there is something wrong
|
|
* with the query because having must contain aggregates in its expressions!
|
|
* Otherwise the query could have been formulated using the where clause. */
|
|
if((qry->hasAggs == false) && (qry->havingQual != NULL))
|
|
{
|
|
elog(ERROR,"This is not a valid having query!");
|
|
return (Query *)NIL;
|
|
}
|
|
|
|
return (Query *) qry;
|
|
}
|
|
|
|
/*
|
|
* makeTableName()
|
|
* Create a table name from a list of fields.
|
|
*/
|
|
static char *
|
|
makeTableName(void *elem,...)
|
|
{
|
|
va_list args;
|
|
|
|
char *name;
|
|
char buf[NAMEDATALEN + 1];
|
|
|
|
buf[0] = '\0';
|
|
|
|
va_start(args, elem);
|
|
|
|
name = elem;
|
|
while (name != NULL)
|
|
{
|
|
/* not enough room for next part? then return nothing */
|
|
if ((strlen(buf) + strlen(name)) >= (sizeof(buf) - 1))
|
|
return (NULL);
|
|
|
|
if (strlen(buf) > 0)
|
|
strcat(buf, "_");
|
|
strcat(buf, name);
|
|
|
|
name = va_arg(args, void *);
|
|
}
|
|
|
|
va_end(args);
|
|
|
|
name = palloc(strlen(buf) + 1);
|
|
strcpy(name, buf);
|
|
|
|
return (name);
|
|
}
|
|
|
|
static char *
|
|
CreateIndexName(char *tname, char *cname, char *label, List *indices)
|
|
{
|
|
int pass = 0;
|
|
char *iname = NULL;
|
|
List *ilist;
|
|
IndexStmt *index;
|
|
char name2[NAMEDATALEN + 1];
|
|
|
|
/* use working storage, since we might be trying several possibilities */
|
|
strcpy(name2, cname);
|
|
while (iname == NULL)
|
|
{
|
|
iname = makeTableName(tname, name2, label, NULL);
|
|
/* unable to make a name at all? then quit */
|
|
if (iname == NULL)
|
|
break;
|
|
|
|
ilist = indices;
|
|
while (ilist != NIL)
|
|
{
|
|
index = lfirst(ilist);
|
|
if (strcasecmp(iname, index->idxname) == 0)
|
|
break;
|
|
|
|
ilist = lnext(ilist);
|
|
}
|
|
/* ran through entire list? then no name conflict found so done */
|
|
if (ilist == NIL)
|
|
break;
|
|
|
|
/* the last one conflicted, so try a new name component */
|
|
pfree(iname);
|
|
iname = NULL;
|
|
pass++;
|
|
sprintf(name2, "%s_%d", cname, (pass + 1));
|
|
}
|
|
|
|
return (iname);
|
|
}
|
|
|
|
/*
|
|
* transformCreateStmt -
|
|
* transforms the "create table" statement
|
|
* SQL92 allows constraints to be scattered all over, so thumb through
|
|
* the columns and collect all constraints into one place.
|
|
* If there are any implied indices (e.g. UNIQUE or PRIMARY KEY)
|
|
* then expand those into multiple IndexStmt blocks.
|
|
* - thomas 1997-12-02
|
|
*/
|
|
static Query *
|
|
transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
|
|
{
|
|
Query *q;
|
|
int have_pkey = FALSE;
|
|
List *elements;
|
|
Node *element;
|
|
List *columns;
|
|
List *dlist;
|
|
ColumnDef *column;
|
|
List *constraints,
|
|
*clist;
|
|
Constraint *constraint;
|
|
List *keys;
|
|
Ident *key;
|
|
List *ilist;
|
|
IndexStmt *index;
|
|
IndexElem *iparam;
|
|
|
|
q = makeNode(Query);
|
|
q->commandType = CMD_UTILITY;
|
|
|
|
elements = stmt->tableElts;
|
|
constraints = stmt->constraints;
|
|
columns = NIL;
|
|
dlist = NIL;
|
|
|
|
while (elements != NIL)
|
|
{
|
|
element = lfirst(elements);
|
|
switch (nodeTag(element))
|
|
{
|
|
case T_ColumnDef:
|
|
column = (ColumnDef *) element;
|
|
columns = lappend(columns, column);
|
|
if (column->constraints != NIL)
|
|
{
|
|
clist = column->constraints;
|
|
while (clist != NIL)
|
|
{
|
|
constraint = lfirst(clist);
|
|
switch (constraint->contype)
|
|
{
|
|
case CONSTR_NOTNULL:
|
|
if (column->is_not_null)
|
|
elog(ERROR, "CREATE TABLE/NOT NULL already specified"
|
|
" for %s.%s", stmt->relname, column->colname);
|
|
column->is_not_null = TRUE;
|
|
break;
|
|
|
|
case CONSTR_DEFAULT:
|
|
if (column->defval != NULL)
|
|
elog(ERROR, "CREATE TABLE/DEFAULT multiple values specified"
|
|
" for %s.%s", stmt->relname, column->colname);
|
|
column->defval = constraint->def;
|
|
break;
|
|
|
|
case CONSTR_PRIMARY:
|
|
if (constraint->name == NULL)
|
|
constraint->name = makeTableName(stmt->relname, "pkey", NULL);
|
|
if (constraint->keys == NIL)
|
|
constraint->keys = lappend(constraint->keys, column);
|
|
dlist = lappend(dlist, constraint);
|
|
break;
|
|
|
|
case CONSTR_UNIQUE:
|
|
if (constraint->name == NULL)
|
|
constraint->name = makeTableName(stmt->relname, column->colname, "key", NULL);
|
|
if (constraint->keys == NIL)
|
|
constraint->keys = lappend(constraint->keys, column);
|
|
dlist = lappend(dlist, constraint);
|
|
break;
|
|
|
|
case CONSTR_CHECK:
|
|
constraints = lappend(constraints, constraint);
|
|
if (constraint->name == NULL)
|
|
constraint->name = makeTableName(stmt->relname, column->colname, NULL);
|
|
break;
|
|
|
|
default:
|
|
elog(ERROR, "parser: internal error; unrecognized constraint", NULL);
|
|
break;
|
|
}
|
|
clist = lnext(clist);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case T_Constraint:
|
|
constraint = (Constraint *) element;
|
|
switch (constraint->contype)
|
|
{
|
|
case CONSTR_PRIMARY:
|
|
if (constraint->name == NULL)
|
|
constraint->name = makeTableName(stmt->relname, "pkey", NULL);
|
|
dlist = lappend(dlist, constraint);
|
|
break;
|
|
|
|
case CONSTR_UNIQUE:
|
|
#if FALSE
|
|
if (constraint->name == NULL)
|
|
constraint->name = makeTableName(stmt->relname, "key", NULL);
|
|
#endif
|
|
dlist = lappend(dlist, constraint);
|
|
break;
|
|
|
|
case CONSTR_CHECK:
|
|
constraints = lappend(constraints, constraint);
|
|
break;
|
|
|
|
case CONSTR_NOTNULL:
|
|
case CONSTR_DEFAULT:
|
|
elog(ERROR, "parser: internal error; illegal context for constraint", NULL);
|
|
break;
|
|
default:
|
|
elog(ERROR, "parser: internal error; unrecognized constraint", NULL);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
elog(ERROR, "parser: internal error; unrecognized node", NULL);
|
|
}
|
|
|
|
elements = lnext(elements);
|
|
}
|
|
|
|
stmt->tableElts = columns;
|
|
stmt->constraints = constraints;
|
|
|
|
/* Now run through the "deferred list" to complete the query transformation.
|
|
* For PRIMARY KEYs, mark each column as NOT NULL and create an index.
|
|
* For UNIQUE, create an index as for PRIMARY KEYS, but do not insist on NOT NULL.
|
|
*
|
|
* Note that this code does not currently look for all possible redundant cases
|
|
* and either ignore or stop with warning. The create might fail later when
|
|
* names for indices turn out to be redundant, or a user might have specified
|
|
* extra useless indices which might hurt performance. - thomas 1997-12-08
|
|
*/
|
|
ilist = NIL;
|
|
while (dlist != NIL)
|
|
{
|
|
constraint = lfirst(dlist);
|
|
if (nodeTag(constraint) != T_Constraint)
|
|
elog(ERROR, "parser: internal error; unrecognized deferred node", NULL);
|
|
|
|
if (constraint->contype == CONSTR_PRIMARY)
|
|
{
|
|
if (have_pkey)
|
|
elog(ERROR, "CREATE TABLE/PRIMARY KEY multiple primary keys"
|
|
" for table %s are not legal", stmt->relname);
|
|
else
|
|
have_pkey = TRUE;
|
|
}
|
|
else if (constraint->contype != CONSTR_UNIQUE)
|
|
elog(ERROR, "parser: internal error; unrecognized deferred constraint", NULL);
|
|
|
|
index = makeNode(IndexStmt);
|
|
|
|
index->unique = TRUE;
|
|
if (constraint->name != NULL)
|
|
index->idxname = constraint->name;
|
|
else if (constraint->contype == CONSTR_PRIMARY)
|
|
{
|
|
if (have_pkey)
|
|
elog(ERROR, "CREATE TABLE/PRIMARY KEY multiple keys for table %s are not legal", stmt->relname);
|
|
|
|
have_pkey = TRUE;
|
|
index->idxname = makeTableName(stmt->relname, "pkey", NULL);
|
|
}
|
|
else
|
|
index->idxname = NULL;
|
|
|
|
index->relname = stmt->relname;
|
|
index->accessMethod = "btree";
|
|
index->indexParams = NIL;
|
|
index->withClause = NIL;
|
|
index->whereClause = NULL;
|
|
|
|
keys = constraint->keys;
|
|
while (keys != NIL)
|
|
{
|
|
key = lfirst(keys);
|
|
columns = stmt->tableElts;
|
|
column = NULL;
|
|
while (columns != NIL)
|
|
{
|
|
column = lfirst(columns);
|
|
if (strcasecmp(column->colname, key->name) == 0)
|
|
break;
|
|
else
|
|
column = NULL;
|
|
columns = lnext(columns);
|
|
}
|
|
if (column == NULL)
|
|
elog(ERROR, "parser: column '%s' in key does not exist", key->name);
|
|
|
|
if (constraint->contype == CONSTR_PRIMARY)
|
|
column->is_not_null = TRUE;
|
|
iparam = makeNode(IndexElem);
|
|
iparam->name = strcpy(palloc(strlen(column->colname) + 1), column->colname);
|
|
iparam->args = NIL;
|
|
iparam->class = NULL;
|
|
iparam->tname = NULL;
|
|
index->indexParams = lappend(index->indexParams, iparam);
|
|
|
|
if (index->idxname == NULL)
|
|
index->idxname = CreateIndexName(stmt->relname, iparam->name, "key", ilist);
|
|
|
|
keys = lnext(keys);
|
|
}
|
|
|
|
if (index->idxname == NULL)
|
|
elog(ERROR, "parser: unable to construct implicit index for table %s"
|
|
"; name too long", stmt->relname);
|
|
else
|
|
elog(NOTICE, "CREATE TABLE/%s will create implicit index %s for table %s",
|
|
((constraint->contype == CONSTR_PRIMARY) ? "PRIMARY KEY" : "UNIQUE"),
|
|
index->idxname, stmt->relname);
|
|
|
|
ilist = lappend(ilist, index);
|
|
dlist = lnext(dlist);
|
|
}
|
|
|
|
q->utilityStmt = (Node *) stmt;
|
|
extras = ilist;
|
|
|
|
return q;
|
|
}
|
|
|
|
/*
|
|
* transformIndexStmt -
|
|
* transforms the qualification of the index statement
|
|
*/
|
|
static Query *
|
|
transformIndexStmt(ParseState *pstate, IndexStmt *stmt)
|
|
{
|
|
Query *qry;
|
|
|
|
qry = makeNode(Query);
|
|
qry->commandType = CMD_UTILITY;
|
|
|
|
/* take care of the where clause */
|
|
stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
|
|
qry->hasSubLinks = pstate->p_hasSubLinks;
|
|
|
|
stmt->rangetable = pstate->p_rtable;
|
|
|
|
qry->utilityStmt = (Node *) stmt;
|
|
|
|
return qry;
|
|
}
|
|
|
|
/*
|
|
* transformExtendStmt -
|
|
* transform the qualifications of the Extend Index Statement
|
|
*
|
|
*/
|
|
static Query *
|
|
transformExtendStmt(ParseState *pstate, ExtendStmt *stmt)
|
|
{
|
|
Query *qry;
|
|
|
|
qry = makeNode(Query);
|
|
qry->commandType = CMD_UTILITY;
|
|
|
|
/* take care of the where clause */
|
|
stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
|
|
qry->hasSubLinks = pstate->p_hasSubLinks;
|
|
|
|
stmt->rangetable = pstate->p_rtable;
|
|
|
|
qry->utilityStmt = (Node *) stmt;
|
|
return qry;
|
|
}
|
|
|
|
/*
|
|
* transformRuleStmt -
|
|
* transform a Create Rule Statement. The actions is a list of parse
|
|
* trees which is transformed into a list of query trees.
|
|
*/
|
|
static Query *
|
|
transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
|
|
{
|
|
Query *qry;
|
|
List *actions;
|
|
|
|
qry = makeNode(Query);
|
|
qry->commandType = CMD_UTILITY;
|
|
|
|
actions = stmt->actions;
|
|
|
|
/*
|
|
* transform each statment, like parse_analyze()
|
|
*/
|
|
while (actions != NIL)
|
|
{
|
|
|
|
/*
|
|
* NOTE: 'CURRENT' must always have a varno equal to 1 and 'NEW'
|
|
* equal to 2.
|
|
*/
|
|
addRangeTableEntry(pstate, stmt->object->relname, "*CURRENT*",
|
|
FALSE, FALSE);
|
|
addRangeTableEntry(pstate, stmt->object->relname, "*NEW*",
|
|
FALSE, FALSE);
|
|
|
|
pstate->p_last_resno = 1;
|
|
pstate->p_is_rule = true; /* for expand all */
|
|
pstate->p_hasAggs = false;
|
|
|
|
lfirst(actions) = transformStmt(pstate, lfirst(actions));
|
|
actions = lnext(actions);
|
|
}
|
|
|
|
/* take care of the where clause */
|
|
stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
|
|
qry->hasSubLinks = pstate->p_hasSubLinks;
|
|
|
|
qry->utilityStmt = (Node *) stmt;
|
|
return qry;
|
|
}
|
|
|
|
|
|
/*
|
|
* transformSelectStmt -
|
|
* transforms a Select Statement
|
|
*
|
|
*/
|
|
static Query *
|
|
transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
|
|
{
|
|
Query *qry = makeNode(Query);
|
|
|
|
qry->commandType = CMD_SELECT;
|
|
|
|
/* set up a range table */
|
|
makeRangeTable(pstate, NULL, stmt->fromClause);
|
|
|
|
qry->uniqueFlag = stmt->unique;
|
|
|
|
qry->into = stmt->into;
|
|
qry->isPortal = FALSE;
|
|
|
|
qry->targetList = transformTargetList(pstate, stmt->targetList);
|
|
|
|
qry->qual = transformWhereClause(pstate, stmt->whereClause);
|
|
|
|
/* The havingQual has a similar meaning as "qual" in the where statement.
|
|
* So we can easily use the code from the "where clause" with some additional
|
|
* traversals done in .../optimizer/plan/planner.c */
|
|
qry->havingQual = transformWhereClause(pstate, stmt->havingClause);
|
|
|
|
qry->hasSubLinks = pstate->p_hasSubLinks;
|
|
|
|
qry->sortClause = transformSortClause(pstate,
|
|
stmt->sortClause,
|
|
NIL,
|
|
qry->targetList,
|
|
qry->uniqueFlag);
|
|
|
|
qry->groupClause = transformGroupClause(pstate,
|
|
stmt->groupClause,
|
|
qry->targetList);
|
|
qry->rtable = pstate->p_rtable;
|
|
|
|
qry->hasAggs = pstate->p_hasAggs;
|
|
if (pstate->p_hasAggs)
|
|
parseCheckAggregates(pstate, qry);
|
|
|
|
/* The INSERT INTO ... SELECT ... could have a UNION
|
|
* in child, so unionClause may be false
|
|
*/
|
|
qry->unionall = stmt->unionall;
|
|
qry->unionClause = transformUnionClause(stmt->unionClause, qry->targetList);
|
|
|
|
/* If there is a havingQual but there are no aggregates, then there is something wrong
|
|
* with the query because having must contain aggregates in its expressions!
|
|
* Otherwise the query could have been formulated using the where clause. */
|
|
if((qry->hasAggs == false) && (qry->havingQual != NULL))
|
|
{
|
|
elog(ERROR,"This is not a valid having query!");
|
|
return (Query *)NIL;
|
|
}
|
|
|
|
return (Query *) qry;
|
|
}
|
|
|
|
/*
|
|
* transformUpdateStmt -
|
|
* transforms an update statement
|
|
*
|
|
*/
|
|
static Query *
|
|
transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
|
|
{
|
|
Query *qry = makeNode(Query);
|
|
|
|
qry->commandType = CMD_UPDATE;
|
|
pstate->p_is_update = true;
|
|
|
|
/*
|
|
* the FROM clause is non-standard SQL syntax. We used to be able to
|
|
* do this with REPLACE in POSTQUEL so we keep the feature.
|
|
*/
|
|
makeRangeTable(pstate, stmt->relname, stmt->fromClause);
|
|
|
|
qry->targetList = transformTargetList(pstate, stmt->targetList);
|
|
|
|
qry->qual = transformWhereClause(pstate, stmt->whereClause);
|
|
qry->hasSubLinks = pstate->p_hasSubLinks;
|
|
|
|
qry->rtable = pstate->p_rtable;
|
|
|
|
qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
|
|
|
|
qry->hasAggs = pstate->p_hasAggs;
|
|
if (pstate->p_hasAggs)
|
|
parseCheckAggregates(pstate, qry);
|
|
|
|
return (Query *) qry;
|
|
}
|
|
|
|
/*
|
|
* transformCursorStmt -
|
|
* transform a Create Cursor Statement
|
|
*
|
|
*/
|
|
static Query *
|
|
transformCursorStmt(ParseState *pstate, SelectStmt *stmt)
|
|
{
|
|
Query *qry;
|
|
|
|
qry = transformSelectStmt(pstate, stmt);
|
|
|
|
qry->into = stmt->portalname;
|
|
qry->isPortal = TRUE;
|
|
qry->isBinary = stmt->binary; /* internal portal */
|
|
|
|
return qry;
|
|
}
|