diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgml
new file mode 100644
index ca7bd44..2355f1a
*** a/doc/src/sgml/pgstatstatements.sgml
--- b/doc/src/sgml/pgstatstatements.sgml
***************
*** 24,33 ****
The statistics gathered by the module are made available via a system view
! named pg_stat_statements>. This view contains one row for
! each distinct query text, database ID, and user ID (up to the maximum
! number of distinct statements that the module can track). The columns
! of the view are shown in .
--- 24,78 ----
The statistics gathered by the module are made available via a system view
! named pg_stat_statements>. The module normalizes queries,
! matching them based on an internal hash value.
! pg_stat_statements> contains one row for each distinct
! combination of internal query hash, database ID, and user ID (up to the
! maximum number of distinct statements that the module can track). The
! columns of the view are shown in
! .
!
!
!
! Utility commands are all those other than SELECT>,
! INSERT>, UPDATE> and DELETE>. In the case
! of utility statements, the query hash value identifier is derived from the
! query string. In the case of all other statements, the statement's query
! tree is hashed to produce its query hash identifier. The query tree is an
! internal structure that results from the transformation process, later in the
! parser stage, immediately prior to the rewrite stage. Queries that are
! deemed to be equivalent, due to not actually differing in ways that the
! implementation deems essential to the query, have execution statistics
! aggregated into a single entry. This is particularly useful for queries with
! inline parameters.
!
!
!
! Matching queries based on their query tree hash value can produce results
! that are not consistent with an implementation that differentiates based on
! each query's SQL string, in non-obvious ways. For example, the current value
! of search_path> might affect query differentiation if a change
! caused parse analysis to resolve different relations at different times, such
! as relations that have identical definitions but are located in different
! schemas. The two resulting distinct entries would have identical query
! strings.
!
!
!
! The execution costs of DO INSTEAD> and DO ALSO> rules
! are attributed to their originating query; a separate entry will not be
! created for the rule action. DO NOTHING> rules will naturally not
! add an entry at all, since a query is not actually executed.
!
!
!
! pg_stat_statements considers some utility commands to
! consist of two distinct commands: The utility command proper, and a nested
! DML> command. Such utility commands include DECLARE
! CURSOR>, SELECT INTO> and EXPLAIN ANALYZE>.
! Therefore, to see the true execution costs of these statements, it is
! necessary to set both pg_stat_statements.track_utility to 'on' and
! pg_stat_statements.track to 'all'.
***************
*** 61,67 ****
query
text
! Text of the statement (up to bytes)
--- 106,112 ----
query
text
! Representative, canonicalized text of the statement (up to bytes)
***************
*** 193,206 ****
queries executed by other users. They can see the statistics, however,
if the view has been installed in their database.
!
! Note that statements are considered the same if they have the same text,
! regardless of the values of any out-of-line parameters used in the
! statement. Using out-of-line parameters will help to group statements
! together and may make the statistics more useful.
!
!
Functions
--- 238,256 ----
queries executed by other users. They can see the statistics, however,
if the view has been installed in their database.
+
+
+ A notable artefact of the module's query hash matching based implementation
+ is that there may be undetectable collisions. While such collisions are very
+ unlikely, the possibility cannot be absolutely precluded, and as such the
+ count values of two distinct queries may be incorrectly aggregated together
+ as a single entry within pg_stat_statements> in isolated
+ cases. However, it is guaranteed that such collisions cannot occur between
+ entries for different databases or different users.
+
+
!
Functions
***************
*** 254,264 ****
pg_stat_statements.track controls which statements
are counted by the module.
! Specify top> to track top-level statements (those issued
! directly by clients), all> to also track nested statements
! (such as statements invoked within functions), or none> to
! disable.
! The default value is top>.
Only superusers can change this setting.
--- 304,314 ----
pg_stat_statements.track controls which statements
are counted by the module.
! Specify top> to track top-level statements (those issued
! directly by clients), all> to also track nested statements
! (such as statements invoked within functions), or none> to
! disable.
! The default value is top>.
Only superusers can change this setting.
***************
*** 271,282 ****
! pg_stat_statements.track_utility controls whether
! utility commands are tracked by the module. Utility commands are
! all those other than SELECT>, INSERT>,
! UPDATE> and DELETE>.
! The default value is on>.
! Only superusers can change this setting.
--- 321,329 ----
! pg_stat_statements.track_utility controls whether
! utility commands are tracked by the module. The default value is
! on>. Only superusers can change this setting.
*************** pg_stat_statements.track = all
*** 329,348 ****
bench=# SELECT pg_stat_statements_reset();
$ pgbench -i bench
! $ pgbench -c10 -t300 -M prepared bench
bench=# \x
bench=# SELECT query, calls, total_time, rows, 100.0 * shared_blks_hit /
nullif(shared_blks_hit + shared_blks_read, 0) AS hit_percent
FROM pg_stat_statements ORDER BY total_time DESC LIMIT 5;
-[ RECORD 1 ]---------------------------------------------------------------------
! query | UPDATE pgbench_branches SET bbalance = bbalance + $1 WHERE bid = $2;
calls | 3000
total_time | 9.60900100000002
rows | 2836
hit_percent | 99.9778970000200936
-[ RECORD 2 ]---------------------------------------------------------------------
! query | UPDATE pgbench_tellers SET tbalance = tbalance + $1 WHERE tid = $2;
calls | 3000
total_time | 8.015156
rows | 2990
--- 376,395 ----
bench=# SELECT pg_stat_statements_reset();
$ pgbench -i bench
! $ pgbench -c10 -t300
bench=# \x
bench=# SELECT query, calls, total_time, rows, 100.0 * shared_blks_hit /
nullif(shared_blks_hit + shared_blks_read, 0) AS hit_percent
FROM pg_stat_statements ORDER BY total_time DESC LIMIT 5;
-[ RECORD 1 ]---------------------------------------------------------------------
! query | UPDATE pgbench_branches SET bbalance = bbalance + ? WHERE bid = ?;
calls | 3000
total_time | 9.60900100000002
rows | 2836
hit_percent | 99.9778970000200936
-[ RECORD 2 ]---------------------------------------------------------------------
! query | UPDATE pgbench_tellers SET tbalance = tbalance + ? WHERE tid = ?;
calls | 3000
total_time | 8.015156
rows | 2990
*************** total_time | 0.310624
*** 354,360 ****
rows | 100000
hit_percent | 0.30395136778115501520
-[ RECORD 4 ]---------------------------------------------------------------------
! query | UPDATE pgbench_accounts SET abalance = abalance + $1 WHERE aid = $2;
calls | 3000
total_time | 0.271741999999997
rows | 3000
--- 401,407 ----
rows | 100000
hit_percent | 0.30395136778115501520
-[ RECORD 4 ]---------------------------------------------------------------------
! query | UPDATE pgbench_accounts SET abalance = abalance + ? WHERE aid = ?;
calls | 3000
total_time | 0.271741999999997
rows | 3000