Yes. I'll try to put together a patch and submit it to the next CF.
Here it is. I'll add this to the next CF.
This patch fixes the handling of compound/combined queries by
pg_stat_statements (i.e. several queries sent together, eg with psql:
"SELECT 1 \; SELECT 2;").
This bug was found in passing while investigating a strange performance
issue I had with compound statements.
Collect query location information in parser:
* create a ParseNode for statements with location information fields
(qlocation & qlength).
* add these fields to all parsed statements (*Stmt), Query and PlannedStmt.
* statements location is filled in:
- the lexer keep track of the last ';' encountered.
- the information is used by the parser to compute the query length,
which depends on whether the query ended on ';' or on the input end.
- the query location is propagated to Query and PlannedStmt when built.
Fix pg_stat_statement:
* the information is used by pg_stat_statement so as to extract the relevant
part
of the query string, including some trimming.
* pg_stat_statement validation is greatly extended so as to test options
and exercise various cases, including compound statements.
note 1: two non-statements tags (use for alter table commands) have been
moved so that all statements tags are contiguous and easy to check.
note 2: the impact on the lexer & parser is quite minimal with this
approach, about 30 LOC, most of which in one function. The largest changes
are in the node header to add location fields.
note 3: the query length excludes the final separator, so that
;-terminated and end of input terminated queries show the same.
note 4: the added test suggests that when tracking "all", queries in SQL
user functions are not tracked, this might be a bug.
--
Fabien.
diff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.out
index 3573c19..3826d38 100644
--- a/contrib/pg_stat_statements/expected/pg_stat_statements.out
+++ b/contrib/pg_stat_statements/expected/pg_stat_statements.out
@@ -1,21 +1,346 @@
CREATE EXTENSION pg_stat_statements;
-CREATE TABLE test (a int, b char(20));
--- test the basic functionality of pg_stat_statements
+--
+--
+-- simple and compound statements
+--
+SET pg_stat_statements.track_utility = FALSE;
SELECT pg_stat_statements_reset();
pg_stat_statements_reset
--------------------------
(1 row)
+SELECT 1 AS "int";
+ int
+-----
+ 1
+(1 row)
+
+SELECT 'hello'
+ -- multiline
+ AS "text";
+ text
+-------
+ hello
+(1 row)
+
+SELECT 'world' AS "text";
+ text
+-------
+ world
+(1 row)
+
+-- transaction
+BEGIN;
+SELECT 1 AS "int";
+ int
+-----
+ 1
+(1 row)
+
+SELECT 'hello' AS "text";
+ text
+-------
+ hello
+(1 row)
+
+COMMIT;
+-- compound transaction
+BEGIN \;
+SELECT 2.0 AS "float" \;
+SELECT 'world' AS "text" \;
+COMMIT;
+-- compound with empty statements and spurious leading spacing
+\;\; SELECT 3 + 3 \;\;\; SELECT ' ' || ' !' \;\; SELECT 1 + 4 \;;
+ ?column?
+----------
+ 5
+(1 row)
+
+-- non ;-terminated statements
+SELECT 1 + 1 + 1 AS "add" \gset
+SELECT :add + 1 + 1 AS "add" \;
+SELECT :add + 1 + 1 AS "add" \gset
+-- set operator
+SELECT 1 AS i UNION SELECT 2 ORDER BY i;
+ i
+---
+ 1
+ 2
+(2 rows)
+
+-- cte
+WITH t(f) AS (
+ VALUES (1.0), (2.0)
+)
+ SELECT f FROM t ORDER BY f;
+ f
+-----
+ 1.0
+ 2.0
+(2 rows)
+
+SELECT query, calls, rows FROM pg_stat_statements ORDER BY rows, query;
+ query | calls | rows
+-----------------------------------------+-------+------
+ SELECT ? || ? | 1 | 1
+ SELECT ? AS "float" | 1 | 1
+ | 1 | 1
+ SELECT ? + ? | 2 | 2
+ SELECT ? AS "int" | 2 | 2
+ SELECT ? AS i UNION SELECT ? ORDER BY i | 1 | 2
+ WITH t(f) AS ( +| 1 | 2
+ VALUES (?), (?) +| |
+ ) +| |
+ SELECT f FROM t ORDER BY f | |
+ SELECT ? + ? + ? AS "add" | 3 | 3
+ SELECT ? +| 4 | 4
+ +| |
+ AS "text" | |
+(9 rows)
+
+--
+--
+-- CRUD: INSERT SELECT UPDATE DELETE on test table
+--
+SELECT pg_stat_statements_reset();
+ pg_stat_statements_reset
+--------------------------
+
+(1 row)
+
+-- utility "create table" must not show
+CREATE TABLE test (a int, b char(20));
INSERT INTO test VALUES(generate_series(1, 10), 'aaa');
-UPDATE test SET b = 'bbb' WHERE a > 5;
-SELECT query, calls, rows from pg_stat_statements ORDER BY rows;
- query | calls | rows
-----------------------------------------------------+-------+------
- SELECT pg_stat_statements_reset(); | 1 | 1
- UPDATE test SET b = ? WHERE a > ?; | 1 | 5
- INSERT INTO test VALUES(generate_series(?, ?), ?); | 1 | 10
+UPDATE test SET b = 'bbb' WHERE a > 7;
+DELETE FROM test WHERE a > 9;
+-- explicit transaction
+BEGIN;
+UPDATE test SET b = '111' WHERE a = 1 ;
+COMMIT;
+BEGIN \;
+UPDATE test SET b = '222' WHERE a = 2 \;
+COMMIT ;
+UPDATE test SET b = '333' WHERE a = 3 \;
+UPDATE test SET b = '444' WHERE a = 4 ;
+BEGIN \;
+UPDATE test SET b = '555' WHERE a = 5 \;
+UPDATE test SET b = '666' WHERE a = 6 \;
+COMMIT ;
+-- SELECT with constants
+SELECT * FROM test WHERE a > 5 ORDER BY a ;
+ a | b
+---+----------------------
+ 6 | 666
+ 7 | aaa
+ 8 | bbb
+ 9 | bbb
+(4 rows)
+
+SELECT *
+ FROM test
+ WHERE a > 9
+ ORDER BY a ;
+ a | b
+---+---
+(0 rows)
+
+-- SELECT without constants
+SELECT * FROM test ORDER BY a;
+ a | b
+---+----------------------
+ 1 | 111
+ 2 | 222
+ 3 | 333
+ 4 | 444
+ 5 | 555
+ 6 | 666
+ 7 | aaa
+ 8 | bbb
+ 9 | bbb
+(9 rows)
+
+SELECT query, calls, rows FROM pg_stat_statements ORDER BY rows, query;
+ query | calls | rows
+---------------------------------------------------+-------+------
+ DELETE FROM test WHERE a > ? | 1 | 1
+ | 1 | 1
+ UPDATE test SET b = ? WHERE a > ? | 1 | 3
+ SELECT * FROM test WHERE a > ? ORDER BY a | 2 | 4
+ UPDATE test SET b = ? WHERE a = ? | 6 | 6
+ SELECT * FROM test ORDER BY a | 1 | 9
+ INSERT INTO test VALUES(generate_series(?, ?), ?) | 1 | 10
+(7 rows)
+
+--
+--
+-- pg_stat_statements.track = none
+--
+SET pg_stat_statements.track = 'none';
+SELECT pg_stat_statements_reset();
+ pg_stat_statements_reset
+--------------------------
+
+(1 row)
+
+SELECT 1 AS "one";
+ one
+-----
+ 1
+(1 row)
+
+SELECT 1 + 1 AS "two";
+ two
+-----
+ 2
+(1 row)
+
+SELECT query, calls, rows FROM pg_stat_statements ORDER BY rows, query;
+ query | calls | rows
+-------+-------+------
+(0 rows)
+
+--
+--
+-- pg_stat_statements.track = top
+--
+SELECT pg_stat_statements_reset();
+ pg_stat_statements_reset
+--------------------------
+
+(1 row)
+
+SET pg_stat_statements.track = 'top';
+DO LANGUAGE plpgsql $$
+BEGIN
+ -- this is a SELECT
+ PERFORM 'hello world'::TEXT;
+END;
+$$;
+-- PL/pgSQL function
+CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$
+DECLARE
+ r INTEGER;
+BEGIN
+ SELECT (i + 1 + 1.0)::INTEGER INTO r;
+ RETURN r;
+END; $$ LANGUAGE plpgsql;
+SELECT PLUS_TWO(3);
+ plus_two
+----------
+ 5
+(1 row)
+
+SELECT PLUS_TWO(7);
+ plus_two
+----------
+ 9
+(1 row)
+
+-- SQL function
+CREATE FUNCTION PLUS_ONE(i INTEGER) RETURNS INTEGER AS
+$$ SELECT (i + 1.0)::INTEGER $$ LANGUAGE SQL;
+SELECT PLUS_ONE(8);
+ plus_one
+----------
+ 9
+(1 row)
+
+SELECT PLUS_ONE(10);
+ plus_one
+----------
+ 11
+(1 row)
+
+DROP FUNCTION PLUS_ONE(INTEGER);
+DROP FUNCTION PLUS_TWO(INTEGER);
+SELECT query, calls, rows FROM pg_stat_statements ORDER BY rows, query;
+ query | calls | rows
+--------------------+-------+------
+ SELECT ?::TEXT | 1 | 1
+ SELECT PLUS_ONE(?) | 2 | 2
+ SELECT PLUS_TWO(?) | 2 | 2
(3 rows)
+--
+--
+-- pg_stat_statements.track = all
+--
+SELECT pg_stat_statements_reset();
+ pg_stat_statements_reset
+--------------------------
+
+(1 row)
+
+SET pg_stat_statements.track = 'all';
+-- recreate PL/pgSQL function
+CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$
+DECLARE
+ r INTEGER;
+BEGIN
+ SELECT (i + 1 + 1.0)::INTEGER INTO r;
+ RETURN r;
+END; $$ LANGUAGE plpgsql;
+SELECT PLUS_TWO(-1);
+ plus_two
+----------
+ 1
+(1 row)
+
+SELECT PLUS_TWO(2);
+ plus_two
+----------
+ 4
+(1 row)
+
+-- SQL function nesting
+CREATE FUNCTION PLUS_ONE(i INTEGER) RETURNS INTEGER AS
+$$ SELECT (i + 1.0)::INTEGER $$ LANGUAGE SQL;
+SELECT PLUS_ONE(3);
+ plus_one
+----------
+ 4
+(1 row)
+
+SELECT PLUS_ONE(1);
+ plus_one
+----------
+ 2
+(1 row)
+
+-- bug? PLUS_ONE expansion is missing
+SELECT query, calls, rows FROM pg_stat_statements ORDER BY rows, query;
+ query | calls | rows
+-----------------------------+-------+------
+ | 1 | 1
+ SELECT (i + ? + ?)::INTEGER | 2 | 2
+ SELECT PLUS_ONE(?) | 2 | 2
+ SELECT PLUS_TWO(?) | 2 | 2
+(4 rows)
+
+--
+--
+-- utility commands
+--
+SELECT pg_stat_statements_reset();
+ pg_stat_statements_reset
+--------------------------
+
+(1 row)
+
+SET pg_stat_statements.track_utility = TRUE;
+CREATE INDEX test_b ON test(b);
DROP TABLE test;
+DROP FUNCTION PLUS_ONE(INTEGER);
+DROP FUNCTION PLUS_TWO(INTEGER);
+SELECT query, calls, rows FROM pg_stat_statements ORDER BY rows, query;
+ query | calls | rows
+---------------------------------+-------+------
+ CREATE INDEX test_b ON test(b) | 1 | 0
+ DROP FUNCTION PLUS_ONE(INTEGER) | 1 | 0
+ DROP FUNCTION PLUS_TWO(INTEGER) | 1 | 0
+ DROP TABLE test | 1 | 0
+ | 1 | 1
+(5 rows)
+
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 8ce24e0..423f950 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -298,10 +298,9 @@ static void pgss_ProcessUtility(Node *parsetree, const char *queryString,
static uint32 pgss_hash_fn(const void *key, Size keysize);
static int pgss_match_fn(const void *key1, const void *key2, Size keysize);
static uint32 pgss_hash_string(const char *str);
-static void pgss_store(const char *query, uint32 queryId,
- double total_time, uint64 rows,
- const BufferUsage *bufusage,
- pgssJumbleState *jstate);
+static void pgss_store(const char *query, int query_loc, int query_len,
+ uint32 queryId, double total_time, uint64 rows,
+ const BufferUsage *bufusage, pgssJumbleState *jstate);
static void pg_stat_statements_internal(FunctionCallInfo fcinfo,
pgssVersion api_version,
bool showtext);
@@ -324,7 +323,7 @@ static void JumbleRangeTable(pgssJumbleState *jstate, List *rtable);
static void JumbleExpr(pgssJumbleState *jstate, Node *node);
static void RecordConstLocation(pgssJumbleState *jstate, int location);
static char *generate_normalized_query(pgssJumbleState *jstate, const char *query,
- int *query_len_p, int encoding);
+ int query_len, int *query_len_p, int encoding);
static void fill_in_constant_lengths(pgssJumbleState *jstate, const char *query);
static int comp_location(const void *a, const void *b);
@@ -820,7 +819,7 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query)
* there's no need for an early entry.
*/
if (jstate.clocations_count > 0)
- pgss_store(pstate->p_sourcetext,
+ pgss_store(pstate->p_sourcetext, query->qlocation, query->qlength,
query->queryId,
0,
0,
@@ -925,6 +924,8 @@ pgss_ExecutorEnd(QueryDesc *queryDesc)
InstrEndLoop(queryDesc->totaltime);
pgss_store(queryDesc->sourceText,
+ queryDesc->plannedstmt->qlocation,
+ queryDesc->plannedstmt->qlength,
queryId,
queryDesc->totaltime->total * 1000.0, /* convert to msec */
queryDesc->estate->es_processed,
@@ -971,6 +972,9 @@ pgss_ProcessUtility(Node *parsetree, const char *queryString,
BufferUsage bufusage_start,
bufusage;
uint32 queryId;
+ ParseNode *parsenode = (ParseNode *) parsetree;
+
+ Assert(isParseNode(node));
bufusage_start = pgBufferUsage;
INSTR_TIME_SET_CURRENT(start);
@@ -1034,7 +1038,7 @@ pgss_ProcessUtility(Node *parsetree, const char *queryString,
/* For utility statements, we just hash the query string directly */
queryId = pgss_hash_string(queryString);
- pgss_store(queryString,
+ pgss_store(queryString, parsenode->qlocation, parsenode->qlength,
queryId,
INSTR_TIME_GET_MILLISEC(duration),
rows,
@@ -1103,7 +1107,7 @@ pgss_hash_string(const char *str)
* query string. total_time, rows, bufusage are ignored in this case.
*/
static void
-pgss_store(const char *query, uint32 queryId,
+pgss_store(const char *query, int query_loc, int query_len, uint32 queryId,
double total_time, uint64 rows,
const BufferUsage *bufusage,
pgssJumbleState *jstate)
@@ -1112,15 +1116,15 @@ pgss_store(const char *query, uint32 queryId,
pgssEntry *entry;
char *norm_query = NULL;
int encoding = GetDatabaseEncoding();
- int query_len;
- Assert(query != NULL);
+ Assert(query != NULL && query_loc >= 0 && query_len >= 0);
/* Safety check... */
if (!pgss || !pgss_hash)
return;
- query_len = strlen(query);
+ if (query_len == 0)
+ query_len = strlen(query);
/* Set up key for hashtable search */
key.userid = GetUserId();
@@ -1140,6 +1144,11 @@ pgss_store(const char *query, uint32 queryId,
bool stored;
bool do_gc;
+ /* skip leading spaces */
+ while (isspace(query[query_loc]))
+ query_loc++, query_len--;
+ /* should skip trailing spaces? encoding dependence? */
+
/*
* Create a new, normalized query string if caller asked. We don't
* need to hold the lock while doing this work. (Note: in any case,
@@ -1150,11 +1159,17 @@ pgss_store(const char *query, uint32 queryId,
if (jstate)
{
LWLockRelease(pgss->lock);
- norm_query = generate_normalized_query(jstate, query,
+ norm_query = generate_normalized_query(jstate, query, query_loc,
&query_len,
encoding);
LWLockAcquire(pgss->lock, LW_SHARED);
}
+ else if (strlen(query) != query_len)
+ {
+ norm_query = palloc(query_len + 1);
+ memcpy(norm_query, query + query_loc, query_len);
+ norm_query[query_len+1] = '\0';
+ }
/* Append new query text to file with only shared lock held */
stored = qtext_store(norm_query ? norm_query : query, query_len,
@@ -2882,15 +2897,15 @@ RecordConstLocation(pgssJumbleState *jstate, int location)
*/
static char *
generate_normalized_query(pgssJumbleState *jstate, const char *query,
- int *query_len_p, int encoding)
+ int query_loc, int *query_len_p, int encoding)
{
char *norm_query;
int query_len = *query_len_p;
int i,
len_to_wrt, /* Length (in bytes) to write */
- quer_loc = 0, /* Source query byte location */
+ quer_loc = query_loc, /* Source query byte location */
n_quer_loc = 0, /* Normalized query byte location */
- last_off = 0, /* Offset from start for previous tok */
+ last_off = query_loc, /* Offset from start for previous tok */
last_tok_len = 0; /* Length (in bytes) of that tok */
/*
@@ -2933,7 +2948,7 @@ generate_normalized_query(pgssJumbleState *jstate, const char *query,
* We've copied up until the last ignorable constant. Copy over the
* remaining bytes of the original query string.
*/
- len_to_wrt = query_len - quer_loc;
+ len_to_wrt = query_loc + query_len - quer_loc;
Assert(len_to_wrt >= 0);
memcpy(norm_query + n_quer_loc, query + quer_loc, len_to_wrt);
diff --git a/contrib/pg_stat_statements/sql/pg_stat_statements.sql b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
index 7e2b263..5a30436 100644
--- a/contrib/pg_stat_statements/sql/pg_stat_statements.sql
+++ b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
@@ -1,15 +1,183 @@
CREATE EXTENSION pg_stat_statements;
-CREATE TABLE test (a int, b char(20));
+--
+--
+-- simple and compound statements
+--
+SET pg_stat_statements.track_utility = FALSE;
+SELECT pg_stat_statements_reset();
+
+SELECT 1 AS "int";
+
+
+SELECT 'hello'
+ -- multiline
+ AS "text";
+SELECT 'world' AS "text";
+
+-- transaction
+BEGIN;
+SELECT 1 AS "int";
+SELECT 'hello' AS "text";
+COMMIT;
+
+-- compound transaction
+BEGIN \;
+SELECT 2.0 AS "float" \;
+SELECT 'world' AS "text" \;
+COMMIT;
+
+-- compound with empty statements and spurious leading spacing
+\;\; SELECT 3 + 3 \;\;\; SELECT ' ' || ' !' \;\; SELECT 1 + 4 \;;
+
+-- non ;-terminated statements
+SELECT 1 + 1 + 1 AS "add" \gset
+SELECT :add + 1 + 1 AS "add" \;
+SELECT :add + 1 + 1 AS "add" \gset
+
+-- set operator
+SELECT 1 AS i UNION SELECT 2 ORDER BY i;
+
+-- cte
+WITH t(f) AS (
+ VALUES (1.0), (2.0)
+)
+ SELECT f FROM t ORDER BY f;
--- test the basic functionality of pg_stat_statements
+SELECT query, calls, rows FROM pg_stat_statements ORDER BY rows, query;
+
+--
+--
+-- CRUD: INSERT SELECT UPDATE DELETE on test table
+--
SELECT pg_stat_statements_reset();
+-- utility "create table" must not show
+CREATE TABLE test (a int, b char(20));
+
INSERT INTO test VALUES(generate_series(1, 10), 'aaa');
-UPDATE test SET b = 'bbb' WHERE a > 5;
+UPDATE test SET b = 'bbb' WHERE a > 7;
+DELETE FROM test WHERE a > 9;
+
+-- explicit transaction
+BEGIN;
+UPDATE test SET b = '111' WHERE a = 1 ;
+COMMIT;
+
+BEGIN \;
+UPDATE test SET b = '222' WHERE a = 2 \;
+COMMIT ;
+
+UPDATE test SET b = '333' WHERE a = 3 \;
+UPDATE test SET b = '444' WHERE a = 4 ;
+
+BEGIN \;
+UPDATE test SET b = '555' WHERE a = 5 \;
+UPDATE test SET b = '666' WHERE a = 6 \;
+COMMIT ;
+
+-- SELECT with constants
+SELECT * FROM test WHERE a > 5 ORDER BY a ;
+SELECT *
+ FROM test
+ WHERE a > 9
+ ORDER BY a ;
+
+-- SELECT without constants
+SELECT * FROM test ORDER BY a;
+
+SELECT query, calls, rows FROM pg_stat_statements ORDER BY rows, query;
-SELECT query, calls, rows from pg_stat_statements ORDER BY rows;
+--
+--
+-- pg_stat_statements.track = none
+--
+SET pg_stat_statements.track = 'none';
+SELECT pg_stat_statements_reset();
+SELECT 1 AS "one";
+SELECT 1 + 1 AS "two";
+SELECT query, calls, rows FROM pg_stat_statements ORDER BY rows, query;
+
+--
+--
+-- pg_stat_statements.track = top
+--
+SELECT pg_stat_statements_reset();
+SET pg_stat_statements.track = 'top';
+
+DO LANGUAGE plpgsql $$
+BEGIN
+ -- this is a SELECT
+ PERFORM 'hello world'::TEXT;
+END;
+$$;
+
+-- PL/pgSQL function
+CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$
+DECLARE
+ r INTEGER;
+BEGIN
+ SELECT (i + 1 + 1.0)::INTEGER INTO r;
+ RETURN r;
+END; $$ LANGUAGE plpgsql;
+
+SELECT PLUS_TWO(3);
+SELECT PLUS_TWO(7);
+-- SQL function
+CREATE FUNCTION PLUS_ONE(i INTEGER) RETURNS INTEGER AS
+$$ SELECT (i + 1.0)::INTEGER $$ LANGUAGE SQL;
+
+SELECT PLUS_ONE(8);
+SELECT PLUS_ONE(10);
+
+DROP FUNCTION PLUS_ONE(INTEGER);
+DROP FUNCTION PLUS_TWO(INTEGER);
+
+SELECT query, calls, rows FROM pg_stat_statements ORDER BY rows, query;
+
+--
+--
+-- pg_stat_statements.track = all
+--
+SELECT pg_stat_statements_reset();
+SET pg_stat_statements.track = 'all';
+
+-- recreate PL/pgSQL function
+CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$
+DECLARE
+ r INTEGER;
+BEGIN
+ SELECT (i + 1 + 1.0)::INTEGER INTO r;
+ RETURN r;
+END; $$ LANGUAGE plpgsql;
+
+SELECT PLUS_TWO(-1);
+SELECT PLUS_TWO(2);
+
+-- SQL function nesting
+CREATE FUNCTION PLUS_ONE(i INTEGER) RETURNS INTEGER AS
+$$ SELECT (i + 1.0)::INTEGER $$ LANGUAGE SQL;
+
+SELECT PLUS_ONE(3);
+SELECT PLUS_ONE(1);
+
+-- bug? PLUS_ONE expansion is missing
+SELECT query, calls, rows FROM pg_stat_statements ORDER BY rows, query;
+
+--
+--
+-- utility commands
+--
+SELECT pg_stat_statements_reset();
+SET pg_stat_statements.track_utility = TRUE;
+
+CREATE INDEX test_b ON test(b);
DROP TABLE test;
+DROP FUNCTION PLUS_ONE(INTEGER);
+DROP FUNCTION PLUS_TWO(INTEGER);
+
+SELECT query, calls, rows FROM pg_stat_statements ORDER BY rows, query;
+
DROP EXTENSION pg_stat_statements;
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 41dde50..e652feb 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -177,6 +177,11 @@ planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
result = (*planner_hook) (parse, cursorOptions, boundParams);
else
result = standard_planner(parse, cursorOptions, boundParams);
+
+ /* copy query location to planned statement */
+ result->qlocation = parse->qlocation;
+ result->qlength = parse->qlength;
+
return result;
}
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 5e65fe7..01a450d 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -307,6 +307,11 @@ transformStmt(ParseState *pstate, Node *parseTree)
result->querySource = QSRC_ORIGINAL;
result->canSetTag = true;
+ /* keep track of location & length */
+ Assert(isParseNode(parseTree));
+ result->qlocation = ((ParseNode*) parseTree)->qlocation;
+ result->qlength = ((ParseNode*) parseTree)->qlength;
+
return result;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 2ed7b52..d720972 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -179,6 +179,7 @@ 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);
static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
+static void setQueryLocation(Node *node, core_yyscan_t yyscanner, int start);
%}
@@ -762,14 +763,20 @@ stmtblock: stmtmulti
stmtmulti: stmtmulti ';' stmt
{
if ($3 != NULL)
+ {
+ setQueryLocation($3, yyscanner, @2+1);
$$ = lappend($1, $3);
+ }
else
$$ = $1;
}
| stmt
{
if ($1 != NULL)
+ {
+ setQueryLocation($1, yyscanner, 0);
$$ = list_make1($1);
+ }
else
$$ = NIL;
}
@@ -15284,6 +15291,23 @@ makeRecursiveViewSelect(char *relname, List *aliases, Node *query)
return (Node *) s;
}
+/* set qlocation & qlength for statements starting at "start" */
+static void
+setQueryLocation(Node *node, core_yyscan_t yyscanner, int start)
+{
+ base_yy_extra_type *extra = pg_yyget_extra(yyscanner);
+ ParseNode *pn = (ParseNode *) node;
+ Assert(isParseNode(node));
+
+ pn->qlocation = start;
+ if (extra->last_semicolon == -1 || start == (extra->last_semicolon + 1))
+ /* reduction triggered by end of input */
+ pn->qlength = strlen(extra->core_yy_extra.scanbuf) - start;
+ else
+ /* reduction triggered by ';' */
+ pn->qlength = extra->last_semicolon - start;
+}
+
/* parser_init()
* Initialize to parse one query string
*/
@@ -15291,4 +15315,5 @@ void
parser_init(base_yy_extra_type *yyext)
{
yyext->parsetree = NIL; /* in case grammar forgets to set it */
+ yyext->last_semicolon = -1;
}
diff --git a/src/backend/parser/scan.l b/src/backend/parser/scan.l
index acd9269..470a697 100644
--- a/src/backend/parser/scan.l
+++ b/src/backend/parser/scan.l
@@ -351,7 +351,8 @@ not_equals "!="
* If you change either set, adjust the character lists appearing in the
* rule for "operator"!
*/
-self [,()\[\].;\:\+\-\*\/\%\^\<\>\=]
+semicolon ;
+self [,()\[\].\:\+\-\*\/\%\^\<\>\=]
op_chars [\~\!\@\#\^\&\|\`\?\+\-\*\/\%\<\>\=]
operator {op_chars}+
@@ -845,7 +846,11 @@ other .
SET_YYLLOC();
return NOT_EQUALS;
}
-
+{semicolon} {
+ SET_YYLLOC();
+ pg_yyget_extra(yyscanner)->last_semicolon = *yylloc;
+ return yytext[0];
+ }
{self} {
SET_YYLLOC();
return yytext[0];
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index c514d3f..1a5e68d 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -308,7 +308,6 @@ typedef enum NodeTag
T_UpdateStmt,
T_SelectStmt,
T_AlterTableStmt,
- T_AlterTableCmd,
T_AlterDomainStmt,
T_SetOperationStmt,
T_GrantStmt,
@@ -406,7 +405,6 @@ typedef enum NodeTag
T_AlterPolicyStmt,
T_CreateTransformStmt,
T_CreateAmStmt,
- T_PartitionCmd,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
@@ -459,6 +457,8 @@ typedef enum NodeTag
T_PartitionSpec,
T_PartitionBoundSpec,
T_PartitionRangeDatum,
+ T_AlterTableCmd,
+ T_PartitionCmd,
/*
* TAGS FOR REPLICATION GRAMMAR PARSE NODES (replnodes.h)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index fc532fb..704694c 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -78,6 +78,24 @@ typedef uint32 AclMode; /* a bitmask of privilege bits */
/* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */
#define ACL_SELECT_FOR_UPDATE ACL_UPDATE
+/*
+ * A ParseNode is a Node with additional location information.
+ * Zero qlengh means not set.
+ * If non-zero, then qlocation is within to the initial query string.
+ */
+typedef struct ParseNode
+{
+ NodeTag type;
+ int qlocation;
+ int qlength;
+} ParseNode;
+
+/*
+ * All high-level statements coming out of the parser are ParseNode,
+ * plus Query & PlannedStmt.
+ */
+#define isParseNodeTag(tag) ((T_Query <= (tag)) && ((tag) < T_A_Expr))
+#define isParseNode(nodeptr) isParseNodeTag(nodeTag(nodeptr))
/*****************************************************************************
* Query Tree
@@ -99,6 +117,8 @@ typedef uint32 AclMode; /* a bitmask of privilege bits */
typedef struct Query
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
CmdType commandType; /* select|insert|update|delete|utility */
@@ -1322,6 +1342,8 @@ typedef struct TriggerTransition
typedef struct InsertStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
RangeVar *relation; /* relation to insert into */
List *cols; /* optional: names of the target columns */
Node *selectStmt; /* the source SELECT/VALUES, or NULL */
@@ -1337,6 +1359,8 @@ typedef struct InsertStmt
typedef struct DeleteStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
RangeVar *relation; /* relation to delete from */
List *usingClause; /* optional using clause for more tables */
Node *whereClause; /* qualifications */
@@ -1351,6 +1375,8 @@ typedef struct DeleteStmt
typedef struct UpdateStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
RangeVar *relation; /* relation to update */
List *targetList; /* the target list (of ResTarget) */
Node *whereClause; /* qualifications */
@@ -1383,6 +1409,8 @@ typedef enum SetOperation
typedef struct SelectStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
/*
* These fields are used only in "leaf" SelectStmts.
@@ -1450,6 +1478,8 @@ typedef struct SelectStmt
typedef struct SetOperationStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
SetOperation op; /* type of set op */
bool all; /* ALL specified? */
Node *larg; /* left child */
@@ -1541,6 +1571,8 @@ typedef enum ObjectType
typedef struct CreateSchemaStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
char *schemaname; /* the name of the schema to create */
Node *authrole; /* the owner of the created schema */
List *schemaElts; /* schema components (list of parsenodes) */
@@ -1560,6 +1592,8 @@ typedef enum DropBehavior
typedef struct AlterTableStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
RangeVar *relation; /* table to work on */
List *cmds; /* list of subcommands */
ObjectType relkind; /* type of object */
@@ -1637,6 +1671,8 @@ typedef enum AlterTableType
typedef struct ReplicaIdentityStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
char identity_type;
char *name;
} ReplicaIdentityStmt;
@@ -1665,6 +1701,8 @@ typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */
typedef struct AlterDomainStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
char subtype; /*------------
* T = alter column default
* N = alter column drop not null
@@ -1712,6 +1750,8 @@ typedef enum GrantObjectType
typedef struct GrantStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
bool is_grant; /* true = GRANT, false = REVOKE */
GrantTargetType targtype; /* type of the grant target */
GrantObjectType objtype; /* kind of object being operated on */
@@ -1762,6 +1802,8 @@ typedef struct AccessPriv
typedef struct GrantRoleStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
List *granted_roles; /* list of roles to be granted/revoked */
List *grantee_roles; /* list of member roles to add/delete */
bool is_grant; /* true = GRANT, false = REVOKE */
@@ -1777,6 +1819,8 @@ typedef struct GrantRoleStmt
typedef struct AlterDefaultPrivilegesStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
List *options; /* list of DefElem */
GrantStmt *action; /* GRANT/REVOKE action (with objects=NIL) */
} AlterDefaultPrivilegesStmt;
@@ -1792,6 +1836,8 @@ typedef struct AlterDefaultPrivilegesStmt
typedef struct CopyStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
RangeVar *relation; /* the relation to copy */
Node *query; /* the query (SELECT or DML statement with
* RETURNING) to copy */
@@ -1823,6 +1869,8 @@ typedef enum
typedef struct VariableSetStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
VariableSetKind kind;
char *name; /* variable to be set */
List *args; /* List of A_Const nodes */
@@ -1836,6 +1884,8 @@ typedef struct VariableSetStmt
typedef struct VariableShowStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
char *name;
} VariableShowStmt;
@@ -1853,6 +1903,8 @@ typedef struct VariableShowStmt
typedef struct CreateStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
RangeVar *relation; /* relation to create */
List *tableElts; /* column definitions (list of ColumnDef) */
List *inhRelations; /* relations to inherit from (list of
@@ -1980,6 +2032,8 @@ typedef struct Constraint
typedef struct CreateTableSpaceStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
char *tablespacename;
Node *owner;
char *location;
@@ -1989,6 +2043,8 @@ typedef struct CreateTableSpaceStmt
typedef struct DropTableSpaceStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
char *tablespacename;
bool missing_ok; /* skip error if missing? */
} DropTableSpaceStmt;
@@ -1996,6 +2052,8 @@ typedef struct DropTableSpaceStmt
typedef struct AlterTableSpaceOptionsStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
char *tablespacename;
List *options;
bool isReset;
@@ -2004,6 +2062,8 @@ typedef struct AlterTableSpaceOptionsStmt
typedef struct AlterTableMoveAllStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
char *orig_tablespacename;
ObjectType objtype; /* Object type to move */
List *roles; /* List of roles to move objects of */
@@ -2019,6 +2079,8 @@ typedef struct AlterTableMoveAllStmt
typedef struct CreateExtensionStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
char *extname;
bool if_not_exists; /* just do nothing if it already exists? */
List *options; /* List of DefElem nodes */
@@ -2028,6 +2090,8 @@ typedef struct CreateExtensionStmt
typedef struct AlterExtensionStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
char *extname;
List *options; /* List of DefElem nodes */
} AlterExtensionStmt;
@@ -2035,6 +2099,8 @@ typedef struct AlterExtensionStmt
typedef struct AlterExtensionContentsStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
char *extname; /* Extension's name */
int action; /* +1 = add object, -1 = drop object */
ObjectType objtype; /* Object's type */
@@ -2050,6 +2116,8 @@ typedef struct AlterExtensionContentsStmt
typedef struct CreateFdwStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
char *fdwname; /* foreign-data wrapper name */
List *func_options; /* HANDLER/VALIDATOR options */
List *options; /* generic options to FDW */
@@ -2058,6 +2126,8 @@ typedef struct CreateFdwStmt
typedef struct AlterFdwStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
char *fdwname; /* foreign-data wrapper name */
List *func_options; /* HANDLER/VALIDATOR options */
List *options; /* generic options to FDW */
@@ -2071,6 +2141,8 @@ typedef struct AlterFdwStmt
typedef struct CreateForeignServerStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
char *servername; /* server name */
char *servertype; /* optional server type */
char *version; /* optional server version */
@@ -2081,6 +2153,8 @@ typedef struct CreateForeignServerStmt
typedef struct AlterForeignServerStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
char *servername; /* server name */
char *version; /* optional server version */
List *options; /* generic options to server */
@@ -2094,7 +2168,7 @@ typedef struct AlterForeignServerStmt
typedef struct CreateForeignTableStmt
{
- CreateStmt base;
+ CreateStmt base; /* is a ParseNode */
char *servername;
List *options;
} CreateForeignTableStmt;
@@ -2107,6 +2181,8 @@ typedef struct CreateForeignTableStmt
typedef struct CreateUserMappingStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
Node *user; /* user role */
char *servername; /* server name */
List *options; /* generic options to server */
@@ -2115,6 +2191,8 @@ typedef struct CreateUserMappingStmt
typedef struct AlterUserMappingStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
Node *user; /* user role */
char *servername; /* server name */
List *options; /* generic options to server */
@@ -2123,6 +2201,8 @@ typedef struct AlterUserMappingStmt
typedef struct DropUserMappingStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
Node *user; /* user role */
char *servername; /* server name */
bool missing_ok; /* ignore missing mappings */
@@ -2143,6 +2223,8 @@ typedef enum ImportForeignSchemaType
typedef struct ImportForeignSchemaStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
char *server_name; /* FDW server name */
char *remote_schema; /* remote schema name to query */
char *local_schema; /* local schema to create objects in */
@@ -2158,6 +2240,8 @@ typedef struct ImportForeignSchemaStmt
typedef struct CreatePolicyStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
char *policy_name; /* Policy's name */
RangeVar *table; /* the table name the policy applies to */
char *cmd_name; /* the command name the policy applies to */
@@ -2174,6 +2258,8 @@ typedef struct CreatePolicyStmt
typedef struct AlterPolicyStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
char *policy_name; /* Policy's name */
RangeVar *table; /* the table name the policy applies to */
List *roles; /* the roles associated with the policy */
@@ -2188,6 +2274,8 @@ typedef struct AlterPolicyStmt
typedef struct CreateAmStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
char *amname; /* access method name */
List *handler_name; /* handler function name */
char amtype; /* type of access method */
@@ -2200,6 +2288,8 @@ typedef struct CreateAmStmt
typedef struct CreateTrigStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
char *trigname; /* TRIGGER's name */
RangeVar *relation; /* relation trigger is on */
List *funcname; /* qual. name of function to call */
@@ -2227,6 +2317,8 @@ typedef struct CreateTrigStmt
typedef struct CreateEventTrigStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
char *trigname; /* TRIGGER's name */
char *eventname; /* event's identifier */
List *whenclause; /* list of DefElems indicating filtering */
@@ -2240,6 +2332,8 @@ typedef struct CreateEventTrigStmt
typedef struct AlterEventTrigStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
char *trigname; /* TRIGGER's name */
char tgenabled; /* trigger's firing configuration WRT
* session_replication_role */
@@ -2253,6 +2347,8 @@ typedef struct AlterEventTrigStmt
typedef struct CreatePLangStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
bool replace; /* T => replace if already exists */
char *plname; /* PL name */
List *plhandler; /* PL call handler function (qual. name) */
@@ -2280,6 +2376,8 @@ typedef enum RoleStmtType
typedef struct CreateRoleStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
RoleStmtType stmt_type; /* ROLE/USER/GROUP */
char *role; /* role name */
List *options; /* List of DefElem nodes */
@@ -2288,6 +2386,8 @@ typedef struct CreateRoleStmt
typedef struct AlterRoleStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
Node *role; /* role */
List *options; /* List of DefElem nodes */
int action; /* +1 = add members, -1 = drop members */
@@ -2296,6 +2396,8 @@ typedef struct AlterRoleStmt
typedef struct AlterRoleSetStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
Node *role; /* role */
char *database; /* database name, or NULL */
VariableSetStmt *setstmt; /* SET or RESET subcommand */
@@ -2304,6 +2406,8 @@ typedef struct AlterRoleSetStmt
typedef struct DropRoleStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
List *roles; /* List of roles to remove */
bool missing_ok; /* skip error if a role is missing? */
} DropRoleStmt;
@@ -2316,6 +2420,8 @@ typedef struct DropRoleStmt
typedef struct CreateSeqStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
RangeVar *sequence; /* the sequence to create */
List *options;
Oid ownerId; /* ID of owner, or InvalidOid for default */
@@ -2325,6 +2431,8 @@ typedef struct CreateSeqStmt
typedef struct AlterSeqStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
RangeVar *sequence; /* the sequence to alter */
List *options;
bool missing_ok; /* skip error if a role is missing? */
@@ -2337,6 +2445,8 @@ typedef struct AlterSeqStmt
typedef struct DefineStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
ObjectType kind; /* aggregate, operator, type */
bool oldstyle; /* hack to signal old CREATE AGG syntax */
List *defnames; /* qualified name (list of Value strings) */
@@ -2351,6 +2461,8 @@ typedef struct DefineStmt
typedef struct CreateDomainStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
List *domainname; /* qualified name (list of Value strings) */
TypeName *typeName; /* the base type */
CollateClause *collClause; /* untransformed COLLATE spec, if any */
@@ -2364,6 +2476,8 @@ typedef struct CreateDomainStmt
typedef struct CreateOpClassStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
List *opclassname; /* qualified name (list of Value strings) */
List *opfamilyname; /* qualified name (ditto); NIL if omitted */
char *amname; /* name of index AM opclass is for */
@@ -2397,6 +2511,8 @@ typedef struct CreateOpClassItem
typedef struct CreateOpFamilyStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
List *opfamilyname; /* qualified name (list of Value strings) */
char *amname; /* name of index AM opfamily is for */
} CreateOpFamilyStmt;
@@ -2408,6 +2524,8 @@ typedef struct CreateOpFamilyStmt
typedef struct AlterOpFamilyStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
List *opfamilyname; /* qualified name (list of Value strings) */
char *amname; /* name of index AM opfamily is for */
bool isDrop; /* ADD or DROP the items? */
@@ -2422,6 +2540,8 @@ typedef struct AlterOpFamilyStmt
typedef struct DropStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
List *objects; /* list of sublists of names (as Values) */
List *arguments; /* list of sublists of arguments (as Values) */
ObjectType removeType; /* object type */
@@ -2437,6 +2557,8 @@ typedef struct DropStmt
typedef struct TruncateStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
List *relations; /* relations (RangeVars) to be truncated */
bool restart_seqs; /* restart owned sequences? */
DropBehavior behavior; /* RESTRICT or CASCADE behavior */
@@ -2449,6 +2571,8 @@ typedef struct TruncateStmt
typedef struct CommentStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
ObjectType objtype; /* Object's type */
List *objname; /* Qualified name of the object */
List *objargs; /* Arguments if needed (eg, for functions) */
@@ -2462,6 +2586,8 @@ typedef struct CommentStmt
typedef struct SecLabelStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
ObjectType objtype; /* Object's type */
List *objname; /* Qualified name of the object */
List *objargs; /* Arguments if needed (eg, for functions) */
@@ -2491,6 +2617,8 @@ typedef struct SecLabelStmt
typedef struct DeclareCursorStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
char *portalname; /* name of the portal (cursor) */
int options; /* bitmask of options (see above) */
Node *query; /* the raw SELECT query */
@@ -2503,6 +2631,8 @@ typedef struct DeclareCursorStmt
typedef struct ClosePortalStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
char *portalname; /* name of the portal (cursor) */
/* NULL means CLOSE ALL */
} ClosePortalStmt;
@@ -2526,6 +2656,8 @@ typedef enum FetchDirection
typedef struct FetchStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
FetchDirection direction; /* see above */
long howMany; /* number of rows, or position argument */
char *portalname; /* name of portal (cursor) */
@@ -2546,6 +2678,8 @@ typedef struct FetchStmt
typedef struct IndexStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
char *idxname; /* name of new index, or NULL for default */
RangeVar *relation; /* relation to build index on */
char *accessMethod; /* name of access method (eg. btree) */
@@ -2574,6 +2708,8 @@ typedef struct IndexStmt
typedef struct CreateFunctionStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
bool replace; /* T => replace if already exists */
List *funcname; /* qualified name of function to create */
List *parameters; /* a list of FunctionParameter */
@@ -2604,6 +2740,8 @@ typedef struct FunctionParameter
typedef struct AlterFunctionStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
FuncWithArgs *func; /* name and args of function */
List *actions; /* list of DefElem */
} AlterFunctionStmt;
@@ -2617,6 +2755,8 @@ typedef struct AlterFunctionStmt
typedef struct DoStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
List *args; /* List of DefElem nodes */
} DoStmt;
@@ -2635,6 +2775,8 @@ typedef struct InlineCodeBlock
typedef struct RenameStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
ObjectType renameType; /* OBJECT_TABLE, OBJECT_COLUMN, etc */
ObjectType relationType; /* if column name, associated relation type */
RangeVar *relation; /* in case it's a table */
@@ -2654,6 +2796,8 @@ typedef struct RenameStmt
typedef struct AlterObjectDependsStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
ObjectType objectType; /* OBJECT_FUNCTION, OBJECT_TRIGGER, etc */
RangeVar *relation; /* in case a table is involved */
List *objname; /* name of the object */
@@ -2668,6 +2812,8 @@ typedef struct AlterObjectDependsStmt
typedef struct AlterObjectSchemaStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
ObjectType objectType; /* OBJECT_TABLE, OBJECT_TYPE, etc */
RangeVar *relation; /* in case it's a table */
List *object; /* in case it's some other object */
@@ -2683,6 +2829,8 @@ typedef struct AlterObjectSchemaStmt
typedef struct AlterOwnerStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
ObjectType objectType; /* OBJECT_TABLE, OBJECT_TYPE, etc */
RangeVar *relation; /* in case it's a table */
List *object; /* in case it's some other object */
@@ -2698,6 +2846,8 @@ typedef struct AlterOwnerStmt
typedef struct AlterOperatorStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
List *opername; /* operator name */
List *operargs; /* operator's argument TypeNames */
List *options; /* List of DefElem nodes */
@@ -2711,6 +2861,8 @@ typedef struct AlterOperatorStmt
typedef struct RuleStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
RangeVar *relation; /* relation the rule is for */
char *rulename; /* name of the rule */
Node *whereClause; /* qualifications */
@@ -2727,6 +2879,8 @@ typedef struct RuleStmt
typedef struct NotifyStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
char *conditionname; /* condition name to notify */
char *payload; /* the payload string, or NULL if none */
} NotifyStmt;
@@ -2738,6 +2892,8 @@ typedef struct NotifyStmt
typedef struct ListenStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
char *conditionname; /* condition name to listen on */
} ListenStmt;
@@ -2748,6 +2904,8 @@ typedef struct ListenStmt
typedef struct UnlistenStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
char *conditionname; /* name to unlisten on, or NULL for all */
} UnlistenStmt;
@@ -2772,6 +2930,8 @@ typedef enum TransactionStmtKind
typedef struct TransactionStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
TransactionStmtKind kind; /* see above */
List *options; /* for BEGIN/START and savepoint commands */
char *gid; /* for two-phase-commit related commands */
@@ -2784,6 +2944,8 @@ typedef struct TransactionStmt
typedef struct CompositeTypeStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
RangeVar *typevar; /* the composite type to be created */
List *coldeflist; /* list of ColumnDef nodes */
} CompositeTypeStmt;
@@ -2795,6 +2957,8 @@ typedef struct CompositeTypeStmt
typedef struct CreateEnumStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
List *typeName; /* qualified name (list of Value strings) */
List *vals; /* enum values (list of Value strings) */
} CreateEnumStmt;
@@ -2806,6 +2970,8 @@ typedef struct CreateEnumStmt
typedef struct CreateRangeStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
List *typeName; /* qualified name (list of Value strings) */
List *params; /* range parameters (list of DefElem) */
} CreateRangeStmt;
@@ -2817,6 +2983,8 @@ typedef struct CreateRangeStmt
typedef struct AlterEnumStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
List *typeName; /* qualified name (list of Value strings) */
char *oldVal; /* old enum value's name, if renaming */
char *newVal; /* new enum value's name */
@@ -2839,6 +3007,8 @@ typedef enum ViewCheckOption
typedef struct ViewStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
RangeVar *view; /* the view to be created */
List *aliases; /* target column names */
Node *query; /* the SELECT query */
@@ -2854,6 +3024,8 @@ typedef struct ViewStmt
typedef struct LoadStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
char *filename; /* file to load */
} LoadStmt;
@@ -2864,6 +3036,8 @@ typedef struct LoadStmt
typedef struct CreatedbStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
char *dbname; /* name of database to create */
List *options; /* List of DefElem nodes */
} CreatedbStmt;
@@ -2875,6 +3049,8 @@ typedef struct CreatedbStmt
typedef struct AlterDatabaseStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
char *dbname; /* name of database to alter */
List *options; /* List of DefElem nodes */
} AlterDatabaseStmt;
@@ -2882,6 +3058,8 @@ typedef struct AlterDatabaseStmt
typedef struct AlterDatabaseSetStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
char *dbname; /* database name */
VariableSetStmt *setstmt; /* SET or RESET subcommand */
} AlterDatabaseSetStmt;
@@ -2893,6 +3071,8 @@ typedef struct AlterDatabaseSetStmt
typedef struct DropdbStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
char *dbname; /* database to drop */
bool missing_ok; /* skip error if db is missing? */
} DropdbStmt;
@@ -2904,6 +3084,8 @@ typedef struct DropdbStmt
typedef struct AlterSystemStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
VariableSetStmt *setstmt; /* SET subcommand */
} AlterSystemStmt;
@@ -2914,6 +3096,8 @@ typedef struct AlterSystemStmt
typedef struct ClusterStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
RangeVar *relation; /* relation being indexed, or NULL if all */
char *indexname; /* original index defined */
bool verbose; /* print progress info */
@@ -2942,6 +3126,8 @@ typedef enum VacuumOption
typedef struct VacuumStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
int options; /* OR of VacuumOption flags */
RangeVar *relation; /* single table to process, or NULL */
List *va_cols; /* list of column names, or NIL for all */
@@ -2958,6 +3144,8 @@ typedef struct VacuumStmt
typedef struct ExplainStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
Node *query; /* the query (see comments above) */
List *options; /* list of DefElem nodes */
} ExplainStmt;
@@ -2978,6 +3166,8 @@ typedef struct ExplainStmt
typedef struct CreateTableAsStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
Node *query; /* the query (see comments above) */
IntoClause *into; /* destination table */
ObjectType relkind; /* OBJECT_TABLE or OBJECT_MATVIEW */
@@ -2992,6 +3182,8 @@ typedef struct CreateTableAsStmt
typedef struct RefreshMatViewStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
bool concurrent; /* allow concurrent access? */
bool skipData; /* true for WITH NO DATA */
RangeVar *relation; /* relation to insert into */
@@ -3004,6 +3196,8 @@ typedef struct RefreshMatViewStmt
typedef struct CheckPointStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
} CheckPointStmt;
/* ----------------------
@@ -3022,6 +3216,8 @@ typedef enum DiscardMode
typedef struct DiscardStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
DiscardMode target;
} DiscardStmt;
@@ -3032,6 +3228,8 @@ typedef struct DiscardStmt
typedef struct LockStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
List *relations; /* relations to lock */
int mode; /* lock mode */
bool nowait; /* no wait mode */
@@ -3044,6 +3242,8 @@ typedef struct LockStmt
typedef struct ConstraintsSetStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
List *constraints; /* List of names as RangeVars */
bool deferred;
} ConstraintsSetStmt;
@@ -3068,6 +3268,8 @@ typedef enum ReindexObjectType
typedef struct ReindexStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
ReindexObjectType kind; /* REINDEX_OBJECT_INDEX, REINDEX_OBJECT_TABLE,
* etc. */
RangeVar *relation; /* Table or index to reindex */
@@ -3082,6 +3284,8 @@ typedef struct ReindexStmt
typedef struct CreateConversionStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
List *conversion_name; /* Name of the conversion */
char *for_encoding_name; /* source encoding name */
char *to_encoding_name; /* destination encoding name */
@@ -3096,6 +3300,8 @@ typedef struct CreateConversionStmt
typedef struct CreateCastStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
TypeName *sourcetype;
TypeName *targettype;
FuncWithArgs *func;
@@ -3110,6 +3316,8 @@ typedef struct CreateCastStmt
typedef struct CreateTransformStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
bool replace;
TypeName *type_name;
char *lang;
@@ -3124,6 +3332,8 @@ typedef struct CreateTransformStmt
typedef struct PrepareStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
char *name; /* Name of plan, arbitrary */
List *argtypes; /* Types of parameters (List of TypeName) */
Node *query; /* The query itself (as a raw parsetree) */
@@ -3138,6 +3348,8 @@ typedef struct PrepareStmt
typedef struct ExecuteStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
char *name; /* The name of the plan to execute */
List *params; /* Values to assign to parameters */
} ExecuteStmt;
@@ -3150,6 +3362,8 @@ typedef struct ExecuteStmt
typedef struct DeallocateStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
char *name; /* The name of the plan to remove */
/* NULL means DEALLOCATE ALL */
} DeallocateStmt;
@@ -3160,6 +3374,8 @@ typedef struct DeallocateStmt
typedef struct DropOwnedStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
List *roles;
DropBehavior behavior;
} DropOwnedStmt;
@@ -3170,6 +3386,8 @@ typedef struct DropOwnedStmt
typedef struct ReassignOwnedStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
List *roles;
Node *newrole;
} ReassignOwnedStmt;
@@ -3180,6 +3398,8 @@ typedef struct ReassignOwnedStmt
typedef struct AlterTSDictionaryStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
List *dictname; /* qualified name (list of Value strings) */
List *options; /* List of DefElem nodes */
} AlterTSDictionaryStmt;
@@ -3199,6 +3419,8 @@ typedef enum AlterTSConfigType
typedef struct AlterTSConfigurationStmt
{
NodeTag type;
+ int qlocation; /* query location */
+ int qlength; /* query length, 0 if unset */
AlterTSConfigType kind; /* ALTER_TSCONFIG_ADD_MAPPING, etc */
List *cfgname; /* qualified name (list of Value strings) */
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index e2fbc7d..bf9cae4 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -36,6 +36,8 @@
typedef struct PlannedStmt
{
NodeTag type;
+ int qlocation; /* query location, or -1 if unknown */
+ int qlength; /* query length, 0 if unknown */
CmdType commandType; /* select|insert|update|delete */
diff --git a/src/include/parser/gramparse.h b/src/include/parser/gramparse.h
index 6d8e493..e373ab2 100644
--- a/src/include/parser/gramparse.h
+++ b/src/include/parser/gramparse.h
@@ -49,6 +49,8 @@ typedef struct base_yy_extra_type
char *lookahead_end; /* end of current token */
char lookahead_hold_char; /* to be put back at *lookahead_end */
+ int last_semicolon; /* for query length computation */
+
/*
* State variables that belong to the grammar.
*/
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers