F.52. pg_query_id#

F.52. pg_query_id

F.52. pg_query_id #

Расширение для управления аспектами вычисления идентификатора запроса.

F.52.1. О pg_query_id #

Версия: 1.0.0

F.52.2. Начало работы #

Предостережение

Расширение должно быть последним в shared_preload_libraries или, по крайней мере, последним расширением, которое использует идентификатор запроса. Проверка на это отсутствует, поэтому будьте осторожны.

Установите расширение, добавив его в shared_preload_libraries и установите GUC для игнорирования массивов констант. А также добавьте pg_stat_statements для дальнейшей проверки.

shared_preload_libraries = 'pg_query_id'
pg_query_id.ignore_array_of_constants = on

Запустите БД и выполните эти 2 запроса:

SELECT * FROM generate_series(1, 100) x WHERE x IN (101, 102, 103);
SELECT * FROM generate_series(1, 100) x WHERE x IN (101, 102, 103, 104, 105);

pg_stat_statements должен содержать один запрос только с 3 константами (другие запросы опущены):

SELECT query FROM pg_stat_statements;

                              query                              
-----------------------------------------------------------------
 SELECT * FROM generate_series($1, $2) x WHERE x IN ($3, $4, $5)

F.52.3. Функциональность #

Примечание

Для проверки идентификатора запроса мы используем pg_stat_statements в примерах

Чтобы изменить конфигурацию ниже, измените файлы конфигурации и затем отправьте SIGHUP.

F.52.3.1. Включение/отключение #

GUC По умолчанию
pg_query_id.enabled on

Этот параметр управляет тем, запущено ли расширение. С его помощью вы можете отключить расширение во время выполнения, но учтите, что вычисленные идентификаторы запросов будут отличаться.

F.52.3.2. Игнорирование имен временных таблиц #

GUC По умолчанию
pg_query_id.ignore_temp_tables off

Если этот параметр установлен в on, то OID временных таблиц не используются при вычислении идентификатора запроса. Это приводит к одинаковым идентификаторам запросов для запросов, где различаются только временные таблицы, но остальная часть запроса остается неизменной.

Пример:

CREATE TEMP TABLE temp_tbl1(id int, str text);
CREATE TEMP TABLE temp_tbl2(id int, str text);

SELECT * FROM temp_tbl1 WHERE id = 1;
SELECT * FROM temp_tbl2 WHERE id = 1;

SELECT query FROM pg_stat_statements;

                     query                     
-----------------------------------------------
 CREATE TEMP TABLE temp_tbl1(id int, str text)
 SELECT * FROM temp_tbl1 WHERE id = $1

Как вы можете видеть, только первый выполненный запрос был записан, а второй выполненный запрос (с другим именем таблицы) имеет тот же идентификатор запроса, поэтому попадает в ту же кортеж.

F.52.3.3. Игнорировать массивы констант #

GUC По умолчанию
pg_query_id.ignore_constant_arrays off

Если этот параметр установлен в on, то массивы, состоящие только из констант (в разных запросах с разной длиной массива), не будут влиять на вычисление идентификатора запроса. Это включает:

  • ARRAY[]

  • VALUES () (если каждая строка содержит только константы)

  • IN ()

Если такая конструкция содержит CONSTANTs или IMPLICIT CASTs OF CONSTANT, то она будет проигнорирована. Т.е. если у вас есть SELECT 1 внутри массива, это НЕ будет проигнорировано и будет вычислено по умолчанию.

Пример:

SELECT * FROM generate_series(1, 10) x WHERE x IN (11, 12, 13);
SELECT * FROM generate_series(1, 10) x WHERE x IN (11, 12, 13, 14, 15);

SELECT * FROM generate_series(1, 10) x WHERE x = ANY(VALUES (11), (12), (13));
SELECT * FROM generate_series(1, 10) x WHERE x = ANY(VALUES (11), (12), (13), (14), (15));

SELECT * FROM generate_series(1, 10) x WHERE x = ALL(ARRAY[11, 12, 13]);
SELECT * FROM generate_series(1, 10) x WHERE x = ALL(ARRAY[11, 12, 13, 14, 15]);

SELECT query FROM pg_stat_statements;

                                           query                                            
--------------------------------------------------------------------------------------------
 SELECT * FROM generate_series($1, $2) x WHERE x = ALL(ARRAY[$3, $4, $5])
 SELECT * FROM generate_series($1, $2) x WHERE x = ANY(VALUES ($3), ($4), ($5))
 SELECT * FROM generate_series($1, $2) x WHERE x IN ($3, $4, $5)

Но работают не только константы, но и неявное приведение:

CREATE TABLE tbl(id int, str char(20));
SELECT * FROM tbl WHERE str IN ('a', 'b', 'c');
SELECT * FROM tbl WHERE str IN ('a', 'b', 'c', 'd', 'e');

SELECT query FROM pg_stat_statements;

                    query                    
---------------------------------------------
 SELECT * FROM tbl WHERE str IN ($1, $2, $3)
 CREATE TABLE tbl(id int, str char(20))

Но это НЕ сработает:

SELECT * FROM generate_series(1, 10) x WHERE x IN (11, 12, (SELECT 13));
SELECT * FROM generate_series(1, 10) x WHERE x IN (11, 12, 13, (SELECT 14));

SELECT * FROM generate_series(1, 10) x WHERE x = ANY(VALUES (11), (12), ((SELECT 13)));
SELECT * FROM generate_series(1, 10) x WHERE x = ANY(VALUES (11), (12), (13), ((SELECT 14)));

SELECT * FROM generate_series(1, 10) x WHERE x = ALL(ARRAY[11, 12, (SELECT 13)]);
SELECT * FROM generate_series(1, 10) x WHERE x = ALL(ARRAY[11, 12, 13, (SELECT 14)]);

