SELECT toplevel, calls, query FROM pg_stat_statements
ORDER BY query COLLATE "C";
- toplevel | calls | query
-----------+-------+---------------------------------------------------------------------
+ toplevel | calls | query
+----------+-------+--------------------------------------------------------------------
+ f | 1 | DELETE FROM stats_track_tab
t | 1 | EXPLAIN (COSTS OFF) (SELECT $1, $2)
- f | 1 | EXPLAIN (COSTS OFF) (SELECT $1, $2);
t | 1 | EXPLAIN (COSTS OFF) (TABLE test_table)
- f | 1 | EXPLAIN (COSTS OFF) (TABLE test_table);
t | 1 | EXPLAIN (COSTS OFF) (VALUES ($1, $2))
- f | 1 | EXPLAIN (COSTS OFF) (VALUES ($1, $2));
t | 1 | EXPLAIN (COSTS OFF) DELETE FROM stats_track_tab
- f | 1 | EXPLAIN (COSTS OFF) DELETE FROM stats_track_tab;
t | 1 | EXPLAIN (COSTS OFF) INSERT INTO stats_track_tab VALUES (($1))
- f | 1 | EXPLAIN (COSTS OFF) INSERT INTO stats_track_tab VALUES (($1));
- t | 1 | EXPLAIN (COSTS OFF) MERGE INTO stats_track_tab +
- | | USING (SELECT id FROM generate_series($1, $2) id) ON x = id +
- | | WHEN MATCHED THEN UPDATE SET x = id +
+ t | 1 | EXPLAIN (COSTS OFF) MERGE INTO stats_track_tab +
+ | | USING (SELECT id FROM generate_series($1, $2) id) ON x = id +
+ | | WHEN MATCHED THEN UPDATE SET x = id +
| | WHEN NOT MATCHED THEN INSERT (x) VALUES (id)
- f | 1 | EXPLAIN (COSTS OFF) MERGE INTO stats_track_tab +
- | | USING (SELECT id FROM generate_series($1, $2) id) ON x = id +
- | | WHEN MATCHED THEN UPDATE SET x = id +
- | | WHEN NOT MATCHED THEN INSERT (x) VALUES (id);
t | 1 | EXPLAIN (COSTS OFF) SELECT $1
t | 1 | EXPLAIN (COSTS OFF) SELECT $1 UNION SELECT $2
- f | 1 | EXPLAIN (COSTS OFF) SELECT $1 UNION SELECT $2;
- f | 1 | EXPLAIN (COSTS OFF) SELECT $1;
t | 1 | EXPLAIN (COSTS OFF) TABLE stats_track_tab
- f | 1 | EXPLAIN (COSTS OFF) TABLE stats_track_tab;
t | 1 | EXPLAIN (COSTS OFF) UPDATE stats_track_tab SET x = $1 WHERE x = $2
- f | 1 | EXPLAIN (COSTS OFF) UPDATE stats_track_tab SET x = $1 WHERE x = $2;
t | 1 | EXPLAIN (COSTS OFF) VALUES ($1)
- f | 1 | EXPLAIN (COSTS OFF) VALUES ($1);
+ f | 1 | INSERT INTO stats_track_tab VALUES (($1))
+ f | 1 | MERGE INTO stats_track_tab +
+ | | USING (SELECT id FROM generate_series($1, $2) id) ON x = id +
+ | | WHEN MATCHED THEN UPDATE SET x = id +
+ | | WHEN NOT MATCHED THEN INSERT (x) VALUES (id)
+ f | 1 | SELECT $1
+ f | 1 | SELECT $1 UNION SELECT $2
+ f | 1 | SELECT $1, $2
t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
+ f | 1 | TABLE stats_track_tab
+ f | 1 | TABLE test_table
+ f | 1 | UPDATE stats_track_tab SET x = $1 WHERE x = $2
+ f | 1 | VALUES ($1)
+ f | 1 | VALUES ($1, $2)
(23 rows)
-- EXPLAIN - top-level tracking.
SELECT toplevel, calls, query FROM pg_stat_statements
ORDER BY query COLLATE "C";
- toplevel | calls | query
-----------+-------+---------------------------------------------------------------------------------------------------------------------
+ toplevel | calls | query
+----------+-------+-----------------------------------------------------------------
+ f | 1 | (SELECT $1, $2, $3) UNION SELECT $4, $5, $6
t | 1 | EXPLAIN (COSTS OFF) (SELECT $1, $2, $3)
t | 1 | EXPLAIN (COSTS OFF) (SELECT $1, $2, $3) UNION SELECT $4, $5, $6
- f | 1 | EXPLAIN (COSTS OFF) (SELECT $1, $2, $3); EXPLAIN (COSTS OFF) (SELECT 1, 2, 3, 4);
t | 1 | EXPLAIN (COSTS OFF) (SELECT $1, $2, $3, $4)
- f | 1 | EXPLAIN (COSTS OFF) (SELECT 1, 2, 3); EXPLAIN (COSTS OFF) (SELECT $1, $2, $3, $4);
t | 1 | EXPLAIN (COSTS OFF) SELECT $1
t | 1 | EXPLAIN (COSTS OFF) SELECT $1, $2
t | 1 | EXPLAIN (COSTS OFF) SELECT $1, $2 UNION SELECT $3, $4
- f | 1 | EXPLAIN (COSTS OFF) SELECT $1, $2 UNION SELECT $3, $4; EXPLAIN (COSTS OFF) (SELECT 1, 2, 3) UNION SELECT 3, 4, 5;
- f | 1 | EXPLAIN (COSTS OFF) SELECT $1; EXPLAIN (COSTS OFF) SELECT 1, 2;
- f | 1 | EXPLAIN (COSTS OFF) SELECT 1, 2 UNION SELECT 3, 4; EXPLAIN (COSTS OFF) (SELECT $1, $2, $3) UNION SELECT $4, $5, $6;
- f | 1 | EXPLAIN (COSTS OFF) SELECT 1; EXPLAIN (COSTS OFF) SELECT $1, $2;
+ f | 1 | SELECT $1
+ f | 1 | SELECT $1, $2
+ f | 1 | SELECT $1, $2 UNION SELECT $3, $4
+ f | 1 | SELECT $1, $2, $3
+ f | 1 | SELECT $1, $2, $3, $4
t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(13 rows)
SELECT toplevel, calls, query FROM pg_stat_statements
ORDER BY query COLLATE "C";
- toplevel | calls | query
-----------+-------+----------------------------------------------------------------------------------------------------------------------------------
+ toplevel | calls | query
+----------+-------+--------------------------------------------------------------------
+ f | 1 | DELETE FROM stats_track_tab
+ f | 1 | DELETE FROM stats_track_tab WHERE x = $1
t | 1 | EXPLAIN (COSTS OFF) (TABLE test_table)
t | 1 | EXPLAIN (COSTS OFF) (VALUES ($1, $2))
t | 1 | EXPLAIN (COSTS OFF) DELETE FROM stats_track_tab
t | 1 | EXPLAIN (COSTS OFF) DELETE FROM stats_track_tab WHERE x = $1
- f | 1 | EXPLAIN (COSTS OFF) DELETE FROM stats_track_tab; EXPLAIN (COSTS OFF) DELETE FROM stats_track_tab WHERE x = $1;
- f | 1 | EXPLAIN (COSTS OFF) DELETE FROM stats_track_tab; EXPLAIN (COSTS OFF) DELETE FROM stats_track_tab WHERE x = 1;
t | 1 | EXPLAIN (COSTS OFF) INSERT INTO stats_track_tab VALUES ($1), ($2)
t | 1 | EXPLAIN (COSTS OFF) INSERT INTO stats_track_tab VALUES (($1))
- f | 1 | EXPLAIN (COSTS OFF) INSERT INTO stats_track_tab VALUES (($1)); EXPLAIN (COSTS OFF) INSERT INTO stats_track_tab VALUES (1), (2);
- f | 1 | EXPLAIN (COSTS OFF) INSERT INTO stats_track_tab VALUES ((1)); EXPLAIN (COSTS OFF) INSERT INTO stats_track_tab VALUES ($1), ($2);
t | 1 | EXPLAIN (COSTS OFF) TABLE stats_track_tab
- f | 1 | EXPLAIN (COSTS OFF) TABLE stats_track_tab; EXPLAIN (COSTS OFF) (TABLE test_table);
- f | 1 | EXPLAIN (COSTS OFF) TABLE stats_track_tab; EXPLAIN (COSTS OFF) (TABLE test_table);
t | 1 | EXPLAIN (COSTS OFF) UPDATE stats_track_tab SET x = $1
t | 1 | EXPLAIN (COSTS OFF) UPDATE stats_track_tab SET x = $1 WHERE x = $2
- f | 1 | EXPLAIN (COSTS OFF) UPDATE stats_track_tab SET x = $1 WHERE x = $2; EXPLAIN (COSTS OFF) UPDATE stats_track_tab SET x = 1;
- f | 1 | EXPLAIN (COSTS OFF) UPDATE stats_track_tab SET x = 1 WHERE x = 1; EXPLAIN (COSTS OFF) UPDATE stats_track_tab SET x = $1;
t | 1 | EXPLAIN (COSTS OFF) VALUES ($1)
- f | 1 | EXPLAIN (COSTS OFF) VALUES ($1); EXPLAIN (COSTS OFF) (VALUES (1, 2));
- f | 1 | EXPLAIN (COSTS OFF) VALUES (1); EXPLAIN (COSTS OFF) (VALUES ($1, $2));
+ f | 1 | INSERT INTO stats_track_tab VALUES ($1), ($2)
+ f | 1 | INSERT INTO stats_track_tab VALUES (($1))
t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
+ f | 1 | TABLE stats_track_tab
+ f | 1 | TABLE test_table
+ f | 1 | UPDATE stats_track_tab SET x = $1
+ f | 1 | UPDATE stats_track_tab SET x = $1 WHERE x = $2
+ f | 1 | VALUES ($1)
+ f | 1 | VALUES ($1, $2)
(21 rows)
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT toplevel, calls, query FROM pg_stat_statements
ORDER BY query COLLATE "C";
- toplevel | calls | query
-----------+-------+------------------------------------------------------------------------------------------------
- t | 1 | EXPLAIN (COSTS OFF) MERGE INTO stats_track_tab +
- | | USING (SELECT id FROM generate_series($1, $2) id) ON x = id +
- | | WHEN MATCHED THEN UPDATE SET x = id +
+ toplevel | calls | query
+----------+-------+---------------------------------------------------------------
+ t | 1 | EXPLAIN (COSTS OFF) MERGE INTO stats_track_tab +
+ | | USING (SELECT id FROM generate_series($1, $2) id) ON x = id+
+ | | WHEN MATCHED THEN UPDATE SET x = id +
| | WHEN NOT MATCHED THEN INSERT (x) VALUES (id)
- f | 1 | EXPLAIN (COSTS OFF) MERGE INTO stats_track_tab +
- | | USING (SELECT id FROM generate_series($1, $2) id) ON x = id +
- | | WHEN MATCHED THEN UPDATE SET x = id +
- | | WHEN NOT MATCHED THEN INSERT (x) VALUES (id); EXPLAIN (COSTS OFF) SELECT 1, 2, 3, 4, 5;
- f | 1 | EXPLAIN (COSTS OFF) MERGE INTO stats_track_tab +
- | | USING (SELECT id FROM generate_series(1, 10) id) ON x = id +
- | | WHEN MATCHED THEN UPDATE SET x = id +
- | | WHEN NOT MATCHED THEN INSERT (x) VALUES (id); EXPLAIN (COSTS OFF) SELECT $1, $2, $3, $4, $5;
t | 1 | EXPLAIN (COSTS OFF) SELECT $1, $2, $3, $4, $5
+ f | 1 | MERGE INTO stats_track_tab +
+ | | USING (SELECT id FROM generate_series($1, $2) id) ON x = id+
+ | | WHEN MATCHED THEN UPDATE SET x = id +
+ | | WHEN NOT MATCHED THEN INSERT (x) VALUES (id)
+ f | 1 | SELECT $1, $2, $3, $4, $5
t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
SELECT toplevel, calls, query FROM pg_stat_statements
ORDER BY query COLLATE "C";
- toplevel | calls | query
-----------+-------+-------------------------------------------------------------------------------------------
+ toplevel | calls | query
+----------+-------+------------------------------------------------------------------------------------------
t | 1 | EXPLAIN (COSTS OFF) (WITH a AS (SELECT $1) (SELECT $2, $3))
- f | 1 | EXPLAIN (COSTS OFF) (WITH a AS (SELECT $1) (SELECT $2, $3));
t | 1 | EXPLAIN (COSTS OFF) WITH a AS (SELECT $1) DELETE FROM stats_track_tab
- f | 1 | EXPLAIN (COSTS OFF) WITH a AS (SELECT $1) DELETE FROM stats_track_tab;
t | 1 | EXPLAIN (COSTS OFF) WITH a AS (SELECT $1) INSERT INTO stats_track_tab VALUES (($2))
- f | 1 | EXPLAIN (COSTS OFF) WITH a AS (SELECT $1) INSERT INTO stats_track_tab VALUES (($2));
- t | 1 | EXPLAIN (COSTS OFF) WITH a AS (SELECT $1) MERGE INTO stats_track_tab +
- | | USING (SELECT id FROM generate_series($2, $3) id) ON x = id +
- | | WHEN MATCHED THEN UPDATE SET x = id +
+ t | 1 | EXPLAIN (COSTS OFF) WITH a AS (SELECT $1) MERGE INTO stats_track_tab +
+ | | USING (SELECT id FROM generate_series($2, $3) id) ON x = id +
+ | | WHEN MATCHED THEN UPDATE SET x = id +
| | WHEN NOT MATCHED THEN INSERT (x) VALUES (id)
- f | 1 | EXPLAIN (COSTS OFF) WITH a AS (SELECT $1) MERGE INTO stats_track_tab +
- | | USING (SELECT id FROM generate_series($2, $3) id) ON x = id +
- | | WHEN MATCHED THEN UPDATE SET x = id +
- | | WHEN NOT MATCHED THEN INSERT (x) VALUES (id);
t | 1 | EXPLAIN (COSTS OFF) WITH a AS (SELECT $1) SELECT $2
- f | 1 | EXPLAIN (COSTS OFF) WITH a AS (SELECT $1) SELECT $2;
t | 1 | EXPLAIN (COSTS OFF) WITH a AS (SELECT $1) UPDATE stats_track_tab SET x = $2 WHERE x = $3
- f | 1 | EXPLAIN (COSTS OFF) WITH a AS (SELECT $1) UPDATE stats_track_tab SET x = $2 WHERE x = $3;
t | 1 | EXPLAIN (COSTS OFF) WITH a AS (select $1) SELECT $2 UNION SELECT $3
- f | 1 | EXPLAIN (COSTS OFF) WITH a AS (select $1) SELECT $2 UNION SELECT $3;
t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
+ f | 1 | WITH a AS (SELECT $1) (SELECT $2, $3)
+ f | 1 | WITH a AS (SELECT $1) DELETE FROM stats_track_tab
+ f | 1 | WITH a AS (SELECT $1) INSERT INTO stats_track_tab VALUES (($2))
+ f | 1 | WITH a AS (SELECT $1) MERGE INTO stats_track_tab +
+ | | USING (SELECT id FROM generate_series($2, $3) id) ON x = id +
+ | | WHEN MATCHED THEN UPDATE SET x = id +
+ | | WHEN NOT MATCHED THEN INSERT (x) VALUES (id)
+ f | 1 | WITH a AS (SELECT $1) SELECT $2
+ f | 1 | WITH a AS (SELECT $1) UPDATE stats_track_tab SET x = $2 WHERE x = $3
+ f | 1 | WITH a AS (select $1) SELECT $2 UNION SELECT $3
(15 rows)
-- EXPLAIN with CTEs - top-level tracking
SELECT toplevel, calls, query FROM pg_stat_statements
ORDER BY query COLLATE "C";
- toplevel | calls | query
-----------+-------+------------------------------------------------------------------
- t | 1 | EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF) +
+ toplevel | calls | query
+----------+-------+-----------------------------------------------------------------
+ t | 1 | EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF) +
| | DECLARE foocur CURSOR FOR SELECT * FROM stats_track_tab
t | 1 | EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF) SELECT $1
- f | 1 | EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF) SELECT $1;
+ f | 1 | SELECT $1
t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(4 rows)
----------+-------+-----------------------------------------------------------------
t | 1 | CREATE TEMPORARY TABLE pgss_ctas_1 AS SELECT $1
t | 1 | CREATE TEMPORARY TABLE pgss_ctas_2 AS EXECUTE test_prepare_pgss
- f | 1 | PREPARE test_prepare_pgss AS select generate_series($1, $2)
t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
+ f | 1 | select generate_series($1, $2)
(4 rows)
-- CREATE TABLE AS, top-level tracking.
2
SELECT toplevel, calls, query FROM pg_stat_statements
ORDER BY query COLLATE "C";
- toplevel | calls | query
-----------+-------+-----------------------------------------------------------------------------
- f | 1 | COPY (DELETE FROM stats_track_tab WHERE x = $1 RETURNING x) TO stdout
+ toplevel | calls | query
+----------+-------+---------------------------------------------------------------------------
t | 1 | COPY (DELETE FROM stats_track_tab WHERE x = 2 RETURNING x) TO stdout
- f | 1 | COPY (INSERT INTO stats_track_tab (x) VALUES ($1) RETURNING x) TO stdout
t | 1 | COPY (INSERT INTO stats_track_tab (x) VALUES (1) RETURNING x) TO stdout
- f | 1 | COPY (MERGE INTO stats_track_tab USING (SELECT $1 id) ON x = id +
- | | WHEN MATCHED THEN UPDATE SET x = id +
- | | WHEN NOT MATCHED THEN INSERT (x) VALUES (id) RETURNING x) TO stdout
- t | 1 | COPY (MERGE INTO stats_track_tab USING (SELECT 1 id) ON x = id +
- | | WHEN MATCHED THEN UPDATE SET x = id +
+ t | 1 | COPY (MERGE INTO stats_track_tab USING (SELECT 1 id) ON x = id +
+ | | WHEN MATCHED THEN UPDATE SET x = id +
| | WHEN NOT MATCHED THEN INSERT (x) VALUES (id) RETURNING x) TO stdout
- f | 1 | COPY (SELECT $1 UNION SELECT $2) TO stdout
- f | 1 | COPY (SELECT $1) TO stdout
t | 1 | COPY (SELECT 1 UNION SELECT 2) TO stdout
t | 1 | COPY (SELECT 1) TO stdout
- f | 1 | COPY (UPDATE stats_track_tab SET x = $1 WHERE x = $2 RETURNING x) TO stdout
t | 1 | COPY (UPDATE stats_track_tab SET x = 2 WHERE x = 1 RETURNING x) TO stdout
+ f | 1 | DELETE FROM stats_track_tab WHERE x = $1 RETURNING x
+ f | 1 | INSERT INTO stats_track_tab (x) VALUES ($1) RETURNING x
+ f | 1 | MERGE INTO stats_track_tab USING (SELECT $1 id) ON x = id +
+ | | WHEN MATCHED THEN UPDATE SET x = id +
+ | | WHEN NOT MATCHED THEN INSERT (x) VALUES (id) RETURNING x
+ f | 1 | SELECT $1
+ f | 1 | SELECT $1 UNION SELECT $2
t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
+ f | 1 | UPDATE stats_track_tab SET x = $1 WHERE x = $2 RETURNING x
(13 rows)
-- COPY - top-level tracking.
return query;
}
+/*
+ * 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;
}
/* Mark as original query until we learn differently */
result->querySource = QSRC_ORIGINAL;
result->canSetTag = true;
+ setQueryLocationAndLength(pstate, result, parseTree);
return result;
}
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);
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);
{
CopyStmt *n = makeNode(CopyStmt);
+ updatePreparableStmtEnd($3, @4);
n->relation = NULL;
n->query = $3;
n->attlist = NIL;
$5->onConflictClause = $6;
$5->returningList = $7;
$5->withClause = $1;
+ $5->stmt_location = @$;
$$ = (Node *) $5;
}
;
n->whereClause = $6;
n->returningList = $7;
n->withClause = $1;
+ n->stmt_location = @$;
$$ = (Node *) n;
}
;
n->whereClause = $7;
n->returningList = $8;
n->withClause = $1;
+ n->stmt_location = @$;
$$ = (Node *) n;
}
;
m->joinCondition = $8;
m->mergeWhenClauses = $9;
m->returningList = $10;
+ m->stmt_location = @$;
$$ = (Node *) m;
}
;
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; }
;
n->groupDistinct = ($7)->distinct;
n->havingClause = $8;
n->windowClause = $9;
+ n->stmt_location = @1;
$$ = (Node *) n;
}
| SELECT distinct_clause target_list
n->groupDistinct = ($7)->distinct;
n->havingClause = $8;
n->windowClause = $9;
+ n->stmt_location = @1;
$$ = (Node *) n;
}
| values_clause { $$ = $1; }
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);
}
;
{
SelectStmt *n = makeNode(SelectStmt);
+ n->stmt_location = @1;
n->valuesLists = list_make1($3);
$$ = (Node *) n;
}
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)
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);
n->all = all;
n->larg = (SelectStmt *) larg;
n->rarg = (SelectStmt *) rarg;
+ n->stmt_location = location;
return (Node *) n;
}