/* * 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