should be it
This commit is contained in:
756
external/duckdb/third_party/libpg_query/grammar/grammar.cpp
vendored
Normal file
756
external/duckdb/third_party/libpg_query/grammar/grammar.cpp
vendored
Normal file
@@ -0,0 +1,756 @@
|
||||
/*
|
||||
* 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
|
||||
Reference in New Issue
Block a user