mirror of
https://github.com/postgres/postgres.git
synced 2025-04-22 23:02:54 +03:00
Parser Overhaul
This commit is contained in:
parent
1dfe4eaeb1
commit
f59a46a8c8
@ -7,7 +7,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/recipe.c,v 1.2 1996/08/28 07:16:17 scrappy Exp $
|
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/recipe.c,v 1.3 1996/10/30 02:01:45 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -690,10 +690,12 @@ tg_parseTeeNode(TgRecipe *r,
|
|||||||
same Tee. */
|
same Tee. */
|
||||||
if (rt_ind == 0) {
|
if (rt_ind == 0) {
|
||||||
orig->rtable = lappend(orig->rtable,
|
orig->rtable = lappend(orig->rtable,
|
||||||
makeRangeTableEntry(tt,
|
addRangeTableEntry(NULL,
|
||||||
|
tt,
|
||||||
|
tt,
|
||||||
FALSE,
|
FALSE,
|
||||||
NULL,
|
FALSE,
|
||||||
tt));
|
NULL));
|
||||||
rt_ind = length(orig->rtable);
|
rt_ind = length(orig->rtable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/commands/view.c,v 1.1.1.1 1996/07/09 06:21:22 scrappy Exp $
|
* $Header: /cvsroot/pgsql/src/backend/commands/view.c,v 1.2 1996/10/30 02:01:47 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -235,9 +235,11 @@ UpdateRangeTableOfViewParse(char *viewName, Query *viewParse)
|
|||||||
* CURRENT first, then NEW....
|
* CURRENT first, then NEW....
|
||||||
*/
|
*/
|
||||||
rt_entry1 =
|
rt_entry1 =
|
||||||
makeRangeTableEntry((char*)viewName, FALSE, NULL, "*CURRENT*");
|
addRangeTableEntry(NULL, (char*)viewName, "*CURRENT*",
|
||||||
|
FALSE, FALSE, NULL);
|
||||||
rt_entry2 =
|
rt_entry2 =
|
||||||
makeRangeTableEntry((char*)viewName, FALSE, NULL, "*NEW*");
|
addRangeTableEntry(NULL, (char*)viewName, "*NEW*",
|
||||||
|
FALSE, FALSE, NULL);
|
||||||
new_rt = lcons(rt_entry2, old_rt);
|
new_rt = lcons(rt_entry2, old_rt);
|
||||||
new_rt = lcons(rt_entry1, new_rt);
|
new_rt = lcons(rt_entry1, new_rt);
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.7 1996/10/14 03:53:53 momjian Exp $
|
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.8 1996/10/30 02:01:51 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -52,14 +52,11 @@ static Node *transformExpr(ParseState *pstate, Node *expr);
|
|||||||
static void makeRangeTable(ParseState *pstate, char *relname, List *frmList);
|
static void makeRangeTable(ParseState *pstate, char *relname, List *frmList);
|
||||||
static List *expandAllTables(ParseState *pstate);
|
static List *expandAllTables(ParseState *pstate);
|
||||||
static char *figureColname(Node *expr, Node *resval);
|
static char *figureColname(Node *expr, Node *resval);
|
||||||
static List *makeTargetList(ParseState *pstate, List *cols, List *exprs);
|
static List *makeTargetNames(ParseState *pstate, List *cols);
|
||||||
static List *transformTargetList(ParseState *pstate,
|
static List *transformTargetList(ParseState *pstate, List *targetlist);
|
||||||
List *targetlist, bool isInsert,
|
|
||||||
bool isUpdate);
|
|
||||||
static TargetEntry *make_targetlist_expr(ParseState *pstate,
|
static TargetEntry *make_targetlist_expr(ParseState *pstate,
|
||||||
char *name, Node *expr,
|
char *colname, Node *expr,
|
||||||
List *arrayRef,
|
List *arrayRef);
|
||||||
bool ResdomNoIsAttrNo);
|
|
||||||
static Node *transformWhereClause(ParseState *pstate, Node *a_expr);
|
static Node *transformWhereClause(ParseState *pstate, Node *a_expr);
|
||||||
static List *transformGroupClause(ParseState *pstate, List *grouplist);
|
static List *transformGroupClause(ParseState *pstate, List *grouplist);
|
||||||
static List *transformSortClause(ParseState *pstate,
|
static List *transformSortClause(ParseState *pstate,
|
||||||
@ -69,10 +66,10 @@ static List *transformSortClause(ParseState *pstate,
|
|||||||
static void parseFromClause(ParseState *pstate, List *frmList);
|
static void parseFromClause(ParseState *pstate, List *frmList);
|
||||||
static Node *ParseFunc(ParseState *pstate, char *funcname,
|
static Node *ParseFunc(ParseState *pstate, char *funcname,
|
||||||
List *fargs, int *curr_resno);
|
List *fargs, int *curr_resno);
|
||||||
static char *ParseColumnName(ParseState *pstate, char *name, bool *isRelName);
|
|
||||||
static List *setup_tlist(char *attname, Oid relid);
|
static List *setup_tlist(char *attname, Oid relid);
|
||||||
static List *setup_base_tlist(Oid typeid);
|
static List *setup_base_tlist(Oid typeid);
|
||||||
static void make_arguments(int nargs, List *fargs, Oid *input_typeids, Oid *function_typeids);
|
static void make_arguments(int nargs, List *fargs, Oid *input_typeids,
|
||||||
|
Oid *function_typeids);
|
||||||
static void AddAggToParseState(ParseState *pstate, Aggreg *aggreg);
|
static void AddAggToParseState(ParseState *pstate, Aggreg *aggreg);
|
||||||
static void finalizeAggregates(ParseState *pstate, Query *qry);
|
static void finalizeAggregates(ParseState *pstate, Query *qry);
|
||||||
static void parseCheckAggregates(ParseState *pstate, Query *qry);
|
static void parseCheckAggregates(ParseState *pstate, Query *qry);
|
||||||
@ -94,15 +91,19 @@ makeParseState() {
|
|||||||
|
|
||||||
pstate = malloc(sizeof(ParseState));
|
pstate = malloc(sizeof(ParseState));
|
||||||
pstate->p_last_resno = 1;
|
pstate->p_last_resno = 1;
|
||||||
pstate->p_target_resnos = NIL;
|
|
||||||
pstate->p_current_rel = NULL;
|
|
||||||
pstate->p_rtable = NIL;
|
pstate->p_rtable = NIL;
|
||||||
pstate->p_query_is_rule = 0;
|
|
||||||
pstate->p_numAgg = 0;
|
pstate->p_numAgg = 0;
|
||||||
pstate->p_aggs = NIL;
|
pstate->p_aggs = NIL;
|
||||||
|
pstate->p_is_insert = false;
|
||||||
|
pstate->p_insert_columns = NIL;
|
||||||
|
pstate->p_is_update = false;
|
||||||
|
pstate->p_is_rule = false;
|
||||||
|
pstate->p_target_relation = NULL;
|
||||||
|
pstate->p_target_rangetblentry = NULL;
|
||||||
|
|
||||||
return (pstate);
|
return (pstate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* parse_analyze -
|
* parse_analyze -
|
||||||
* analyze a list of parse trees and transform them if necessary.
|
* analyze a list of parse trees and transform them if necessary.
|
||||||
@ -127,8 +128,8 @@ parse_analyze(List *pl)
|
|||||||
pstate = makeParseState();
|
pstate = makeParseState();
|
||||||
result->qtrees[i++] = transformStmt(pstate, lfirst(pl));
|
result->qtrees[i++] = transformStmt(pstate, lfirst(pl));
|
||||||
pl = lnext(pl);
|
pl = lnext(pl);
|
||||||
if (pstate->p_current_rel != NULL)
|
if (pstate->p_target_relation != NULL)
|
||||||
heap_close(pstate->p_current_rel);
|
heap_close(pstate->p_target_relation);
|
||||||
free(pstate);
|
free(pstate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,14 +248,13 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
|
|||||||
/* set up a range table */
|
/* set up a range table */
|
||||||
makeRangeTable(pstate, stmt->relname, NULL);
|
makeRangeTable(pstate, stmt->relname, NULL);
|
||||||
|
|
||||||
/* qry->uniqueFlag = FALSE; */
|
|
||||||
qry->uniqueFlag = NULL;
|
qry->uniqueFlag = NULL;
|
||||||
|
|
||||||
/* fix where clause */
|
/* fix where clause */
|
||||||
qry->qual = transformWhereClause(pstate, stmt->whereClause);
|
qry->qual = transformWhereClause(pstate, stmt->whereClause);
|
||||||
|
|
||||||
qry->rtable = pstate->p_rtable;
|
qry->rtable = pstate->p_rtable;
|
||||||
qry->resultRelation = RangeTablePosn(pstate->p_rtable, stmt->relname);
|
qry->resultRelation = refnameRangeTablePosn(pstate->p_rtable, stmt->relname);
|
||||||
|
|
||||||
/* make sure we don't have aggregates in the where clause */
|
/* make sure we don't have aggregates in the where clause */
|
||||||
if (pstate->p_numAgg > 0)
|
if (pstate->p_numAgg > 0)
|
||||||
@ -274,26 +274,24 @@ transformInsertStmt(ParseState *pstate, AppendStmt *stmt)
|
|||||||
List *targetlist;
|
List *targetlist;
|
||||||
|
|
||||||
qry->commandType = CMD_INSERT;
|
qry->commandType = CMD_INSERT;
|
||||||
|
pstate->p_is_insert = true;
|
||||||
|
|
||||||
/* set up a range table */
|
/* set up a range table */
|
||||||
makeRangeTable(pstate, stmt->relname, stmt->fromClause);
|
makeRangeTable(pstate, stmt->relname, stmt->fromClause);
|
||||||
|
|
||||||
/* qry->uniqueFlag = FALSE; */
|
|
||||||
qry->uniqueFlag = NULL;
|
qry->uniqueFlag = NULL;
|
||||||
|
|
||||||
/* fix the target list */
|
/* fix the target list */
|
||||||
targetlist = makeTargetList(pstate, stmt->cols, stmt->exprs);
|
pstate->p_insert_columns = makeTargetNames(pstate, stmt->cols);
|
||||||
qry->targetList = transformTargetList(pstate,
|
|
||||||
targetlist,
|
qry->targetList = transformTargetList(pstate, stmt->targetList);
|
||||||
TRUE /* is insert */,
|
|
||||||
FALSE /*not update*/);
|
|
||||||
|
|
||||||
/* fix where clause */
|
/* fix where clause */
|
||||||
qry->qual = transformWhereClause(pstate, stmt->whereClause);
|
qry->qual = transformWhereClause(pstate, stmt->whereClause);
|
||||||
|
|
||||||
/* now the range table will not change */
|
/* now the range table will not change */
|
||||||
qry->rtable = pstate->p_rtable;
|
qry->rtable = pstate->p_rtable;
|
||||||
qry->resultRelation = RangeTablePosn(pstate->p_rtable, stmt->relname);
|
qry->resultRelation = refnameRangeTablePosn(pstate->p_rtable, stmt->relname);
|
||||||
|
|
||||||
if (pstate->p_numAgg > 0)
|
if (pstate->p_numAgg > 0)
|
||||||
finalizeAggregates(pstate, qry);
|
finalizeAggregates(pstate, qry);
|
||||||
@ -362,21 +360,17 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
|
|||||||
* transform each statment, like parse_analyze()
|
* transform each statment, like parse_analyze()
|
||||||
*/
|
*/
|
||||||
while (actions != NIL) {
|
while (actions != NIL) {
|
||||||
RangeTblEntry *curEnt, *newEnt;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* NOTE: 'CURRENT' must always have a varno equal to 1 and 'NEW'
|
* NOTE: 'CURRENT' must always have a varno equal to 1 and 'NEW'
|
||||||
* equal to 2.
|
* equal to 2.
|
||||||
*/
|
*/
|
||||||
curEnt = makeRangeTableEntry(stmt->object->relname, FALSE,
|
addRangeTableEntry(pstate, stmt->object->relname, "*CURRENT*",
|
||||||
NULL, "*CURRENT*");
|
FALSE, FALSE, NULL);
|
||||||
newEnt = makeRangeTableEntry(stmt->object->relname, FALSE,
|
addRangeTableEntry(pstate, stmt->object->relname, "*NEW*",
|
||||||
NULL, "*NEW*");
|
FALSE, FALSE, NULL);
|
||||||
pstate->p_rtable = makeList(curEnt, newEnt, -1);
|
|
||||||
|
|
||||||
pstate->p_last_resno = 1;
|
pstate->p_last_resno = 1;
|
||||||
pstate->p_target_resnos = NIL;
|
pstate->p_is_rule = true; /* for expand all */
|
||||||
pstate->p_query_is_rule = 1; /* for expand all */
|
|
||||||
pstate->p_numAgg = 0;
|
pstate->p_numAgg = 0;
|
||||||
pstate->p_aggs = NULL;
|
pstate->p_aggs = NULL;
|
||||||
|
|
||||||
@ -413,10 +407,7 @@ transformSelectStmt(ParseState *pstate, RetrieveStmt *stmt)
|
|||||||
qry->isPortal = FALSE;
|
qry->isPortal = FALSE;
|
||||||
|
|
||||||
/* fix the target list */
|
/* fix the target list */
|
||||||
qry->targetList = transformTargetList(pstate,
|
qry->targetList = transformTargetList(pstate, stmt->targetList);
|
||||||
stmt->targetList,
|
|
||||||
FALSE, /*is insert */
|
|
||||||
FALSE /*not update*/);
|
|
||||||
|
|
||||||
/* fix where clause */
|
/* fix where clause */
|
||||||
qry->qual = transformWhereClause(pstate,stmt->whereClause);
|
qry->qual = transformWhereClause(pstate,stmt->whereClause);
|
||||||
@ -449,7 +440,7 @@ transformUpdateStmt(ParseState *pstate, ReplaceStmt *stmt)
|
|||||||
Query *qry = makeNode(Query);
|
Query *qry = makeNode(Query);
|
||||||
|
|
||||||
qry->commandType = CMD_UPDATE;
|
qry->commandType = CMD_UPDATE;
|
||||||
|
pstate->p_is_update = true;
|
||||||
/*
|
/*
|
||||||
* the FROM clause is non-standard SQL syntax. We used to be able to
|
* 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.
|
* do this with REPLACE in POSTQUEL so we keep the feature.
|
||||||
@ -457,16 +448,13 @@ transformUpdateStmt(ParseState *pstate, ReplaceStmt *stmt)
|
|||||||
makeRangeTable(pstate, stmt->relname, stmt->fromClause);
|
makeRangeTable(pstate, stmt->relname, stmt->fromClause);
|
||||||
|
|
||||||
/* fix the target list */
|
/* fix the target list */
|
||||||
qry->targetList = transformTargetList(pstate,
|
qry->targetList = transformTargetList(pstate, stmt->targetList);
|
||||||
stmt->targetList,
|
|
||||||
FALSE, /* not insert */
|
|
||||||
TRUE /* is update */);
|
|
||||||
|
|
||||||
/* fix where clause */
|
/* fix where clause */
|
||||||
qry->qual = transformWhereClause(pstate,stmt->whereClause);
|
qry->qual = transformWhereClause(pstate,stmt->whereClause);
|
||||||
|
|
||||||
qry->rtable = pstate->p_rtable;
|
qry->rtable = pstate->p_rtable;
|
||||||
qry->resultRelation = RangeTablePosn(pstate->p_rtable, stmt->relname);
|
qry->resultRelation = refnameRangeTablePosn(pstate->p_rtable, stmt->relname);
|
||||||
|
|
||||||
/* make sure we don't have aggregates in the where clause */
|
/* make sure we don't have aggregates in the where clause */
|
||||||
if (pstate->p_numAgg > 0)
|
if (pstate->p_numAgg > 0)
|
||||||
@ -502,10 +490,7 @@ transformCursorStmt(ParseState *pstate, CursorStmt *stmt)
|
|||||||
qry->isBinary = stmt->binary; /* internal portal */
|
qry->isBinary = stmt->binary; /* internal portal */
|
||||||
|
|
||||||
/* fix the target list */
|
/* fix the target list */
|
||||||
qry->targetList = transformTargetList(pstate,
|
qry->targetList = transformTargetList(pstate, stmt->targetList);
|
||||||
stmt->targetList,
|
|
||||||
FALSE, /*is insert */
|
|
||||||
FALSE /*not update*/);
|
|
||||||
|
|
||||||
/* fix where clause */
|
/* fix where clause */
|
||||||
qry->qual = transformWhereClause(pstate,stmt->whereClause);
|
qry->qual = transformWhereClause(pstate,stmt->whereClause);
|
||||||
@ -700,33 +685,23 @@ transformExpr(ParseState *pstate, Node *expr)
|
|||||||
}
|
}
|
||||||
case T_Ident: {
|
case T_Ident: {
|
||||||
Ident *ident = (Ident*)expr;
|
Ident *ident = (Ident*)expr;
|
||||||
bool isrel;
|
RangeTblEntry *rte;
|
||||||
char *reln= ParseColumnName(pstate,ident->name, &isrel);
|
|
||||||
|
|
||||||
/* could be a column name or a relation_name */
|
/* could be a column name or a relation_name */
|
||||||
if (reln==NULL) {
|
if (refnameRangeTableEntry(pstate->p_rtable, ident->name) != NULL) {
|
||||||
/*
|
|
||||||
* may be a relation_name
|
|
||||||
*
|
|
||||||
* ??? in fact, every ident left after transfromExpr() is called
|
|
||||||
* will be assumed to be a relation.
|
|
||||||
*/
|
|
||||||
if (isrel) {
|
|
||||||
ident->isRel = TRUE;
|
ident->isRel = TRUE;
|
||||||
result = (Node*)ident;
|
result = (Node*)ident;
|
||||||
} else {
|
|
||||||
elog(WARN, "attribute \"%s\" not found", ident->name);
|
|
||||||
}
|
}
|
||||||
}else {
|
else if ((rte = colnameRangeTableEntry(pstate, ident->name)) != NULL)
|
||||||
|
{
|
||||||
Attr *att = makeNode(Attr);
|
Attr *att = makeNode(Attr);
|
||||||
att->relname = reln;
|
|
||||||
|
att->relname = rte->refname;
|
||||||
att->attrs = lcons(makeString(ident->name), NIL);
|
att->attrs = lcons(makeString(ident->name), NIL);
|
||||||
/*
|
|
||||||
* a column name
|
|
||||||
*/
|
|
||||||
result =
|
result =
|
||||||
(Node*)handleNestedDots(pstate, att, &pstate->p_last_resno);
|
(Node*)handleNestedDots(pstate, att, &pstate->p_last_resno);
|
||||||
}
|
} else
|
||||||
|
elog(WARN, "attribute \"%s\" not found", ident->name);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case T_FuncCall: {
|
case T_FuncCall: {
|
||||||
@ -734,9 +709,8 @@ transformExpr(ParseState *pstate, Node *expr)
|
|||||||
List *args;
|
List *args;
|
||||||
|
|
||||||
/* transform the list of arguments */
|
/* transform the list of arguments */
|
||||||
foreach(args, fn->args) {
|
foreach(args, fn->args)
|
||||||
lfirst(args) = transformExpr(pstate, (Node*)lfirst(args));
|
lfirst(args) = transformExpr(pstate, (Node*)lfirst(args));
|
||||||
}
|
|
||||||
result = ParseFunc(pstate,
|
result = ParseFunc(pstate,
|
||||||
fn->funcname, fn->args, &pstate->p_last_resno);
|
fn->funcname, fn->args, &pstate->p_last_resno);
|
||||||
break;
|
break;
|
||||||
@ -769,30 +743,21 @@ transformExpr(ParseState *pstate, Node *expr)
|
|||||||
static void
|
static void
|
||||||
parseFromClause(ParseState *pstate, List *frmList)
|
parseFromClause(ParseState *pstate, List *frmList)
|
||||||
{
|
{
|
||||||
List *fl= frmList;
|
List *fl;
|
||||||
|
|
||||||
while(fl!=NIL) {
|
foreach(fl, frmList)
|
||||||
|
{
|
||||||
RangeVar *r = lfirst(fl);
|
RangeVar *r = lfirst(fl);
|
||||||
RelExpr *baserel = r->relExpr;
|
RelExpr *baserel = r->relExpr;
|
||||||
RangeTblEntry *ent;
|
|
||||||
char *relname = baserel->relname;
|
char *relname = baserel->relname;
|
||||||
char *refname = r->name;
|
char *refname = r->name;
|
||||||
|
RangeTblEntry *rte;
|
||||||
|
|
||||||
if (refname==NULL) {
|
if (refname==NULL)
|
||||||
refname = relname;
|
refname = relname;
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
* check whether refname exists already
|
|
||||||
*/
|
|
||||||
if (RangeTablePosn(pstate->p_rtable, refname) != 0)
|
|
||||||
elog(WARN, "parser: range variable \"%s\" duplicated",
|
|
||||||
refname);
|
|
||||||
}
|
|
||||||
|
|
||||||
ent = makeRangeTableEntry(relname, baserel->inh,
|
|
||||||
baserel->timeRange, refname);
|
|
||||||
/*
|
/*
|
||||||
* marks this entry to indicate it comes from the from clause. In
|
* marks this entry to indicate it comes from the FROM clause. In
|
||||||
* SQL, the target list can only refer to range variables specified
|
* SQL, the target list can only refer to range variables specified
|
||||||
* in the from clause but we follow the more powerful POSTQUEL
|
* in the from clause but we follow the more powerful POSTQUEL
|
||||||
* semantics and automatically generate the range variable if not
|
* semantics and automatically generate the range variable if not
|
||||||
@ -802,10 +767,8 @@ parseFromClause(ParseState *pstate, List *frmList)
|
|||||||
* eg. select * from foo f where f.x = 1; will generate wrong answer
|
* eg. select * from foo f where f.x = 1; will generate wrong answer
|
||||||
* if we expand * to foo.x.
|
* if we expand * to foo.x.
|
||||||
*/
|
*/
|
||||||
ent->inFromCl = true;
|
rte = addRangeTableEntry(pstate, relname, refname, baserel->inh, TRUE,
|
||||||
|
baserel->timeRange);
|
||||||
pstate->p_rtable = lappend(pstate->p_rtable, ent);
|
|
||||||
fl= lnext(fl);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -817,25 +780,23 @@ parseFromClause(ParseState *pstate, List *frmList)
|
|||||||
static void
|
static void
|
||||||
makeRangeTable(ParseState *pstate, char *relname, List *frmList)
|
makeRangeTable(ParseState *pstate, char *relname, List *frmList)
|
||||||
{
|
{
|
||||||
int x;
|
RangeTblEntry *rte;
|
||||||
|
|
||||||
parseFromClause(pstate, frmList);
|
parseFromClause(pstate, frmList);
|
||||||
|
|
||||||
if (relname == NULL)
|
if (relname == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (RangeTablePosn(pstate->p_rtable, relname) < 1) {
|
if (refnameRangeTablePosn(pstate->p_rtable, relname) < 1)
|
||||||
RangeTblEntry *ent;
|
rte = addRangeTableEntry(pstate, relname, relname, FALSE, FALSE, NULL);
|
||||||
|
else
|
||||||
|
rte = refnameRangeTableEntry(pstate->p_rtable, relname);
|
||||||
|
|
||||||
ent = makeRangeTableEntry(relname, FALSE, NULL, relname);
|
pstate->p_target_rangetblentry = rte;
|
||||||
pstate->p_rtable = lappend(pstate->p_rtable, ent);
|
Assert(pstate->p_target_relation == NULL);
|
||||||
}
|
pstate->p_target_relation = heap_open(rte->relid);
|
||||||
x = RangeTablePosn(pstate->p_rtable, relname);
|
Assert(pstate->p_target_relation != NULL);
|
||||||
if (pstate->p_current_rel != NULL)
|
/* will close relation later */
|
||||||
heap_close(pstate->p_current_rel);
|
|
||||||
pstate->p_current_rel = heap_openr(VarnoGetRelname(pstate,x));
|
|
||||||
if (pstate->p_current_rel == NULL)
|
|
||||||
elog(WARN,"invalid relation name");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -897,7 +858,7 @@ expandAllTables(ParseState *pstate)
|
|||||||
List *rt, *rtable;
|
List *rt, *rtable;
|
||||||
|
|
||||||
rtable = pstate->p_rtable;
|
rtable = pstate->p_rtable;
|
||||||
if (pstate->p_query_is_rule) {
|
if (pstate->p_is_rule) {
|
||||||
/*
|
/*
|
||||||
* skip first two entries, "*new*" and "*current*"
|
* skip first two entries, "*new*" and "*current*"
|
||||||
*/
|
*/
|
||||||
@ -927,16 +888,16 @@ expandAllTables(ParseState *pstate)
|
|||||||
|
|
||||||
foreach(rt, legit_rtable) {
|
foreach(rt, legit_rtable) {
|
||||||
RangeTblEntry *rte = lfirst(rt);
|
RangeTblEntry *rte = lfirst(rt);
|
||||||
char *rt_name= rte->refname; /* use refname here so that we
|
|
||||||
refer to the right entry */
|
|
||||||
List *temp = target;
|
List *temp = target;
|
||||||
|
|
||||||
if(temp == NIL )
|
if(temp == NIL )
|
||||||
target = expandAll(pstate, rt_name, &pstate->p_last_resno);
|
target = expandAll(pstate, rte->relname, rte->refname,
|
||||||
|
&pstate->p_last_resno);
|
||||||
else {
|
else {
|
||||||
while (temp != NIL && lnext(temp) != NIL)
|
while (temp != NIL && lnext(temp) != NIL)
|
||||||
temp = lnext(temp);
|
temp = lnext(temp);
|
||||||
lnext(temp) = expandAll(pstate, rt_name, &pstate->p_last_resno);
|
lnext(temp) = expandAll(pstate, rte->relname, rte->refname,
|
||||||
|
&pstate->p_last_resno);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return target;
|
return target;
|
||||||
@ -976,96 +937,46 @@ figureColname(Node *expr, Node *resval)
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* makeTargetList -
|
* makeTargetNames -
|
||||||
* turn a list of column names and expressions (in the same order) into
|
* generate a list of column names if not supplied or
|
||||||
* a target list (used exclusively for inserts)
|
* test supplied column names to make sure they are in target table
|
||||||
|
* (used exclusively for inserts)
|
||||||
*/
|
*/
|
||||||
static List *
|
static List *
|
||||||
makeTargetList(ParseState *pstate, List *cols, List *exprs)
|
makeTargetNames(ParseState *pstate, List *cols)
|
||||||
{
|
{
|
||||||
List *tlist, *tl=NULL;
|
List *tl=NULL;
|
||||||
if (cols != NIL) {
|
|
||||||
/* has to transform colElem too (opt_indirection can be exprs) */
|
|
||||||
while(cols!=NIL) {
|
|
||||||
ResTarget *res = makeNode(ResTarget);
|
|
||||||
Ident *id = lfirst(cols);
|
|
||||||
/* Id opt_indirection */
|
|
||||||
res->name = id->name;
|
|
||||||
res->indirection = id->indirection;
|
|
||||||
if (exprs == NIL) {
|
|
||||||
elog(WARN, "insert: number of expressions less than columns");
|
|
||||||
}else {
|
|
||||||
res->val = (Node *)lfirst(exprs);
|
|
||||||
}
|
|
||||||
if (tl==NIL) {
|
|
||||||
tlist = tl = lcons(res, NIL);
|
|
||||||
}else {
|
|
||||||
lnext(tl) = lcons(res,NIL);
|
|
||||||
tl = lnext(tl);
|
|
||||||
}
|
|
||||||
cols = lnext(cols);
|
|
||||||
exprs = lnext(exprs);
|
|
||||||
}
|
|
||||||
if (cols != NIL) {
|
|
||||||
elog(WARN, "insert: number of columns more than expressions");
|
|
||||||
}
|
|
||||||
}else {
|
|
||||||
bool has_star = false;
|
|
||||||
|
|
||||||
if (exprs==NIL)
|
/* Generate ResTarget if not supplied */
|
||||||
return NIL;
|
|
||||||
if (IsA(lfirst(exprs),Attr)) {
|
|
||||||
Attr *att = lfirst(exprs);
|
|
||||||
|
|
||||||
if ((att->relname!=NULL && !strcmp(att->relname,"*")) ||
|
if (cols == NIL) {
|
||||||
(att->attrs!=NIL && !strcmp(strVal(lfirst(att->attrs)),"*")))
|
|
||||||
has_star = true;
|
|
||||||
}
|
|
||||||
if (has_star) {
|
|
||||||
/*
|
|
||||||
* right now, these better be 'relname.*' or '*' (this can happen
|
|
||||||
* in eg. insert into tenk2 values (tenk1.*); or
|
|
||||||
* insert into tenk2 select * from tenk1;
|
|
||||||
*/
|
|
||||||
while(exprs!=NIL) {
|
|
||||||
ResTarget *res = makeNode(ResTarget);
|
|
||||||
res->name = NULL;
|
|
||||||
res->indirection = NULL;
|
|
||||||
res->val = (Node *)lfirst(exprs);
|
|
||||||
if (tl==NIL) {
|
|
||||||
tlist = tl = lcons(res, NIL);
|
|
||||||
}else {
|
|
||||||
lnext(tl) = lcons(res,NIL);
|
|
||||||
tl = lnext(tl);
|
|
||||||
}
|
|
||||||
exprs = lnext(exprs);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Relation insertRel = pstate->p_current_rel;
|
|
||||||
int numcol;
|
int numcol;
|
||||||
int i;
|
int i;
|
||||||
AttributeTupleForm *attr = insertRel->rd_att->attrs;
|
AttributeTupleForm *attr = pstate->p_target_relation->rd_att->attrs;
|
||||||
|
|
||||||
numcol = Min(length(exprs), insertRel->rd_rel->relnatts);
|
numcol = pstate->p_target_relation->rd_rel->relnatts;
|
||||||
for(i=0; i < numcol; i++) {
|
for(i=0; i < numcol; i++) {
|
||||||
ResTarget *res = makeNode(ResTarget);
|
Ident *id = makeNode(Ident);
|
||||||
|
|
||||||
res->name = palloc(NAMEDATALEN+1);
|
id->name = palloc(NAMEDATALEN+1);
|
||||||
strncpy(res->name, attr[i]->attname.data, NAMEDATALEN);
|
strncpy(id->name, attr[i]->attname.data, NAMEDATALEN);
|
||||||
res->name[NAMEDATALEN]='\0';
|
id->name[NAMEDATALEN]='\0';
|
||||||
res->indirection = NULL;
|
id->indirection = NIL;
|
||||||
res->val = (Node *)lfirst(exprs);
|
id->isRel = false;
|
||||||
if (tl==NIL) {
|
if (tl == NIL)
|
||||||
tlist = tl = lcons(res, NIL);
|
cols = tl = lcons(id, NIL);
|
||||||
}else {
|
else {
|
||||||
lnext(tl) = lcons(res,NIL);
|
lnext(tl) = lcons(id,NIL);
|
||||||
tl = lnext(tl);
|
tl = lnext(tl);
|
||||||
}
|
}
|
||||||
exprs = lnext(exprs);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
return tlist;
|
foreach(tl, cols)
|
||||||
|
/* elog on failure */
|
||||||
|
(void)varattno(pstate->p_target_relation,((Ident *)lfirst(tl))->name);
|
||||||
|
|
||||||
|
return cols;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1073,13 +984,10 @@ makeTargetList(ParseState *pstate, List *cols, List *exprs)
|
|||||||
* turns a list of ResTarget's into a list of TargetEntry's
|
* turns a list of ResTarget's into a list of TargetEntry's
|
||||||
*/
|
*/
|
||||||
static List *
|
static List *
|
||||||
transformTargetList(ParseState *pstate,
|
transformTargetList(ParseState *pstate, List *targetlist)
|
||||||
List *targetlist,
|
|
||||||
bool isInsert,
|
|
||||||
bool isUpdate)
|
|
||||||
{
|
{
|
||||||
List *p_target= NIL;
|
List *p_target= NIL;
|
||||||
List *temp = NIL;
|
List *tail_p_target = NIL;
|
||||||
|
|
||||||
while(targetlist != NIL) {
|
while(targetlist != NIL) {
|
||||||
ResTarget *res= (ResTarget *)lfirst(targetlist);
|
ResTarget *res= (ResTarget *)lfirst(targetlist);
|
||||||
@ -1094,6 +1002,7 @@ transformTargetList(ParseState *pstate,
|
|||||||
char *resname;
|
char *resname;
|
||||||
|
|
||||||
identname = ((Ident*)res->val)->name;
|
identname = ((Ident*)res->val)->name;
|
||||||
|
handleTargetColname(pstate, &res->name, NULL, res->name);
|
||||||
expr = transformExpr(pstate, (Node*)res->val);
|
expr = transformExpr(pstate, (Node*)res->val);
|
||||||
type_id = exprType(expr);
|
type_id = exprType(expr);
|
||||||
type_len = tlen(get_id_type(type_id));
|
type_len = tlen(get_id_type(type_id));
|
||||||
@ -1115,11 +1024,9 @@ transformTargetList(ParseState *pstate,
|
|||||||
case T_A_Expr: {
|
case T_A_Expr: {
|
||||||
Node *expr = transformExpr(pstate, (Node *)res->val);
|
Node *expr = transformExpr(pstate, (Node *)res->val);
|
||||||
|
|
||||||
if (isInsert && res->name==NULL)
|
handleTargetColname(pstate, &res->name, NULL, NULL);
|
||||||
elog(WARN, "Sorry, have to specify the column list");
|
|
||||||
|
|
||||||
/* note indirection has not been transformed */
|
/* note indirection has not been transformed */
|
||||||
if (isInsert && res->indirection!=NIL) {
|
if (pstate->p_is_insert && res->indirection!=NIL) {
|
||||||
/* this is an array assignment */
|
/* this is an array assignment */
|
||||||
char *val;
|
char *val;
|
||||||
char *str, *save_str;
|
char *str, *save_str;
|
||||||
@ -1160,7 +1067,7 @@ transformTargetList(ParseState *pstate,
|
|||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
sprintf(str, "=%s", val);
|
sprintf(str, "=%s", val);
|
||||||
rd = pstate->p_current_rel;
|
rd = pstate->p_target_relation;
|
||||||
Assert(rd != NULL);
|
Assert(rd != NULL);
|
||||||
resdomno = varattno(rd, res->name);
|
resdomno = varattno(rd, res->name);
|
||||||
ndims = att_attnelems(rd, resdomno);
|
ndims = att_attnelems(rd, resdomno);
|
||||||
@ -1171,8 +1078,7 @@ transformTargetList(ParseState *pstate,
|
|||||||
constval->val.str = save_str;
|
constval->val.str = save_str;
|
||||||
tent = make_targetlist_expr(pstate, res->name,
|
tent = make_targetlist_expr(pstate, res->name,
|
||||||
(Node*)make_const(constval),
|
(Node*)make_const(constval),
|
||||||
NULL,
|
NULL);
|
||||||
(isInsert||isUpdate));
|
|
||||||
pfree(save_str);
|
pfree(save_str);
|
||||||
} else {
|
} else {
|
||||||
char *colname= res->name;
|
char *colname= res->name;
|
||||||
@ -1192,9 +1098,9 @@ transformTargetList(ParseState *pstate,
|
|||||||
ilist = lnext(ilist);
|
ilist = lnext(ilist);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tent = make_targetlist_expr(pstate, colname, expr,
|
res->name = colname;
|
||||||
res->indirection,
|
tent = make_targetlist_expr(pstate, res->name, expr,
|
||||||
(isInsert||isUpdate));
|
res->indirection);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1208,7 +1114,6 @@ transformTargetList(ParseState *pstate,
|
|||||||
Resdom *resnode;
|
Resdom *resnode;
|
||||||
List *attrs = att->attrs;
|
List *attrs = att->attrs;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Target item is a single '*', expand all tables
|
* Target item is a single '*', expand all tables
|
||||||
* (eg. SELECT * FROM emp)
|
* (eg. SELECT * FROM emp)
|
||||||
@ -1231,19 +1136,20 @@ transformTargetList(ParseState *pstate,
|
|||||||
*/
|
*/
|
||||||
attrname = strVal(lfirst(att->attrs));
|
attrname = strVal(lfirst(att->attrs));
|
||||||
if (att->attrs!=NIL && !strcmp(attrname,"*")) {
|
if (att->attrs!=NIL && !strcmp(attrname,"*")) {
|
||||||
/* temp is the target list we're building in the while
|
/* tail_p_target is the target list we're building in the while
|
||||||
* loop. Make sure we fix it after appending more nodes.
|
* loop. Make sure we fix it after appending more nodes.
|
||||||
*/
|
*/
|
||||||
if (temp == NIL) {
|
if (tail_p_target == NIL) {
|
||||||
p_target = temp =
|
p_target = tail_p_target = expandAll(pstate, att->relname,
|
||||||
expandAll(pstate, att->relname, &pstate->p_last_resno);
|
att->relname, &pstate->p_last_resno);
|
||||||
} else {
|
} else {
|
||||||
lnext(temp) =
|
lnext(tail_p_target) =
|
||||||
expandAll(pstate, att->relname, &pstate->p_last_resno);
|
expandAll(pstate, att->relname, att->relname,
|
||||||
|
&pstate->p_last_resno);
|
||||||
}
|
}
|
||||||
while(lnext(temp)!=NIL)
|
while(lnext(tail_p_target)!=NIL)
|
||||||
temp = lnext(temp); /* make sure we point to the last
|
/* make sure we point to the last target entry */
|
||||||
target entry */
|
tail_p_target = lnext(tail_p_target);
|
||||||
/*
|
/*
|
||||||
* skip the rest of the while loop
|
* skip the rest of the while loop
|
||||||
*/
|
*/
|
||||||
@ -1256,6 +1162,7 @@ transformTargetList(ParseState *pstate,
|
|||||||
* Target item is fully specified: ie. relation.attribute
|
* Target item is fully specified: ie. relation.attribute
|
||||||
*/
|
*/
|
||||||
result = handleNestedDots(pstate, att, &pstate->p_last_resno);
|
result = handleNestedDots(pstate, att, &pstate->p_last_resno);
|
||||||
|
handleTargetColname(pstate, &res->name, att->relname, attrname);
|
||||||
if (att->indirection != NIL) {
|
if (att->indirection != NIL) {
|
||||||
List *ilist = att->indirection;
|
List *ilist = att->indirection;
|
||||||
while (ilist!=NIL) {
|
while (ilist!=NIL) {
|
||||||
@ -1268,6 +1175,7 @@ transformTargetList(ParseState *pstate,
|
|||||||
}
|
}
|
||||||
type_id = exprType(result);
|
type_id = exprType(result);
|
||||||
type_len = tlen(get_id_type(type_id));
|
type_len = tlen(get_id_type(type_id));
|
||||||
|
/* move to last entry */
|
||||||
while(lnext(attrs)!=NIL)
|
while(lnext(attrs)!=NIL)
|
||||||
attrs=lnext(attrs);
|
attrs=lnext(attrs);
|
||||||
resname = (res->name) ? res->name : strVal(lfirst(attrs));
|
resname = (res->name) ? res->name : strVal(lfirst(attrs));
|
||||||
@ -1290,29 +1198,29 @@ transformTargetList(ParseState *pstate,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (p_target == NIL) {
|
if (p_target == NIL) {
|
||||||
p_target = temp = lcons(tent, NIL);
|
p_target = tail_p_target = lcons(tent, NIL);
|
||||||
}else {
|
}else {
|
||||||
lnext(temp) = lcons(tent, NIL);
|
lnext(tail_p_target) = lcons(tent, NIL);
|
||||||
temp = lnext(temp);
|
tail_p_target = lnext(tail_p_target);
|
||||||
}
|
}
|
||||||
targetlist = lnext(targetlist);
|
targetlist = lnext(targetlist);
|
||||||
}
|
}
|
||||||
|
|
||||||
return p_target;
|
return p_target;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* make_targetlist_expr -
|
* make_targetlist_expr -
|
||||||
* make a TargetEntry
|
* make a TargetEntry from an expression
|
||||||
*
|
*
|
||||||
* arrayRef is a list of transformed A_Indices
|
* arrayRef is a list of transformed A_Indices
|
||||||
*/
|
*/
|
||||||
static TargetEntry *
|
static TargetEntry *
|
||||||
make_targetlist_expr(ParseState *pstate,
|
make_targetlist_expr(ParseState *pstate,
|
||||||
char *name,
|
char *colname,
|
||||||
Node *expr,
|
Node *expr,
|
||||||
List *arrayRef,
|
List *arrayRef)
|
||||||
bool ResdomNoIsAttrNo)
|
|
||||||
{
|
{
|
||||||
int type_id, type_len, attrtype, attrlen;
|
int type_id, type_len, attrtype, attrlen;
|
||||||
int resdomno;
|
int resdomno;
|
||||||
@ -1333,16 +1241,17 @@ make_targetlist_expr(ParseState *pstate,
|
|||||||
type_len = tlen(get_id_type(type_id));
|
type_len = tlen(get_id_type(type_id));
|
||||||
|
|
||||||
/* I have no idea what the following does! */
|
/* I have no idea what the following does! */
|
||||||
if (ResdomNoIsAttrNo) {
|
/* It appears to process target columns that will be receiving results */
|
||||||
|
if (pstate->p_is_insert||pstate->p_is_update) {
|
||||||
/*
|
/*
|
||||||
* append or replace query --
|
* append or replace query --
|
||||||
* append, replace work only on one relation,
|
* append, replace work only on one relation,
|
||||||
* so multiple occurence of same resdomno is bogus
|
* so multiple occurence of same resdomno is bogus
|
||||||
*/
|
*/
|
||||||
rd = pstate->p_current_rel;
|
rd = pstate->p_target_relation;
|
||||||
Assert(rd != NULL);
|
Assert(rd != NULL);
|
||||||
resdomno = varattno(rd,name);
|
resdomno = varattno(rd,colname);
|
||||||
attrisset = varisset(rd,name);
|
attrisset = varisset(rd,colname);
|
||||||
attrtype = att_typeid(rd,resdomno);
|
attrtype = att_typeid(rd,resdomno);
|
||||||
if ((arrayRef != NIL) && (lfirst(arrayRef) == NIL))
|
if ((arrayRef != NIL) && (lfirst(arrayRef) == NIL))
|
||||||
attrtype = GetArrayElementType(attrtype);
|
attrtype = GetArrayElementType(attrtype);
|
||||||
@ -1388,13 +1297,14 @@ make_targetlist_expr(ParseState *pstate,
|
|||||||
lfirst(expr) = lispInteger (FLOAT4OID);
|
lfirst(expr) = lispInteger (FLOAT4OID);
|
||||||
else
|
else
|
||||||
elog(WARN, "unequal type in tlist : %s \n",
|
elog(WARN, "unequal type in tlist : %s \n",
|
||||||
name));
|
colname));
|
||||||
}
|
}
|
||||||
|
|
||||||
Input_is_string = false;
|
Input_is_string = false;
|
||||||
Input_is_integer = false;
|
Input_is_integer = false;
|
||||||
Typecast_ok = true;
|
Typecast_ok = true;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (attrtype != type_id) {
|
if (attrtype != type_id) {
|
||||||
if (IsA(expr,Const)) {
|
if (IsA(expr,Const)) {
|
||||||
/* try to cast the constant */
|
/* try to cast the constant */
|
||||||
@ -1415,18 +1325,12 @@ make_targetlist_expr(ParseState *pstate,
|
|||||||
} else {
|
} else {
|
||||||
/* currently, we can't handle casting of expressions */
|
/* currently, we can't handle casting of expressions */
|
||||||
elog(WARN, "parser: attribute '%s' is of type '%.*s' but expression is of type '%.*s'",
|
elog(WARN, "parser: attribute '%s' is of type '%.*s' but expression is of type '%.*s'",
|
||||||
name,
|
colname,
|
||||||
NAMEDATALEN, get_id_typname(attrtype),
|
NAMEDATALEN, get_id_typname(attrtype),
|
||||||
NAMEDATALEN, get_id_typname(type_id));
|
NAMEDATALEN, get_id_typname(type_id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (intMember(resdomno, pstate->p_target_resnos)) {
|
|
||||||
elog(WARN,"two or more occurrences of same attr");
|
|
||||||
} else {
|
|
||||||
pstate->p_target_resnos = lconsi(resdomno,
|
|
||||||
pstate->p_target_resnos);
|
|
||||||
}
|
|
||||||
if (arrayRef != NIL) {
|
if (arrayRef != NIL) {
|
||||||
Expr *target_expr;
|
Expr *target_expr;
|
||||||
Attr *att = makeNode(Attr);
|
Attr *att = makeNode(Attr);
|
||||||
@ -1435,7 +1339,7 @@ make_targetlist_expr(ParseState *pstate,
|
|||||||
List *lowerIndexpr = NIL;
|
List *lowerIndexpr = NIL;
|
||||||
|
|
||||||
att->relname = pstrdup(RelationGetRelationName(rd)->data);
|
att->relname = pstrdup(RelationGetRelationName(rd)->data);
|
||||||
att->attrs = lcons(makeString(name), NIL);
|
att->attrs = lcons(makeString(colname), NIL);
|
||||||
target_expr = (Expr*)handleNestedDots(pstate, att,
|
target_expr = (Expr*)handleNestedDots(pstate, att,
|
||||||
&pstate->p_last_resno);
|
&pstate->p_last_resno);
|
||||||
while(ar!=NIL) {
|
while(ar!=NIL) {
|
||||||
@ -1471,7 +1375,7 @@ make_targetlist_expr(ParseState *pstate,
|
|||||||
resnode = makeResdom((AttrNumber)resdomno,
|
resnode = makeResdom((AttrNumber)resdomno,
|
||||||
(Oid) attrtype,
|
(Oid) attrtype,
|
||||||
(Size) attrlen,
|
(Size) attrlen,
|
||||||
name,
|
colname,
|
||||||
(Index)0,
|
(Index)0,
|
||||||
(Oid)0,
|
(Oid)0,
|
||||||
0);
|
0);
|
||||||
@ -1524,14 +1428,13 @@ transformWhereClause(ParseState *pstate, Node *a_expr)
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
static Resdom *
|
static Resdom *
|
||||||
find_tl_elt(ParseState *pstate, char *range, char *varname, List *tlist)
|
find_tl_elt(ParseState *pstate, char *refname, char *colname, List *tlist)
|
||||||
{
|
{
|
||||||
List *i;
|
List *i;
|
||||||
int real_rtable_pos;
|
int real_rtable_pos;
|
||||||
|
|
||||||
if(range) {
|
if(refname)
|
||||||
real_rtable_pos = RangeTablePosn(pstate->p_rtable, range);
|
real_rtable_pos = refnameRangeTablePosn(pstate->p_rtable, refname);
|
||||||
}
|
|
||||||
|
|
||||||
foreach(i, tlist) {
|
foreach(i, tlist) {
|
||||||
TargetEntry *target = (TargetEntry *)lfirst(i);
|
TargetEntry *target = (TargetEntry *)lfirst(i);
|
||||||
@ -1540,8 +1443,8 @@ find_tl_elt(ParseState *pstate, char *range, char *varname, List *tlist)
|
|||||||
char *resname = resnode->resname;
|
char *resname = resnode->resname;
|
||||||
int test_rtable_pos = var->varno;
|
int test_rtable_pos = var->varno;
|
||||||
|
|
||||||
if (!strcmp(resname, varname)) {
|
if (!strcmp(resname, colname)) {
|
||||||
if(range) {
|
if(refname) {
|
||||||
if(real_rtable_pos == test_rtable_pos) {
|
if(real_rtable_pos == test_rtable_pos) {
|
||||||
return (resnode);
|
return (resnode);
|
||||||
}
|
}
|
||||||
@ -1979,7 +1882,8 @@ ParseFunc(ParseState *pstate, char *funcname, List *fargs, int *curr_resno)
|
|||||||
Oid funcid = (Oid)0;
|
Oid funcid = (Oid)0;
|
||||||
List *i = NIL;
|
List *i = NIL;
|
||||||
Node *first_arg= NULL;
|
Node *first_arg= NULL;
|
||||||
char *relname, *oldname;
|
char *relname;
|
||||||
|
char *refname;
|
||||||
Relation rd;
|
Relation rd;
|
||||||
Oid relid;
|
Oid relid;
|
||||||
int nargs;
|
int nargs;
|
||||||
@ -2005,28 +1909,23 @@ ParseFunc(ParseState *pstate, char *funcname, List *fargs, int *curr_resno)
|
|||||||
** type, then the function could be a projection.
|
** type, then the function could be a projection.
|
||||||
*/
|
*/
|
||||||
if (length(fargs) == 1) {
|
if (length(fargs) == 1) {
|
||||||
|
|
||||||
if (nodeTag(first_arg)==T_Ident && ((Ident*)first_arg)->isRel) {
|
if (nodeTag(first_arg)==T_Ident && ((Ident*)first_arg)->isRel) {
|
||||||
|
RangeTblEntry *rte;
|
||||||
Ident *ident = (Ident*)first_arg;
|
Ident *ident = (Ident*)first_arg;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* first arg is a relation. This could be a projection.
|
* first arg is a relation. This could be a projection.
|
||||||
*/
|
*/
|
||||||
relname = ident->name;
|
refname = ident->name;
|
||||||
if (RangeTablePosn(pstate->p_rtable, relname)== 0) {
|
|
||||||
RangeTblEntry *ent;
|
rte = refnameRangeTableEntry(pstate->p_rtable, refname);
|
||||||
|
if (rte == NULL)
|
||||||
|
rte = addRangeTableEntry(pstate, refname, refname, FALSE, FALSE,NULL);
|
||||||
|
|
||||||
|
relname = rte->relname;
|
||||||
|
relid = rte->relid;
|
||||||
|
|
||||||
ent =
|
|
||||||
makeRangeTableEntry(relname,
|
|
||||||
FALSE, NULL, relname);
|
|
||||||
pstate->p_rtable = lappend(pstate->p_rtable, ent);
|
|
||||||
}
|
|
||||||
oldname = relname;
|
|
||||||
relname = VarnoGetRelname(pstate,
|
|
||||||
RangeTablePosn(pstate->p_rtable,
|
|
||||||
oldname));
|
|
||||||
rd = heap_openr(relname);
|
|
||||||
relid = RelationGetRelationId(rd);
|
|
||||||
heap_close(rd);
|
|
||||||
/* If the attr isn't a set, just make a var for it. If
|
/* If the attr isn't a set, just make a var for it. If
|
||||||
* it is a set, treat it like a function and drop through.
|
* it is a set, treat it like a function and drop through.
|
||||||
*/
|
*/
|
||||||
@ -2035,7 +1934,7 @@ ParseFunc(ParseState *pstate, char *funcname, List *fargs, int *curr_resno)
|
|||||||
|
|
||||||
return
|
return
|
||||||
((Node*)make_var(pstate,
|
((Node*)make_var(pstate,
|
||||||
oldname,
|
refname,
|
||||||
funcname,
|
funcname,
|
||||||
&dummyTypeId));
|
&dummyTypeId));
|
||||||
} else {
|
} else {
|
||||||
@ -2064,10 +1963,10 @@ ParseFunc(ParseState *pstate, char *funcname, List *fargs, int *curr_resno)
|
|||||||
tname(get_id_type(toid)));
|
tname(get_id_type(toid)));
|
||||||
argrelid = typeid_get_relid(toid);
|
argrelid = typeid_get_relid(toid);
|
||||||
/* A projection contains either an attribute name or the
|
/* A projection contains either an attribute name or the
|
||||||
* word "all".
|
* "*".
|
||||||
*/
|
*/
|
||||||
if ((get_attnum(argrelid, funcname) == InvalidAttrNumber)
|
if ((get_attnum(argrelid, funcname) == InvalidAttrNumber)
|
||||||
&& strcmp(funcname, "all")) {
|
&& strcmp(funcname, "*")) {
|
||||||
elog(WARN, "Functions on sets are not yet supported");
|
elog(WARN, "Functions on sets are not yet supported");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2109,34 +2008,22 @@ ParseFunc(ParseState *pstate, char *funcname, List *fargs, int *curr_resno)
|
|||||||
nargs=0;
|
nargs=0;
|
||||||
foreach ( i , fargs ) {
|
foreach ( i , fargs ) {
|
||||||
int vnum;
|
int vnum;
|
||||||
|
RangeTblEntry *rte;
|
||||||
Node *pair = lfirst(i);
|
Node *pair = lfirst(i);
|
||||||
|
|
||||||
if (nodeTag(pair)==T_Ident && ((Ident*)pair)->isRel) {
|
if (nodeTag(pair)==T_Ident && ((Ident*)pair)->isRel) {
|
||||||
/*
|
/*
|
||||||
* a relation
|
* a relation
|
||||||
*/
|
*/
|
||||||
relname = ((Ident*)pair)->name;
|
refname = ((Ident*)pair)->name;
|
||||||
|
|
||||||
/* get the range table entry for the var node */
|
rte = refnameRangeTableEntry(pstate->p_rtable, refname);
|
||||||
vnum = RangeTablePosn(pstate->p_rtable, relname);
|
if (rte == NULL)
|
||||||
if (vnum == 0) {
|
rte = addRangeTableEntry(pstate, refname, refname,
|
||||||
pstate->p_rtable =
|
FALSE, FALSE, NULL);
|
||||||
lappend(pstate->p_rtable ,
|
relname = rte->relname;
|
||||||
makeRangeTableEntry(relname, FALSE,
|
|
||||||
NULL, relname));
|
|
||||||
vnum = RangeTablePosn (pstate->p_rtable, relname);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
vnum = refnameRangeTablePosn (pstate->p_rtable, rte->refname);
|
||||||
* We have to do this because the relname in the pair
|
|
||||||
* may have been a range table variable name, rather
|
|
||||||
* than a real relation name.
|
|
||||||
*/
|
|
||||||
relname = VarnoGetRelname(pstate, vnum);
|
|
||||||
|
|
||||||
rd = heap_openr(relname);
|
|
||||||
relid = RelationGetRelationId(rd);
|
|
||||||
heap_close(rd);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* for func(relname), the param to the function
|
* for func(relname), the param to the function
|
||||||
@ -2225,9 +2112,9 @@ ParseFunc(ParseState *pstate, char *funcname, List *fargs, int *curr_resno)
|
|||||||
* attribute of the set tuples.
|
* attribute of the set tuples.
|
||||||
*/
|
*/
|
||||||
if (attisset) {
|
if (attisset) {
|
||||||
if (!strcmp(funcname, "all")) {
|
if (!strcmp(funcname, "*")) {
|
||||||
funcnode->func_tlist =
|
funcnode->func_tlist =
|
||||||
expandAll(pstate, (char*)relname, curr_resno);
|
expandAll(pstate, relname, refname, curr_resno);
|
||||||
} else {
|
} else {
|
||||||
funcnode->func_tlist = setup_tlist(funcname,argrelid);
|
funcnode->func_tlist = setup_tlist(funcname,argrelid);
|
||||||
rettype = find_atttype(argrelid, funcname);
|
rettype = find_atttype(argrelid, funcname);
|
||||||
@ -2258,55 +2145,6 @@ ParseFunc(ParseState *pstate, char *funcname, List *fargs, int *curr_resno)
|
|||||||
return(retval);
|
return(retval);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* returns (relname) if found, NIL if not a column
|
|
||||||
*/
|
|
||||||
static char*
|
|
||||||
ParseColumnName(ParseState *pstate, char *name, bool *isRelName)
|
|
||||||
{
|
|
||||||
List *et;
|
|
||||||
Relation rd;
|
|
||||||
List *rtable;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* see if it is a relation name. If so, leave it as it is
|
|
||||||
*/
|
|
||||||
if (RangeTablePosn(pstate->p_rtable, name)!=0) {
|
|
||||||
*isRelName = TRUE;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pstate->p_query_is_rule) {
|
|
||||||
rtable = lnext(lnext(pstate->p_rtable));
|
|
||||||
} else {
|
|
||||||
rtable = pstate->p_rtable;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* search each relation in the FROM list and see if we have a match
|
|
||||||
*/
|
|
||||||
foreach(et, rtable) {
|
|
||||||
RangeTblEntry *rte = lfirst(et);
|
|
||||||
char *relname= rte->relname;
|
|
||||||
char *refname= rte->refname;
|
|
||||||
Oid relid;
|
|
||||||
|
|
||||||
rd= heap_openr(relname);
|
|
||||||
relid = RelationGetRelationId(rd);
|
|
||||||
heap_close(rd);
|
|
||||||
if (get_attnum(relid, name) != InvalidAttrNumber) {
|
|
||||||
/* found */
|
|
||||||
*isRelName = FALSE;
|
|
||||||
return refname;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* attribute not found */
|
|
||||||
*isRelName = FALSE;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
*
|
*
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
@ -2365,10 +2203,9 @@ finalizeAggregates(ParseState *pstate, Query *qry)
|
|||||||
qry->qry_aggs =
|
qry->qry_aggs =
|
||||||
(Aggreg **)palloc(sizeof(Aggreg *) * qry->qry_numAgg);
|
(Aggreg **)palloc(sizeof(Aggreg *) * qry->qry_numAgg);
|
||||||
i = 0;
|
i = 0;
|
||||||
foreach(l, pstate->p_aggs) {
|
foreach(l, pstate->p_aggs)
|
||||||
qry->qry_aggs[i++] = (Aggreg*)lfirst(l);
|
qry->qry_aggs[i++] = (Aggreg*)lfirst(l);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* contain_agg_clause--
|
* contain_agg_clause--
|
||||||
@ -2390,30 +2227,26 @@ contain_agg_clause(Node *clause)
|
|||||||
else if (or_clause(clause)) {
|
else if (or_clause(clause)) {
|
||||||
List *temp;
|
List *temp;
|
||||||
|
|
||||||
foreach (temp, ((Expr*)clause)->args) {
|
foreach (temp, ((Expr*)clause)->args)
|
||||||
if (contain_agg_clause(lfirst(temp)))
|
if (contain_agg_clause(lfirst(temp)))
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
} else if (is_funcclause (clause)) {
|
} else if (is_funcclause (clause)) {
|
||||||
List *temp;
|
List *temp;
|
||||||
|
|
||||||
foreach(temp, ((Expr *)clause)->args) {
|
foreach(temp, ((Expr *)clause)->args)
|
||||||
if (contain_agg_clause(lfirst(temp)))
|
if (contain_agg_clause(lfirst(temp)))
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
} else if (IsA(clause,ArrayRef)) {
|
} else if (IsA(clause,ArrayRef)) {
|
||||||
List *temp;
|
List *temp;
|
||||||
|
|
||||||
foreach(temp, ((ArrayRef*)clause)->refupperindexpr) {
|
foreach(temp, ((ArrayRef*)clause)->refupperindexpr)
|
||||||
if (contain_agg_clause(lfirst(temp)))
|
if (contain_agg_clause(lfirst(temp)))
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
foreach(temp, ((ArrayRef*)clause)->reflowerindexpr)
|
||||||
foreach(temp, ((ArrayRef*)clause)->reflowerindexpr) {
|
|
||||||
if (contain_agg_clause(lfirst(temp)))
|
if (contain_agg_clause(lfirst(temp)))
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
|
||||||
if (contain_agg_clause(((ArrayRef*)clause)->refexpr))
|
if (contain_agg_clause(((ArrayRef*)clause)->refexpr))
|
||||||
return TRUE;
|
return TRUE;
|
||||||
if (contain_agg_clause(((ArrayRef*)clause)->refassgnexpr))
|
if (contain_agg_clause(((ArrayRef*)clause)->refassgnexpr))
|
||||||
@ -2459,10 +2292,9 @@ exprIsAggOrGroupCol(Node *expr, List *groupClause)
|
|||||||
else if (IsA(expr,Expr)) {
|
else if (IsA(expr,Expr)) {
|
||||||
List *temp;
|
List *temp;
|
||||||
|
|
||||||
foreach (temp, ((Expr*)expr)->args) {
|
foreach (temp, ((Expr*)expr)->args)
|
||||||
if (!exprIsAggOrGroupCol(lfirst(temp),groupClause))
|
if (!exprIsAggOrGroupCol(lfirst(temp),groupClause))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2510,5 +2342,3 @@ parseCheckAggregates(ParseState *pstate, Query *qry)
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 1.12 1996/09/20 08:34:14 scrappy Exp $
|
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 1.13 1996/10/30 02:01:54 momjian Exp $
|
||||||
*
|
*
|
||||||
* HISTORY
|
* HISTORY
|
||||||
* AUTHOR DATE MAJOR EVENT
|
* AUTHOR DATE MAJOR EVENT
|
||||||
@ -124,7 +124,7 @@ static Node *makeA_Expr(int op, char *opname, Node *lexpr, Node *rexpr);
|
|||||||
tableElementList, OptInherit, definition,
|
tableElementList, OptInherit, definition,
|
||||||
opt_with_func, def_args, def_name_list, func_argtypes,
|
opt_with_func, def_args, def_name_list, func_argtypes,
|
||||||
oper_argtypes, OptStmtList, OptStmtBlock, opt_column_list, columnList,
|
oper_argtypes, OptStmtList, OptStmtBlock, opt_column_list, columnList,
|
||||||
exprList, sort_clause, sortby_list, index_params,
|
sort_clause, sortby_list, index_params,
|
||||||
name_list, from_clause, from_list, opt_array_bounds, nest_array_bounds,
|
name_list, from_clause, from_list, opt_array_bounds, nest_array_bounds,
|
||||||
expr_list, attrs, res_target_list, res_target_list2, def_list,
|
expr_list, attrs, res_target_list, res_target_list2, def_list,
|
||||||
opt_indirection, group_clause, groupby_list, explain_options
|
opt_indirection, group_clause, groupby_list, explain_options
|
||||||
@ -143,7 +143,7 @@ static Node *makeA_Expr(int op, char *opname, Node *lexpr, Node *rexpr);
|
|||||||
%type <typnam> Typename, typname, opt_type
|
%type <typnam> Typename, typname, opt_type
|
||||||
%type <coldef> columnDef
|
%type <coldef> columnDef
|
||||||
%type <defelt> def_elem
|
%type <defelt> def_elem
|
||||||
%type <node> def_arg, columnElem, exprElem, where_clause,
|
%type <node> def_arg, columnElem, where_clause,
|
||||||
a_expr, AexprConst, having_clause, groupby
|
a_expr, AexprConst, having_clause, groupby
|
||||||
%type <value> NumConst
|
%type <value> NumConst
|
||||||
%type <attr> event_object, attr
|
%type <attr> event_object, attr
|
||||||
@ -1244,17 +1244,17 @@ AppendStmt: INSERT INTO relation_name opt_column_list insert_rest
|
|||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
insert_rest: VALUES '(' exprList ')'
|
insert_rest: VALUES '(' res_target_list2 ')'
|
||||||
{
|
{
|
||||||
$$ = makeNode(AppendStmt);
|
$$ = makeNode(AppendStmt);
|
||||||
$$->exprs = $3;
|
$$->targetList = $3;
|
||||||
$$->fromClause = NIL;
|
$$->fromClause = NIL;
|
||||||
$$->whereClause = NULL;
|
$$->whereClause = NULL;
|
||||||
}
|
}
|
||||||
| SELECT exprList from_clause where_clause
|
| SELECT res_target_list2 from_clause where_clause
|
||||||
{
|
{
|
||||||
$$ = makeNode(AppendStmt);
|
$$ = makeNode(AppendStmt);
|
||||||
$$->exprs = $2;
|
$$->targetList = $2;
|
||||||
$$->fromClause = $3;
|
$$->fromClause = $3;
|
||||||
$$->whereClause = $4;
|
$$->whereClause = $4;
|
||||||
}
|
}
|
||||||
@ -1280,36 +1280,6 @@ columnElem: Id opt_indirection
|
|||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
exprList: exprList ',' exprElem
|
|
||||||
{ $$ = lappend($1, $3); }
|
|
||||||
| exprElem
|
|
||||||
{ $$ = lcons($1, NIL); }
|
|
||||||
|
|
||||||
;
|
|
||||||
|
|
||||||
exprElem: a_expr
|
|
||||||
{ $$ = (Node *)$1; }
|
|
||||||
| relation_name '.' '*'
|
|
||||||
{
|
|
||||||
Attr *n = makeNode(Attr);
|
|
||||||
n->relname = $1;
|
|
||||||
n->paramNo = NULL;
|
|
||||||
n->attrs = lcons(makeString("*"), NIL);
|
|
||||||
n->indirection = NIL;
|
|
||||||
$$ = (Node *)n;
|
|
||||||
}
|
|
||||||
| '*'
|
|
||||||
{
|
|
||||||
Attr *n = makeNode(Attr);
|
|
||||||
n->relname = "*";
|
|
||||||
n->paramNo = NULL;
|
|
||||||
n->attrs = NIL;
|
|
||||||
n->indirection = NIL;
|
|
||||||
$$ = (Node *)n;
|
|
||||||
}
|
|
||||||
;
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
*
|
*
|
||||||
* QUERY:
|
* QUERY:
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/parser/Attic/parse_query.c,v 1.4 1996/08/28 22:50:24 scrappy Exp $
|
* $Header: /cvsroot/pgsql/src/backend/parser/Attic/parse_query.c,v 1.5 1996/10/30 02:01:59 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -40,55 +40,96 @@
|
|||||||
Oid *param_type_info;
|
Oid *param_type_info;
|
||||||
int pfunc_num_args;
|
int pfunc_num_args;
|
||||||
|
|
||||||
extern int Quiet;
|
/* given refname, return a pointer to the range table entry */
|
||||||
|
RangeTblEntry *
|
||||||
|
refnameRangeTableEntry(List *rtable, char *refname)
|
||||||
|
{
|
||||||
|
List *temp;
|
||||||
|
|
||||||
|
foreach(temp, rtable) {
|
||||||
|
RangeTblEntry *rte = lfirst(temp);
|
||||||
|
|
||||||
/* given range variable, return id of variable; position starts with 1 */
|
if (!strcmp(rte->refname, refname))
|
||||||
|
return rte;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* given refname, return id of variable; position starts with 1 */
|
||||||
int
|
int
|
||||||
RangeTablePosn(List *rtable, char *rangevar)
|
refnameRangeTablePosn(List *rtable, char *refname)
|
||||||
{
|
{
|
||||||
int index;
|
int index;
|
||||||
List *temp;
|
List *temp;
|
||||||
|
|
||||||
index = 1;
|
index = 1;
|
||||||
/* temp = pstate->p_rtable; */
|
foreach(temp, rtable) {
|
||||||
temp = rtable;
|
RangeTblEntry *rte = lfirst(temp);
|
||||||
while (temp != NIL) {
|
|
||||||
RangeTblEntry *rt_entry = lfirst(temp);
|
|
||||||
|
|
||||||
if (!strcmp(rt_entry->refname, rangevar))
|
if (!strcmp(rte->refname, refname))
|
||||||
return index;
|
return index;
|
||||||
|
|
||||||
temp = lnext(temp);
|
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
return(0);
|
return(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
char*
|
/*
|
||||||
VarnoGetRelname(ParseState *pstate, int vnum)
|
* returns range entry if found, else NULL
|
||||||
|
*/
|
||||||
|
RangeTblEntry *
|
||||||
|
colnameRangeTableEntry(ParseState *pstate, char *colname)
|
||||||
{
|
{
|
||||||
int i;
|
List *et;
|
||||||
List *temp = pstate->p_rtable;
|
List *rtable;
|
||||||
for( i = 1; i < vnum ; i++)
|
RangeTblEntry *rte_result;
|
||||||
temp = lnext(temp);
|
|
||||||
return(((RangeTblEntry*)lfirst(temp))->relname);
|
if (pstate->p_is_rule)
|
||||||
|
rtable = lnext(lnext(pstate->p_rtable));
|
||||||
|
else
|
||||||
|
rtable = pstate->p_rtable;
|
||||||
|
|
||||||
|
rte_result = NULL;
|
||||||
|
foreach(et, rtable) {
|
||||||
|
RangeTblEntry *rte = lfirst(et);
|
||||||
|
|
||||||
|
/* only entries on outer(non-function?) scope */
|
||||||
|
if (!rte->inFromCl && rte != pstate->p_target_rangetblentry)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (get_attnum(rte->relid, colname) != InvalidAttrNumber) {
|
||||||
|
if (rte_result != NULL) {
|
||||||
|
if (!pstate->p_is_insert ||
|
||||||
|
rte != pstate->p_target_rangetblentry)
|
||||||
|
elog(WARN, "Column %s is ambiguous", colname);
|
||||||
|
}
|
||||||
|
else rte_result = rte;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rte_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* put new entry in pstate p_rtable structure, or return pointer
|
||||||
|
* if pstate null
|
||||||
|
*/
|
||||||
RangeTblEntry *
|
RangeTblEntry *
|
||||||
makeRangeTableEntry(char *relname,
|
addRangeTableEntry(ParseState *pstate,
|
||||||
bool inh,
|
char *relname,
|
||||||
TimeRange *timeRange,
|
char *refname,
|
||||||
char *refname)
|
bool inh, bool inFromCl,
|
||||||
|
TimeRange *timeRange)
|
||||||
{
|
{
|
||||||
Relation relation;
|
Relation relation;
|
||||||
RangeTblEntry *ent = makeNode(RangeTblEntry);
|
RangeTblEntry *rte = makeNode(RangeTblEntry);
|
||||||
|
|
||||||
ent->relname = pstrdup(relname);
|
if (pstate != NULL &&
|
||||||
ent->refname = refname;
|
refnameRangeTableEntry(pstate->p_rtable, refname) != NULL)
|
||||||
|
elog(WARN,"Table name %s specified more than once",refname);
|
||||||
|
|
||||||
relation = heap_openr(ent->relname);
|
rte->relname = pstrdup(relname);
|
||||||
|
rte->refname = pstrdup(refname);
|
||||||
|
|
||||||
|
relation = heap_openr(relname);
|
||||||
if (relation == NULL) {
|
if (relation == NULL) {
|
||||||
elog(WARN,"%s: %s",
|
elog(WARN,"%s: %s",
|
||||||
relname, ACL_NO_PRIV_WARNING);
|
relname, ACL_NO_PRIV_WARNING);
|
||||||
@ -99,18 +140,26 @@ makeRangeTableEntry(char *relname,
|
|||||||
* or recursive (transitive closure)
|
* or recursive (transitive closure)
|
||||||
* [we don't support them all -- ay 9/94 ]
|
* [we don't support them all -- ay 9/94 ]
|
||||||
*/
|
*/
|
||||||
ent->inh = inh;
|
rte->inh = inh;
|
||||||
|
|
||||||
ent->timeRange = timeRange;
|
rte->timeRange = timeRange;
|
||||||
|
|
||||||
/* RelOID */
|
/* RelOID */
|
||||||
ent->relid = RelationGetRelationId(relation);
|
rte->relid = RelationGetRelationId(relation);
|
||||||
|
|
||||||
|
rte->archive = false;
|
||||||
|
|
||||||
|
rte->inFromCl = inFromCl;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* close the relation we're done with it for now.
|
* close the relation we're done with it for now.
|
||||||
*/
|
*/
|
||||||
|
if (pstate != NULL)
|
||||||
|
pstate->p_rtable = lappend(pstate->p_rtable, rte);
|
||||||
|
|
||||||
heap_close(relation);
|
heap_close(relation);
|
||||||
return ent;
|
|
||||||
|
return rte;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -119,65 +168,59 @@ makeRangeTableEntry(char *relname,
|
|||||||
* assumes reldesc caching works
|
* assumes reldesc caching works
|
||||||
*/
|
*/
|
||||||
List *
|
List *
|
||||||
expandAll(ParseState* pstate, char *relname, int *this_resno)
|
expandAll(ParseState *pstate, char *relname, char *refname, int *this_resno)
|
||||||
{
|
{
|
||||||
Relation rdesc;
|
Relation rdesc;
|
||||||
List *tall = NIL;
|
List *te_tail = NIL, *te_head = NIL;
|
||||||
Var *varnode;
|
Var *varnode;
|
||||||
int i, maxattrs, first_resno;
|
int varattno, maxattrs;
|
||||||
int type_id, type_len, vnum;
|
int type_id, type_len;
|
||||||
char *physical_relname;
|
RangeTblEntry *rte;
|
||||||
|
|
||||||
first_resno = *this_resno;
|
rte = refnameRangeTableEntry(pstate->p_rtable, refname);
|
||||||
|
if (rte == NULL)
|
||||||
|
rte = addRangeTableEntry(pstate, relname, refname, FALSE, FALSE, NULL);
|
||||||
|
|
||||||
/* printf("\nExpanding %.*s.all\n", NAMEDATALEN, relname); */
|
rdesc = heap_open(rte->relid);
|
||||||
vnum = RangeTablePosn(pstate->p_rtable, relname);
|
|
||||||
if ( vnum == 0 ) {
|
|
||||||
pstate->p_rtable = lappend(pstate->p_rtable,
|
|
||||||
makeRangeTableEntry(relname, FALSE, NULL,
|
|
||||||
relname));
|
|
||||||
vnum = RangeTablePosn(pstate->p_rtable, relname);
|
|
||||||
}
|
|
||||||
|
|
||||||
physical_relname = VarnoGetRelname(pstate, vnum);
|
|
||||||
|
|
||||||
rdesc = heap_openr(physical_relname);
|
|
||||||
|
|
||||||
if (rdesc == NULL ) {
|
if (rdesc == NULL ) {
|
||||||
elog(WARN,"Unable to expand all -- heap_openr failed on %s",
|
elog(WARN,"Unable to expand all -- heap_open failed on %s",
|
||||||
physical_relname);
|
rte->refname);
|
||||||
return NIL;
|
return NIL;
|
||||||
}
|
}
|
||||||
maxattrs = RelationGetNumberOfAttributes(rdesc);
|
maxattrs = RelationGetNumberOfAttributes(rdesc);
|
||||||
|
|
||||||
for ( i = maxattrs-1 ; i > -1 ; --i ) {
|
for ( varattno = 0; varattno <= maxattrs-1 ; varattno++ ) {
|
||||||
char *attrname;
|
char *attrname;
|
||||||
TargetEntry *rte = makeNode(TargetEntry);
|
char *resname = NULL;
|
||||||
|
TargetEntry *te = makeNode(TargetEntry);
|
||||||
|
|
||||||
attrname = pstrdup ((rdesc->rd_att->attrs[i]->attname).data);
|
attrname = pstrdup ((rdesc->rd_att->attrs[varattno]->attname).data);
|
||||||
varnode = (Var*)make_var(pstate, relname, attrname, &type_id);
|
varnode = (Var*)make_var(pstate, refname, attrname, &type_id);
|
||||||
type_len = (int)tlen(get_id_type(type_id));
|
type_len = (int)tlen(get_id_type(type_id));
|
||||||
|
|
||||||
|
handleTargetColname(pstate, &resname, refname, attrname);
|
||||||
|
if (resname != NULL)
|
||||||
|
attrname = resname;
|
||||||
|
|
||||||
/* Even if the elements making up a set are complex, the
|
/* Even if the elements making up a set are complex, the
|
||||||
* set itself is not. */
|
* set itself is not. */
|
||||||
|
|
||||||
rte->resdom = makeResdom((AttrNumber) i + first_resno,
|
te->resdom = makeResdom((AttrNumber) (*this_resno)++,
|
||||||
(Oid)type_id,
|
(Oid)type_id,
|
||||||
(Size)type_len,
|
(Size)type_len,
|
||||||
attrname,
|
attrname,
|
||||||
(Index)0,
|
(Index)0,
|
||||||
(Oid)0,
|
(Oid)0,
|
||||||
0);
|
0);
|
||||||
rte->expr = (Node *)varnode;
|
te->expr = (Node *)varnode;
|
||||||
tall = lcons(rte, tall);
|
if (te_head == NIL)
|
||||||
|
te_head = te_tail = lcons(te, NIL);
|
||||||
|
else te_tail = lappend(te_tail, te);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Close the reldesc - we're done with it now
|
|
||||||
*/
|
|
||||||
heap_close(rdesc);
|
heap_close(rdesc);
|
||||||
*this_resno = first_resno + maxattrs;
|
return(te_head);
|
||||||
return(tall);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TimeQual
|
TimeQual
|
||||||
@ -385,27 +428,21 @@ find_atttype(Oid relid, char *attrname)
|
|||||||
|
|
||||||
|
|
||||||
Var *
|
Var *
|
||||||
make_var(ParseState *pstate, char *relname, char *attrname, int *type_id)
|
make_var(ParseState *pstate, char *refname, char *attrname, int *type_id)
|
||||||
{
|
{
|
||||||
Var *varnode;
|
Var *varnode;
|
||||||
int vnum, attid, vartypeid;
|
int vnum, attid, vartypeid;
|
||||||
Relation rd;
|
Relation rd;
|
||||||
|
RangeTblEntry *rte;
|
||||||
|
|
||||||
vnum = RangeTablePosn(pstate->p_rtable, relname);
|
rte = refnameRangeTableEntry(pstate->p_rtable, refname);
|
||||||
|
if (rte == NULL)
|
||||||
|
rte = addRangeTableEntry(pstate, refname, refname, FALSE, FALSE, NULL);
|
||||||
|
|
||||||
if (vnum == 0) {
|
vnum = refnameRangeTablePosn(pstate->p_rtable, refname);
|
||||||
pstate->p_rtable =
|
|
||||||
lappend(pstate->p_rtable,
|
rd = heap_open(rte->relid);
|
||||||
makeRangeTableEntry(relname, FALSE,
|
|
||||||
NULL, relname));
|
|
||||||
vnum = RangeTablePosn (pstate->p_rtable, relname);
|
|
||||||
relname = VarnoGetRelname(pstate, vnum);
|
|
||||||
} else {
|
|
||||||
relname = VarnoGetRelname(pstate, vnum);
|
|
||||||
}
|
|
||||||
|
|
||||||
rd = heap_openr(relname);
|
|
||||||
/* relid = RelationGetRelationId(rd); */
|
|
||||||
attid = nf_varattno(rd, (char *) attrname);
|
attid = nf_varattno(rd, (char *) attrname);
|
||||||
if (attid == InvalidAttrNumber)
|
if (attid == InvalidAttrNumber)
|
||||||
elog(WARN, "Invalid attribute %s\n", attrname);
|
elog(WARN, "Invalid attribute %s\n", attrname);
|
||||||
@ -413,9 +450,6 @@ make_var(ParseState *pstate, char *relname, char *attrname, int *type_id)
|
|||||||
|
|
||||||
varnode = makeVar(vnum, attid, vartypeid, vnum, attid);
|
varnode = makeVar(vnum, attid, vartypeid, vnum, attid);
|
||||||
|
|
||||||
/*
|
|
||||||
* close relation we're done with it now
|
|
||||||
*/
|
|
||||||
heap_close(rd);
|
heap_close(rd);
|
||||||
|
|
||||||
*type_id = vartypeid;
|
*type_id = vartypeid;
|
||||||
@ -655,3 +689,76 @@ param_type(int t)
|
|||||||
return param_type_info[t-1];
|
return param_type_info[t-1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* handleTargetColname -
|
||||||
|
* use column names from insert
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
handleTargetColname(ParseState *pstate, char **resname,
|
||||||
|
char *refname, char *colname)
|
||||||
|
{
|
||||||
|
if (pstate->p_is_insert) {
|
||||||
|
if (pstate->p_insert_columns != NIL ) {
|
||||||
|
Ident *id = lfirst(pstate->p_insert_columns);
|
||||||
|
Assert(lfirst(pstate->p_insert_columns) != NIL);
|
||||||
|
*resname = id->name;
|
||||||
|
pstate->p_insert_columns = lnext(pstate->p_insert_columns);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
elog(WARN, "insert: more expressions than target columns");
|
||||||
|
}
|
||||||
|
if (pstate->p_is_insert||pstate->p_is_update)
|
||||||
|
checkTargetTypes(pstate, *resname, refname, colname);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* checkTargetTypes -
|
||||||
|
* checks value and target column types
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
checkTargetTypes(ParseState *pstate, char *target_colname,
|
||||||
|
char *refname, char *colname)
|
||||||
|
{
|
||||||
|
int attrtype_id, attrtype_target, resdomno_id, resdomno_target;
|
||||||
|
Relation rd;
|
||||||
|
RangeTblEntry *rte;
|
||||||
|
|
||||||
|
if (target_colname == NULL || colname == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (refname != NULL)
|
||||||
|
rte = refnameRangeTableEntry(pstate->p_rtable, refname);
|
||||||
|
else {
|
||||||
|
rte = colnameRangeTableEntry(pstate, colname);
|
||||||
|
refname = rte->refname;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert(refname != NULL && rte != NULL);
|
||||||
|
|
||||||
|
Assert(rte != NULL);
|
||||||
|
/*
|
||||||
|
if (pstate->p_is_insert && rte == pstate->p_target_rangetblentry)
|
||||||
|
elog(WARN, "%s not available in this context", colname);
|
||||||
|
*/
|
||||||
|
rd = heap_open(rte->relid);
|
||||||
|
Assert(RelationIsValid(rd));
|
||||||
|
|
||||||
|
resdomno_id = varattno(rd,colname);
|
||||||
|
attrtype_id = att_typeid(rd,resdomno_id);
|
||||||
|
|
||||||
|
resdomno_target = varattno(pstate->p_target_relation,target_colname);
|
||||||
|
attrtype_target = att_typeid(pstate->p_target_relation, resdomno_target);
|
||||||
|
|
||||||
|
if (attrtype_id != attrtype_target)
|
||||||
|
elog(WARN, "Type of %s does not match target column %s",
|
||||||
|
colname, target_colname);
|
||||||
|
|
||||||
|
if ((attrtype_id == BPCHAROID || attrtype_id == VARCHAROID) &&
|
||||||
|
rd->rd_att->attrs[resdomno_id-1]->attlen !=
|
||||||
|
pstate->p_target_relation->rd_att->attrs[resdomno_target-1]->attlen)
|
||||||
|
elog(WARN, "Length of %s does not match length of target column %s",
|
||||||
|
colname, target_colname);
|
||||||
|
|
||||||
|
heap_close(rd);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 1994, Regents of the University of California
|
* Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: parsenodes.h,v 1.3 1996/10/19 04:49:29 scrappy Exp $
|
* $Id: parsenodes.h,v 1.4 1996/10/30 02:02:08 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -422,8 +422,7 @@ typedef struct AppendStmt {
|
|||||||
NodeTag type;
|
NodeTag type;
|
||||||
char *relname; /* relation to insert into */
|
char *relname; /* relation to insert into */
|
||||||
List *cols; /* names of the columns */
|
List *cols; /* names of the columns */
|
||||||
List *exprs; /* the expressions (same order as
|
List *targetList; /* the target list (of ResTarget) */
|
||||||
the columns) */
|
|
||||||
List *fromClause; /* the from clause */
|
List *fromClause; /* the from clause */
|
||||||
Node *whereClause; /* qualifications */
|
Node *whereClause; /* qualifications */
|
||||||
} AppendStmt;
|
} AppendStmt;
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 1994, Regents of the University of California
|
* Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: parse_state.h,v 1.3 1996/10/13 17:13:58 momjian Exp $
|
* $Id: parse_state.h,v 1.4 1996/10/30 02:02:13 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -15,12 +15,15 @@
|
|||||||
/* state information used during parse analysis */
|
/* state information used during parse analysis */
|
||||||
typedef struct ParseState {
|
typedef struct ParseState {
|
||||||
int p_last_resno;
|
int p_last_resno;
|
||||||
List *p_target_resnos;
|
|
||||||
Relation p_current_rel;
|
|
||||||
List *p_rtable;
|
List *p_rtable;
|
||||||
int p_query_is_rule;
|
|
||||||
int p_numAgg;
|
int p_numAgg;
|
||||||
List *p_aggs;
|
List *p_aggs;
|
||||||
|
bool p_is_insert;
|
||||||
|
List *p_insert_columns;
|
||||||
|
bool p_is_update;
|
||||||
|
bool p_is_rule;
|
||||||
|
Relation p_target_relation;
|
||||||
|
RangeTblEntry *p_target_rangetblentry;
|
||||||
} ParseState;
|
} ParseState;
|
||||||
|
|
||||||
|
|
||||||
|
@ -4761,7 +4761,7 @@ QUERY: CLOSE foo23;
|
|||||||
QUERY: CLOSE foo24;
|
QUERY: CLOSE foo24;
|
||||||
QUERY: CLOSE foo25;
|
QUERY: CLOSE foo25;
|
||||||
QUERY: END;
|
QUERY: END;
|
||||||
QUERY: PURGE hash_f8_heap BEFORE 'now'; -- absolute time
|
QUERY: PURGE hash_f8_heap BEFORE 'now';
|
||||||
SELECT count(*) AS has_10002 FROM hash_f8_heap[,] h;
|
SELECT count(*) AS has_10002 FROM hash_f8_heap[,] h;
|
||||||
QUERY: VACUUM hash_f8_heap;
|
QUERY: VACUUM hash_f8_heap;
|
||||||
QUERY: SELECT count(*) AS has_10000 FROM hash_f8_heap[,] h;
|
QUERY: SELECT count(*) AS has_10000 FROM hash_f8_heap[,] h;
|
||||||
@ -4770,7 +4770,7 @@ has_10000
|
|||||||
10002
|
10002
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
QUERY: PURGE hash_i4_heap AFTER '@ 1 second ago'; -- relative time
|
QUERY: PURGE hash_i4_heap AFTER '@ 1 second ago';
|
||||||
SELECT count(*) AS has_10002 FROM hash_i4_heap[,] h;
|
SELECT count(*) AS has_10002 FROM hash_i4_heap[,] h;
|
||||||
QUERY: VACUUM hash_i4_heap;
|
QUERY: VACUUM hash_i4_heap;
|
||||||
QUERY: SELECT count(*) AS has_10000 FROM hash_i4_heap[,] h;
|
QUERY: SELECT count(*) AS has_10000 FROM hash_i4_heap[,] h;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
--
|
--
|
||||||
-- queries.source
|
-- queries.source
|
||||||
--
|
--
|
||||||
-- $Header: /cvsroot/pgsql/src/test/regress/Attic/queries.source,v 1.2 1996/10/07 02:33:25 momjian Exp $
|
-- $Header: /cvsroot/pgsql/src/test/regress/Attic/queries.source,v 1.3 1996/10/30 02:02:39 momjian Exp $
|
||||||
--
|
--
|
||||||
-- The comments that contain sequences of UNIX commands generate the
|
-- The comments that contain sequences of UNIX commands generate the
|
||||||
-- desired output for the POSTQUEL statement(s).
|
-- desired output for the POSTQUEL statement(s).
|
||||||
@ -721,7 +721,7 @@ SELECT '' AS three, f.f1, f.f1 * '-10' AS x FROM FLOAT4_TBL f
|
|||||||
WHERE f.f1 > '0.0';
|
WHERE f.f1 > '0.0';
|
||||||
|
|
||||||
SELECT '' AS three, f.f1, f.f1 + '-10' AS x FROM FLOAT4_TBL f
|
SELECT '' AS three, f.f1, f.f1 + '-10' AS x FROM FLOAT4_TBL f
|
||||||
WHERE f.f1 > '0.0'
|
WHERE f.f1 > '0.0';
|
||||||
|
|
||||||
SELECT '' AS three, f.f1, f.f1 / '-10' AS x FROM FLOAT4_TBL f
|
SELECT '' AS three, f.f1, f.f1 / '-10' AS x FROM FLOAT4_TBL f
|
||||||
WHERE f.f1 > '0.0';
|
WHERE f.f1 > '0.0';
|
||||||
@ -2195,7 +2195,8 @@ END;
|
|||||||
-- miss deleting a bunch of index tuples, which caused big problems when
|
-- miss deleting a bunch of index tuples, which caused big problems when
|
||||||
-- you dereferenced the tids and found garbage..
|
-- you dereferenced the tids and found garbage..
|
||||||
--
|
--
|
||||||
PURGE hash_f8_heap BEFORE 'now'; -- absolute time
|
-- absolute time
|
||||||
|
PURGE hash_f8_heap BEFORE 'now';
|
||||||
|
|
||||||
SELECT count(*) AS has_10002 FROM hash_f8_heap[,] h;
|
SELECT count(*) AS has_10002 FROM hash_f8_heap[,] h;
|
||||||
|
|
||||||
@ -2203,7 +2204,8 @@ VACUUM hash_f8_heap;
|
|||||||
|
|
||||||
SELECT count(*) AS has_10000 FROM hash_f8_heap[,] h;
|
SELECT count(*) AS has_10000 FROM hash_f8_heap[,] h;
|
||||||
|
|
||||||
PURGE hash_i4_heap AFTER '@ 1 second ago'; -- relative time
|
-- relative time
|
||||||
|
PURGE hash_i4_heap AFTER '@ 1 second ago';
|
||||||
|
|
||||||
SELECT count(*) AS has_10002 FROM hash_i4_heap[,] h;
|
SELECT count(*) AS has_10002 FROM hash_i4_heap[,] h;
|
||||||
|
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# $Header: /cvsroot/pgsql/src/test/regress/Attic/regress.sh,v 1.1.1.1 1996/07/09 06:22:24 scrappy Exp $
|
# $Header: /cvsroot/pgsql/src/test/regress/Attic/regress.sh,v 1.2 1996/10/30 02:02:41 momjian Exp $
|
||||||
#
|
#
|
||||||
if [ -d ./obj ]; then
|
if [ -d ./obj ]; then
|
||||||
cd ./obj
|
cd ./obj
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
TZ="PST8PDT"; export TZ
|
||||||
|
|
||||||
#FRONTEND=monitor
|
#FRONTEND=monitor
|
||||||
FRONTEND="psql -n -e -q"
|
FRONTEND="psql -n -e -q"
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user