SELECT query FROM pg_stat_statements;

                                             query                                             
-----------------------------------------------------------------------------------------------
 SELECT * FROM generate_series($1, $2) x WHERE x IN ($3, $4, (SELECT $5))
 SELECT * FROM generate_series($1, $2) x WHERE x IN ($3, $4, $5, (SELECT $6))
 SELECT * FROM generate_series($1, $2) x WHERE x = ALL(ARRAY[$3, $4, (SELECT $5)])
 SELECT * FROM generate_series($1, $2) x WHERE x = ALL(ARRAY[$3, $4, $5, (SELECT $6)])
 SELECT * FROM generate_series($1, $2) x WHERE x = ANY(VALUES ($3), ($4), ((SELECT $5)))
 SELECT * FROM generate_series($1, $2) x WHERE x = ANY(VALUES ($3), ($4), ($5), ((SELECT $6)))

F.52.3.4. Постоянный идентификатор запроса для служебных операторов #

GUC По умолчанию
pg_query_id.utility_constant_query_id off

Если этот параметр установлен в on, то идентификатор запроса для некоторых типов служебных операторов назначается с предопределенной константой. Таблица назначения идентификаторов запросов:

Утилита Идентификатор запроса
СОЗДАТЬ ТАБЛИЦУ 1
СОЗДАТЬ ВРЕМЕННУЮ ТАБЛИЦУ 2
CREATE TABLE ... AS 3
CREATE TEMP TABLE ... AS 4
СОЗДАТЬ ИНДЕКС 5
CREATE INDEX (временная таблица) 6
VACUUM 7
ОБРЕЗАТЬ 8
DROP TABLE 9
DROP INDEX 10
COPY FROM ... (временная таблица) 11
COPY FROM ... 12
COPY TO ... (временная таблица) 13
COPY TO ... 14

Эта функциональность может быть полезна для сбора статистики с грубой детализацией, когда мы хотим измерить накладные расходы нескольких DML.

Пример:

CREATE TABLE tbl1(id int);
CREATE TABLE tbl2(id int, str text);

CREATE TEMP TABLE temp_tbl1(id int);
CREATE TEMP TABLE temp_tbl2(id int, str text);

CREATE TABLE tbl3 AS SELECT x FROM generate_series(1, 10) x;
CREATE TABLE tbl4 AS SELECT x, x::text y FROM generate_series(1, 10) x;

CREATE TEMP TABLE temp_tbl3 AS SELECT x FROM generate_series(1, 10) x;
CREATE TEMP TABLE temp_tbl4 AS SELECT x, x::text y FROM generate_series(1, 10) x;

CREATE INDEX tbl1_id_idx ON tbl1(id);
CREATE INDEX temp_tbl1_id_idx ON temp_tbl1(id);

VACUUM tbl1;
VACUUM temp_tbl1;

COPY tbl1 TO STDOUT;
COPY tbl2 TO STDOUT;
COPY tbl3 TO STDOUT;
COPY tbl4 TO STDOUT;
COPY temp_tbl1 TO STDOUT;
COPY temp_tbl2 TO STDOUT;
COPY temp_tbl3 TO STDOUT;
COPY temp_tbl4 TO STDOUT;

COPY tbl1 FROM STDIN;
COPY tbl2 FROM STDIN;
COPY tbl3 FROM STDIN;
COPY tbl4 FROM STDIN;
COPY temp_tbl1 FROM STDIN;
COPY temp_tbl2 FROM STDIN;
COPY temp_tbl3 FROM STDIN;
COPY temp_tbl4 FROM STDIN;

TRUNCATE tbl1;
TRUNCATE temp_tbl1;

DROP INDEX tbl1_id_idx;

DROP TABLE tbl1;
DROP TABLE tbl2;
DROP TABLE tbl3;
DROP TABLE tbl4;
DROP TABLE temp_tbl1;
DROP TABLE temp_tbl2;
DROP TABLE temp_tbl3;
DROP TABLE temp_tbl4;

SELECT queryid, query FROM pg_stat_statements ORDER BY queryid;

       queryid       |                                 query                                  
---------------------+------------------------------------------------------------------------
                   1 | CREATE TABLE tbl1(id int)
                   2 | CREATE TEMP TABLE temp_tbl1(id int)
                   3 | CREATE TABLE tbl3 AS SELECT x FROM generate_series($1, $2) x
                   4 | CREATE TEMP TABLE temp_tbl3 AS SELECT x FROM generate_series($1, $2) x
                   5 | CREATE INDEX tbl1_id_idx ON tbl1(id)
                   6 | CREATE INDEX temp_tbl1_id_idx ON temp_tbl1(id)
                   7 | VACUUM tbl1
                   8 | TRUNCATE tbl1
                   9 | DROP TABLE tbl1
                  10 | DROP INDEX tbl1_id_idx
                  11 | COPY temp_tbl1 FROM STDIN
                  12 | COPY tbl1 FROM STDIN
                  13 | COPY temp_tbl1 TO STDOUT
                  14 | COPY tbl1 TO STDOUT

F.52.4. Известные проблемы #

F.52.4.1. Массив с одним элементом #

Если pg_query_id.ignore_constant_arrays установлен и массив содержит один элемент, он автоматически преобразуется в бинарный оператор или другую форму узла дерева выражений. Например: WHERE a IN (1) при разборе преобразуется в WHERE a = 1.

В настоящее время мы не можем отслеживать такие ситуации, поэтому для таких запросов у нас будет 2 идентификатора запроса: для одного элемента и для нескольких элементов. Т.е. для WHERE a IN (1) и для WHERE a IN (1, 2), WHERE a IN (1, 2, 3) и т.д.