BUG: pg_stat_statements query normalization issues with combined queries
Hello,
While investigating a performance issue, I tried to get informations from
pg_stat_statements, however I ran into another issue: it seems that when
using combined queries pg_stat_statements query normalization does not
work properly... 2 queries that should have been mapped to only one are
instead map to... 3 cases, as constants are not all ignored:
query
BEGIN ; +
SELECT data FROM Stuff WHERE id = 1 ; +
SELECT data FROM Stuff WHERE id = 2 ; +
SELECT data FROM Stuff WHERE id = 3 ; +
COMMIT;
BEGIN ; +
SELECT data FROM Stuff WHERE id = 4 ; +
SELECT data FROM Stuff WHERE id = 5 ; +
SELECT data FROM Stuff WHERE id = 6 ; +
COMMIT;
BEGIN ; +
SELECT data FROM Stuff WHERE id = ? +
SELECT data FROM Stuff WHERE id = 2 +
SELECT data FROM Stuff WHERE id = 3 +
COMMIT;
I was expecting the 2 combined queries either to be separated in
individual queries "SELECT data FROM Stuff WHERE id = ?" or in one large
queries with three ?, but not the above result...
--
Fabien
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
I was expecting the 2 combined queries either to be separated in individual
queries "SELECT data FROM Stuff WHERE id = ?" or in one large queries with
three ?, but not the above result...
Oops, I forgot the attachement, see repeat script on 9.6.1
--
Fabien.
Attachments:
I was expecting the 2 combined queries either to be separated in individual
queries "SELECT data FROM Stuff WHERE id = ?" or in one large queries with
three ?, but not the above result...Oops, I forgot the attachement, see repeat script on 9.6.1
After some quick investigation, I concluded that the issue is that the
whole combined query string is used instead of the part refering to the
actual query being processed.
As a result:
- the full un-normalized string is used for both BEGIN & COMMIT
=> n shared entries, one for each actual query, begin & commit are mixed together
- the fist-part only normalized string is used for each SELECT
=> 1 shared entry with "query" is the first partially
normalied encountered combined query
--
Fabien.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hello again pgdevs,
More investigations conclude that the information is lost by the parser.
From a ;-separated string raw_parser returns a List<Node> which are
directly the nodes of the statements, with empty statements silently
skipped.
The Node entry of high level statements (*Stmt) does NOT contain any
location information, only some lower level nodes have it.
A possible fix of this would be to add the query string location
information systematically at the head of every high level *Stmt, which
would then share both NodeTag and this information (say location and
length). This would be a new intermediate kind of node of which all these
statements would "inherit", say a "ParseNode" structure.
The actual location information may be filled-in at quite a high level in
the parser, maybe around "stmtmulti" and "stmt" rules, so it would not
necessarily be too intrusive in the overall parser grammar.
Then once the information is available, the query string may be cut where
appropriate to only store the relevant string in pg_stat_statements or
above.
Would this approach be acceptable, or is modifying Nodes a no-go area?
If it is acceptable, I can probably put together a patch and submit it.
If not, I suggest to update the documentation to tell that
pg_stat_statements does not work properly with combined queries.
--
Fabien.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Tue, Dec 20, 2016 at 6:18 AM, Fabien COELHO <coelho@cri.ensmp.fr> wrote:
Would this approach be acceptable, or is modifying Nodes a no-go area?
If it is acceptable, I can probably put together a patch and submit it.
If not, I suggest to update the documentation to tell that
pg_stat_statements does not work properly with combined queries.
I think you've found a bug, but I'm a little doubtful about your
proposed fix. However, I haven't studied the code, so I don't know
what other approach might be better.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
At Tue, 20 Dec 2016 22:42:48 -0500, Robert Haas <robertmhaas@gmail.com> wrote in <CA+TgmoZT94brLAGK7gCmxB4mO=C-Cdz1N8KN8Xen4sexHozN=Q@mail.gmail.com>
On Tue, Dec 20, 2016 at 6:18 AM, Fabien COELHO <coelho@cri.ensmp.fr> wrote:
Would this approach be acceptable, or is modifying Nodes a no-go area?
If it is acceptable, I can probably put together a patch and submit it.
If not, I suggest to update the documentation to tell that
pg_stat_statements does not work properly with combined queries.I think you've found a bug, but I'm a little doubtful about your
proposed fix. However, I haven't studied the code, so I don't know
what other approach might be better.
That will work and doable, but the more fundamental issue here
seems to be that post_parse_analyze_hook or other similar hooks
are called with a Query incosistent with query_debug_string. It
is very conveniently used but the name seems to me to be
suggesting that such usage is out of purpose. I'm not sure,
though.
A similar behavior is shown in error messages but this harms far
less than the case of pg_stat_statements.
ERROR: column "b" does not exist
LINE 1: ...elect * from t where a = 1; select * from t where b = 2; com...
^
1. Let all of the parse node have location in
debug_query_string. (The original proposal)
2. Let the parser return once for each statement (like psql
parser) and corresponding query string is stored in a
varialble other than debug_query_string.
...
regards,
--
Kyotaro Horiguchi
NTT Open Source Software Center
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hello Robert & Kyotaro,
I'm a little doubtful about your proposed fix. However, I haven't
studied the code, so I don't know what other approach might be better.
That is indeed the question!
The key point is that the parser parses several statements from a string,
but currently there is no clue about where the queries was found in the
string. Only a parser may know about what is being parsed so can generate
the location information. Now the possible solutions I see are:
- the string is split per query before parsing, but this requires at
least a lexer... it looks pretty uneffective to have another lexing
phase involved, even if an existing lexer is reused.
- the parser processes one query at a time and keep the "remaining
unparse string" in some state that can be queried to check up to
where it proceeded, but then the result must be stored somewhere
and it would make sense that it would be in the statement anyway,
just the management of the location information would be outside
the parser. Also that would add the cost of relaunching the parser,
not sure how bad or insignificant this is. This is (2) below.
- the parser still generates a List<Node> but keep track of the location
of statements doing so, somehow... propably as I outlined.
The query string information can be at some way of pointing in the initial
string, or the substring itself that could be extracted at some point.
I initially suggested the former because this is already what the parser
does for some nodes, and because it seems simpler to do so.
Extracting the string instead would suggest that the location of tokens
within this statement are relative to this string rather than the initial
one, but that involves a lot more changes and it is easier to miss
something doing so.
That will work and doable, but the more fundamental issue here
seems to be that post_parse_analyze_hook or other similar hooks
are called with a Query incosistent with query_debug_string.
Sure.
It is very conveniently used but the name seems to me to be suggesting
that such usage is out of purpose. I'm not sure, though.A similar behavior is shown in error messages but this harms far
less than the case of pg_stat_statements.ERROR: column "b" does not exist
LINE 1: ...elect * from t where a = 1; select * from t where b = 2; com...
^
Ah, I wrote this piece of code a long time ago:-) The location is relative
to the full string, see comment above, changing that would involve much
more changes, and I'm not sure whether it is desirable.
Also, think of:
SELECT * FROM foo; DROP TABLE foo; SELECT * FROM foo;
Maybe the context to know which "SELECT * FROM foo" generates an error
should be kept.
1. Let all of the parse node have location in
debug_query_string. (The original proposal)
Yep.
2. Let the parser return once for each statement (like psql
parser)
I'm not sure it does... "\;" generates ";" in the output and the psql
lexer keeps on lexing.
and corresponding query string is stored in a
varialble other than debug_query_string.
I think that would involve many changes because of the way postgres is
written, the list is expected and returned by quite a few functions.
Moreover query rewriting may generate several queries out of one anyway,
so the list would be kept.
So I'm rather still in favor of my initial proposal, that is extend the
existing location information to statements, not only some tokens.
--
Fabien.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
At Wed, 21 Dec 2016 09:28:58 +0100 (CET), Fabien COELHO <coelho@cri.ensmp.fr> wrote in <alpine.DEB.2.20.1612210841370.3892@lancre>
Hello Robert & Kyotaro,
I'm a little doubtful about your proposed fix. However, I haven't
studied the code, so I don't know what other approach might be better.That is indeed the question!
The key point is that the parser parses several statements from a
string, but currently there is no clue about where the queries was
found in the string. Only a parser may know about what is being parsed
so can generate the location information. Now the possible solutions I
see are:- the string is split per query before parsing, but this requires at
least a lexer... it looks pretty uneffective to have another lexing
phase involved, even if an existing lexer is reused.
I don't see this is promising. Apparently a waste of CPU cycles.
- the parser processes one query at a time and keep the "remaining
unparse string" in some state that can be queried to check up to
where it proceeded, but then the result must be stored somewhere
and it would make sense that it would be in the statement anyway,
just the management of the location information would be outside
the parser. Also that would add the cost of relaunching the parser,
not sure how bad or insignificant this is. This is (2) below.
I raised this as a spoiler, I see this is somewhat too invasive
for the problem to be solved.
- the parser still generates a List<Node> but keep track of the location
of statements doing so, somehow... propably as I outlined.
Yeah, this seems most reasonable so far. It seems to me to be
better that the statement location is conveyed as a part of a
parse tree so as not to need need a side channel for location.
I'd like to rename debug_query_string to more reasonable name if
we are allowed but perhaps not.
The query string information can be at some way of pointing in the
initial
string, or the substring itself that could be extracted at some point.
I initially suggested the former because this is already what the
parser does for some nodes, and because it seems simpler to do so.Extracting the string instead would suggest that the location of
tokens
within this statement are relative to this string rather than the
initial one, but that involves a lot more changes and it is easier to
miss something doing so.
Agreed that copying statement string would be too much. But the
new *Stmt node should somehow have also the end location of the
statement since the user of a parse tree cannot look into another
one.
That will work and doable, but the more fundamental issue here
seems to be that post_parse_analyze_hook or other similar hooks
are called with a Query incosistent with query_debug_string.Sure.
It is very conveniently used but the name seems to me to be suggesting
that such usage is out of purpose. I'm not sure, though.A similar behavior is shown in error messages but this harms far
less than the case of pg_stat_statements.ERROR: column "b" does not exist
LINE 1: ...elect * from t where a = 1; select * from t where b = 2;
com...
^Ah, I wrote this piece of code a long time ago:-) The location is
relative to the full string, see comment above, changing that would
involve much
more changes, and I'm not sure whether it is desirable.Also, think of:
SELECT * FROM foo; DROP TABLE foo; SELECT * FROM foo;
Maybe the context to know which "SELECT * FROM foo" generates an error
should be kept.1. Let all of the parse node have location in
debug_query_string. (The original proposal)Yep.
2. Let the parser return once for each statement (like psql
parser)I'm not sure it does... "\;" generates ";" in the output and the psql
lexer keeps on lexing.and corresponding query string is stored in a
varialble other than debug_query_string.I think that would involve many changes because of the way postgres is
written, the list is expected and returned by quite a few
functions. Moreover query rewriting may generate several queries out
of one anyway, so the list would be kept.So I'm rather still in favor of my initial proposal, that is extend
the existing location information to statements, not only some tokens.
I thought that it's too much to let all types of parse node have
location but grepping the gram.y with "makeNode" pursuaded me to
agree with you. After changing all *Stmt nodes, only several
types of nodes seems missing it.
regards,
--
Kyotaro Horiguchi
NTT Open Source Software Center
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hello Kyotaro-san,
[...] Agreed that copying statement string would be too much. But the
new *Stmt node should somehow have also the end location of the
statement since the user of a parse tree cannot look into another one.
Yes. I was thinking of adding a "length" field next to "location", where
appropriate.
So I'm rather still in favor of my initial proposal, that is extend
the existing location information to statements, not only some tokens.I thought that it's too much to let all types of parse node have
location but grepping the gram.y with "makeNode" pursuaded me to
agree with you.
Indeed...
After changing all *Stmt nodes, only several types of nodes seems
missing it.
Yes. I'll try to put together a patch and submit it to the next CF.
--
Fabien.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
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.
Attachments:
parsenodes-1.patchtext/x-diff; name=parsenodes-1.patchDownload
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.
*/
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.
Oops... better without a stupid overflow. Shame on me!
--
Fabien.
Attachments:
parsenodes-2.patchtext/x-diff; name=parsenodes-2.patchDownload
diff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.out
index 3573c19..5a1b227 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
+ SELECT pg_stat_statements_reset() | 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
+ SELECT pg_stat_statements_reset() | 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
+-----------------------------------+-------+------
+ SELECT pg_stat_statements_reset() | 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
+ SELECT pg_stat_statements_reset() | 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..d265de9 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] = '\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 a558083..bdff79c 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 834a009..b0657f4 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -178,6 +178,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);
%}
@@ -761,14 +762,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;
}
@@ -15283,6 +15290,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
*/
@@ -15290,4 +15314,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.
*/
On 21 Dec. 2016 11:44, "Robert Haas" <robertmhaas@gmail.com> wrote:
On Tue, Dec 20, 2016 at 6:18 AM, Fabien COELHO <coelho@cri.ensmp.fr> wrote:
Would this approach be acceptable, or is modifying Nodes a no-go area?
If it is acceptable, I can probably put together a patch and submit it.
If not, I suggest to update the documentation to tell that
pg_stat_statements does not work properly with combined queries.
I think you've found a bug, but I'm a little doubtful about your
proposed fix. However, I haven't studied the code, so I don't know
what other approach might be better.
FWIW this issue with multi-statements also causes issues with
ProcessUtility_hook. It gets the char* querytext of the whole
multistatement. Then gets invoked once for each utility command within. It
has no information about which statement text the current invocation
corresponds to.
Having a.pointer into the query text for the start and end would be good
there too. Not as good as doing away with multis entirely as a bad hack but
that's not practical for BC and protocol reasons.
BTW we should be sure the somewhat wacky semantics of multi-statements with
embedded commits are documented. I'll check tomorrow.
On 23 Dec. 2016 17:53, "Fabien COELHO" <coelho@cri.ensmp.fr> wrote:
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.
Great. Please put me (ringerc) down as a reviewer. I'll get to this as soon
as holidays settle down. It'd be very useful for some things I'm working on
too, and is much better then my early draft of similar functionality.
Hello Craig,
Please put me (ringerc) down as a reviewer.
Done.
Please do not loose time reviewing stupid version 1... skip to version 2
directly:-)
Also, note that the query-location part may be considered separately from
the pg_stat_statements part which uses it.
--
Fabien.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hello,
At Mon, 26 Dec 2016 16:00:57 +0100 (CET), Fabien COELHO <coelho@cri.ensmp.fr> wrote in <alpine.DEB.2.20.1612261554510.4911@lancre>
Hello Craig,
Please put me (ringerc) down as a reviewer.
Done.
Please do not loose time reviewing stupid version 1... skip to version
2 directly:-)Also, note that the query-location part may be considered separately
from the pg_stat_statements part which uses it.
In nonterminal stmtmulti, setQueryLocation is added and used to
calcualte and store the length of every stmt's. This needs an
extra storage in bse_yy_extra_type and introduces a bit
complicated stuff. But I think raw_parser can do that without
such extra storage and labor, then gram.y gets far simpler.
The struct member to store the location and length is named
'qlocation', and 'qlength' in the added ParseNode. But many nodes
already have 'location' of exactly the same purpopse. I don't see
a necessity to differentiate them.
Putting the two above together, the following is my suggestion
for the parser part.
- Make all parse nodes have the following two members at the
beginning. This unifies all parse node from the view of setting
their location. Commenting about this would be better.
| NodeTag type;
| int location;
- Remove struct ParseNode and setQueryLocation. stmtmulti sets
location in the same manner to other kind of nodes, just doing
'= @n'. base_yyparse() doesn't calculate statement length.
- Make raw_parser caluclate statement length then store it into
each stmt scanning the yyextra.parsetree.
What do you think about this?
regards,
--
Kyotaro Horiguchi
NTT Open Source Software Center
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hello,
At Tue, 27 Dec 2016 10:28:53 +0900 (Tokyo Standard Time), Kyotaro HORIGUCHI <horiguchi.kyotaro@lab.ntt.co.jp> wrote in <20161227.102853.204155513.horiguchi.kyotaro@lab.ntt.co.jp>
Putting the two above together, the following is my suggestion
for the parser part.- Make all parse nodes have the following two members at the
beginning. This unifies all parse node from the view of setting
their location. Commenting about this would be better.| NodeTag type;
| int location;- Remove struct ParseNode and setQueryLocation. stmtmulti sets
location in the same manner to other kind of nodes, just doing
'= @n'. base_yyparse() doesn't calculate statement length.- Make raw_parser caluclate statement length then store it into
each stmt scanning the yyextra.parsetree.
An additional comment on parser(planner?) part.
This patch make planner() copy the location and length from
parse to result, but copying such stuffs is a job of
standard_planner.
Then the following is a comment on pg_stat_statements.c
- pg_stat_statements.c:977 - isParseNode(node)
node should be parsenode.
- The name for the addional parameter query_loc is written as
query_len in its prototype.
- In pgss_store, "else if (strlen(query)) != query_len)" doesn't
work as expected because of one-off error. query_len here is
the length of the query *excluding* the last semicolon.
- qtext_store doesn't rely on terminating nul character and the
function already takes query length as a parameter. So, there's
no need to copy the partial debug_query_string into palloc'ed
memory. Just replacing the query with query_loc will be
sufficient.
Have a nice holidays.
--
Kyotaro Horiguchi
NTT Open Source Software Center
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hello Kyotaro-san,
In nonterminal stmtmulti, setQueryLocation is added and used to
calcualte and store the length of every stmt's. This needs an
extra storage in bse_yy_extra_type
The extra storage is one int.
and introduces a bit complicated stuff. But I think raw_parser can do
that without such extra storage and labor,
How? The issue is that stmtmulti silently skip some ';' when empty
statements are found, so I need to keep track of that to know where to
stop, using the beginning location of the next statement, which is
probably your idea, does not work.
then gram.y gets far simpler.
The struct member to store the location and length is named
'qlocation', and 'qlength' in the added ParseNode. But many nodes
already have 'location' of exactly the same purpopse. I don't see
a necessity to differentiate them.
Alas, the "CreateTableSpaceStmt" struct already had a "char *location"
that I did not want to change... If I use "location", then I have to
change it, why not...
Another reason for the name difference is that qlocation/qlength
convention is slightly different from location when not set: location is
set manually to -1 when the information is not available, but this
convention is not possible for statements without changing all their
allocations and initializations (there are hundreds...), so the convention
I used for qlocation/qlength is that it is not set if qlength is zero,
which is the default value set by makeNode, so no change was needed...
Changing this point would mean a *lot* of possibly error prone changes in
the parser code everywhere statements are allocated...
Putting the two above together, the following is my suggestion
for the parser part.- Make all parse nodes have the following two members at the
beginning. This unifies all parse node from the view of setting
their location. Commenting about this would be better.| NodeTag type;
| int location;
Currently only a few parse nodes have locations, those about variables and
constants that can be designated by an error message. I added the
information to about 100 statements, but for the many remaining parse
nodes the information does not exist and is not needed, so I would prefer
to avoid adding a field both unset and unused.
- Remove struct ParseNode
"ParseNode" is needed to access the location and length of statements,
otherwise they are only "Node", which does not have a location.
and setQueryLocation.
The function is called twice, I wanted to avoid duplicating code.
stmtmulti sets location in the same manner to other kind of nodes, just
doing '= @n'. base_yyparse() doesn't calculate statement length.
But...
- Make raw_parser caluclate statement length then store it into
each stmt scanning the yyextra.parsetree.
... I cannot calculate the statement length cleanly because of empty
statements. If I know where the last ';' is, then the length computation
must be when the reduction occurs, hence the function called from the
stmtmulti rule.
What do you think about this?
I think that I do not know how to compute the length without knowing where
the last ';' was, because of how empty statements are silently skipped
around the stmtmulti/stmt rules, so I think that the length computation
should stay where it is.
What I can certainly do is:
- add more comments to explain why it is done like that
What I could do with some inputs from reviewers/committers is:
- rename the "char *location" field of the create table space statement
to "directory" or something else... but what?
- change qlocation/qlength to location/length...
However, then the -1 convention for location not set is not true, which
is annoying. Using this convention means hundreds of changes because every
statement allocation & initialization must be changed. This is
obviously possible, but is a much larger patch, which I cannot say
would be much better than this one, it would just be different.
What I'm reserved about:
- adding a location field to nodes that do not need it.
--
Fabien.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
An additional comment on parser(planner?) part.
This patch make planner() copy the location and length from
parse to result, but copying such stuffs is a job of
standard_planner.
I put the copy in planner because standard_planner may not be called by
planner, and in all cases I think that the fields should be copied...
Otherwise it is the responsability of the planner hook to do the copy, it
would is ok if the planner hook calls standard_planner, but the fact is
not warranted, the comments says "the plugin would normally call
standard_planner". What if it does not?
So it seemed safer this way.
Then the following is a comment on pg_stat_statements.c
- pg_stat_statements.c:977 - isParseNode(node)
node should be parsenode.
Could be. ParseNode is a Node, it is just a pointer cast. I can assert on
pn instead of node.
- The name for the addional parameter query_loc is written as
query_len in its prototype.
Indeed, a typo on "generate_normalized_query" prototype.
- In pgss_store, "else if (strlen(query)) != query_len)" doesn't
work as expected because of one-off error. query_len here is
the length of the query *excluding* the last semicolon.
It was somehow intentional: if the query is not the same as the string,
then a copy is performed to have the right null-terminated string...
but
- qtext_store doesn't rely on terminating nul character and the
function already takes query length as a parameter. So, there's
no need to copy the partial debug_query_string into palloc'ed
memory. Just replacing the query with query_loc will be
sufficient.
Hmmm... it seemed good so I tried it, and it did not work...
The subtle reason is that qtext_store does assume that the query string is
null terminated... it just does not recompute the length its length.
However it can be changed to behave correctly in this case, so as to avoid
the copy.
--
Fabien.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Fabien COELHO <coelho@cri.ensmp.fr> writes:
How? The issue is that stmtmulti silently skip some ';' when empty
statements are found, so I need to keep track of that to know where to
stop, using the beginning location of the next statement, which is
probably your idea, does not work.
Maybe you should undo that. I've generally found that trying to put
optimizations into the grammar is a bad idea; it's better to KISS in
gram.y and apply optimizations later on.
- Make all parse nodes have the following two members at the
beginning. This unifies all parse node from the view of setting
their location. Commenting about this would be better.| NodeTag type;
| int location;
I do not like this. You are making what should be a small patch into
a giant, invasive one. I'd think about adding a single parse node type
that corresponds to "stmt" in the grammar and really doesn't do anything
but carry the location info for the overall statement. That info could
then be transferred into the resulting Query node. Problem solved with
(I think) very little code touched.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hello Tom,
How? The issue is that stmtmulti silently skip some ';' when empty
statements are found, [...]Maybe you should undo that.
I was trying to be minimally invasive in the parser, i.e. not to change
any rules.
I've generally found that trying to put optimizations into the grammar
is a bad idea; it's better to KISS in gram.y and apply optimizations
later on.
I can change rules, but I'm not sure it will be better. It would allow to
remove the last ';' location in the lexer which Kyotar does not like, but
it would add some new stretching to compute the length of statements and
remove the empty statements.
I would say that it would be different, but not necessarily better.
- Make all parse nodes have the following two members at the
beginning. This unifies all parse node from the view of setting
their location. Commenting about this would be better.| NodeTag type;
| int location;I do not like this. You are making what should be a small patch into
a giant, invasive one.
I would not say that the current patch is giant & invasive, if you
abstract the two added fields to high-level statements.
I'd think about adding a single parse node type that corresponds to
"stmt" in the grammar and really doesn't do anything but carry the
location info for the overall statement. That info could then be
transferred into the resulting Query node.
I'm not sure I understand your suggestion. Currently I have added the
location & length information to all high-level statements nodes, plus
query and planned. I think that planned is necessary for utility
statements.
I understand that you suggest to add a new intermediate structure:
typedef struct {
NodeTag tag;
int location, length;
Node *stmt;
} ParseNode;
So that raw_parser would return a List<ParseNode> instead of a List<Node>,
and the statements would be unmodified.
Problem solved with (I think) very little code touched.
Hmmm...
Then raw_parser() callers have to manage a List<ParseNode> instead of the
List<Node>, I'm not sure of the size of the propagation to manage the
added indirection level appropriately: raw_parser is called 4 times (in
parser, tcop, commands, plpgsql...). The first call in tcop is
copyObject(), equal(), check_log_statement(), errdetail_execute()...
Probably it can be done, but I'm moderately unthousiastic: it looks like
starting unwinding a ball of wool, and I'm not sure where it would stop,
as it means touching at least the 4 uses of raw_parser in 4 distinct part
of postgres, whereas the current approach did not change anything for
raw_parser callers, although at the price of modifying all statement
nodes.
Did I read you correctly?
--
Fabien.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Fabien COELHO <coelho@cri.ensmp.fr> writes:
How? The issue is that stmtmulti silently skip some ';' when empty
statements are found, [...]
Maybe you should undo that.
I was trying to be minimally invasive in the parser, i.e. not to change
any rules.
That's fairly silly when the alternative is to be maximally invasive
at the parse-nodes level, thereby affecting lots and lots of code that
isn't really related to the problem at hand.
I do not like this. You are making what should be a small patch into
a giant, invasive one.
I would not say that the current patch is giant & invasive, if you
abstract the two added fields to high-level statements.
It's touching every single utility statement type, which is not only
pretty invasive in itself, but will break any pending patches that
add more utility statement types. And you've not followed through on the
implications of adding those fields in, for instance, src/backend/nodes/;
so the patch will be even bigger once all the required tidying-up is done.
I understand that you suggest to add a new intermediate structure:
typedef struct {
NodeTag tag;
int location, length;
Node *stmt;
} ParseNode;
So that raw_parser would return a List<ParseNode> instead of a List<Node>,
and the statements would be unmodified.
Yeah, that's what I was thinking of. There aren't very many places that
would need to know about that, I believe; possibly just gram.y itself
and transformTopLevelStmt(). The stuff in between only knows that it's
passing around lists of statement parse trees, it doesn't know much about
what's in those trees.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 28 December 2016 at 08:33, Tom Lane <tgl@sss.pgh.pa.us> wrote:
I would not say that the current patch is giant & invasive, if you
abstract the two added fields to high-level statements.It's touching every single utility statement type, which is not only
pretty invasive in itself, but will break any pending patches that
add more utility statement types. And you've not followed through on the
implications of adding those fields in, for instance, src/backend/nodes/;
so the patch will be even bigger once all the required tidying-up is done.
I echo Tom's complaint. Adding fields to every node is definitely not
the way to do this.
I understand that you suggest to add a new intermediate structure:
typedef struct {
NodeTag tag;
int location, length;
Node *stmt;
} ParseNode;So that raw_parser would return a List<ParseNode> instead of a List<Node>,
and the statements would be unmodified.Yeah, that's what I was thinking of.
I strongly agree. That's what I'd done in my draft patch, but I hadn't
got the details of actually collecting position tracking info sorted
out yet.
The information will need to be copied to plans later, but that's
easy, and would've been necessary anyway.
--
Craig Ringer http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hello Tom,
[...] It's touching every single utility statement type, which is not
only pretty invasive in itself, but will break any pending patches that
add more utility statement types.
Yep, but it is limited to headers and the break is trivial...
And you've not followed through on the implications of adding those
fields in, for instance, src/backend/nodes/;
Hmmm, probably you are pointing out structure read/write functions.
I would have hoped that such code would be automatically derived from the
corresponding struct definition...
I understand that you suggest to add a new intermediate structure [...]
So that raw_parser would return a List<ParseNode> instead of a
List<Node>, and the statements would be unmodified.Yeah, that's what I was thinking of. There aren't very many places that
would need to know about that, I believe; [...]
Hmmm. I've run into a tiny small ever so little detail in trying to apply
this clever approach...
For fixing the information in pg_stat_statement, the location data must be
transported from the parsed node to the query to the planned node, because
the later two nodes types are passed to different hooks.
Now the detail is that utility statements, which seems to be nearly all of
them but select/update/delete/insert, do not have plans: The statement
itself is its own plan... so there is no place to store the location &
length.
As adding such fields to store the information in the structures is no go
area, I'm hesitating about the course to follow to provide a solution
acceptable to you.
Would you have any other clever proposition or advice about how to
proceed?
I thought of creating yet another node "utility plans with locations" or
maybe reuse the "parsed node" for the purpose, but then it has to be
managed in quite a few places as well.
For the parser output, there is 4 raw_parser calls but also 10
pg_parse_query calls to manage. I'm not sure that going this way will fit
the "few lines of code" bill...
--
Fabien.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 28 December 2016 at 20:11, Fabien COELHO <coelho@cri.ensmp.fr> wrote:
Hello Tom,
[...] It's touching every single utility statement type, which is not only
pretty invasive in itself, but will break any pending patches that add more
utility statement types.Yep, but it is limited to headers and the break is trivial...
I'm not sure how else that part can be done.
We can add a common parent for all utility statement types, but that
also touches all utility statements, and since we're working with pure
C, it means 'type' won't be accessible as e.g. someCreateStmt.type,
but only as something like ((UtilityStatement)someCreateStmt).type or
someCreateStmt.hdr.type .
e.g.
typedef struct UtilityStatement
{
NodeTag type;
int querytext_offset;
int querytext_length;
};
typedef struct CreateStmt
{
UtilityStatement hdr;
RangeVar *relation; /* relation to create */
....
}
I don't think that's so bad, but it's not exactly less invasive.
And you've not followed through on the implications of adding those fields
in, for instance, src/backend/nodes/;Hmmm, probably you are pointing out structure read/write functions.
I'm sure that's his intent, yes.
I would have hoped that such code would be automatically derived from the
corresponding struct definition...
That would be nice. But no, unfortunately, we have no such preprocessing.
To do so would require that we either generate things like
parsenodes.h and the relevant parts of copyfuncs.c etc from a common
source, or have a toolchain that can ingest parsenodes.h and emit
suitable code. The latter means either "fragile, hideous Perl script"
or requiring something like LLVM or the gcc frontend libraries in the
toolchain to parse the C code and emit an intermediate representation
of the structures that we could then generate our functions from.
If you've come from the Java world you'd expect that we'd just
generate the copy and makenode stuff from introspection of the node
structs, or preprocess them or something, but it's not very practical.
They don't change enough to really make it worth it either.
Hmmm. I've run into a tiny small ever so little detail in trying to apply
this clever approach...For fixing the information in pg_stat_statement, the location data must be
transported from the parsed node to the query to the planned node, because
the later two nodes types are passed to different hooks.
Yep.
Now the detail is that utility statements, which seems to be nearly all of
them but select/update/delete/insert, do not have plans: The statement
itself is its own plan... so there is no place to store the location &
length.
Yeah. I don't see any way around adding location info for utility
statements one way or the other.
We could wrap every utility statement up in a container with the
location info, but that'd break every user of ProcessUtility_hook and
unless we put the container as the first by-value member of each
utility struct anyway, it'd mean extra allocations for each utility
statement. Not very appealing.
TBH I think that for utility statements, adding a member struct with
location info is the least-bad option. Something like:
/*
* Location of the original querytext of an individual parsetree or
plan relative to the
* start of the querytext. This is usually offset 0 and the length of
the querytext its self,
* but in multi-statements and other cases where a single parse has
multiple products
* hooks may need to know which part of the querytext corresponds to
which product
* plan.
*
* offset and length are both -1 for plans with no corresponding
querytext, such as
* additional/replacement queries emitted by the rewrite phase or
additional statements
* produced as a result of processing some utility statements.
*
typedef struct StmtLocation
{
int offset;
int length;
}
typedef struct CreateStmt
{
NodeTag type;
StmtLocation location;
....
}
Personally I don't much care if we do it this way, or by embedding the
NodeTag too and creating a common UtilityStmt. The latter probably has
benefits for later extension, but is a bit more annoying in the
shorter term.
--
Craig Ringer http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hello Craig,
[...] It's touching every single utility statement type, which is not
only pretty invasive in itself, but will break any pending patches
that add more utility statement types.Yep, but it is limited to headers and the break is trivial...
I'm not sure how else that part can be done.
We can add a common parent for all utility statement types, but that
also touches all utility statements, and since we're working with pure
C, it means 'type' won't be accessible as e.g. someCreateStmt.type, but
only as something like ((UtilityStatement)someCreateStmt).type or
someCreateStmt.hdr.type. [...] I don't think that's so bad, but it's not
exactly less invasive.
I thought of this way of implementing that on the submitted version, I
decided against because then it is less obvious what is directly in the
structure, existing code may reference the tag field, and the number of
header changes is the same, if a little less verbose.
[...] For fixing the information in pg_stat_statement, the location
data must be transported from the parsed node to the query to the
planned node, because the later two nodes types are passed to different
hooks.Yep.
Now the detail is that utility statements, which seems to be nearly all of
them but select/update/delete/insert, do not have plans: The statement
itself is its own plan... so there is no place to store the location &
length.Yeah. I don't see any way around adding location info for utility
statements one way or the other.
If utility statements are done this way, that's 95% of all statements, so
the point of doing the remaining 5% with Tom's neat intermediate node
trick seems void, I think that it is better to have all of them the same
way.
TBH I think that for utility statements, adding a member struct with
location info is the least-bad option. Something like:
typedef struct StmtLocation
{
int offset;
int length;
}
It is possible, but:
typedef struct CreateStmt
{
NodeTag type;
StmtLocation location;
....
}
Name "location" is already used meaning the offset or the directory
destination for create table space, that would create a third option.
For homogeneity, ISTM that keeping location & length directly and renaming
the table space location is the simplest & most homogeneous option.
--
Fabien.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hello,
Yeah, that's what I was thinking of. There aren't very many places that
would need to know about that, I believe; [...]For fixing the information in pg_stat_statement, the location data must be
transported from the parsed node to the query to the planned node, because
the later two nodes types are passed to different hooks.Now the detail is that utility statements, which seems to be nearly all of
them but select/update/delete/insert, do not have plans: The statement itself
is its own plan... so there is no place to store the location & length.
Here is an updated version:
Changes wrt v2:
- I have added the missing stuff under /nodes/, this is stupid code that
should be automatically generated:-(
- I have added comments in "gram.y" about how the length is computed.
I have also slightly simplified the rule code there.
- I have rename "location" in create table space to "location_dir"
to avoid confusion.
- I have renamed the fields "location" and "length" instead of q*.
- I have moved the location & lenth copies in standard_planner.
- I have fixed the function declaration typo.
- I have simplified pgss_store code to avoid a copy, and move the
length truncation in qtext_store.
- I have improved again the pg_stat_statement regression tests with
combined utility statement tests, which implied some fixes to
extract the right substring for utility queries.
However, not changed:
- I cannot use the intermediate node trick suggested by Tom because
it does not work for utility statements which do not have plans, so
the code still adds location & length, sorry.
- I still use the 'last_semicolon' lexer variable. The alternative is to
change rules so as not to skip empty statements, then write a loop to
compute the length based on successor location, and remove the empty
statements. It can be done, I do not think it is better, it is only
different and more verbose. I'll do it if required by a committer.
--
Fabien.
Attachments:
parsenodes-3.patchtext/x-diff; name=parsenodes-3.patchDownload
diff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.out
index 3573c19..06f6ffe 100644
--- a/contrib/pg_stat_statements/expected/pg_stat_statements.out
+++ b/contrib/pg_stat_statements/expected/pg_stat_statements.out
@@ -1,21 +1,363 @@
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
+ SELECT pg_stat_statements_reset() | 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
+ SELECT pg_stat_statements_reset() | 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)
-DROP TABLE test;
+--
+--
+-- 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
+-----------------------------------+-------+------
+ SELECT pg_stat_statements_reset() | 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;
+SELECT 1;
+ ?column?
+----------
+ 1
+(1 row)
+
+CREATE INDEX test_b ON test(b);
+DROP TABLE test \;
+DROP TABLE IF EXISTS test \;
+DROP FUNCTION PLUS_ONE(INTEGER);
+NOTICE: table "test" does not exist, skipping
+DROP TABLE IF EXISTS test \;
+DROP TABLE IF EXISTS test \;
+DROP FUNCTION IF EXISTS PLUS_ONE(INTEGER);
+NOTICE: table "test" does not exist, skipping
+NOTICE: table "test" does not exist, skipping
+NOTICE: function plus_one(pg_catalog.int4) does not exist, skipping
+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 IF EXISTS PLUS_ONE(INTEGER) | 1 | 0
+ DROP FUNCTION PLUS_ONE(INTEGER) | 1 | 0
+ DROP FUNCTION PLUS_TWO(INTEGER) | 1 | 0
+ DROP TABLE IF EXISTS test | 3 | 0
+ DROP TABLE test | 1 | 0
+ SELECT ? | 1 | 1
+ SELECT pg_stat_statements_reset() | 1 | 1
+(8 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..471a24c 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -297,11 +297,10 @@ static void pgss_ProcessUtility(Node *parsetree, const char *queryString,
DestReceiver *dest, char *completionTag);
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 uint32 pgss_hash_string(char *str, int length);
+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_loc, 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->location, query->length,
query->queryId,
0,
0,
@@ -925,6 +924,8 @@ pgss_ExecutorEnd(QueryDesc *queryDesc)
InstrEndLoop(queryDesc->totaltime);
pgss_store(queryDesc->sourceText,
+ queryDesc->plannedstmt->location,
+ queryDesc->plannedstmt->length,
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(parsetree));
bufusage_start = pgBufferUsage;
INSTR_TIME_SET_CURRENT(start);
@@ -1031,10 +1035,11 @@ pgss_ProcessUtility(Node *parsetree, const char *queryString,
bufusage.blk_write_time = pgBufferUsage.blk_write_time;
INSTR_TIME_SUBTRACT(bufusage.blk_write_time, bufusage_start.blk_write_time);
- /* For utility statements, we just hash the query string directly */
- queryId = pgss_hash_string(queryString);
+ /* For utility statements, we just hash the relevant part of query string */
+ queryId = pgss_hash_string((char *) queryString + parsenode->location,
+ parsenode->length);
- pgss_store(queryString,
+ pgss_store(queryString, parsenode->location, parsenode->length,
queryId,
INSTR_TIME_GET_MILLISEC(duration),
rows,
@@ -1084,15 +1089,18 @@ pgss_match_fn(const void *key1, const void *key2, Size keysize)
return 1;
}
+
/*
- * Given an arbitrarily long query string, produce a hash for the purposes of
- * identifying the query, without normalizing constants. Used when hashing
- * utility statements.
+ * Given an arbitrarily query string, produce a hash for the purposes of
+ * identifying the query, without normalizing constants, and with some trimming.
+ * Used when hashing utility statements.
*/
static uint32
-pgss_hash_string(const char *str)
+pgss_hash_string(char *str, int len)
{
- return hash_any((const unsigned char *) str, strlen(str));
+ while (isspace(*str))
+ str++, len--;
+ return hash_any((const unsigned char *) str, len);
}
/*
@@ -1103,7 +1111,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 +1120,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 +1148,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,14 +1163,14 @@ 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);
}
/* Append new query text to file with only shared lock held */
- stored = qtext_store(norm_query ? norm_query : query, query_len,
+ stored = qtext_store(norm_query ? norm_query : query + query_loc, query_len,
&query_offset, &gc_count);
/*
@@ -1771,10 +1784,7 @@ entry_dealloc(void)
/*
* Given a null-terminated string, allocate a new entry in the external query
- * text file and store the string there.
- *
- * Although we could compute the string length via strlen(), callers already
- * have it handy, so we require them to pass it too.
+ * text file and store the string there, possibly truncated at query_len.
*
* If successful, returns true, and stores the new entry's offset in the file
* into *query_offset. Also, if gc_count isn't NULL, *gc_count is set to the
@@ -1822,7 +1832,9 @@ qtext_store(const char *query, int query_len,
if (lseek(fd, off, SEEK_SET) != off)
goto error;
- if (write(fd, query, query_len + 1) != query_len + 1)
+ /* add truncated query and 0 termination */
+ if (write(fd, query, query_len) != query_len ||
+ write(fd, "\0", 1) != 1)
goto error;
CloseTransientFile(fd);
@@ -2882,15 +2894,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 +2945,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..2a32f23 100644
--- a/contrib/pg_stat_statements/sql/pg_stat_statements.sql
+++ b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
@@ -1,15 +1,188 @@
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;
+
+--
+--
+-- 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;
+
+SELECT 1;
+CREATE INDEX test_b ON test(b);
+DROP TABLE test \;
+DROP TABLE IF EXISTS test \;
+DROP FUNCTION PLUS_ONE(INTEGER);
+DROP TABLE IF EXISTS test \;
+DROP TABLE IF EXISTS test \;
+DROP FUNCTION IF EXISTS PLUS_ONE(INTEGER);
+DROP FUNCTION PLUS_TWO(INTEGER);
-SELECT query, calls, rows from pg_stat_statements ORDER BY rows;
+SELECT query, calls, rows FROM pg_stat_statements ORDER BY rows, query;
-DROP TABLE test;
DROP EXTENSION pg_stat_statements;
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index 7902d43..e3f3030 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -257,7 +257,7 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
ownerId = GetUserId();
/* Unix-ify the offered path, and strip any trailing slashes */
- location = pstrdup(stmt->location);
+ location = pstrdup(stmt->location_dir);
canonicalize_path(location);
/* disallow quotes, else CREATE DATABASE would be at risk */
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 6955298..8a6afa8 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -79,6 +79,8 @@ _copyPlannedStmt(const PlannedStmt *from)
{
PlannedStmt *newnode = makeNode(PlannedStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_SCALAR_FIELD(commandType);
COPY_SCALAR_FIELD(queryId);
COPY_SCALAR_FIELD(hasReturning);
@@ -2734,6 +2736,8 @@ _copyQuery(const Query *from)
{
Query *newnode = makeNode(Query);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_SCALAR_FIELD(commandType);
COPY_SCALAR_FIELD(querySource);
COPY_SCALAR_FIELD(queryId);
@@ -2776,6 +2780,8 @@ _copyInsertStmt(const InsertStmt *from)
{
InsertStmt *newnode = makeNode(InsertStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(relation);
COPY_NODE_FIELD(cols);
COPY_NODE_FIELD(selectStmt);
@@ -2791,6 +2797,8 @@ _copyDeleteStmt(const DeleteStmt *from)
{
DeleteStmt *newnode = makeNode(DeleteStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(relation);
COPY_NODE_FIELD(usingClause);
COPY_NODE_FIELD(whereClause);
@@ -2805,6 +2813,8 @@ _copyUpdateStmt(const UpdateStmt *from)
{
UpdateStmt *newnode = makeNode(UpdateStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(relation);
COPY_NODE_FIELD(targetList);
COPY_NODE_FIELD(whereClause);
@@ -2820,6 +2830,8 @@ _copySelectStmt(const SelectStmt *from)
{
SelectStmt *newnode = makeNode(SelectStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(distinctClause);
COPY_NODE_FIELD(intoClause);
COPY_NODE_FIELD(targetList);
@@ -2847,6 +2859,8 @@ _copySetOperationStmt(const SetOperationStmt *from)
{
SetOperationStmt *newnode = makeNode(SetOperationStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_SCALAR_FIELD(op);
COPY_SCALAR_FIELD(all);
COPY_NODE_FIELD(larg);
@@ -2864,6 +2878,8 @@ _copyAlterTableStmt(const AlterTableStmt *from)
{
AlterTableStmt *newnode = makeNode(AlterTableStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(relation);
COPY_NODE_FIELD(cmds);
COPY_SCALAR_FIELD(relkind);
@@ -2892,6 +2908,8 @@ _copyAlterDomainStmt(const AlterDomainStmt *from)
{
AlterDomainStmt *newnode = makeNode(AlterDomainStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_SCALAR_FIELD(subtype);
COPY_NODE_FIELD(typeName);
COPY_STRING_FIELD(name);
@@ -2907,6 +2925,8 @@ _copyGrantStmt(const GrantStmt *from)
{
GrantStmt *newnode = makeNode(GrantStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_SCALAR_FIELD(is_grant);
COPY_SCALAR_FIELD(targtype);
COPY_SCALAR_FIELD(objtype);
@@ -2946,6 +2966,8 @@ _copyGrantRoleStmt(const GrantRoleStmt *from)
{
GrantRoleStmt *newnode = makeNode(GrantRoleStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(granted_roles);
COPY_NODE_FIELD(grantee_roles);
COPY_SCALAR_FIELD(is_grant);
@@ -2961,6 +2983,8 @@ _copyAlterDefaultPrivilegesStmt(const AlterDefaultPrivilegesStmt *from)
{
AlterDefaultPrivilegesStmt *newnode = makeNode(AlterDefaultPrivilegesStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(options);
COPY_NODE_FIELD(action);
@@ -2972,6 +2996,8 @@ _copyDeclareCursorStmt(const DeclareCursorStmt *from)
{
DeclareCursorStmt *newnode = makeNode(DeclareCursorStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_STRING_FIELD(portalname);
COPY_SCALAR_FIELD(options);
COPY_NODE_FIELD(query);
@@ -2984,6 +3010,8 @@ _copyClosePortalStmt(const ClosePortalStmt *from)
{
ClosePortalStmt *newnode = makeNode(ClosePortalStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_STRING_FIELD(portalname);
return newnode;
@@ -2994,6 +3022,8 @@ _copyClusterStmt(const ClusterStmt *from)
{
ClusterStmt *newnode = makeNode(ClusterStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(relation);
COPY_STRING_FIELD(indexname);
COPY_SCALAR_FIELD(verbose);
@@ -3006,6 +3036,8 @@ _copyCopyStmt(const CopyStmt *from)
{
CopyStmt *newnode = makeNode(CopyStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(relation);
COPY_NODE_FIELD(query);
COPY_NODE_FIELD(attlist);
@@ -3026,6 +3058,8 @@ _copyCopyStmt(const CopyStmt *from)
static void
CopyCreateStmtFields(const CreateStmt *from, CreateStmt *newnode)
{
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(relation);
COPY_NODE_FIELD(tableElts);
COPY_NODE_FIELD(inhRelations);
@@ -3065,6 +3099,8 @@ _copyDefineStmt(const DefineStmt *from)
{
DefineStmt *newnode = makeNode(DefineStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_SCALAR_FIELD(kind);
COPY_SCALAR_FIELD(oldstyle);
COPY_NODE_FIELD(defnames);
@@ -3079,6 +3115,8 @@ _copyDropStmt(const DropStmt *from)
{
DropStmt *newnode = makeNode(DropStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(objects);
COPY_NODE_FIELD(arguments);
COPY_SCALAR_FIELD(removeType);
@@ -3094,6 +3132,8 @@ _copyTruncateStmt(const TruncateStmt *from)
{
TruncateStmt *newnode = makeNode(TruncateStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(relations);
COPY_SCALAR_FIELD(restart_seqs);
COPY_SCALAR_FIELD(behavior);
@@ -3106,6 +3146,8 @@ _copyCommentStmt(const CommentStmt *from)
{
CommentStmt *newnode = makeNode(CommentStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_SCALAR_FIELD(objtype);
COPY_NODE_FIELD(objname);
COPY_NODE_FIELD(objargs);
@@ -3119,6 +3161,8 @@ _copySecLabelStmt(const SecLabelStmt *from)
{
SecLabelStmt *newnode = makeNode(SecLabelStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_SCALAR_FIELD(objtype);
COPY_NODE_FIELD(objname);
COPY_NODE_FIELD(objargs);
@@ -3133,6 +3177,8 @@ _copyFetchStmt(const FetchStmt *from)
{
FetchStmt *newnode = makeNode(FetchStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_SCALAR_FIELD(direction);
COPY_SCALAR_FIELD(howMany);
COPY_STRING_FIELD(portalname);
@@ -3146,6 +3192,8 @@ _copyIndexStmt(const IndexStmt *from)
{
IndexStmt *newnode = makeNode(IndexStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_STRING_FIELD(idxname);
COPY_NODE_FIELD(relation);
COPY_STRING_FIELD(accessMethod);
@@ -3174,6 +3222,8 @@ _copyCreateFunctionStmt(const CreateFunctionStmt *from)
{
CreateFunctionStmt *newnode = makeNode(CreateFunctionStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_SCALAR_FIELD(replace);
COPY_NODE_FIELD(funcname);
COPY_NODE_FIELD(parameters);
@@ -3202,6 +3252,8 @@ _copyAlterFunctionStmt(const AlterFunctionStmt *from)
{
AlterFunctionStmt *newnode = makeNode(AlterFunctionStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(func);
COPY_NODE_FIELD(actions);
@@ -3213,6 +3265,8 @@ _copyDoStmt(const DoStmt *from)
{
DoStmt *newnode = makeNode(DoStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(args);
return newnode;
@@ -3223,6 +3277,8 @@ _copyRenameStmt(const RenameStmt *from)
{
RenameStmt *newnode = makeNode(RenameStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_SCALAR_FIELD(renameType);
COPY_SCALAR_FIELD(relationType);
COPY_NODE_FIELD(relation);
@@ -3241,6 +3297,8 @@ _copyAlterObjectDependsStmt(const AlterObjectDependsStmt *from)
{
AlterObjectDependsStmt *newnode = makeNode(AlterObjectDependsStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_SCALAR_FIELD(objectType);
COPY_NODE_FIELD(relation);
COPY_NODE_FIELD(objname);
@@ -3255,6 +3313,8 @@ _copyAlterObjectSchemaStmt(const AlterObjectSchemaStmt *from)
{
AlterObjectSchemaStmt *newnode = makeNode(AlterObjectSchemaStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_SCALAR_FIELD(objectType);
COPY_NODE_FIELD(relation);
COPY_NODE_FIELD(object);
@@ -3270,6 +3330,8 @@ _copyAlterOwnerStmt(const AlterOwnerStmt *from)
{
AlterOwnerStmt *newnode = makeNode(AlterOwnerStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_SCALAR_FIELD(objectType);
COPY_NODE_FIELD(relation);
COPY_NODE_FIELD(object);
@@ -3284,6 +3346,8 @@ _copyAlterOperatorStmt(const AlterOperatorStmt *from)
{
AlterOperatorStmt *newnode = makeNode(AlterOperatorStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(opername);
COPY_NODE_FIELD(operargs);
COPY_NODE_FIELD(options);
@@ -3296,6 +3360,8 @@ _copyRuleStmt(const RuleStmt *from)
{
RuleStmt *newnode = makeNode(RuleStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(relation);
COPY_STRING_FIELD(rulename);
COPY_NODE_FIELD(whereClause);
@@ -3312,6 +3378,8 @@ _copyNotifyStmt(const NotifyStmt *from)
{
NotifyStmt *newnode = makeNode(NotifyStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_STRING_FIELD(conditionname);
COPY_STRING_FIELD(payload);
@@ -3323,6 +3391,8 @@ _copyListenStmt(const ListenStmt *from)
{
ListenStmt *newnode = makeNode(ListenStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_STRING_FIELD(conditionname);
return newnode;
@@ -3333,6 +3403,8 @@ _copyUnlistenStmt(const UnlistenStmt *from)
{
UnlistenStmt *newnode = makeNode(UnlistenStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_STRING_FIELD(conditionname);
return newnode;
@@ -3343,6 +3415,8 @@ _copyTransactionStmt(const TransactionStmt *from)
{
TransactionStmt *newnode = makeNode(TransactionStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_SCALAR_FIELD(kind);
COPY_NODE_FIELD(options);
COPY_STRING_FIELD(gid);
@@ -3355,6 +3429,8 @@ _copyCompositeTypeStmt(const CompositeTypeStmt *from)
{
CompositeTypeStmt *newnode = makeNode(CompositeTypeStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(typevar);
COPY_NODE_FIELD(coldeflist);
@@ -3366,6 +3442,8 @@ _copyCreateEnumStmt(const CreateEnumStmt *from)
{
CreateEnumStmt *newnode = makeNode(CreateEnumStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(typeName);
COPY_NODE_FIELD(vals);
@@ -3377,6 +3455,8 @@ _copyCreateRangeStmt(const CreateRangeStmt *from)
{
CreateRangeStmt *newnode = makeNode(CreateRangeStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(typeName);
COPY_NODE_FIELD(params);
@@ -3388,6 +3468,8 @@ _copyAlterEnumStmt(const AlterEnumStmt *from)
{
AlterEnumStmt *newnode = makeNode(AlterEnumStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(typeName);
COPY_STRING_FIELD(oldVal);
COPY_STRING_FIELD(newVal);
@@ -3403,6 +3485,8 @@ _copyViewStmt(const ViewStmt *from)
{
ViewStmt *newnode = makeNode(ViewStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(view);
COPY_NODE_FIELD(aliases);
COPY_NODE_FIELD(query);
@@ -3418,6 +3502,8 @@ _copyLoadStmt(const LoadStmt *from)
{
LoadStmt *newnode = makeNode(LoadStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_STRING_FIELD(filename);
return newnode;
@@ -3428,6 +3514,8 @@ _copyCreateDomainStmt(const CreateDomainStmt *from)
{
CreateDomainStmt *newnode = makeNode(CreateDomainStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(domainname);
COPY_NODE_FIELD(typeName);
COPY_NODE_FIELD(collClause);
@@ -3441,6 +3529,8 @@ _copyCreateOpClassStmt(const CreateOpClassStmt *from)
{
CreateOpClassStmt *newnode = makeNode(CreateOpClassStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(opclassname);
COPY_NODE_FIELD(opfamilyname);
COPY_STRING_FIELD(amname);
@@ -3472,6 +3562,8 @@ _copyCreateOpFamilyStmt(const CreateOpFamilyStmt *from)
{
CreateOpFamilyStmt *newnode = makeNode(CreateOpFamilyStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(opfamilyname);
COPY_STRING_FIELD(amname);
@@ -3483,6 +3575,8 @@ _copyAlterOpFamilyStmt(const AlterOpFamilyStmt *from)
{
AlterOpFamilyStmt *newnode = makeNode(AlterOpFamilyStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(opfamilyname);
COPY_STRING_FIELD(amname);
COPY_SCALAR_FIELD(isDrop);
@@ -3496,6 +3590,8 @@ _copyCreatedbStmt(const CreatedbStmt *from)
{
CreatedbStmt *newnode = makeNode(CreatedbStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_STRING_FIELD(dbname);
COPY_NODE_FIELD(options);
@@ -3507,6 +3603,8 @@ _copyAlterDatabaseStmt(const AlterDatabaseStmt *from)
{
AlterDatabaseStmt *newnode = makeNode(AlterDatabaseStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_STRING_FIELD(dbname);
COPY_NODE_FIELD(options);
@@ -3518,6 +3616,8 @@ _copyAlterDatabaseSetStmt(const AlterDatabaseSetStmt *from)
{
AlterDatabaseSetStmt *newnode = makeNode(AlterDatabaseSetStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_STRING_FIELD(dbname);
COPY_NODE_FIELD(setstmt);
@@ -3529,6 +3629,8 @@ _copyDropdbStmt(const DropdbStmt *from)
{
DropdbStmt *newnode = makeNode(DropdbStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_STRING_FIELD(dbname);
COPY_SCALAR_FIELD(missing_ok);
@@ -3540,6 +3642,8 @@ _copyVacuumStmt(const VacuumStmt *from)
{
VacuumStmt *newnode = makeNode(VacuumStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_SCALAR_FIELD(options);
COPY_NODE_FIELD(relation);
COPY_NODE_FIELD(va_cols);
@@ -3552,6 +3656,8 @@ _copyExplainStmt(const ExplainStmt *from)
{
ExplainStmt *newnode = makeNode(ExplainStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(query);
COPY_NODE_FIELD(options);
@@ -3563,6 +3669,8 @@ _copyCreateTableAsStmt(const CreateTableAsStmt *from)
{
CreateTableAsStmt *newnode = makeNode(CreateTableAsStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(query);
COPY_NODE_FIELD(into);
COPY_SCALAR_FIELD(relkind);
@@ -3577,6 +3685,8 @@ _copyRefreshMatViewStmt(const RefreshMatViewStmt *from)
{
RefreshMatViewStmt *newnode = makeNode(RefreshMatViewStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_SCALAR_FIELD(concurrent);
COPY_SCALAR_FIELD(skipData);
COPY_NODE_FIELD(relation);
@@ -3589,6 +3699,8 @@ _copyReplicaIdentityStmt(const ReplicaIdentityStmt *from)
{
ReplicaIdentityStmt *newnode = makeNode(ReplicaIdentityStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_SCALAR_FIELD(identity_type);
COPY_STRING_FIELD(name);
@@ -3600,6 +3712,8 @@ _copyAlterSystemStmt(const AlterSystemStmt *from)
{
AlterSystemStmt *newnode = makeNode(AlterSystemStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(setstmt);
return newnode;
@@ -3610,6 +3724,8 @@ _copyCreateSeqStmt(const CreateSeqStmt *from)
{
CreateSeqStmt *newnode = makeNode(CreateSeqStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(sequence);
COPY_NODE_FIELD(options);
COPY_SCALAR_FIELD(ownerId);
@@ -3623,6 +3739,8 @@ _copyAlterSeqStmt(const AlterSeqStmt *from)
{
AlterSeqStmt *newnode = makeNode(AlterSeqStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(sequence);
COPY_NODE_FIELD(options);
COPY_SCALAR_FIELD(missing_ok);
@@ -3635,6 +3753,8 @@ _copyVariableSetStmt(const VariableSetStmt *from)
{
VariableSetStmt *newnode = makeNode(VariableSetStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_SCALAR_FIELD(kind);
COPY_STRING_FIELD(name);
COPY_NODE_FIELD(args);
@@ -3648,6 +3768,8 @@ _copyVariableShowStmt(const VariableShowStmt *from)
{
VariableShowStmt *newnode = makeNode(VariableShowStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_STRING_FIELD(name);
return newnode;
@@ -3658,6 +3780,8 @@ _copyDiscardStmt(const DiscardStmt *from)
{
DiscardStmt *newnode = makeNode(DiscardStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_SCALAR_FIELD(target);
return newnode;
@@ -3668,9 +3792,11 @@ _copyCreateTableSpaceStmt(const CreateTableSpaceStmt *from)
{
CreateTableSpaceStmt *newnode = makeNode(CreateTableSpaceStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_STRING_FIELD(tablespacename);
COPY_NODE_FIELD(owner);
- COPY_STRING_FIELD(location);
+ COPY_STRING_FIELD(location_dir);
COPY_NODE_FIELD(options);
return newnode;
@@ -3681,6 +3807,8 @@ _copyDropTableSpaceStmt(const DropTableSpaceStmt *from)
{
DropTableSpaceStmt *newnode = makeNode(DropTableSpaceStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_STRING_FIELD(tablespacename);
COPY_SCALAR_FIELD(missing_ok);
@@ -3692,6 +3820,8 @@ _copyAlterTableSpaceOptionsStmt(const AlterTableSpaceOptionsStmt *from)
{
AlterTableSpaceOptionsStmt *newnode = makeNode(AlterTableSpaceOptionsStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_STRING_FIELD(tablespacename);
COPY_NODE_FIELD(options);
COPY_SCALAR_FIELD(isReset);
@@ -3704,6 +3834,8 @@ _copyAlterTableMoveAllStmt(const AlterTableMoveAllStmt *from)
{
AlterTableMoveAllStmt *newnode = makeNode(AlterTableMoveAllStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_STRING_FIELD(orig_tablespacename);
COPY_SCALAR_FIELD(objtype);
COPY_NODE_FIELD(roles);
@@ -3718,6 +3850,8 @@ _copyCreateExtensionStmt(const CreateExtensionStmt *from)
{
CreateExtensionStmt *newnode = makeNode(CreateExtensionStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_STRING_FIELD(extname);
COPY_SCALAR_FIELD(if_not_exists);
COPY_NODE_FIELD(options);
@@ -3730,6 +3864,8 @@ _copyAlterExtensionStmt(const AlterExtensionStmt *from)
{
AlterExtensionStmt *newnode = makeNode(AlterExtensionStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_STRING_FIELD(extname);
COPY_NODE_FIELD(options);
@@ -3741,6 +3877,8 @@ _copyAlterExtensionContentsStmt(const AlterExtensionContentsStmt *from)
{
AlterExtensionContentsStmt *newnode = makeNode(AlterExtensionContentsStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_STRING_FIELD(extname);
COPY_SCALAR_FIELD(action);
COPY_SCALAR_FIELD(objtype);
@@ -3755,6 +3893,8 @@ _copyCreateFdwStmt(const CreateFdwStmt *from)
{
CreateFdwStmt *newnode = makeNode(CreateFdwStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_STRING_FIELD(fdwname);
COPY_NODE_FIELD(func_options);
COPY_NODE_FIELD(options);
@@ -3767,6 +3907,8 @@ _copyAlterFdwStmt(const AlterFdwStmt *from)
{
AlterFdwStmt *newnode = makeNode(AlterFdwStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_STRING_FIELD(fdwname);
COPY_NODE_FIELD(func_options);
COPY_NODE_FIELD(options);
@@ -3779,6 +3921,8 @@ _copyCreateForeignServerStmt(const CreateForeignServerStmt *from)
{
CreateForeignServerStmt *newnode = makeNode(CreateForeignServerStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_STRING_FIELD(servername);
COPY_STRING_FIELD(servertype);
COPY_STRING_FIELD(version);
@@ -3793,6 +3937,8 @@ _copyAlterForeignServerStmt(const AlterForeignServerStmt *from)
{
AlterForeignServerStmt *newnode = makeNode(AlterForeignServerStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_STRING_FIELD(servername);
COPY_STRING_FIELD(version);
COPY_NODE_FIELD(options);
@@ -3806,6 +3952,8 @@ _copyCreateUserMappingStmt(const CreateUserMappingStmt *from)
{
CreateUserMappingStmt *newnode = makeNode(CreateUserMappingStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(user);
COPY_STRING_FIELD(servername);
COPY_NODE_FIELD(options);
@@ -3818,6 +3966,8 @@ _copyAlterUserMappingStmt(const AlterUserMappingStmt *from)
{
AlterUserMappingStmt *newnode = makeNode(AlterUserMappingStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(user);
COPY_STRING_FIELD(servername);
COPY_NODE_FIELD(options);
@@ -3830,6 +3980,8 @@ _copyDropUserMappingStmt(const DropUserMappingStmt *from)
{
DropUserMappingStmt *newnode = makeNode(DropUserMappingStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(user);
COPY_STRING_FIELD(servername);
COPY_SCALAR_FIELD(missing_ok);
@@ -3855,6 +4007,8 @@ _copyImportForeignSchemaStmt(const ImportForeignSchemaStmt *from)
{
ImportForeignSchemaStmt *newnode = makeNode(ImportForeignSchemaStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_STRING_FIELD(server_name);
COPY_STRING_FIELD(remote_schema);
COPY_STRING_FIELD(local_schema);
@@ -3870,6 +4024,8 @@ _copyCreateTransformStmt(const CreateTransformStmt *from)
{
CreateTransformStmt *newnode = makeNode(CreateTransformStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_SCALAR_FIELD(replace);
COPY_NODE_FIELD(type_name);
COPY_STRING_FIELD(lang);
@@ -3884,6 +4040,8 @@ _copyCreateAmStmt(const CreateAmStmt *from)
{
CreateAmStmt *newnode = makeNode(CreateAmStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_STRING_FIELD(amname);
COPY_NODE_FIELD(handler_name);
COPY_SCALAR_FIELD(amtype);
@@ -3896,6 +4054,8 @@ _copyCreateTrigStmt(const CreateTrigStmt *from)
{
CreateTrigStmt *newnode = makeNode(CreateTrigStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_STRING_FIELD(trigname);
COPY_NODE_FIELD(relation);
COPY_NODE_FIELD(funcname);
@@ -3919,6 +4079,8 @@ _copyCreateEventTrigStmt(const CreateEventTrigStmt *from)
{
CreateEventTrigStmt *newnode = makeNode(CreateEventTrigStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_STRING_FIELD(trigname);
COPY_STRING_FIELD(eventname);
COPY_NODE_FIELD(whenclause);
@@ -3932,6 +4094,8 @@ _copyAlterEventTrigStmt(const AlterEventTrigStmt *from)
{
AlterEventTrigStmt *newnode = makeNode(AlterEventTrigStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_STRING_FIELD(trigname);
COPY_SCALAR_FIELD(tgenabled);
@@ -3943,6 +4107,8 @@ _copyCreatePLangStmt(const CreatePLangStmt *from)
{
CreatePLangStmt *newnode = makeNode(CreatePLangStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_SCALAR_FIELD(replace);
COPY_STRING_FIELD(plname);
COPY_NODE_FIELD(plhandler);
@@ -3958,6 +4124,8 @@ _copyCreateRoleStmt(const CreateRoleStmt *from)
{
CreateRoleStmt *newnode = makeNode(CreateRoleStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_SCALAR_FIELD(stmt_type);
COPY_STRING_FIELD(role);
COPY_NODE_FIELD(options);
@@ -3970,6 +4138,8 @@ _copyAlterRoleStmt(const AlterRoleStmt *from)
{
AlterRoleStmt *newnode = makeNode(AlterRoleStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(role);
COPY_NODE_FIELD(options);
COPY_SCALAR_FIELD(action);
@@ -3982,6 +4152,8 @@ _copyAlterRoleSetStmt(const AlterRoleSetStmt *from)
{
AlterRoleSetStmt *newnode = makeNode(AlterRoleSetStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(role);
COPY_STRING_FIELD(database);
COPY_NODE_FIELD(setstmt);
@@ -3994,6 +4166,8 @@ _copyDropRoleStmt(const DropRoleStmt *from)
{
DropRoleStmt *newnode = makeNode(DropRoleStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(roles);
COPY_SCALAR_FIELD(missing_ok);
@@ -4005,6 +4179,8 @@ _copyLockStmt(const LockStmt *from)
{
LockStmt *newnode = makeNode(LockStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(relations);
COPY_SCALAR_FIELD(mode);
COPY_SCALAR_FIELD(nowait);
@@ -4017,6 +4193,8 @@ _copyConstraintsSetStmt(const ConstraintsSetStmt *from)
{
ConstraintsSetStmt *newnode = makeNode(ConstraintsSetStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(constraints);
COPY_SCALAR_FIELD(deferred);
@@ -4028,6 +4206,8 @@ _copyReindexStmt(const ReindexStmt *from)
{
ReindexStmt *newnode = makeNode(ReindexStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_SCALAR_FIELD(kind);
COPY_NODE_FIELD(relation);
COPY_STRING_FIELD(name);
@@ -4041,6 +4221,8 @@ _copyCreateSchemaStmt(const CreateSchemaStmt *from)
{
CreateSchemaStmt *newnode = makeNode(CreateSchemaStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_STRING_FIELD(schemaname);
COPY_NODE_FIELD(authrole);
COPY_NODE_FIELD(schemaElts);
@@ -4054,6 +4236,8 @@ _copyCreateConversionStmt(const CreateConversionStmt *from)
{
CreateConversionStmt *newnode = makeNode(CreateConversionStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(conversion_name);
COPY_STRING_FIELD(for_encoding_name);
COPY_STRING_FIELD(to_encoding_name);
@@ -4068,6 +4252,8 @@ _copyCreateCastStmt(const CreateCastStmt *from)
{
CreateCastStmt *newnode = makeNode(CreateCastStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(sourcetype);
COPY_NODE_FIELD(targettype);
COPY_NODE_FIELD(func);
@@ -4082,6 +4268,8 @@ _copyPrepareStmt(const PrepareStmt *from)
{
PrepareStmt *newnode = makeNode(PrepareStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_STRING_FIELD(name);
COPY_NODE_FIELD(argtypes);
COPY_NODE_FIELD(query);
@@ -4094,6 +4282,8 @@ _copyExecuteStmt(const ExecuteStmt *from)
{
ExecuteStmt *newnode = makeNode(ExecuteStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_STRING_FIELD(name);
COPY_NODE_FIELD(params);
@@ -4105,6 +4295,8 @@ _copyDeallocateStmt(const DeallocateStmt *from)
{
DeallocateStmt *newnode = makeNode(DeallocateStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_STRING_FIELD(name);
return newnode;
@@ -4115,6 +4307,8 @@ _copyDropOwnedStmt(const DropOwnedStmt *from)
{
DropOwnedStmt *newnode = makeNode(DropOwnedStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(roles);
COPY_SCALAR_FIELD(behavior);
@@ -4126,6 +4320,8 @@ _copyReassignOwnedStmt(const ReassignOwnedStmt *from)
{
ReassignOwnedStmt *newnode = makeNode(ReassignOwnedStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(roles);
COPY_NODE_FIELD(newrole);
@@ -4137,6 +4333,8 @@ _copyAlterTSDictionaryStmt(const AlterTSDictionaryStmt *from)
{
AlterTSDictionaryStmt *newnode = makeNode(AlterTSDictionaryStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_NODE_FIELD(dictname);
COPY_NODE_FIELD(options);
@@ -4148,6 +4346,8 @@ _copyAlterTSConfigurationStmt(const AlterTSConfigurationStmt *from)
{
AlterTSConfigurationStmt *newnode = makeNode(AlterTSConfigurationStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_SCALAR_FIELD(kind);
COPY_NODE_FIELD(cfgname);
COPY_NODE_FIELD(tokentype);
@@ -4164,6 +4364,8 @@ _copyCreatePolicyStmt(const CreatePolicyStmt *from)
{
CreatePolicyStmt *newnode = makeNode(CreatePolicyStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_STRING_FIELD(policy_name);
COPY_NODE_FIELD(table);
COPY_STRING_FIELD(cmd_name);
@@ -4180,6 +4382,8 @@ _copyAlterPolicyStmt(const AlterPolicyStmt *from)
{
AlterPolicyStmt *newnode = makeNode(AlterPolicyStmt);
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
COPY_STRING_FIELD(policy_name);
COPY_NODE_FIELD(table);
COPY_NODE_FIELD(roles);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 548a2aa..8916028 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -913,6 +913,8 @@ _equalExtensibleNode(const ExtensibleNode *a, const ExtensibleNode *b)
static bool
_equalQuery(const Query *a, const Query *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_SCALAR_FIELD(commandType);
COMPARE_SCALAR_FIELD(querySource);
/* we intentionally ignore queryId, since it might not be set */
@@ -953,6 +955,8 @@ _equalQuery(const Query *a, const Query *b)
static bool
_equalInsertStmt(const InsertStmt *a, const InsertStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(relation);
COMPARE_NODE_FIELD(cols);
COMPARE_NODE_FIELD(selectStmt);
@@ -966,6 +970,8 @@ _equalInsertStmt(const InsertStmt *a, const InsertStmt *b)
static bool
_equalDeleteStmt(const DeleteStmt *a, const DeleteStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(relation);
COMPARE_NODE_FIELD(usingClause);
COMPARE_NODE_FIELD(whereClause);
@@ -978,6 +984,8 @@ _equalDeleteStmt(const DeleteStmt *a, const DeleteStmt *b)
static bool
_equalUpdateStmt(const UpdateStmt *a, const UpdateStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(relation);
COMPARE_NODE_FIELD(targetList);
COMPARE_NODE_FIELD(whereClause);
@@ -991,6 +999,8 @@ _equalUpdateStmt(const UpdateStmt *a, const UpdateStmt *b)
static bool
_equalSelectStmt(const SelectStmt *a, const SelectStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(distinctClause);
COMPARE_NODE_FIELD(intoClause);
COMPARE_NODE_FIELD(targetList);
@@ -1016,6 +1026,8 @@ _equalSelectStmt(const SelectStmt *a, const SelectStmt *b)
static bool
_equalSetOperationStmt(const SetOperationStmt *a, const SetOperationStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_SCALAR_FIELD(op);
COMPARE_SCALAR_FIELD(all);
COMPARE_NODE_FIELD(larg);
@@ -1031,6 +1043,8 @@ _equalSetOperationStmt(const SetOperationStmt *a, const SetOperationStmt *b)
static bool
_equalAlterTableStmt(const AlterTableStmt *a, const AlterTableStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(relation);
COMPARE_NODE_FIELD(cmds);
COMPARE_SCALAR_FIELD(relkind);
@@ -1055,6 +1069,8 @@ _equalAlterTableCmd(const AlterTableCmd *a, const AlterTableCmd *b)
static bool
_equalAlterDomainStmt(const AlterDomainStmt *a, const AlterDomainStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_SCALAR_FIELD(subtype);
COMPARE_NODE_FIELD(typeName);
COMPARE_STRING_FIELD(name);
@@ -1068,6 +1084,8 @@ _equalAlterDomainStmt(const AlterDomainStmt *a, const AlterDomainStmt *b)
static bool
_equalGrantStmt(const GrantStmt *a, const GrantStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_SCALAR_FIELD(is_grant);
COMPARE_SCALAR_FIELD(targtype);
COMPARE_SCALAR_FIELD(objtype);
@@ -1101,6 +1119,8 @@ _equalAccessPriv(const AccessPriv *a, const AccessPriv *b)
static bool
_equalGrantRoleStmt(const GrantRoleStmt *a, const GrantRoleStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(granted_roles);
COMPARE_NODE_FIELD(grantee_roles);
COMPARE_SCALAR_FIELD(is_grant);
@@ -1114,6 +1134,8 @@ _equalGrantRoleStmt(const GrantRoleStmt *a, const GrantRoleStmt *b)
static bool
_equalAlterDefaultPrivilegesStmt(const AlterDefaultPrivilegesStmt *a, const AlterDefaultPrivilegesStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(options);
COMPARE_NODE_FIELD(action);
@@ -1123,6 +1145,8 @@ _equalAlterDefaultPrivilegesStmt(const AlterDefaultPrivilegesStmt *a, const Alte
static bool
_equalDeclareCursorStmt(const DeclareCursorStmt *a, const DeclareCursorStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_STRING_FIELD(portalname);
COMPARE_SCALAR_FIELD(options);
COMPARE_NODE_FIELD(query);
@@ -1133,6 +1157,8 @@ _equalDeclareCursorStmt(const DeclareCursorStmt *a, const DeclareCursorStmt *b)
static bool
_equalClosePortalStmt(const ClosePortalStmt *a, const ClosePortalStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_STRING_FIELD(portalname);
return true;
@@ -1141,6 +1167,8 @@ _equalClosePortalStmt(const ClosePortalStmt *a, const ClosePortalStmt *b)
static bool
_equalClusterStmt(const ClusterStmt *a, const ClusterStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(relation);
COMPARE_STRING_FIELD(indexname);
COMPARE_SCALAR_FIELD(verbose);
@@ -1151,6 +1179,8 @@ _equalClusterStmt(const ClusterStmt *a, const ClusterStmt *b)
static bool
_equalCopyStmt(const CopyStmt *a, const CopyStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(relation);
COMPARE_NODE_FIELD(query);
COMPARE_NODE_FIELD(attlist);
@@ -1165,6 +1195,8 @@ _equalCopyStmt(const CopyStmt *a, const CopyStmt *b)
static bool
_equalCreateStmt(const CreateStmt *a, const CreateStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(relation);
COMPARE_NODE_FIELD(tableElts);
COMPARE_NODE_FIELD(inhRelations);
@@ -1192,6 +1224,8 @@ _equalTableLikeClause(const TableLikeClause *a, const TableLikeClause *b)
static bool
_equalDefineStmt(const DefineStmt *a, const DefineStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_SCALAR_FIELD(kind);
COMPARE_SCALAR_FIELD(oldstyle);
COMPARE_NODE_FIELD(defnames);
@@ -1204,6 +1238,8 @@ _equalDefineStmt(const DefineStmt *a, const DefineStmt *b)
static bool
_equalDropStmt(const DropStmt *a, const DropStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(objects);
COMPARE_NODE_FIELD(arguments);
COMPARE_SCALAR_FIELD(removeType);
@@ -1217,6 +1253,8 @@ _equalDropStmt(const DropStmt *a, const DropStmt *b)
static bool
_equalTruncateStmt(const TruncateStmt *a, const TruncateStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(relations);
COMPARE_SCALAR_FIELD(restart_seqs);
COMPARE_SCALAR_FIELD(behavior);
@@ -1227,6 +1265,8 @@ _equalTruncateStmt(const TruncateStmt *a, const TruncateStmt *b)
static bool
_equalCommentStmt(const CommentStmt *a, const CommentStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_SCALAR_FIELD(objtype);
COMPARE_NODE_FIELD(objname);
COMPARE_NODE_FIELD(objargs);
@@ -1238,6 +1278,8 @@ _equalCommentStmt(const CommentStmt *a, const CommentStmt *b)
static bool
_equalSecLabelStmt(const SecLabelStmt *a, const SecLabelStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_SCALAR_FIELD(objtype);
COMPARE_NODE_FIELD(objname);
COMPARE_NODE_FIELD(objargs);
@@ -1250,6 +1292,8 @@ _equalSecLabelStmt(const SecLabelStmt *a, const SecLabelStmt *b)
static bool
_equalFetchStmt(const FetchStmt *a, const FetchStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_SCALAR_FIELD(direction);
COMPARE_SCALAR_FIELD(howMany);
COMPARE_STRING_FIELD(portalname);
@@ -1261,6 +1305,8 @@ _equalFetchStmt(const FetchStmt *a, const FetchStmt *b)
static bool
_equalIndexStmt(const IndexStmt *a, const IndexStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_STRING_FIELD(idxname);
COMPARE_NODE_FIELD(relation);
COMPARE_STRING_FIELD(accessMethod);
@@ -1287,6 +1333,8 @@ _equalIndexStmt(const IndexStmt *a, const IndexStmt *b)
static bool
_equalCreateFunctionStmt(const CreateFunctionStmt *a, const CreateFunctionStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_SCALAR_FIELD(replace);
COMPARE_NODE_FIELD(funcname);
COMPARE_NODE_FIELD(parameters);
@@ -1311,6 +1359,8 @@ _equalFunctionParameter(const FunctionParameter *a, const FunctionParameter *b)
static bool
_equalAlterFunctionStmt(const AlterFunctionStmt *a, const AlterFunctionStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(func);
COMPARE_NODE_FIELD(actions);
@@ -1320,6 +1370,8 @@ _equalAlterFunctionStmt(const AlterFunctionStmt *a, const AlterFunctionStmt *b)
static bool
_equalDoStmt(const DoStmt *a, const DoStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(args);
return true;
@@ -1328,6 +1380,8 @@ _equalDoStmt(const DoStmt *a, const DoStmt *b)
static bool
_equalRenameStmt(const RenameStmt *a, const RenameStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_SCALAR_FIELD(renameType);
COMPARE_SCALAR_FIELD(relationType);
COMPARE_NODE_FIELD(relation);
@@ -1344,6 +1398,8 @@ _equalRenameStmt(const RenameStmt *a, const RenameStmt *b)
static bool
_equalAlterObjectDependsStmt(const AlterObjectDependsStmt *a, const AlterObjectDependsStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_SCALAR_FIELD(objectType);
COMPARE_NODE_FIELD(relation);
COMPARE_NODE_FIELD(objname);
@@ -1356,6 +1412,8 @@ _equalAlterObjectDependsStmt(const AlterObjectDependsStmt *a, const AlterObjectD
static bool
_equalAlterObjectSchemaStmt(const AlterObjectSchemaStmt *a, const AlterObjectSchemaStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_SCALAR_FIELD(objectType);
COMPARE_NODE_FIELD(relation);
COMPARE_NODE_FIELD(object);
@@ -1369,6 +1427,8 @@ _equalAlterObjectSchemaStmt(const AlterObjectSchemaStmt *a, const AlterObjectSch
static bool
_equalAlterOwnerStmt(const AlterOwnerStmt *a, const AlterOwnerStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_SCALAR_FIELD(objectType);
COMPARE_NODE_FIELD(relation);
COMPARE_NODE_FIELD(object);
@@ -1381,6 +1441,8 @@ _equalAlterOwnerStmt(const AlterOwnerStmt *a, const AlterOwnerStmt *b)
static bool
_equalAlterOperatorStmt(const AlterOperatorStmt *a, const AlterOperatorStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(opername);
COMPARE_NODE_FIELD(operargs);
COMPARE_NODE_FIELD(options);
@@ -1391,6 +1453,8 @@ _equalAlterOperatorStmt(const AlterOperatorStmt *a, const AlterOperatorStmt *b)
static bool
_equalRuleStmt(const RuleStmt *a, const RuleStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(relation);
COMPARE_STRING_FIELD(rulename);
COMPARE_NODE_FIELD(whereClause);
@@ -1405,6 +1469,8 @@ _equalRuleStmt(const RuleStmt *a, const RuleStmt *b)
static bool
_equalNotifyStmt(const NotifyStmt *a, const NotifyStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_STRING_FIELD(conditionname);
COMPARE_STRING_FIELD(payload);
@@ -1414,6 +1480,8 @@ _equalNotifyStmt(const NotifyStmt *a, const NotifyStmt *b)
static bool
_equalListenStmt(const ListenStmt *a, const ListenStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_STRING_FIELD(conditionname);
return true;
@@ -1422,6 +1490,8 @@ _equalListenStmt(const ListenStmt *a, const ListenStmt *b)
static bool
_equalUnlistenStmt(const UnlistenStmt *a, const UnlistenStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_STRING_FIELD(conditionname);
return true;
@@ -1430,6 +1500,8 @@ _equalUnlistenStmt(const UnlistenStmt *a, const UnlistenStmt *b)
static bool
_equalTransactionStmt(const TransactionStmt *a, const TransactionStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_SCALAR_FIELD(kind);
COMPARE_NODE_FIELD(options);
COMPARE_STRING_FIELD(gid);
@@ -1440,6 +1512,8 @@ _equalTransactionStmt(const TransactionStmt *a, const TransactionStmt *b)
static bool
_equalCompositeTypeStmt(const CompositeTypeStmt *a, const CompositeTypeStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(typevar);
COMPARE_NODE_FIELD(coldeflist);
@@ -1449,6 +1523,8 @@ _equalCompositeTypeStmt(const CompositeTypeStmt *a, const CompositeTypeStmt *b)
static bool
_equalCreateEnumStmt(const CreateEnumStmt *a, const CreateEnumStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(typeName);
COMPARE_NODE_FIELD(vals);
@@ -1458,6 +1534,8 @@ _equalCreateEnumStmt(const CreateEnumStmt *a, const CreateEnumStmt *b)
static bool
_equalCreateRangeStmt(const CreateRangeStmt *a, const CreateRangeStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(typeName);
COMPARE_NODE_FIELD(params);
@@ -1467,6 +1545,8 @@ _equalCreateRangeStmt(const CreateRangeStmt *a, const CreateRangeStmt *b)
static bool
_equalAlterEnumStmt(const AlterEnumStmt *a, const AlterEnumStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(typeName);
COMPARE_STRING_FIELD(oldVal);
COMPARE_STRING_FIELD(newVal);
@@ -1480,6 +1560,8 @@ _equalAlterEnumStmt(const AlterEnumStmt *a, const AlterEnumStmt *b)
static bool
_equalViewStmt(const ViewStmt *a, const ViewStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(view);
COMPARE_NODE_FIELD(aliases);
COMPARE_NODE_FIELD(query);
@@ -1493,6 +1575,8 @@ _equalViewStmt(const ViewStmt *a, const ViewStmt *b)
static bool
_equalLoadStmt(const LoadStmt *a, const LoadStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_STRING_FIELD(filename);
return true;
@@ -1501,6 +1585,8 @@ _equalLoadStmt(const LoadStmt *a, const LoadStmt *b)
static bool
_equalCreateDomainStmt(const CreateDomainStmt *a, const CreateDomainStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(domainname);
COMPARE_NODE_FIELD(typeName);
COMPARE_NODE_FIELD(collClause);
@@ -1512,6 +1598,8 @@ _equalCreateDomainStmt(const CreateDomainStmt *a, const CreateDomainStmt *b)
static bool
_equalCreateOpClassStmt(const CreateOpClassStmt *a, const CreateOpClassStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(opclassname);
COMPARE_NODE_FIELD(opfamilyname);
COMPARE_STRING_FIELD(amname);
@@ -1539,6 +1627,8 @@ _equalCreateOpClassItem(const CreateOpClassItem *a, const CreateOpClassItem *b)
static bool
_equalCreateOpFamilyStmt(const CreateOpFamilyStmt *a, const CreateOpFamilyStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(opfamilyname);
COMPARE_STRING_FIELD(amname);
@@ -1548,6 +1638,8 @@ _equalCreateOpFamilyStmt(const CreateOpFamilyStmt *a, const CreateOpFamilyStmt *
static bool
_equalAlterOpFamilyStmt(const AlterOpFamilyStmt *a, const AlterOpFamilyStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(opfamilyname);
COMPARE_STRING_FIELD(amname);
COMPARE_SCALAR_FIELD(isDrop);
@@ -1559,6 +1651,8 @@ _equalAlterOpFamilyStmt(const AlterOpFamilyStmt *a, const AlterOpFamilyStmt *b)
static bool
_equalCreatedbStmt(const CreatedbStmt *a, const CreatedbStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_STRING_FIELD(dbname);
COMPARE_NODE_FIELD(options);
@@ -1568,6 +1662,8 @@ _equalCreatedbStmt(const CreatedbStmt *a, const CreatedbStmt *b)
static bool
_equalAlterDatabaseStmt(const AlterDatabaseStmt *a, const AlterDatabaseStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_STRING_FIELD(dbname);
COMPARE_NODE_FIELD(options);
@@ -1577,6 +1673,8 @@ _equalAlterDatabaseStmt(const AlterDatabaseStmt *a, const AlterDatabaseStmt *b)
static bool
_equalAlterDatabaseSetStmt(const AlterDatabaseSetStmt *a, const AlterDatabaseSetStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_STRING_FIELD(dbname);
COMPARE_NODE_FIELD(setstmt);
@@ -1586,6 +1684,8 @@ _equalAlterDatabaseSetStmt(const AlterDatabaseSetStmt *a, const AlterDatabaseSet
static bool
_equalDropdbStmt(const DropdbStmt *a, const DropdbStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_STRING_FIELD(dbname);
COMPARE_SCALAR_FIELD(missing_ok);
@@ -1595,6 +1695,8 @@ _equalDropdbStmt(const DropdbStmt *a, const DropdbStmt *b)
static bool
_equalVacuumStmt(const VacuumStmt *a, const VacuumStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_SCALAR_FIELD(options);
COMPARE_NODE_FIELD(relation);
COMPARE_NODE_FIELD(va_cols);
@@ -1605,6 +1707,8 @@ _equalVacuumStmt(const VacuumStmt *a, const VacuumStmt *b)
static bool
_equalExplainStmt(const ExplainStmt *a, const ExplainStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(query);
COMPARE_NODE_FIELD(options);
@@ -1614,6 +1718,8 @@ _equalExplainStmt(const ExplainStmt *a, const ExplainStmt *b)
static bool
_equalCreateTableAsStmt(const CreateTableAsStmt *a, const CreateTableAsStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(query);
COMPARE_NODE_FIELD(into);
COMPARE_SCALAR_FIELD(relkind);
@@ -1626,6 +1732,8 @@ _equalCreateTableAsStmt(const CreateTableAsStmt *a, const CreateTableAsStmt *b)
static bool
_equalRefreshMatViewStmt(const RefreshMatViewStmt *a, const RefreshMatViewStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_SCALAR_FIELD(concurrent);
COMPARE_SCALAR_FIELD(skipData);
COMPARE_NODE_FIELD(relation);
@@ -1636,6 +1744,8 @@ _equalRefreshMatViewStmt(const RefreshMatViewStmt *a, const RefreshMatViewStmt *
static bool
_equalReplicaIdentityStmt(const ReplicaIdentityStmt *a, const ReplicaIdentityStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_SCALAR_FIELD(identity_type);
COMPARE_STRING_FIELD(name);
@@ -1645,6 +1755,8 @@ _equalReplicaIdentityStmt(const ReplicaIdentityStmt *a, const ReplicaIdentityStm
static bool
_equalAlterSystemStmt(const AlterSystemStmt *a, const AlterSystemStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(setstmt);
return true;
@@ -1654,6 +1766,8 @@ _equalAlterSystemStmt(const AlterSystemStmt *a, const AlterSystemStmt *b)
static bool
_equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(sequence);
COMPARE_NODE_FIELD(options);
COMPARE_SCALAR_FIELD(ownerId);
@@ -1665,6 +1779,8 @@ _equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b)
static bool
_equalAlterSeqStmt(const AlterSeqStmt *a, const AlterSeqStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(sequence);
COMPARE_NODE_FIELD(options);
COMPARE_SCALAR_FIELD(missing_ok);
@@ -1675,6 +1791,8 @@ _equalAlterSeqStmt(const AlterSeqStmt *a, const AlterSeqStmt *b)
static bool
_equalVariableSetStmt(const VariableSetStmt *a, const VariableSetStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_SCALAR_FIELD(kind);
COMPARE_STRING_FIELD(name);
COMPARE_NODE_FIELD(args);
@@ -1686,6 +1804,8 @@ _equalVariableSetStmt(const VariableSetStmt *a, const VariableSetStmt *b)
static bool
_equalVariableShowStmt(const VariableShowStmt *a, const VariableShowStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_STRING_FIELD(name);
return true;
@@ -1694,6 +1814,8 @@ _equalVariableShowStmt(const VariableShowStmt *a, const VariableShowStmt *b)
static bool
_equalDiscardStmt(const DiscardStmt *a, const DiscardStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_SCALAR_FIELD(target);
return true;
@@ -1702,9 +1824,11 @@ _equalDiscardStmt(const DiscardStmt *a, const DiscardStmt *b)
static bool
_equalCreateTableSpaceStmt(const CreateTableSpaceStmt *a, const CreateTableSpaceStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_STRING_FIELD(tablespacename);
COMPARE_NODE_FIELD(owner);
- COMPARE_STRING_FIELD(location);
+ COMPARE_STRING_FIELD(location_dir);
COMPARE_NODE_FIELD(options);
return true;
@@ -1713,6 +1837,8 @@ _equalCreateTableSpaceStmt(const CreateTableSpaceStmt *a, const CreateTableSpace
static bool
_equalDropTableSpaceStmt(const DropTableSpaceStmt *a, const DropTableSpaceStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_STRING_FIELD(tablespacename);
COMPARE_SCALAR_FIELD(missing_ok);
@@ -1723,6 +1849,8 @@ static bool
_equalAlterTableSpaceOptionsStmt(const AlterTableSpaceOptionsStmt *a,
const AlterTableSpaceOptionsStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_STRING_FIELD(tablespacename);
COMPARE_NODE_FIELD(options);
COMPARE_SCALAR_FIELD(isReset);
@@ -1734,6 +1862,8 @@ static bool
_equalAlterTableMoveAllStmt(const AlterTableMoveAllStmt *a,
const AlterTableMoveAllStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_STRING_FIELD(orig_tablespacename);
COMPARE_SCALAR_FIELD(objtype);
COMPARE_NODE_FIELD(roles);
@@ -1746,6 +1876,8 @@ _equalAlterTableMoveAllStmt(const AlterTableMoveAllStmt *a,
static bool
_equalCreateExtensionStmt(const CreateExtensionStmt *a, const CreateExtensionStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_STRING_FIELD(extname);
COMPARE_SCALAR_FIELD(if_not_exists);
COMPARE_NODE_FIELD(options);
@@ -1756,6 +1888,8 @@ _equalCreateExtensionStmt(const CreateExtensionStmt *a, const CreateExtensionStm
static bool
_equalAlterExtensionStmt(const AlterExtensionStmt *a, const AlterExtensionStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_STRING_FIELD(extname);
COMPARE_NODE_FIELD(options);
@@ -1765,6 +1899,8 @@ _equalAlterExtensionStmt(const AlterExtensionStmt *a, const AlterExtensionStmt *
static bool
_equalAlterExtensionContentsStmt(const AlterExtensionContentsStmt *a, const AlterExtensionContentsStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_STRING_FIELD(extname);
COMPARE_SCALAR_FIELD(action);
COMPARE_SCALAR_FIELD(objtype);
@@ -1777,6 +1913,8 @@ _equalAlterExtensionContentsStmt(const AlterExtensionContentsStmt *a, const Alte
static bool
_equalCreateFdwStmt(const CreateFdwStmt *a, const CreateFdwStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_STRING_FIELD(fdwname);
COMPARE_NODE_FIELD(func_options);
COMPARE_NODE_FIELD(options);
@@ -1787,6 +1925,8 @@ _equalCreateFdwStmt(const CreateFdwStmt *a, const CreateFdwStmt *b)
static bool
_equalAlterFdwStmt(const AlterFdwStmt *a, const AlterFdwStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_STRING_FIELD(fdwname);
COMPARE_NODE_FIELD(func_options);
COMPARE_NODE_FIELD(options);
@@ -1797,6 +1937,8 @@ _equalAlterFdwStmt(const AlterFdwStmt *a, const AlterFdwStmt *b)
static bool
_equalCreateForeignServerStmt(const CreateForeignServerStmt *a, const CreateForeignServerStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_STRING_FIELD(servername);
COMPARE_STRING_FIELD(servertype);
COMPARE_STRING_FIELD(version);
@@ -1809,6 +1951,8 @@ _equalCreateForeignServerStmt(const CreateForeignServerStmt *a, const CreateFore
static bool
_equalAlterForeignServerStmt(const AlterForeignServerStmt *a, const AlterForeignServerStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_STRING_FIELD(servername);
COMPARE_STRING_FIELD(version);
COMPARE_NODE_FIELD(options);
@@ -1820,6 +1964,8 @@ _equalAlterForeignServerStmt(const AlterForeignServerStmt *a, const AlterForeign
static bool
_equalCreateUserMappingStmt(const CreateUserMappingStmt *a, const CreateUserMappingStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(user);
COMPARE_STRING_FIELD(servername);
COMPARE_NODE_FIELD(options);
@@ -1830,6 +1976,8 @@ _equalCreateUserMappingStmt(const CreateUserMappingStmt *a, const CreateUserMapp
static bool
_equalAlterUserMappingStmt(const AlterUserMappingStmt *a, const AlterUserMappingStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(user);
COMPARE_STRING_FIELD(servername);
COMPARE_NODE_FIELD(options);
@@ -1840,6 +1988,8 @@ _equalAlterUserMappingStmt(const AlterUserMappingStmt *a, const AlterUserMapping
static bool
_equalDropUserMappingStmt(const DropUserMappingStmt *a, const DropUserMappingStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(user);
COMPARE_STRING_FIELD(servername);
COMPARE_SCALAR_FIELD(missing_ok);
@@ -1862,6 +2012,8 @@ _equalCreateForeignTableStmt(const CreateForeignTableStmt *a, const CreateForeig
static bool
_equalImportForeignSchemaStmt(const ImportForeignSchemaStmt *a, const ImportForeignSchemaStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_STRING_FIELD(server_name);
COMPARE_STRING_FIELD(remote_schema);
COMPARE_STRING_FIELD(local_schema);
@@ -1875,6 +2027,8 @@ _equalImportForeignSchemaStmt(const ImportForeignSchemaStmt *a, const ImportFore
static bool
_equalCreateTransformStmt(const CreateTransformStmt *a, const CreateTransformStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_SCALAR_FIELD(replace);
COMPARE_NODE_FIELD(type_name);
COMPARE_STRING_FIELD(lang);
@@ -1887,6 +2041,8 @@ _equalCreateTransformStmt(const CreateTransformStmt *a, const CreateTransformStm
static bool
_equalCreateAmStmt(const CreateAmStmt *a, const CreateAmStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_STRING_FIELD(amname);
COMPARE_NODE_FIELD(handler_name);
COMPARE_SCALAR_FIELD(amtype);
@@ -1897,6 +2053,8 @@ _equalCreateAmStmt(const CreateAmStmt *a, const CreateAmStmt *b)
static bool
_equalCreateTrigStmt(const CreateTrigStmt *a, const CreateTrigStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_STRING_FIELD(trigname);
COMPARE_NODE_FIELD(relation);
COMPARE_NODE_FIELD(funcname);
@@ -1918,6 +2076,8 @@ _equalCreateTrigStmt(const CreateTrigStmt *a, const CreateTrigStmt *b)
static bool
_equalCreateEventTrigStmt(const CreateEventTrigStmt *a, const CreateEventTrigStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_STRING_FIELD(trigname);
COMPARE_STRING_FIELD(eventname);
COMPARE_NODE_FIELD(whenclause);
@@ -1929,6 +2089,8 @@ _equalCreateEventTrigStmt(const CreateEventTrigStmt *a, const CreateEventTrigStm
static bool
_equalAlterEventTrigStmt(const AlterEventTrigStmt *a, const AlterEventTrigStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_STRING_FIELD(trigname);
COMPARE_SCALAR_FIELD(tgenabled);
@@ -1938,6 +2100,8 @@ _equalAlterEventTrigStmt(const AlterEventTrigStmt *a, const AlterEventTrigStmt *
static bool
_equalCreatePLangStmt(const CreatePLangStmt *a, const CreatePLangStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_SCALAR_FIELD(replace);
COMPARE_STRING_FIELD(plname);
COMPARE_NODE_FIELD(plhandler);
@@ -1951,6 +2115,8 @@ _equalCreatePLangStmt(const CreatePLangStmt *a, const CreatePLangStmt *b)
static bool
_equalCreateRoleStmt(const CreateRoleStmt *a, const CreateRoleStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_SCALAR_FIELD(stmt_type);
COMPARE_STRING_FIELD(role);
COMPARE_NODE_FIELD(options);
@@ -1961,6 +2127,8 @@ _equalCreateRoleStmt(const CreateRoleStmt *a, const CreateRoleStmt *b)
static bool
_equalAlterRoleStmt(const AlterRoleStmt *a, const AlterRoleStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(role);
COMPARE_NODE_FIELD(options);
COMPARE_SCALAR_FIELD(action);
@@ -1971,6 +2139,8 @@ _equalAlterRoleStmt(const AlterRoleStmt *a, const AlterRoleStmt *b)
static bool
_equalAlterRoleSetStmt(const AlterRoleSetStmt *a, const AlterRoleSetStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(role);
COMPARE_STRING_FIELD(database);
COMPARE_NODE_FIELD(setstmt);
@@ -1981,6 +2151,8 @@ _equalAlterRoleSetStmt(const AlterRoleSetStmt *a, const AlterRoleSetStmt *b)
static bool
_equalDropRoleStmt(const DropRoleStmt *a, const DropRoleStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(roles);
COMPARE_SCALAR_FIELD(missing_ok);
@@ -1990,6 +2162,8 @@ _equalDropRoleStmt(const DropRoleStmt *a, const DropRoleStmt *b)
static bool
_equalLockStmt(const LockStmt *a, const LockStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(relations);
COMPARE_SCALAR_FIELD(mode);
COMPARE_SCALAR_FIELD(nowait);
@@ -2000,6 +2174,8 @@ _equalLockStmt(const LockStmt *a, const LockStmt *b)
static bool
_equalConstraintsSetStmt(const ConstraintsSetStmt *a, const ConstraintsSetStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(constraints);
COMPARE_SCALAR_FIELD(deferred);
@@ -2009,6 +2185,8 @@ _equalConstraintsSetStmt(const ConstraintsSetStmt *a, const ConstraintsSetStmt *
static bool
_equalReindexStmt(const ReindexStmt *a, const ReindexStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_SCALAR_FIELD(kind);
COMPARE_NODE_FIELD(relation);
COMPARE_STRING_FIELD(name);
@@ -2020,6 +2198,8 @@ _equalReindexStmt(const ReindexStmt *a, const ReindexStmt *b)
static bool
_equalCreateSchemaStmt(const CreateSchemaStmt *a, const CreateSchemaStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_STRING_FIELD(schemaname);
COMPARE_NODE_FIELD(authrole);
COMPARE_NODE_FIELD(schemaElts);
@@ -2031,6 +2211,8 @@ _equalCreateSchemaStmt(const CreateSchemaStmt *a, const CreateSchemaStmt *b)
static bool
_equalCreateConversionStmt(const CreateConversionStmt *a, const CreateConversionStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(conversion_name);
COMPARE_STRING_FIELD(for_encoding_name);
COMPARE_STRING_FIELD(to_encoding_name);
@@ -2043,6 +2225,8 @@ _equalCreateConversionStmt(const CreateConversionStmt *a, const CreateConversion
static bool
_equalCreateCastStmt(const CreateCastStmt *a, const CreateCastStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(sourcetype);
COMPARE_NODE_FIELD(targettype);
COMPARE_NODE_FIELD(func);
@@ -2055,6 +2239,8 @@ _equalCreateCastStmt(const CreateCastStmt *a, const CreateCastStmt *b)
static bool
_equalPrepareStmt(const PrepareStmt *a, const PrepareStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_STRING_FIELD(name);
COMPARE_NODE_FIELD(argtypes);
COMPARE_NODE_FIELD(query);
@@ -2065,6 +2251,8 @@ _equalPrepareStmt(const PrepareStmt *a, const PrepareStmt *b)
static bool
_equalExecuteStmt(const ExecuteStmt *a, const ExecuteStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_STRING_FIELD(name);
COMPARE_NODE_FIELD(params);
@@ -2074,6 +2262,8 @@ _equalExecuteStmt(const ExecuteStmt *a, const ExecuteStmt *b)
static bool
_equalDeallocateStmt(const DeallocateStmt *a, const DeallocateStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_STRING_FIELD(name);
return true;
@@ -2082,6 +2272,8 @@ _equalDeallocateStmt(const DeallocateStmt *a, const DeallocateStmt *b)
static bool
_equalDropOwnedStmt(const DropOwnedStmt *a, const DropOwnedStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(roles);
COMPARE_SCALAR_FIELD(behavior);
@@ -2091,6 +2283,8 @@ _equalDropOwnedStmt(const DropOwnedStmt *a, const DropOwnedStmt *b)
static bool
_equalReassignOwnedStmt(const ReassignOwnedStmt *a, const ReassignOwnedStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(roles);
COMPARE_NODE_FIELD(newrole);
@@ -2100,6 +2294,8 @@ _equalReassignOwnedStmt(const ReassignOwnedStmt *a, const ReassignOwnedStmt *b)
static bool
_equalAlterTSDictionaryStmt(const AlterTSDictionaryStmt *a, const AlterTSDictionaryStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_NODE_FIELD(dictname);
COMPARE_NODE_FIELD(options);
@@ -2110,6 +2306,8 @@ static bool
_equalAlterTSConfigurationStmt(const AlterTSConfigurationStmt *a,
const AlterTSConfigurationStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_SCALAR_FIELD(kind);
COMPARE_NODE_FIELD(cfgname);
COMPARE_NODE_FIELD(tokentype);
@@ -2124,6 +2322,8 @@ _equalAlterTSConfigurationStmt(const AlterTSConfigurationStmt *a,
static bool
_equalCreatePolicyStmt(const CreatePolicyStmt *a, const CreatePolicyStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_STRING_FIELD(policy_name);
COMPARE_NODE_FIELD(table);
COMPARE_STRING_FIELD(cmd_name);
@@ -2138,6 +2338,8 @@ _equalCreatePolicyStmt(const CreatePolicyStmt *a, const CreatePolicyStmt *b)
static bool
_equalAlterPolicyStmt(const AlterPolicyStmt *a, const AlterPolicyStmt *b)
{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
COMPARE_STRING_FIELD(policy_name);
COMPARE_NODE_FIELD(table);
COMPARE_NODE_FIELD(roles);
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 41dde50..475efcf 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -177,6 +177,7 @@ planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
result = (*planner_hook) (parse, cursorOptions, boundParams);
else
result = standard_planner(parse, cursorOptions, boundParams);
+
return result;
}
@@ -410,6 +411,8 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
/* build the PlannedStmt result */
result = makeNode(PlannedStmt);
+ result->location = parse->location;
+ result->length = parse->length;
result->commandType = parse->commandType;
result->queryId = parse->queryId;
result->hasReturning = (parse->returningList != NIL);
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index a558083..018cc75 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->location = ((ParseNode*) parseTree)->location;
+ result->length = ((ParseNode*) parseTree)->length;
+
return result;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 08cf5b7..c65f3b5 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -178,6 +178,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 Node *stmtWithLocation(Node *node, core_yyscan_t yyscanner, int start);
%}
@@ -762,14 +763,14 @@ stmtblock: stmtmulti
stmtmulti: stmtmulti ';' stmt
{
if ($3 != NULL)
- $$ = lappend($1, $3);
+ $$ = lappend($1, stmtWithLocation($3, yyscanner, @2+1));
else
$$ = $1;
}
| stmt
{
if ($1 != NULL)
- $$ = list_make1($1);
+ $$ = list_make1(stmtWithLocation($1, yyscanner, 0));
else
$$ = NIL;
}
@@ -4080,7 +4081,7 @@ CreateTableSpaceStmt: CREATE TABLESPACE name OptTableSpaceOwner LOCATION Sconst
CreateTableSpaceStmt *n = makeNode(CreateTableSpaceStmt);
n->tablespacename = $3;
n->owner = $4;
- n->location = $6;
+ n->location_dir = $6;
n->options = $7;
$$ = (Node *) n;
}
@@ -15284,6 +15285,31 @@ makeRecursiveViewSelect(char *relname, List *aliases, Node *query)
return (Node *) s;
}
+/*
+ * Set query location & length for statement "node" starting at "start"
+ *
+ * Computing the length depends on what triggered the stmtmulti reduction, which may be:
+ * - a ';', in which case statement ends at this last semicolon
+ * - the end of input, in which case statement ends at end of string
+ */
+static Node *
+stmtWithLocation(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->location = start;
+ if (extra->last_semicolon == -1 || start == (extra->last_semicolon + 1))
+ /* reduction triggered by end of input */
+ pn->length = strlen(extra->core_yy_extra.scanbuf) - start;
+ else
+ /* reduction triggered by ';' */
+ pn->length = extra->last_semicolon - start;
+
+ return node;
+}
+
/* parser_init()
* Initialize to parse one query string
*/
@@ -15291,4 +15317,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 201f248..66e049e 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 9d8ef77..561ed01 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 location is within to the initial query string.
+ */
+typedef struct ParseNode
+{
+ NodeTag type;
+ int location;
+ int length;
+} 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* query length, 0 if unset */
char *schemaname; /* the name of the schema to create */
RoleSpec *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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* query length, 0 if unset */
char *name;
} VariableShowStmt;
@@ -1853,6 +1903,8 @@ typedef struct VariableShowStmt
typedef struct CreateStmt
{
NodeTag type;
+ int location; /* query location */
+ int length; /* 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,15 +2032,19 @@ typedef struct Constraint
typedef struct CreateTableSpaceStmt
{
NodeTag type;
+ int location; /* query location */
+ int length; /* query length, 0 if unset */
char *tablespacename;
RoleSpec *owner;
- char *location;
+ char *location_dir;
List *options;
} CreateTableSpaceStmt;
typedef struct DropTableSpaceStmt
{
NodeTag type;
+ int location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* query length, 0 if unset */
char *tablespacename;
List *options;
bool isReset;
@@ -2004,6 +2062,8 @@ typedef struct AlterTableSpaceOptionsStmt
typedef struct AlterTableMoveAllStmt
{
NodeTag type;
+ int location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* query length, 0 if unset */
RoleSpec *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 location; /* query location */
+ int length; /* query length, 0 if unset */
RoleSpec *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 location; /* query location */
+ int length; /* query length, 0 if unset */
RoleSpec *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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* query length, 0 if unset */
RoleSpec *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 location; /* query location */
+ int length; /* query length, 0 if unset */
RoleSpec *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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* query length, 0 if unset */
char *filename; /* file to load */
} LoadStmt;
@@ -2864,6 +3036,8 @@ typedef struct LoadStmt
typedef struct CreatedbStmt
{
NodeTag type;
+ int location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* query length, 0 if unset */
VariableSetStmt *setstmt; /* SET subcommand */
} AlterSystemStmt;
@@ -2914,6 +3096,8 @@ typedef struct AlterSystemStmt
typedef struct ClusterStmt
{
NodeTag type;
+ int location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* query length, 0 if unset */
} CheckPointStmt;
/* ----------------------
@@ -3022,6 +3216,8 @@ typedef enum DiscardMode
typedef struct DiscardStmt
{
NodeTag type;
+ int location; /* query location */
+ int length; /* query length, 0 if unset */
DiscardMode target;
} DiscardStmt;
@@ -3032,6 +3228,8 @@ typedef struct DiscardStmt
typedef struct LockStmt
{
NodeTag type;
+ int location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* query length, 0 if unset */
TypeName *sourcetype;
TypeName *targettype;
FuncWithArgs *func;
@@ -3110,6 +3316,8 @@ typedef struct CreateCastStmt
typedef struct CreateTransformStmt
{
NodeTag type;
+ int location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* query length, 0 if unset */
List *roles;
DropBehavior behavior;
} DropOwnedStmt;
@@ -3170,6 +3386,8 @@ typedef struct DropOwnedStmt
typedef struct ReassignOwnedStmt
{
NodeTag type;
+ int location; /* query location */
+ int length; /* query length, 0 if unset */
List *roles;
RoleSpec *newrole;
} ReassignOwnedStmt;
@@ -3180,6 +3398,8 @@ typedef struct ReassignOwnedStmt
typedef struct AlterTSDictionaryStmt
{
NodeTag type;
+ int location; /* query location */
+ int length; /* 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 location; /* query location */
+ int length; /* 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..7d139a3 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -36,6 +36,8 @@
typedef struct PlannedStmt
{
NodeTag type;
+ int location; /* query location */
+ int length; /* query length, 0 if unset */
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.
*/
Hello,
At Fri, 30 Dec 2016 15:10:42 +0100 (CET), Fabien COELHO <coelho@cri.ensmp.fr> wrote in <alpine.DEB.2.20.1612301453280.32017@lancre>
Hello,
Yeah, that's what I was thinking of. There aren't very many places
that
would need to know about that, I believe; [...]For fixing the information in pg_stat_statement, the location data
must be transported from the parsed node to the query to the planned
node, because the later two nodes types are passed to different hooks.Now the detail is that utility statements, which seems to be nearly
all of them but select/update/delete/insert, do not have plans: The
statement itself is its own plan... so there is no place to store the
location & length.Here is an updated version:
Changes wrt v2:
- I have added the missing stuff under /nodes/, this is stupid code that
should be automatically generated:-(- I have added comments in "gram.y" about how the length is computed.
I have also slightly simplified the rule code there.- I have rename "location" in create table space to "location_dir"
to avoid confusion.- I have renamed the fields "location" and "length" instead of q*.
- I have moved the location & lenth copies in standard_planner.
- I have fixed the function declaration typo.
- I have simplified pgss_store code to avoid a copy, and move the
length truncation in qtext_store.- I have improved again the pg_stat_statement regression tests with
combined utility statement tests, which implied some fixes to
extract the right substring for utility queries.However, not changed:
- I cannot use the intermediate node trick suggested by Tom because
it does not work for utility statements which do not have plans, so
the code still adds location & length, sorry.
Wrapping the output of pg_parse_query still works for non-utility
commands. But pg_stat_statement retrieves the location
information via ProcessUtility, which has plantree. Wrapping
plantree with the same struct also works but it imacts too widely
and extremely error-prone.
One available solution is, as Fabien did, bring location and
length data along with transformation. And anther is give a
chopped query to query tree.
The attached patch does the second way (but quite ugly and
incomplete, it's PoC). This seems working as expected to a
certain degree but somewhat bogus.. A significant drawback of
this is that this makes a copy of each query in multistatement.
- I still use the 'last_semicolon' lexer variable. The alternative is to
change rules so as not to skip empty statements, then write a loop to
compute the length based on successor location, and remove the empty
statements. It can be done, I do not think it is better, it is only
different and more verbose. I'll do it if required by a committer.
I think this is doable in the way shown in this patch. But this
seems somewhat bogus, too..
regards,
--
Kyotaro Horiguchi
NTT Open Source Software Center
Attachments:
0001-Fix-current-query-string-of-query-tree.patchtext/x-patch; charset=us-asciiDownload
From 20b34be19e51b878c33306d13a453bdd05da23bc Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horiguchi.kyotaro@lab.ntt.co.jp>
Date: Thu, 12 Jan 2017 21:51:34 +0900
Subject: [PATCH 1/2] Fix current query string of query tree.
---
src/backend/catalog/pg_proc.c | 2 +-
src/backend/commands/extension.c | 2 +-
src/backend/commands/foreigncmds.c | 3 ++-
src/backend/commands/prepare.c | 2 +-
src/backend/commands/tablecmds.c | 2 +-
src/backend/executor/functions.c | 2 +-
src/backend/executor/spi.c | 4 ++--
src/backend/nodes/copyfuncs.c | 15 +++++++++++++++
src/backend/nodes/equalfuncs.c | 13 +++++++++++++
src/backend/parser/analyze.c | 4 ++++
src/backend/parser/gram.y | 20 ++++++++++++++++++--
src/backend/parser/parse_type.c | 2 +-
src/backend/tcop/postgres.c | 22 ++++++++++++++++------
src/backend/tcop/pquery.c | 1 +
src/include/nodes/nodes.h | 1 +
src/include/nodes/parsenodes.h | 20 ++++++++++++++++++++
src/include/nodes/plannodes.h | 2 ++
17 files changed, 100 insertions(+), 17 deletions(-)
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 6d8f17d..6d9e639 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -934,7 +934,7 @@ fmgr_sql_validator(PG_FUNCTION_ARGS)
querytree_list = NIL;
foreach(lc, raw_parsetree_list)
{
- Node *parsetree = (Node *) lfirst(lc);
+ Node *parsetree = (Node *) stripParseNode(lfirst(lc));
List *querytree_sublist;
querytree_sublist = pg_analyze_and_rewrite_params(parsetree,
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index be52148..36ebf93 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -712,7 +712,7 @@ execute_sql_string(const char *sql, const char *filename)
*/
foreach(lc1, raw_parsetree_list)
{
- Node *parsetree = (Node *) lfirst(lc1);
+ Node *parsetree = (Node *) stripParseNode(lfirst(lc1));
List *stmt_list;
ListCell *lc2;
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index 06b4bc3..12fa20f 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -1572,7 +1572,8 @@ ImportForeignSchema(ImportForeignSchemaStmt *stmt)
*/
foreach(lc2, raw_parsetree_list)
{
- CreateForeignTableStmt *cstmt = lfirst(lc2);
+ CreateForeignTableStmt *cstmt =
+ (CreateForeignTableStmt *) stripParseNode(lfirst(lc2));
/*
* Because we only allow CreateForeignTableStmt, we can skip parse
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index d768cf8..5f6e8e4 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -255,7 +255,7 @@ ExecuteQuery(ExecuteStmt *stmt, IntoClause *intoClause,
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("prepared statement is not a SELECT")));
- pstmt = (PlannedStmt *) linitial(plan_list);
+ pstmt = (PlannedStmt *) stripParseNode(linitial(plan_list));
if (!IsA(pstmt, PlannedStmt) ||
pstmt->commandType != CMD_SELECT ||
pstmt->utilityStmt != NULL)
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 42558ec..5b01cf7 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9285,7 +9285,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
querytree_list = NIL;
foreach(list_item, raw_parsetree_list)
{
- Node *stmt = (Node *) lfirst(list_item);
+ Node *stmt = (Node *) stripParseNode(lfirst(list_item));
if (IsA(stmt, IndexStmt))
querytree_list = lappend(querytree_list,
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 0a1bb9d..46c2388 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -695,7 +695,7 @@ init_sql_fcache(FmgrInfo *finfo, Oid collation, bool lazyEvalOK)
flat_query_list = NIL;
foreach(lc, raw_parsetree_list)
{
- Node *parsetree = (Node *) lfirst(lc);
+ Node *parsetree = (Node *) stripParseNode(lfirst(lc));
List *queryTree_sublist;
queryTree_sublist = pg_analyze_and_rewrite_params(parsetree,
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index ee7a7e2..2f35f6b 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -1757,7 +1757,7 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan)
foreach(list_item, raw_parsetree_list)
{
- Node *parsetree = (Node *) lfirst(list_item);
+ Node *parsetree = (Node *) stripParseNode(lfirst(list_item));
List *stmt_list;
CachedPlanSource *plansource;
@@ -1859,7 +1859,7 @@ _SPI_prepare_oneshot_plan(const char *src, SPIPlanPtr plan)
foreach(list_item, raw_parsetree_list)
{
- Node *parsetree = (Node *) lfirst(list_item);
+ Node *parsetree = (Node *) stripParseNode(lfirst(list_item));
CachedPlanSource *plansource;
plansource = CreateOneShotCachedPlan(parsetree,
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 930f2f1..47d6934 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2771,6 +2771,18 @@ _copyQuery(const Query *from)
return newnode;
}
+static ParseNode *
+_copyParseNode(const ParseNode *from)
+{
+ ParseNode *newnode = makeNode(ParseNode);
+
+ COPY_SCALAR_FIELD(location);
+ COPY_SCALAR_FIELD(length);
+ COPY_NODE_FIELD(stmt);
+
+ return newnode;
+}
+
static InsertStmt *
_copyInsertStmt(const InsertStmt *from)
{
@@ -4728,6 +4740,9 @@ copyObject(const void *from)
case T_Query:
retval = _copyQuery(from);
break;
+ case T_ParseNode:
+ retval = _copyParseNode(from);
+ break;
case T_InsertStmt:
retval = _copyInsertStmt(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index a27e5ed..a81bf87 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -951,6 +951,16 @@ _equalQuery(const Query *a, const Query *b)
}
static bool
+_equalParseNode(const ParseNode *a, const ParseNode *b)
+{
+ COMPARE_SCALAR_FIELD(location);
+ COMPARE_SCALAR_FIELD(length);
+ COMPARE_NODE_FIELD(stmt);
+
+ return true;
+}
+
+static bool
_equalInsertStmt(const InsertStmt *a, const InsertStmt *b)
{
COMPARE_NODE_FIELD(relation);
@@ -3015,6 +3025,9 @@ equal(const void *a, const void *b)
case T_Query:
retval = _equalQuery(a, b);
break;
+ case T_ParseNode:
+ retval = _equalParseNode(a, b);
+ break;
case T_InsertStmt:
retval = _equalInsertStmt(a, b);
break;
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 5116cbb..909734d 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -183,6 +183,8 @@ parse_sub_analyze(Node *parseTree, ParseState *parentParseState,
Query *
transformTopLevelStmt(ParseState *pstate, Node *parseTree)
{
+ parseTree = stripParseNode(parseTree);
+
if (IsA(parseTree, SelectStmt))
{
SelectStmt *stmt = (SelectStmt *) parseTree;
@@ -224,6 +226,8 @@ transformStmt(ParseState *pstate, Node *parseTree)
{
Query *result;
+ Assert(!IsA(parseTree, ParseNode));
+
/*
* We apply RAW_EXPRESSION_COVERAGE_TEST testing to basic DML statements;
* we can't just run it on everything because raw_expression_tree_walker()
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 9eef550..45ac3ae 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -761,15 +761,31 @@ stmtblock: stmtmulti
/* the thrashing around here is to discard "empty" statements... */
stmtmulti: stmtmulti ';' stmt
{
+ if ($1 != NULL)
+ {
+ ParseNode *last = (ParseNode *) llast($1);
+ last->length = @2 - last->location + 1;
+ }
if ($3 != NULL)
- $$ = lappend($1, $3);
+ {
+ ParseNode *pn = makeNode(ParseNode);
+ pn->stmt = $3;
+ pn->location = @3;
+ $$ = lappend($1, pn);
+ }
else
$$ = $1;
}
| stmt
{
if ($1 != NULL)
- $$ = list_make1($1);
+ {
+ ParseNode *pn = makeNode(ParseNode);
+ pn->location = @1;
+ pn->length = 0;
+ pn->stmt = $1;
+ $$ = list_make1(pn);
+ }
else
$$ = NIL;
}
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index e2c884c..f9001a0 100644
--- a/src/backend/parser/parse_type.c
+++ b/src/backend/parser/parse_type.c
@@ -720,7 +720,7 @@ typeStringToTypeName(const char *str)
*/
if (list_length(raw_parsetree_list) != 1)
goto fail;
- stmt = (SelectStmt *) linitial(raw_parsetree_list);
+ stmt = (SelectStmt *) stripParseNode(linitial(raw_parsetree_list));
if (stmt == NULL ||
!IsA(stmt, SelectStmt) ||
stmt->distinctClause != NIL ||
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 05b2e57..1e5b50f 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -881,7 +881,6 @@ exec_simple_query(const char *query_string)
bool isTopLevel;
char msec_str[32];
-
/*
* Report query to various monitoring facilities.
*/
@@ -955,7 +954,8 @@ exec_simple_query(const char *query_string)
*/
foreach(parsetree_item, parsetree_list)
{
- Node *parsetree = (Node *) lfirst(parsetree_item);
+ ParseNode *raw_parsetree = (ParseNode *) lfirst(parsetree_item);
+ Node *parsetree = stripParseNode(raw_parsetree);
bool snapshot_set = false;
const char *commandTag;
char completionTag[COMPLETION_TAG_BUFSIZE];
@@ -964,7 +964,16 @@ exec_simple_query(const char *query_string)
Portal portal;
DestReceiver *receiver;
int16 format;
+ char *current_query_string = (char *)query_string;
+ if (list_length(parsetree_list) > 1)
+ {
+ current_query_string = palloc(raw_parsetree->length + 1);
+ memcpy(current_query_string,
+ query_string + raw_parsetree->location,
+ raw_parsetree->length + 1);
+ current_query_string[raw_parsetree->length] = 0;
+ }
/*
* Get the command name for use in status display (it also becomes the
* default completion tag, down inside PortalRun). Set ps_status and
@@ -1016,7 +1025,8 @@ exec_simple_query(const char *query_string)
*/
oldcontext = MemoryContextSwitchTo(MessageContext);
- querytree_list = pg_analyze_and_rewrite(parsetree, query_string,
+ querytree_list = pg_analyze_and_rewrite(parsetree,
+ current_query_string,
NULL, 0);
plantree_list = pg_plan_queries(querytree_list,
@@ -1044,7 +1054,7 @@ exec_simple_query(const char *query_string)
*/
PortalDefineQuery(portal,
NULL,
- query_string,
+ current_query_string,
commandTag,
plantree_list,
NULL);
@@ -1279,7 +1289,7 @@ exec_parse_message(const char *query_string, /* string to execute */
bool snapshot_set = false;
int i;
- raw_parse_tree = (Node *) linitial(parsetree_list);
+ raw_parse_tree = (Node *) stripParseNode(linitial(parsetree_list));
/*
* Get the command name for possible use in status display.
@@ -2064,7 +2074,7 @@ check_log_statement(List *stmt_list)
/* Else we have to inspect the statement(s) to see whether to log */
foreach(stmt_item, stmt_list)
{
- Node *stmt = (Node *) lfirst(stmt_item);
+ Node *stmt = stripParseNode(lfirst(stmt_item));
if (GetCommandLogLevel(stmt) <= log_statement)
return true;
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 3b5da73..9ddfc55 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -31,6 +31,7 @@
* if there are several).
*/
Portal ActivePortal = NULL;
+char *portal_query_string = NULL;
static void ProcessQuery(PlannedStmt *plan,
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index a1bb0ac..928da44 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -302,6 +302,7 @@ typedef enum NodeTag
* TAGS FOR STATEMENT NODES (mostly in parsenodes.h)
*/
T_Query,
+ T_ParseNode,
T_PlannedStmt,
T_InsertStmt,
T_DeleteStmt,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 7ceaa22..23bbfc8 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -78,6 +78,26 @@ 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 location is within to the initial query string.
+ */
+typedef struct ParseNode
+{
+ NodeTag type;
+ int location;
+ int length;
+ Node *stmt;
+} 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) IsA((nodeptr), ParseNode)
+#define stripParseNode(nodeptr) (isParseNode(nodeptr)? ((ParseNode *)(nodeptr))->stmt : (Node *) (nodeptr))
/*****************************************************************************
* Query Tree
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 692a626..921d04c 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -36,6 +36,8 @@
typedef struct PlannedStmt
{
NodeTag type;
+ int location; /* query location */
+ int length; /* query length, 0 if unset */
CmdType commandType; /* select|insert|update|delete */
--
2.9.2
0002-Change-regtest-of-pg_stat_statements.patchtext/x-patch; charset=us-asciiDownload
From cddfc4d9d7e3d51d19e8ed80c60c34c159869d20 Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horiguchi.kyotaro@lab.ntt.co.jp>
Date: Thu, 12 Jan 2017 21:52:14 +0900
Subject: [PATCH 2/2] Change regtest of pg_stat_statements.
This is just the same as Fabien's patch, but fails in somewhat bogus
way..
---
.../expected/pg_stat_statements.out | 362 ++++++++++++++++++++-
.../pg_stat_statements/sql/pg_stat_statements.sql | 183 ++++++++++-
2 files changed, 530 insertions(+), 15 deletions(-)
diff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.out
index 3573c19..06f6ffe 100644
--- a/contrib/pg_stat_statements/expected/pg_stat_statements.out
+++ b/contrib/pg_stat_statements/expected/pg_stat_statements.out
@@ -1,21 +1,363 @@
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
+ SELECT pg_stat_statements_reset() | 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
+ SELECT pg_stat_statements_reset() | 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)
-DROP TABLE test;
+--
+--
+-- 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
+-----------------------------------+-------+------
+ SELECT pg_stat_statements_reset() | 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;
+SELECT 1;
+ ?column?
+----------
+ 1
+(1 row)
+
+CREATE INDEX test_b ON test(b);
+DROP TABLE test \;
+DROP TABLE IF EXISTS test \;
+DROP FUNCTION PLUS_ONE(INTEGER);
+NOTICE: table "test" does not exist, skipping
+DROP TABLE IF EXISTS test \;
+DROP TABLE IF EXISTS test \;
+DROP FUNCTION IF EXISTS PLUS_ONE(INTEGER);
+NOTICE: table "test" does not exist, skipping
+NOTICE: table "test" does not exist, skipping
+NOTICE: function plus_one(pg_catalog.int4) does not exist, skipping
+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 IF EXISTS PLUS_ONE(INTEGER) | 1 | 0
+ DROP FUNCTION PLUS_ONE(INTEGER) | 1 | 0
+ DROP FUNCTION PLUS_TWO(INTEGER) | 1 | 0
+ DROP TABLE IF EXISTS test | 3 | 0
+ DROP TABLE test | 1 | 0
+ SELECT ? | 1 | 1
+ SELECT pg_stat_statements_reset() | 1 | 1
+(8 rows)
+
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/sql/pg_stat_statements.sql b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
index 7e2b263..2a32f23 100644
--- a/contrib/pg_stat_statements/sql/pg_stat_statements.sql
+++ b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
@@ -1,15 +1,188 @@
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;
+
+--
+--
+-- 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;
+
+SELECT 1;
+CREATE INDEX test_b ON test(b);
+DROP TABLE test \;
+DROP TABLE IF EXISTS test \;
+DROP FUNCTION PLUS_ONE(INTEGER);
+DROP TABLE IF EXISTS test \;
+DROP TABLE IF EXISTS test \;
+DROP FUNCTION IF EXISTS PLUS_ONE(INTEGER);
+DROP FUNCTION PLUS_TWO(INTEGER);
-SELECT query, calls, rows from pg_stat_statements ORDER BY rows;
+SELECT query, calls, rows FROM pg_stat_statements ORDER BY rows, query;
-DROP TABLE test;
DROP EXTENSION pg_stat_statements;
--
2.9.2
Kyotaro HORIGUCHI <horiguchi.kyotaro@lab.ntt.co.jp> writes:
At Fri, 30 Dec 2016 15:10:42 +0100 (CET), Fabien COELHO <coelho@cri.ensmp.fr> wrote in <alpine.DEB.2.20.1612301453280.32017@lancre>
- I cannot use the intermediate node trick suggested by Tom because
it does not work for utility statements which do not have plans, so
the code still adds location & length, sorry.
I still really, really don't like this patch. I think it's going to
create continuing maintenance problems, because of shortcuts like this:
+#define isParseNodeTag(tag) ((T_Query <= (tag)) && ((tag) < T_A_Expr))
We've never had any code that depends on ranges of nodetag numbers, and
now is not the time to start. (The fact that you had to randomly relocate
some existing tags to make this work didn't make me any happier about it.)
- I still use the 'last_semicolon' lexer variable. The alternative is to
change rules so as not to skip empty statements, then write a loop to
compute the length based on successor location, and remove the empty
statements. It can be done, I do not think it is better, it is only
different and more verbose. I'll do it if required by a committer.
I think this is doable in the way shown in this patch. But this
seems somewhat bogus, too..
Yeah, I doubt that this technique for getting the raw locations in the
grammar+lexer works reliably. In general, Bison is a bit asynchronous
in terms of scanning, and may or may not have scanned a token ahead of
what the current production covers. So having production rules that
look at the scanner state is just dangerous. You should be relying on
the Bison locations mechanism (@N), instead. That has some gotchas of
its own with respect to locations of nonterminals, but at least they're
known --- and we could fix them if we had to.
Anyway, I decided to see what it would take to do it the way I had
in mind, which was to stick a separate RawStmt node atop each statement
parsetree. The project turned out a bit larger than I hoped :-(, but
I'm really pleased with the end result, because it makes the APIs around
lists of statements much simpler and more regular than they used to be.
I would probably propose most of the attached patch for adoption even
if we weren't interested in tracking statement boundaries.
In brief, what this does is:
* The output of raw parsing is now always a list of RawStmt nodes;
the statement-type-dependent nodes are one level down from that.
This is similar to the existing rule that the output of parse analysis
is always a list of Query nodes, even though the Query struct is mostly
empty in the CMD_UTILITY case. Furthermore, the output of pg_plan_queries
is now always a list of PlannedStmt nodes, even for utility statements.
In the case of a utility statement, "planning" just consists of wrapping
a CMD_UTILITY PlannedStmt around the utility node. Now, every list of
statements has a consistent head-node type depending on how far along
it is in processing.
* This allows us to keep statement location/length in RawStmt, Query,
and PlannedStmt nodes, and not anywhere else.
* This results in touching quite a bit of code that is concerned with
lists of statements in different formats, but in most places, it's
simple changes like replacing generic Node* pointers with specific
RawStmt* or PlannedStmt* pointers. I think that's an unalloyed good.
* To preserve the new rule that the node passed to top-level
parse_analyze() is now always a RawStmt, I made the grammar insert
RawStmts in the sub-statements of EXPLAIN, DECLARE CURSOR, etc.
This adds a bit of cruft in gram.y but avoids cruft elsewhere.
* In addition, I had to change the API of ProcessUtility() so that
what it's passed is the wrapper PlannedStmt not just the bare
utility statement. This is what allows pg_stat_statements to get at
the location info for a utility statement. This will affect all
users of ProcessUtility_hook, but the changes are pretty trivial
for those that don't care about location info --- see the changes
to contrib/sepgsql/hooks.c for an example.
* Because PlannedStmt also carries a canSetTag field, we're able to
get rid of some ad-hoc rules about how to reconstruct canSetTag for
a bare utility statement (specifically, the assumption that a utility
is canSetTag if and only if it's the only one in its list). While
I see no near-term need for relaxing that, it's nice to get rid of the
ad-hocery.
* I chose to also rearrange the post-parse-analysis representation
of DECLARE CURSOR so that it looks more like EXPLAIN, PREPARE, etc.
That is, the contained SELECT remains a child of the DeclareCursorStmt
rather than getting flipped around to be the other way. Possibly this
patch could have been made to work without that, but the existing code
had some weird rules about how the presence of a PlannedStmt where a
utility command was expected meant it was DECLARE CURSOR, and I was out
to get rid of weird rules like that one. With these changes, it's
true for both Query and PlannedStmt that utilityStmt is non-null if
and only if commandType is CMD_UTILITY. That allows simplifying a
whole lot of places that were testing both fields --- I think a few
of those were just defensive programming, but in a lot of them, it was
actually necessary to avoid confusing DECLARE CURSOR with SELECT.
So this ends up costing one extra palloc per statement, or two extra
in the case of a utility statement, but really that's quite negligible
compared to everything else that happens in processing a statement.
As against that, I think it makes the related code a lot clearer.
The sheer size of the patch is a bit more than Fabien's patch, but
what it is touching is not per-statement-type code but code that
works generically with lists of statements. So I think it's less
likely to cause problems for other patches.
regards, tom lane
Attachments:
stmt-list-rewrite-1.patch.gzapplication/x-gzip; name=stmt-list-rewrite-1.patch.gzDownload
���wXstmt-list-rewrite-1.patch �=�w�F�?���_O
6��7q��&�w;x�=�=!P-$"�8�6���{g$������+m������;�ah�F��`l�L?4�w���l��|��?|�m�;��f���0����}6����=��X���Z�X
k��VcZ�T�V�����6�o/�"��BYc����N���f�_{�����[�s��v��w����|��,�3��l�������|�T�O8��i���6|��u��o�3J�����'�����=�� 8c��*2_���4�:7����E����[�R �����v77�6wx������V ^�lW��]����S$u��{@��lw0�����;m����
��>��{[`�nY^1���u�)sa���v����B/�U��
�i,���_��w�e5�Rp��&�5�F��
�^� RC �2�e�������@r���;�\�S������j�V��ZL����D����9�Y�����t������!�)E�}W7��s�$�z�^�w���-S��e��L`�'O��6����q���M�!��!%$Y���;����R# �tn��lN���$P����zg��q\k��j������mO'��o�.�n��������g=ioC�I����`[.�h�����N�j�P�6}���0>���)�����]��{������P�� ~;�
U[�Wa�����]���?���n����PFdzi8�|j����b.k)�������6��P4e� ��@�1(E���4:Z�B�A�%sfh�7!J&��@�0,��09e�L���3-KKL�������s�<�u������@�S����#��s�����#��F,&� ����8�~�@/�{���\����9�Iw��o����^���}�Y���T��vVO�[���o�+�+�r9��WY��z��PV�zV�{%�S1&�c��?�����]�����������r%�R��v��=���JTl#%����%;�{J\��gJ\��6���t�N�q��*C�S�o`~��@X\�|��1
��� N�����3���%`=����
��\�<�r��
%��uT����i�z8#jIK��h�� �q�����������,�W�RQ{U�P�V��U�V�ZM�T[��^��M� �J9��� ���a/�+�H�toFZ5P4L������=������8��5��)���]���8�=��L��"��=m�ez��W�b:f}�6$)Z]��T����N�CO��Z�Q �kj]
�@,S��������V&-$g��-��/����lX�5,���x�2`��I����w�h���y�BX�.��ke����Qh���d�I >, �:)� �v���R �P�a��B���o�T�q������L�^���SX�M����Lh�Exyz���/�\�i��5{,���A������1�_��Q��v��e��L�����g�����/N���ex���p6���<��G��W'=\,�;���{�/s&����v'�:��U����������N�Pq������ M�:����g 2��pb�
�!���'��"�a���*�+�=I@�5�R�F*Q��{���ah�;����p���� /����0���� �����[��L�i*�V-��y'��e-H����euHq�PbI�Gh��!�W����PZ��g�_�-��a`���Xh�E,L������(���`I?���z���G�B�[�
c�j
�6mh�p�gQI=Jo��R�|�F�@[�xj��pyM@���
W�����VxM@���(I��`�q}x�r���w-�pYL��N���u������[��m�-���g��_i��0�i��D�ZI���(<{����u{�d�rpq�;;i?c"[��4�����(c�?��_`��9����u�\�����v��
�PD��}�r�E�����4Y�F� ����(�b���jfI�������b���]�����"��m(&���X6����m����5��Ck��yZ1�z�� ���%�A�5*�|hT����%��\y1�W� �z����#����3T�g��5�a�����C�������d'���|�g�{�n������Zo>[�;�)$K=�q�1?
�j���U�)�Q�`�
�^���s������A:���m��r�R�4����`\�U��s����L��M��&*������0���b�w�cp��E��*���(�M�"��� 3�� '��"�G��^`�tW���9�G��7/�X�np�#�C�nF��'�,������"�*e��D�&���(����9����_��[����w�zNu�H���
K���3Q��29�bP��,�3�[ n!�J����T`���]��������QB�6 _�G#�^y �lo0��S����|
����l�w���8YnU@h��B��B�0��Sd�����vc�-�!H��
���:Fe}��@(TP������������9�J�Z�T����}��fn.c�6�����r�� N�\�)�G�R! v�����Nu�`�'bg\�&H
I��`�^���L��m����z:2-�o��� v���'U �+���
�q�PO:�R����E����P�
_�y
21\��I�%{4s`aFqK_���w�M� ^A'�m���f����r���8<s�rbVE#�����c/X)O�E1�3j{�b����kp�������1s�V���LbFu`����������N��^�H�Eif
I_���&9��;�IS��L}q���L�l��
"d��QEiJ�����p�T*�
[[�{(T��}L8O=d8L��%_9����P���#� #��B���?����\S�a�"��}3A=������x6b������}r�kG'��?���t"���]�)E�zs�nIh6����"c=���-SB��d��=�'��|���nKn���d�A�Uh�K�l���5\%�������G�����<�yF�������T5\C?�"6J�V���E ��%��5
��&nh ��bX`I������G�a�wF���b���n�����m���^����i_�r�C��Z
���v6�<BjE������K����CL����`����T����D_�`'D_R�VZP�-���1n����/}��(����H~4�V������p|
.������j%�]�RU��7P�������&�������^T��W���I/���0�j��!�P�����Q��l��s��<'�&�:�B�
M��u��IKBI��&�}H�&n/na����
��g��g��In8w��Fi��E��D��m��B�4#����}0�D�y��� ���O�&:����,5�M���C�
��Z���T6� x�k����XbE�mHd�60������p�����P�7&�Rc�XDz��9]��hJl�
�UE������E�������U\��]�,o_�os���Fe�S��3���P���Y~&�P"�E�<l����}=BZ�,:@-Rx���qM�����|�Zs� 9����z31���d�� DZ��-��B�r���v)�E�&�? @���(\���� �*�?�Ly�8m�/�.��W�"5�� y+������(�~/������ z n���E�fLk�<d�z>���C�Mq%� ��i���_R�
n���5�$7((��c��&n�=�*��AId�H}C ��A��*�%�P����:H��-Ks���!�����������m�2G}���������S.��*Gy���]�m��%�'L��x��{�6CD��u9�����B (�w�]c"3�P]��#mCA�+�|6��<��U�F<��&���)&iN{d��9s01���Ce2�rX����0����P���(Z�H`drf�K0��.n�9���H3z*����M���f�+�rG�h�0�� �1���rs��vn��Aa:N?�s��sF�jl���IP����h3E����cQ <<��f����Z�M�n{l���SP!��,#9
[�q�����{� �,�?�������7��_��X� 4�p0�7���d�����?��������!kx/�.���{���|�7���,9=����GU�����2�.z;Y��:�J�8
RI\-����~������
*�P��������.�'�����_H�R�mL��y��h{l�d����F }�vv��O���uU�B��CZ�
�9���Z=��4��J���������q�����K^ O��,����w}�}U���������H`�?%b�X���� }l|����t����w9��OS�H��?���7r���+�� ��h��E��l�1?�y`�����8 �@�(�c6����Z����"��q <�
���5���j�����T�?��~���P�X����fM=�Jyq����
q�PO���aAx<���h���"3��Y��������-�5��z`����p6b�#�������<\'��m�[�Cwl�}l��]�0f{J)�h%����b�,��'2839��6�I�� �8��|���6���o��I%��s�6Ea�/4O/��f,�O��s�a�/�n��w��c�%"�;� w]�
�8�J�O������A��������x��1����p 4�4��: ���"�GA:���8*� ;��v
r�w�\�������Z����8zY�
e<���W�lM�G&��H��������w$������x�|�������R�T0�(��� P�zP���0��Vd��scBTw q\sl�K=���B�\��"a� ���|4�P������a,G������P�P$$�
,�Q`/���5�&([ �����XQ��"��+��S�������
n#�1�}A������6G��<X�$�LN0q�VY�=�n����� c���5�(o�(�z�<�����`p$e��,��%�
,<�\A�iEj�T3[����M�~��H�C�ZnA��
��\��iW���� a
�����='OZ�d������8�j�9�*���G�2�J�� BQ��uB�Y�X�.���A*���Zr
��g"4���@j�M�I-�J�M
�S�E��d�UB�j���ZPTo�l��5���dH��-���"[�c�t-0�n ����y�����)F�}��8��Y_��`�&���t����I*3�U0���@I�������)�{�v]~����D�W�����6��k�%���m������(r�c� w�.�������~��7��� o�D� R8����X�SB���\`��w�u��y%.�(�*���@�91u&���������
�����E�p*<�c(�= @�E�/u&826�g�<�L`O���tp�P&@A��t�Y ��S�W�8��'�7�]����`�������-:}�� 9��UJZ�P)UjJ����_�����/���chR�4���x6�T��J���UJu,jD�Bd������p`QpG����si8j�K�������9/,���=_�w����V���x���~��#�
^���b���M���#�K �|0zI��h�jw��������gZ���U{�_X!()��q������;��c_�/���_���s��������������~�����~���I�M���~��.�y�+����}�5�V������ o|g�c][��7�?������E����_������3�r�b4x����������������=��n����8�^�/��uE��u����:�����j��������S^R��\�����������n;�8i�!^|��0Q#SuZmT-
��b�V���Az�.�7��K�.��$���7�����"v���Z���9�
���(�g0���f�X<8����0�j��.rx�\�����q�*��z���ex2s��+�����!6a�����'�q�]O4�,�X����*ZI��c����Zv�I0���on}�y�G>������Gw��D��y{hn�{������"�r��(G��Vw����v�q$����������"�}�^���(��)�M����sN(�h(�
�i����r� w��=sl�������.W7�d(�^6��Bd���
x�7#��W?m+L������!I� ���I����s�"��`[p�������Y�\N���3���r�����{�,�����m2�������S0
�}Z������������To��F��%R"��xZ}��Hl>^ �AP����z�_�)��J��F���
�����$=���� Y�c�����<��
�����@
����1����5����������'�u1/�
�1��r��T�x�l���~������Fk �����T�L�h�a4
`�#��|���Z��@ ����:%_���m+���|�|����,L8�M>���;�0H�������y��Q�C���N�1�{��X!4"�X!*���j����k��|>
&!.��������P�{�������D��,~<��gH�`��w���,��@�������:�3��
5g<B��Y��G�2�H>����-)��Ll�)�IxG��UTN�S����d��0�&Q0�)��f� !��j�N�2�Et%GXs5/�QD�����6J��������9J�}(����s.Fs��,���#4��)K�P�<���%^ ��g *db^�.�dB�W�R$z�<{:qA��@ �hZ��V��!��\q �u�4�
�V�V�La3�A����d����LVz� ���k~7JxIv��:o��)+h+<���������,�dI���@��Z�,#�Y����7]lJ��DV��)��f��3l5�I�#-9�]
�����z�R��p��sD����h�x������)��;fo|�%�]�(��V��}��n��������4��B����M �Q�#�7Pj*F~~
�s��(�:+|�S���lX8�ysO���������� �O:���:� &_kC28�o ��&4��0���8��%�q�Ms��7���S8�C�e��4��lG;��,[~�[��EI��Xo5�j����jc�49:������D�w�����C����8~
x����,����^�����j4[~����z��Z��T��P�C������`|�a��`jS��u(�/ �w����Ao��������d�~*oz����w����?�`� �D�
�������h��]�� ����D(i�n�^���Ej�tm+�!�������B���pf8Nr�r@0�H���#
����l��5����<����v��������H'o�m������5�m~?�Z�G��L�M�&i4����1�u������0
��%>1�B�b�v8�C�\��a��1X�/f�X�3�2[w�Si�������s���G,��U8��'���P���/�����g����T���z|l4)�cX#�����+�y�/I*�FE9���jUM1�`�<�-����@��8��Yjr���fE�e
F�bN�
���Vmx^)���]i��k0��b �F�~���Gc��^�����TB��7���?!!����{pt�����a��>�����G����wz����5x��
�h?(���O��(��<�W�E:��V��8��A�uh�{����r�JyF��/���_����j8�����HJ\�@0S��q�{'Q|��Jl�z)����g�g�Y<[k4�Z�Bo��P>�5����~�,T."c������M������C��0a`2�f��I$�q�8�q4�G�$�&Q���Z�s���~n:Kr�u����Y��������_g�����oJ����kS�.Fv�;�E/Q�&�Xx�b|V~
��E�����(��i$�)��q���:��t������S�U�W�T�����zt�v��J.`>m �m�B�0��|%��/@c�8�:��8c��{p�:�x{RC�"DJ�r�y%C��6�W��S �n��!���_oV�B`O:/���4��������M`�f�\5{HdJ�c%g5�+�\�����xo�"���KF��JW�����=
��f��[�z�)Nx����[L�
�#Na� ��vpa��n���"���[N�-��IW����`
a��*�H��b����#���� ��I&�!�P)��kF��]�j\������4OUWA@�9��zI0�g�J���>�Y������n�X��{�?�Lsmz�8I�>I�Sw]O���5�ze��';%�T���*�e/���+�\���7�g���Z�t����!��vU�P�(�Y�}��T�
�/J���@�v��5C����
@,�Q�|�_5���S����R��X���{�������
Z��|s�;�j�����%.K��0�-��EC�:Z�^����~|�����������fU������@@��xa�l!��~�0F�v�p���N����U?(5�a�&����pA%��,�S���/F"�I�����9�=0P��Rs�c$CK���Or#�IJn�(���m�/t�e�7f_��z�������4k������m��~�^3�WP��B�LB��qPe���'��6�`b�eM���d�-J��r�Y�.. ����7-�IO��f�f�+wu�=�d�r�������1��h8��������{�j�%����3p'�4�).'����@���6e�p�B�IXOD���O�F��7���q��hX�2���0����U�F�(��V�oX�R�d8.&t4�bh��x�r5��9��K�]�/���������
H��$��bY�T;�/�����qy��?�VM���[GX=n��y���U�|���x���f������V��Y����N���M��������x�m�1cX���j&����Wc���_3����Rq9�E��9B�~MW�Q�3P�7����o3�������������V�== �����>�3���R�?��<30(Fm���~�������9|Ka�1� ~�=z��{��s���^�����������w�i��������[�.F�d�&�Q��GS*����K��h6��iywwy�"�}����%���"���#���4���8/������7�x/�L�|��#�+�rs��g�Iy��*��SS�{�� �m>�A�2���[�~�
t@nr~�y�f��r���h�j�����/:cT���������L�{\��[����&�_�:����`��e�xC��\���}�ly�BJ���.����o<��x�2�RY��A�. ������*e�����N~���2k����KU��������~�����&y��_��cx1�S)B���JJ0����I���^> ����I[S�G��~��-�����<N���%~����ebP�o��*����e��
���L��_E^j��B�8��i������|i;��@G��>