diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/parser/analyze.c | 95 | ||||
-rw-r--r-- | src/backend/parser/gram.y | 80 | ||||
-rw-r--r-- | src/include/nodes/parsenodes.h | 10 | ||||
-rw-r--r-- | src/include/parser/parse_node.h | 16 |
4 files changed, 190 insertions, 11 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 8a6ba1692e8..506e0631615 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -239,23 +239,107 @@ parse_sub_analyze(Node *parseTree, ParseState *parentParseState, } /* + * setQueryLocationAndLength + * Set query's location and length from statement and ParseState + * + * Some statements, like PreparableStmt, can be located within parentheses. + * For example "(SELECT 1)" or "COPY (UPDATE ...) to x;". For those, we + * cannot use the whole string from the statement's location or the SQL + * string would yield incorrectly. The parser will set stmt_len, reflecting + * the size of the statement within the parentheses. Thus, when stmt_len is + * available, we need to use it for the Query's stmt_len. + * + * For other cases, the parser can't provide the length of individual + * statements. However, we have the statement's location plus the length + * (p_stmt_len) and location (p_stmt_location) of the top level RawStmt, + * stored in pstate. Thus, the statement's length is the RawStmt's length + * minus how much we've advanced in the RawStmt's string. + */ +static void +setQueryLocationAndLength(ParseState *pstate, Query *qry, Node *parseTree) +{ + ParseLoc stmt_len = 0; + + /* + * If there is no information about the top RawStmt's length, leave it at + * 0 to use the whole string. + */ + if (pstate->p_stmt_len == 0) + return; + + switch (nodeTag(parseTree)) + { + case T_InsertStmt: + qry->stmt_location = ((InsertStmt *) parseTree)->stmt_location; + stmt_len = ((InsertStmt *) parseTree)->stmt_len; + break; + + case T_DeleteStmt: + qry->stmt_location = ((DeleteStmt *) parseTree)->stmt_location; + stmt_len = ((DeleteStmt *) parseTree)->stmt_len; + break; + + case T_UpdateStmt: + qry->stmt_location = ((UpdateStmt *) parseTree)->stmt_location; + stmt_len = ((UpdateStmt *) parseTree)->stmt_len; + break; + + case T_MergeStmt: + qry->stmt_location = ((MergeStmt *) parseTree)->stmt_location; + stmt_len = ((MergeStmt *) parseTree)->stmt_len; + break; + + case T_SelectStmt: + qry->stmt_location = ((SelectStmt *) parseTree)->stmt_location; + stmt_len = ((SelectStmt *) parseTree)->stmt_len; + break; + + case T_PLAssignStmt: + qry->stmt_location = ((PLAssignStmt *) parseTree)->location; + break; + + default: + qry->stmt_location = pstate->p_stmt_location; + break; + } + + if (stmt_len > 0) + { + /* Statement's length is known, use it */ + qry->stmt_len = stmt_len; + } + else + { + /* + * Compute the statement's length from the statement's location and + * the RawStmt's length and location. + */ + qry->stmt_len = pstate->p_stmt_len - (qry->stmt_location - pstate->p_stmt_location); + } + + /* The calculated statement length should be calculated as positive. */ + Assert(qry->stmt_len >= 0); +} + +/* * transformTopLevelStmt - * transform a Parse tree into a Query tree. * - * This function is just responsible for transferring statement location data - * from the RawStmt into the finished Query. + * This function is just responsible for storing location data + * from the RawStmt into the ParseState. */ Query * transformTopLevelStmt(ParseState *pstate, RawStmt *parseTree) { Query *result; + /* Store RawStmt's length and location in pstate */ + pstate->p_stmt_len = parseTree->stmt_len; + pstate->p_stmt_location = parseTree->stmt_location; + /* We're at top level, so allow SELECT INTO */ result = transformOptionalSelectInto(pstate, parseTree->stmt); - result->stmt_location = parseTree->stmt_location; - result->stmt_len = parseTree->stmt_len; - return result; } @@ -424,6 +508,7 @@ transformStmt(ParseState *pstate, Node *parseTree) /* Mark as original query until we learn differently */ result->querySource = QSRC_ORIGINAL; result->canSetTag = true; + setQueryLocationAndLength(pstate, result, parseTree); return result; } diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 4bab2117d96..baca4059d2e 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -153,6 +153,7 @@ static void base_yyerror(YYLTYPE *yylloc, core_yyscan_t yyscanner, const char *msg); static RawStmt *makeRawStmt(Node *stmt, int stmt_location); static void updateRawStmtEnd(RawStmt *rs, int end_location); +static void updatePreparableStmtEnd(Node *n, int end_location); static Node *makeColumnRef(char *colname, List *indirection, int location, core_yyscan_t yyscanner); static Node *makeTypeCast(Node *arg, TypeName *typename, int location); @@ -176,7 +177,7 @@ static void insertSelectOptions(SelectStmt *stmt, SelectLimit *limitClause, WithClause *withClause, core_yyscan_t yyscanner); -static Node *makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg); +static Node *makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg, int location); static Node *doNegate(Node *n, int location); static void doNegateFloat(Float *v); static Node *makeAndExpr(Node *lexpr, Node *rexpr, int location); @@ -3383,6 +3384,7 @@ CopyStmt: COPY opt_binary qualified_name opt_column_list { CopyStmt *n = makeNode(CopyStmt); + updatePreparableStmtEnd($3, @4); n->relation = NULL; n->query = $3; n->attlist = NIL; @@ -12150,6 +12152,7 @@ InsertStmt: $5->onConflictClause = $6; $5->returningList = $7; $5->withClause = $1; + $5->stmt_location = @$; $$ = (Node *) $5; } ; @@ -12303,6 +12306,7 @@ DeleteStmt: opt_with_clause DELETE_P FROM relation_expr_opt_alias n->whereClause = $6; n->returningList = $7; n->withClause = $1; + n->stmt_location = @$; $$ = (Node *) n; } ; @@ -12377,6 +12381,7 @@ UpdateStmt: opt_with_clause UPDATE relation_expr_opt_alias n->whereClause = $7; n->returningList = $8; n->withClause = $1; + n->stmt_location = @$; $$ = (Node *) n; } ; @@ -12454,6 +12459,7 @@ MergeStmt: m->joinCondition = $8; m->mergeWhenClauses = $9; m->returningList = $10; + m->stmt_location = @$; $$ = (Node *) m; } @@ -12694,7 +12700,20 @@ SelectStmt: select_no_parens %prec UMINUS ; select_with_parens: - '(' select_no_parens ')' { $$ = $2; } + '(' select_no_parens ')' + { + SelectStmt *n = (SelectStmt *) $2; + + /* + * As SelectStmt's location starts at the SELECT keyword, + * we need to track the length of the SelectStmt within + * parentheses to be able to extract the relevant part + * of the query. Without this, the RawStmt's length would + * be used and would include the closing parenthesis. + */ + n->stmt_len = @3 - @2; + $$ = $2; + } | '(' select_with_parens ')' { $$ = $2; } ; @@ -12816,6 +12835,7 @@ simple_select: n->groupDistinct = ($7)->distinct; n->havingClause = $8; n->windowClause = $9; + n->stmt_location = @1; $$ = (Node *) n; } | SELECT distinct_clause target_list @@ -12833,6 +12853,7 @@ simple_select: n->groupDistinct = ($7)->distinct; n->havingClause = $8; n->windowClause = $9; + n->stmt_location = @1; $$ = (Node *) n; } | values_clause { $$ = $1; } @@ -12853,19 +12874,20 @@ simple_select: n->targetList = list_make1(rt); n->fromClause = list_make1($2); + n->stmt_location = @1; $$ = (Node *) n; } | select_clause UNION set_quantifier select_clause { - $$ = makeSetOp(SETOP_UNION, $3 == SET_QUANTIFIER_ALL, $1, $4); + $$ = makeSetOp(SETOP_UNION, $3 == SET_QUANTIFIER_ALL, $1, $4, @1); } | select_clause INTERSECT set_quantifier select_clause { - $$ = makeSetOp(SETOP_INTERSECT, $3 == SET_QUANTIFIER_ALL, $1, $4); + $$ = makeSetOp(SETOP_INTERSECT, $3 == SET_QUANTIFIER_ALL, $1, $4, @1); } | select_clause EXCEPT set_quantifier select_clause { - $$ = makeSetOp(SETOP_EXCEPT, $3 == SET_QUANTIFIER_ALL, $1, $4); + $$ = makeSetOp(SETOP_EXCEPT, $3 == SET_QUANTIFIER_ALL, $1, $4, @1); } ; @@ -13423,6 +13445,7 @@ values_clause: { SelectStmt *n = makeNode(SelectStmt); + n->stmt_location = @1; n->valuesLists = list_make1($3); $$ = (Node *) n; } @@ -18565,6 +18588,47 @@ updateRawStmtEnd(RawStmt *rs, int end_location) rs->stmt_len = end_location - rs->stmt_location; } +/* + * Adjust a PreparableStmt to reflect that it doesn't run to the end of the + * string. + */ +static void +updatePreparableStmtEnd(Node *n, int end_location) +{ + if (IsA(n, SelectStmt)) + { + SelectStmt *stmt = (SelectStmt *)n; + + stmt->stmt_len = end_location - stmt->stmt_location; + } + else if (IsA(n, InsertStmt)) + { + InsertStmt *stmt = (InsertStmt *)n; + + stmt->stmt_len = end_location - stmt->stmt_location; + } + else if (IsA(n, UpdateStmt)) + { + UpdateStmt *stmt = (UpdateStmt *)n; + + stmt->stmt_len = end_location - stmt->stmt_location; + } + else if (IsA(n, DeleteStmt)) + { + DeleteStmt *stmt = (DeleteStmt *)n; + + stmt->stmt_len = end_location - stmt->stmt_location; + } + else if (IsA(n, MergeStmt)) + { + MergeStmt *stmt = (MergeStmt *)n; + + stmt->stmt_len = end_location - stmt->stmt_location; + } + else + elog(ERROR, "unexpected node type %d", (int) n->type); +} + static Node * makeColumnRef(char *colname, List *indirection, int location, core_yyscan_t yyscanner) @@ -18943,11 +19007,14 @@ insertSelectOptions(SelectStmt *stmt, errmsg("multiple WITH clauses not allowed"), parser_errposition(exprLocation((Node *) withClause)))); stmt->withClause = withClause; + + /* Update SelectStmt's location to the start of the WITH clause */ + stmt->stmt_location = withClause->location; } } static Node * -makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg) +makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg, int location) { SelectStmt *n = makeNode(SelectStmt); @@ -18955,6 +19022,7 @@ makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg) n->all = all; n->larg = (SelectStmt *) larg; n->rarg = (SelectStmt *) rarg; + n->stmt_location = location; return (Node *) n; } diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index c92cef3d16d..b40b661ec8a 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -2046,6 +2046,8 @@ typedef struct InsertStmt List *returningList; /* list of expressions to return */ WithClause *withClause; /* WITH clause */ OverridingKind override; /* OVERRIDING clause */ + ParseLoc stmt_location; /* start location, or -1 if unknown */ + ParseLoc stmt_len; /* length in bytes; 0 means "rest of string" */ } InsertStmt; /* ---------------------- @@ -2060,6 +2062,8 @@ typedef struct DeleteStmt Node *whereClause; /* qualifications */ List *returningList; /* list of expressions to return */ WithClause *withClause; /* WITH clause */ + ParseLoc stmt_location; /* start location, or -1 if unknown */ + ParseLoc stmt_len; /* length in bytes; 0 means "rest of string" */ } DeleteStmt; /* ---------------------- @@ -2075,6 +2079,8 @@ typedef struct UpdateStmt List *fromClause; /* optional from clause for more tables */ List *returningList; /* list of expressions to return */ WithClause *withClause; /* WITH clause */ + ParseLoc stmt_location; /* start location, or -1 if unknown */ + ParseLoc stmt_len; /* length in bytes; 0 means "rest of string" */ } UpdateStmt; /* ---------------------- @@ -2090,6 +2096,8 @@ typedef struct MergeStmt List *mergeWhenClauses; /* list of MergeWhenClause(es) */ List *returningList; /* list of expressions to return */ WithClause *withClause; /* WITH clause */ + ParseLoc stmt_location; /* start location, or -1 if unknown */ + ParseLoc stmt_len; /* length in bytes; 0 means "rest of string" */ } MergeStmt; /* ---------------------- @@ -2159,6 +2167,8 @@ typedef struct SelectStmt bool all; /* ALL specified? */ struct SelectStmt *larg; /* left child */ struct SelectStmt *rarg; /* right child */ + ParseLoc stmt_location; /* start location, or -1 if unknown */ + ParseLoc stmt_len; /* length in bytes; 0 means "rest of string" */ /* Eventually add fields for CORRESPONDING spec here */ } SelectStmt; diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index 543df568147..2375e95c107 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -108,6 +108,20 @@ typedef Node *(*CoerceParamHook) (ParseState *pstate, Param *param, * byte-wise locations in parse structures to character-wise cursor * positions.) * + * p_stmt_location: location of the top level RawStmt's start. During + * transformation, the Query's location will be set to the statement's + * location if available. Otherwise, the RawStmt's start location will + * be used. Propagating the location through ParseState is needed for + * the Query length calculation (see p_stmt_len below). + * + * p_stmt_len: length of the top level RawStmt. Most of the time, the + * statement's length is not provided by the parser, with the exception + * of SelectStmt within parentheses and PreparableStmt in COPY. If the + * statement's location is provided by the parser, the top-level location + * and length are needed to accurately compute the Query's length. If the + * statement's location is not provided, the RawStmt's length can be used + * directly. + * * p_rtable: list of RTEs that will become the rangetable of the query. * Note that neither relname nor refname of these entries are necessarily * unique; searching the rtable by name is a bad idea. @@ -193,6 +207,8 @@ struct ParseState { ParseState *parentParseState; /* stack link */ const char *p_sourcetext; /* source text, or NULL if not available */ + ParseLoc p_stmt_location; /* start location, or -1 if unknown */ + ParseLoc p_stmt_len; /* length in bytes; 0 means "rest of string" */ List *p_rtable; /* range table so far */ List *p_rteperminfos; /* list of RTEPermissionInfo nodes for each * RTE_RELATION entry in rtable */ |