756 lines
18 KiB
C++
756 lines
18 KiB
C++
/*
|
|
* The signature of this function is required by bison. However, we
|
|
* ignore the passed yylloc and instead use the last token position
|
|
* available from the scanner.
|
|
*/
|
|
static void
|
|
base_yyerror(YYLTYPE *yylloc, core_yyscan_t yyscanner, const char *msg)
|
|
{
|
|
parser_yyerror(msg);
|
|
}
|
|
|
|
static PGRawStmt *
|
|
makeRawStmt(PGNode *stmt, int stmt_location)
|
|
{
|
|
PGRawStmt *rs = makeNode(PGRawStmt);
|
|
|
|
rs->stmt = stmt;
|
|
rs->stmt_location = stmt_location;
|
|
rs->stmt_len = 0; /* might get changed later */
|
|
return rs;
|
|
}
|
|
|
|
/* Adjust a PGRawStmt to reflect that it doesn't run to the end of the string */
|
|
static void
|
|
updateRawStmtEnd(PGRawStmt *rs, int end_location)
|
|
{
|
|
/*
|
|
* If we already set the length, don't change it. This is for situations
|
|
* like "select foo ;; select bar" where the same statement will be last
|
|
* in the string for more than one semicolon.
|
|
*/
|
|
if (rs->stmt_len > 0)
|
|
return;
|
|
|
|
/* OK, update length of PGRawStmt */
|
|
rs->stmt_len = end_location - rs->stmt_location;
|
|
}
|
|
|
|
static PGNode *
|
|
makeColumnRef(char *colname, PGList *indirection,
|
|
int location, core_yyscan_t yyscanner)
|
|
{
|
|
/*
|
|
* Generate a PGColumnRef node, with an PGAIndirection node added if there
|
|
* is any subscripting in the specified indirection list. However,
|
|
* any field selection at the start of the indirection list must be
|
|
* transposed into the "fields" part of the PGColumnRef node.
|
|
*/
|
|
PGColumnRef *c = makeNode(PGColumnRef);
|
|
int nfields = 0;
|
|
PGListCell *l;
|
|
|
|
c->location = location;
|
|
foreach(l, indirection)
|
|
{
|
|
if (IsA(lfirst(l), PGAIndices))
|
|
{
|
|
PGAIndirection *i = makeNode(PGAIndirection);
|
|
|
|
if (nfields == 0)
|
|
{
|
|
/* easy case - all indirection goes to PGAIndirection */
|
|
c->fields = list_make1(makeString(colname));
|
|
i->indirection = check_indirection(indirection, yyscanner);
|
|
}
|
|
else
|
|
{
|
|
/* got to split the list in two */
|
|
i->indirection = check_indirection(list_copy_tail(indirection,
|
|
nfields),
|
|
yyscanner);
|
|
indirection = list_truncate(indirection, nfields);
|
|
c->fields = lcons(makeString(colname), indirection);
|
|
}
|
|
i->arg = (PGNode *) c;
|
|
return (PGNode *) i;
|
|
}
|
|
else if (IsA(lfirst(l), PGAStar))
|
|
{
|
|
/* We only allow '*' at the end of a PGColumnRef */
|
|
if (lnext(l) != NULL)
|
|
parser_yyerror("improper use of \"*\"");
|
|
}
|
|
nfields++;
|
|
}
|
|
/* No subscripting, so all indirection gets added to field list */
|
|
c->fields = lcons(makeString(colname), indirection);
|
|
return (PGNode *) c;
|
|
}
|
|
|
|
static PGNode *
|
|
makeTypeCast(PGNode *arg, PGTypeName *tpname, int trycast, int location)
|
|
{
|
|
PGTypeCast *n = makeNode(PGTypeCast);
|
|
n->arg = arg;
|
|
n->typeName = tpname;
|
|
n->tryCast = trycast;
|
|
n->location = location;
|
|
return (PGNode *) n;
|
|
}
|
|
|
|
static PGNode *
|
|
makeStringConst(const char *str, int location)
|
|
{
|
|
PGAConst *n = makeNode(PGAConst);
|
|
|
|
n->val.type = T_PGString;
|
|
n->val.val.str = (char *) str;
|
|
n->location = location;
|
|
|
|
return (PGNode *)n;
|
|
}
|
|
|
|
static PGNode *
|
|
makeStringConstCast(const char *str, int location, PGTypeName *tpname)
|
|
{
|
|
PGNode *s = makeStringConst(str, location);
|
|
|
|
return makeTypeCast(s, tpname, 0, -1);
|
|
}
|
|
|
|
static PGNode *
|
|
makeIntervalNode(char *str, int location, PGList *typmods) {
|
|
PGIntervalConstant *n = makeNode(PGIntervalConstant);
|
|
|
|
n->val_type = T_PGString;
|
|
n->sval = str;
|
|
n->location = location;
|
|
n->typmods = typmods;
|
|
|
|
return (PGNode *)n;
|
|
|
|
}
|
|
|
|
static PGNode *
|
|
makeIntervalNode(int val, int location, PGList *typmods) {
|
|
PGIntervalConstant *n = makeNode(PGIntervalConstant);
|
|
|
|
n->val_type = T_PGInteger;
|
|
n->ival = val;
|
|
n->location = location;
|
|
n->typmods = typmods;
|
|
|
|
return (PGNode *)n;
|
|
}
|
|
|
|
static PGNode *
|
|
makeIntervalNode(PGNode *arg, int location, PGList *typmods) {
|
|
PGIntervalConstant *n = makeNode(PGIntervalConstant);
|
|
|
|
n->val_type = T_PGAExpr;
|
|
n->eval = arg;
|
|
n->location = location;
|
|
n->typmods = typmods;
|
|
|
|
return (PGNode *)n;
|
|
}
|
|
|
|
static PGNode *
|
|
makeSampleSize(PGNode *sample_size, bool is_percentage) {
|
|
PGSampleSize *n = makeNode(PGSampleSize);
|
|
|
|
n->sample_size = sample_size;
|
|
n->is_percentage = is_percentage;
|
|
|
|
return (PGNode *)n;
|
|
}
|
|
|
|
static PGNode *
|
|
makeSampleOptions(PGNode *sample_size, char *method, int *seed, int location) {
|
|
PGSampleOptions *n = makeNode(PGSampleOptions);
|
|
|
|
n->sample_size = sample_size;
|
|
n->method = method;
|
|
if (seed) {
|
|
n->has_seed = true;
|
|
n->seed = *seed;
|
|
}
|
|
n->location = location;
|
|
|
|
return (PGNode *)n;
|
|
}
|
|
|
|
/* makeLimitPercent()
|
|
* Make limit percent node
|
|
*/
|
|
static PGNode *
|
|
makeLimitPercent(PGNode *limit_percent) {
|
|
PGLimitPercent *n = makeNode(PGLimitPercent);
|
|
|
|
n->limit_percent = limit_percent;
|
|
|
|
return (PGNode *)n;
|
|
}
|
|
|
|
static PGNode *
|
|
makeIntConst(int val, int location)
|
|
{
|
|
PGAConst *n = makeNode(PGAConst);
|
|
|
|
n->val.type = T_PGInteger;
|
|
n->val.val.ival = val;
|
|
n->location = location;
|
|
|
|
return (PGNode *)n;
|
|
}
|
|
|
|
static PGNode *
|
|
makeFloatConst(char *str, int location)
|
|
{
|
|
PGAConst *n = makeNode(PGAConst);
|
|
|
|
n->val.type = T_PGFloat;
|
|
n->val.val.str = str;
|
|
n->location = location;
|
|
|
|
return (PGNode *)n;
|
|
}
|
|
|
|
static PGNode *
|
|
makeBitStringConst(char *str, int location)
|
|
{
|
|
PGAConst *n = makeNode(PGAConst);
|
|
|
|
n->val.type = T_PGBitString;
|
|
n->val.val.str = str;
|
|
n->location = location;
|
|
|
|
return (PGNode *)n;
|
|
}
|
|
|
|
static PGNode *
|
|
makeNullAConst(int location)
|
|
{
|
|
PGAConst *n = makeNode(PGAConst);
|
|
|
|
n->val.type = T_PGNull;
|
|
n->location = location;
|
|
|
|
return (PGNode *)n;
|
|
}
|
|
|
|
static PGNode *
|
|
makeAConst(PGValue *v, int location)
|
|
{
|
|
PGNode *n;
|
|
|
|
switch (v->type)
|
|
{
|
|
case T_PGFloat:
|
|
n = makeFloatConst(v->val.str, location);
|
|
break;
|
|
|
|
case T_PGInteger:
|
|
n = makeIntConst(v->val.ival, location);
|
|
break;
|
|
|
|
case T_PGString:
|
|
default:
|
|
n = makeStringConst(v->val.str, location);
|
|
break;
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
/* makeBoolAConst()
|
|
* Create an PGAConst string node and put it inside a boolean cast.
|
|
*/
|
|
static PGNode *
|
|
makeBoolAConst(bool state, int location)
|
|
{
|
|
PGAConst *n = makeNode(PGAConst);
|
|
|
|
n->val.type = T_PGString;
|
|
n->val.val.str = (state ? (char*) "t" : (char*) "f");
|
|
n->location = location;
|
|
|
|
return makeTypeCast((PGNode *)n, SystemTypeName("bool"), 0, -1);
|
|
}
|
|
|
|
/* check_qualified_name --- check the result of qualified_name production
|
|
*
|
|
* It's easiest to let the grammar production for qualified_name allow
|
|
* subscripts and '*', which we then must reject here.
|
|
*/
|
|
static void
|
|
check_qualified_name(PGList *names, core_yyscan_t yyscanner)
|
|
{
|
|
PGListCell *i;
|
|
|
|
foreach(i, names)
|
|
{
|
|
if (!IsA(lfirst(i), PGString))
|
|
parser_yyerror("syntax error");
|
|
}
|
|
}
|
|
|
|
/* check_func_name --- check the result of func_name production
|
|
*
|
|
* It's easiest to let the grammar production for func_name allow subscripts
|
|
* and '*', which we then must reject here.
|
|
*/
|
|
static PGList *
|
|
check_func_name(PGList *names, core_yyscan_t yyscanner)
|
|
{
|
|
PGListCell *i;
|
|
|
|
foreach(i, names)
|
|
{
|
|
if (!IsA(lfirst(i), PGString))
|
|
parser_yyerror("syntax error");
|
|
}
|
|
return names;
|
|
}
|
|
|
|
/* check_indirection --- check the result of indirection production
|
|
*
|
|
* We only allow '*' at the end of the list, but it's hard to enforce that
|
|
* in the grammar, so do it here.
|
|
*/
|
|
static PGList *
|
|
check_indirection(PGList *indirection, core_yyscan_t yyscanner)
|
|
{
|
|
PGListCell *l;
|
|
|
|
foreach(l, indirection)
|
|
{
|
|
if (IsA(lfirst(l), PGAStar))
|
|
{
|
|
if (lnext(l) != NULL)
|
|
parser_yyerror("improper use of \"*\"");
|
|
}
|
|
}
|
|
return indirection;
|
|
}
|
|
|
|
/* makeParamRef
|
|
* Creates a new PGParamRef node
|
|
*/
|
|
static PGNode* makeParamRef(int number, int location)
|
|
{
|
|
PGParamRef *p = makeNode(PGParamRef);
|
|
p->number = number;
|
|
p->location = location;
|
|
p->name = NULL;
|
|
return (PGNode *) p;
|
|
}
|
|
|
|
/* makeNamedParamRef
|
|
* Creates a new PGParamRef node
|
|
*/
|
|
static PGNode* makeNamedParamRef(char *name, int location)
|
|
{
|
|
PGParamRef *p = (PGParamRef *)makeParamRef(0, location);
|
|
p->name = name;
|
|
return (PGNode *) p;
|
|
}
|
|
|
|
|
|
/* insertSelectOptions()
|
|
* Insert ORDER BY, etc into an already-constructed SelectStmt.
|
|
*
|
|
* This routine is just to avoid duplicating code in PGSelectStmt productions.
|
|
*/
|
|
static void
|
|
insertSelectOptions(PGSelectStmt *stmt,
|
|
PGList *sortClause, PGList *lockingClause,
|
|
PGNode *limitOffset, PGNode *limitCount, PGNode *isLimitOffsetFirst,
|
|
PGWithClause *withClause,
|
|
core_yyscan_t yyscanner)
|
|
{
|
|
if (stmt->type != T_PGSelectStmt) {
|
|
ereport(ERROR,
|
|
(errcode(PG_ERRCODE_SYNTAX_ERROR),
|
|
errmsg("DESCRIBE/SHOW/SUMMARIZE with CTE/ORDER BY/... not allowed - wrap the statement in a subquery instead"),
|
|
parser_errposition(exprLocation((PGNode *) stmt))));
|
|
}
|
|
Assert(IsA(stmt, PGSelectStmt));
|
|
|
|
/*
|
|
* Tests here are to reject constructs like
|
|
* (SELECT foo ORDER BY bar) ORDER BY baz
|
|
*/
|
|
if (sortClause)
|
|
{
|
|
if (stmt->sortClause)
|
|
ereport(ERROR,
|
|
(errcode(PG_ERRCODE_SYNTAX_ERROR),
|
|
errmsg("multiple ORDER BY clauses not allowed"),
|
|
parser_errposition(exprLocation((PGNode *) sortClause))));
|
|
stmt->sortClause = sortClause;
|
|
}
|
|
/* We can handle multiple locking clauses, though */
|
|
stmt->lockingClause = list_concat(stmt->lockingClause, lockingClause);
|
|
if (limitOffset)
|
|
{
|
|
if (stmt->limitOffset)
|
|
ereport(ERROR,
|
|
(errcode(PG_ERRCODE_SYNTAX_ERROR),
|
|
errmsg("multiple OFFSET clauses not allowed"),
|
|
parser_errposition(exprLocation(limitOffset))));
|
|
stmt->limitOffset = limitOffset;
|
|
}
|
|
if (limitCount)
|
|
{
|
|
if (stmt->limitCount)
|
|
ereport(ERROR,
|
|
(errcode(PG_ERRCODE_SYNTAX_ERROR),
|
|
errmsg("multiple LIMIT clauses not allowed"),
|
|
parser_errposition(exprLocation(limitCount))));
|
|
stmt->limitCount = limitCount;
|
|
}
|
|
if (limitOffset == isLimitOffsetFirst) {
|
|
stmt->offset_first = true;
|
|
}
|
|
if (withClause)
|
|
{
|
|
if (stmt->withClause)
|
|
ereport(ERROR,
|
|
(errcode(PG_ERRCODE_SYNTAX_ERROR),
|
|
errmsg("multiple WITH clauses not allowed"),
|
|
parser_errposition(exprLocation((PGNode *) withClause))));
|
|
stmt->withClause = withClause;
|
|
}
|
|
}
|
|
|
|
static PGNode *
|
|
makeSetOp(PGSetOperation op, bool all, PGNode *larg, PGNode *rarg)
|
|
{
|
|
PGSelectStmt *n = makeNode(PGSelectStmt);
|
|
|
|
n->op = op;
|
|
n->all = all;
|
|
n->larg = larg;
|
|
n->rarg = rarg;
|
|
return (PGNode *) n;
|
|
}
|
|
|
|
/* SystemFuncName()
|
|
* Build a properly-qualified reference to a built-in function.
|
|
*/
|
|
PGList *
|
|
SystemFuncName(const char *name)
|
|
{
|
|
return list_make2(makeString(DEFAULT_SCHEMA), makeString(name));
|
|
}
|
|
|
|
/* SystemTypeName()
|
|
* Build a properly-qualified reference to a built-in type.
|
|
*
|
|
* typmod is defaulted, but may be changed afterwards by caller.
|
|
* Likewise for the location.
|
|
*/
|
|
PGTypeName *
|
|
SystemTypeName(const char *name)
|
|
{
|
|
return makeTypeNameFromNameList(list_make1(makeString(name)));
|
|
}
|
|
|
|
/* doNegate()
|
|
* Handle negation of a numeric constant.
|
|
*
|
|
* Formerly, we did this here because the optimizer couldn't cope with
|
|
* indexquals that looked like "var = -4" --- it wants "var = const"
|
|
* and a unary minus operator applied to a constant didn't qualify.
|
|
* As of Postgres 7.0, that problem doesn't exist anymore because there
|
|
* is a constant-subexpression simplifier in the optimizer. However,
|
|
* there's still a good reason for doing this here, which is that we can
|
|
* postpone committing to a particular internal representation for simple
|
|
* negative constants. It's better to leave "-123.456" in string form
|
|
* until we know what the desired type is.
|
|
*/
|
|
static PGNode *
|
|
doNegate(PGNode *n, int location)
|
|
{
|
|
if (IsA(n, PGAConst))
|
|
{
|
|
PGAConst *con = (PGAConst *)n;
|
|
|
|
/* report the constant's location as that of the '-' sign */
|
|
con->location = location;
|
|
|
|
if (con->val.type == T_PGInteger)
|
|
{
|
|
con->val.val.ival = -con->val.val.ival;
|
|
return n;
|
|
}
|
|
if (con->val.type == T_PGFloat)
|
|
{
|
|
doNegateFloat(&con->val);
|
|
return n;
|
|
}
|
|
}
|
|
|
|
return (PGNode *) makeSimpleAExpr(PG_AEXPR_OP, "-", NULL, n, location);
|
|
}
|
|
|
|
static void
|
|
doNegateFloat(PGValue *v)
|
|
{
|
|
char *oldval = v->val.str;
|
|
|
|
Assert(IsA(v, PGFloat));
|
|
if (*oldval == '+')
|
|
oldval++;
|
|
if (*oldval == '-')
|
|
v->val.str = oldval+1; /* just strip the '-' */
|
|
else
|
|
v->val.str = psprintf("-%s", oldval);
|
|
}
|
|
|
|
static PGNode *
|
|
makeAndExpr(PGNode *lexpr, PGNode *rexpr, int location)
|
|
{
|
|
PGNode *lexp = lexpr;
|
|
|
|
/* Look through AEXPR_PAREN nodes so they don't affect flattening */
|
|
while (IsA(lexp, PGAExpr) &&
|
|
((PGAExpr *) lexp)->kind == AEXPR_PAREN)
|
|
lexp = ((PGAExpr *) lexp)->lexpr;
|
|
/* Flatten "a AND b AND c ..." to a single PGBoolExpr on sight */
|
|
if (IsA(lexp, PGBoolExpr))
|
|
{
|
|
PGBoolExpr *blexpr = (PGBoolExpr *) lexp;
|
|
|
|
if (blexpr->boolop == PG_AND_EXPR)
|
|
{
|
|
blexpr->args = lappend(blexpr->args, rexpr);
|
|
return (PGNode *) blexpr;
|
|
}
|
|
}
|
|
return (PGNode *) makeBoolExpr(PG_AND_EXPR, list_make2(lexpr, rexpr), location);
|
|
}
|
|
|
|
static PGNode *
|
|
makeOrExpr(PGNode *lexpr, PGNode *rexpr, int location)
|
|
{
|
|
PGNode *lexp = lexpr;
|
|
|
|
/* Look through AEXPR_PAREN nodes so they don't affect flattening */
|
|
while (IsA(lexp, PGAExpr) &&
|
|
((PGAExpr *) lexp)->kind == AEXPR_PAREN)
|
|
lexp = ((PGAExpr *) lexp)->lexpr;
|
|
/* Flatten "a OR b OR c ..." to a single PGBoolExpr on sight */
|
|
if (IsA(lexp, PGBoolExpr))
|
|
{
|
|
PGBoolExpr *blexpr = (PGBoolExpr *) lexp;
|
|
|
|
if (blexpr->boolop == PG_OR_EXPR)
|
|
{
|
|
blexpr->args = lappend(blexpr->args, rexpr);
|
|
return (PGNode *) blexpr;
|
|
}
|
|
}
|
|
return (PGNode *) makeBoolExpr(PG_OR_EXPR, list_make2(lexpr, rexpr), location);
|
|
}
|
|
|
|
static PGNode *
|
|
makeNotExpr(PGNode *expr, int location)
|
|
{
|
|
return (PGNode *) makeBoolExpr(PG_NOT_EXPR, list_make1(expr), location);
|
|
}
|
|
|
|
/* Separate PGConstraint nodes from COLLATE clauses in a */
|
|
static void
|
|
SplitColQualList(PGList *qualList,
|
|
PGList **constraintList, PGCollateClause **collClause,
|
|
core_yyscan_t yyscanner)
|
|
{
|
|
PGListCell *cell;
|
|
PGListCell *prev;
|
|
PGListCell *next;
|
|
|
|
*collClause = NULL;
|
|
prev = NULL;
|
|
for (cell = list_head(qualList); cell; cell = next)
|
|
{
|
|
PGNode *n = (PGNode *) lfirst(cell);
|
|
|
|
next = lnext(cell);
|
|
if (IsA(n, PGConstraint))
|
|
{
|
|
/* keep it in list */
|
|
prev = cell;
|
|
continue;
|
|
}
|
|
if (IsA(n, PGCollateClause))
|
|
{
|
|
PGCollateClause *c = (PGCollateClause *) n;
|
|
|
|
if (*collClause)
|
|
ereport(ERROR,
|
|
(errcode(PG_ERRCODE_SYNTAX_ERROR),
|
|
errmsg("multiple COLLATE clauses not allowed"),
|
|
parser_errposition(c->location)));
|
|
*collClause = c;
|
|
}
|
|
else
|
|
elog(ERROR, "unexpected node type %d", (int) n->type);
|
|
/* remove non-Constraint nodes from qualList */
|
|
qualList = list_delete_cell(qualList, cell, prev);
|
|
}
|
|
*constraintList = qualList;
|
|
}
|
|
|
|
/*
|
|
* Process result of ConstraintAttributeSpec, and set appropriate bool flags
|
|
* in the output command node. Pass NULL for any flags the particular
|
|
* command doesn't support.
|
|
*/
|
|
static void
|
|
processCASbits(int cas_bits, int location, const char *constrType,
|
|
bool *deferrable, bool *initdeferred, bool *not_valid,
|
|
bool *no_inherit, core_yyscan_t yyscanner)
|
|
{
|
|
/* defaults */
|
|
if (deferrable)
|
|
*deferrable = false;
|
|
if (initdeferred)
|
|
*initdeferred = false;
|
|
if (not_valid)
|
|
*not_valid = false;
|
|
|
|
if (cas_bits & (CAS_DEFERRABLE | CAS_INITIALLY_DEFERRED))
|
|
{
|
|
if (deferrable)
|
|
*deferrable = true;
|
|
else
|
|
ereport(ERROR,
|
|
(errcode(PG_ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
/* translator: %s is CHECK, UNIQUE, or similar */
|
|
errmsg("%s constraints cannot be marked DEFERRABLE",
|
|
constrType),
|
|
parser_errposition(location)));
|
|
}
|
|
|
|
if (cas_bits & CAS_INITIALLY_DEFERRED)
|
|
{
|
|
if (initdeferred)
|
|
*initdeferred = true;
|
|
else
|
|
ereport(ERROR,
|
|
(errcode(PG_ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
/* translator: %s is CHECK, UNIQUE, or similar */
|
|
errmsg("%s constraints cannot be marked DEFERRABLE",
|
|
constrType),
|
|
parser_errposition(location)));
|
|
}
|
|
|
|
if (cas_bits & CAS_NOT_VALID)
|
|
{
|
|
if (not_valid)
|
|
*not_valid = true;
|
|
else
|
|
ereport(ERROR,
|
|
(errcode(PG_ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
/* translator: %s is CHECK, UNIQUE, or similar */
|
|
errmsg("%s constraints cannot be marked NOT VALID",
|
|
constrType),
|
|
parser_errposition(location)));
|
|
}
|
|
|
|
if (cas_bits & CAS_NO_INHERIT)
|
|
{
|
|
if (no_inherit)
|
|
*no_inherit = true;
|
|
else
|
|
ereport(ERROR,
|
|
(errcode(PG_ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
/* translator: %s is CHECK, UNIQUE, or similar */
|
|
errmsg("%s constraints cannot be marked NO INHERIT",
|
|
constrType),
|
|
parser_errposition(location)));
|
|
}
|
|
}
|
|
|
|
/*----------
|
|
* Recursive view transformation
|
|
*
|
|
* Convert
|
|
*
|
|
* CREATE RECURSIVE VIEW relname (aliases) AS query
|
|
*
|
|
* to
|
|
*
|
|
* CREATE VIEW relname (aliases) AS
|
|
* WITH RECURSIVE relname (aliases) AS (query)
|
|
* SELECT aliases FROM relname
|
|
*
|
|
* Actually, just the WITH ... part, which is then inserted into the original
|
|
* view as the query.
|
|
* ----------
|
|
*/
|
|
static PGNode *
|
|
makeRecursiveViewSelect(char *relname, PGList *aliases, PGNode *query)
|
|
{
|
|
PGSelectStmt *s = makeNode(PGSelectStmt);
|
|
PGWithClause *w = makeNode(PGWithClause);
|
|
PGCommonTableExpr *cte = makeNode(PGCommonTableExpr);
|
|
PGList *tl = NIL;
|
|
PGListCell *lc;
|
|
|
|
/* create common table expression */
|
|
cte->ctename = relname;
|
|
cte->aliascolnames = aliases;
|
|
cte->ctequery = query;
|
|
cte->location = -1;
|
|
|
|
/* create WITH clause and attach CTE */
|
|
w->recursive = true;
|
|
w->ctes = list_make1(cte);
|
|
w->location = -1;
|
|
|
|
/* create target list for the new SELECT from the alias list of the
|
|
* recursive view specification */
|
|
foreach (lc, aliases)
|
|
{
|
|
PGResTarget *rt = makeNode(PGResTarget);
|
|
|
|
rt->name = NULL;
|
|
rt->indirection = NIL;
|
|
rt->val = makeColumnRef(strVal(lfirst(lc)), NIL, -1, 0);
|
|
rt->location = -1;
|
|
|
|
tl = lappend(tl, rt);
|
|
}
|
|
|
|
/* create new SELECT combining WITH clause, target list, and fake FROM
|
|
* clause */
|
|
s->withClause = w;
|
|
s->targetList = tl;
|
|
s->fromClause = list_make1(makeRangeVar(NULL, relname, -1));
|
|
|
|
return (PGNode *) s;
|
|
}
|
|
|
|
/* parser_init()
|
|
* Initialize to parse one query string
|
|
*/
|
|
void
|
|
parser_init(base_yy_extra_type *yyext)
|
|
{
|
|
yyext->parsetree = NIL; /* in case grammar forgets to set it */
|
|
}
|
|
|
|
#undef yyparse
|
|
#undef yylex
|
|
#undef yyerror
|
|
#undef yylval
|
|
#undef yychar
|
|
#undef yydebug
|
|
#undef yynerrs
|
|
#undef yylloc
|
|
|
|
} // namespace duckdb_libpgquery
|