Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ошибка при выборке в цикле из таблицы со вложенным секционированием. #187

Closed
Fenoman opened this issue Dec 18, 2018 · 1 comment

Comments

@Fenoman
Copy link

Fenoman commented Dec 18, 2018

Problem description

Начиная с версии 1.5 у вас имеется поддержка Multi-Level секционирования. Однако я обнаружил довольно интересную проблему.

Есть таблица секционированная по периодам (RANGE), по полю типа int формат записи 201301, 201302 ... 201810. Итого 66 секций!
Скрипт:

select create_range_partitions
(
    'test.test_table',
    'n_period',
    ARRAY[201301,201306,201307,201308,201309,201310,201311,201312,201401,201402,201403,201404,201405,201406,201407,201408,201409,201410,201411,201412,201501,201502,201503,201504,201505,201506,201507,201508,201509,201510,201511,201512,201601,201602,201603,201604,201605,201606,201607,201608,201609,201610,201611,201612,201701,201702,201703,201704,201705,201706,201707,201708,201709,201710,201711,201712,201801,201802,201803,201804,201805,201806,201807,201808,201809,201810,201811],
    ARRAY['test.test_table_201301','test.test_table_201306','test.test_table_201307','test.test_table_201308','test.test_table_201309','test.test_table_201310','test.test_table_201311','test.test_table_201312','test.test_table_201401','test.test_table_201402','test.test_table_201403','test.test_table_201404','test.test_table_201405','test.test_table_201406','test.test_table_201407','test.test_table_201408','test.test_table_201409','test.test_table_201410','test.test_table_201411','test.test_table_201412','test.test_table_201501','test.test_table_201502','test.test_table_201503','test.test_table_201504','test.test_table_201505','test.test_table_201506','test.test_table_201507','test.test_table_201508','test.test_table_201509','test.test_table_201510','test.test_table_201511','test.test_table_201512','test.test_table_201601','test.test_table_201602','test.test_table_201603','test.test_table_201604','test.test_table_201605','test.test_table_201606','test.test_table_201607','test.test_table_201608','test.test_table_201609','test.test_table_201610','test.test_table_201611','test.test_table_201612','test.test_table_201701','test.test_table_201702','test.test_table_201703','test.test_table_201704','test.test_table_201705','test.test_table_201706','test.test_table_201707','test.test_table_201708','test.test_table_201709','test.test_table_201710','test.test_table_201711','test.test_table_201712','test.test_table_201801','test.test_table_201802','test.test_table_201803','test.test_table_201804','test.test_table_201805','test.test_table_201806','test.test_table_201807','test.test_table_201808','test.test_table_201809','test.test_table_201810'],
    ARRAY['ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test']
);

Затем каждая из секций дополнительно секционирована по другому полю типа int (HASH секционирование).
Скрипт на примере секции test.test_table_201301:

select create_hash_partitions
    (
    	'test.test_table_201301', 'f_division', 11, true,
    	ARRAY ['test.test_table_201301_Div_0','test.test_table_201301_Div_1','test.test_table_201301_Div_2','test.test_table_201301_Div_3','test.test_table_201301_Div_4','test.test_table_201301_Div_5','test.test_table_201301_Div_6','test.test_table_201301_Div_7','test.test_table_201301_Div_8','test.test_table_201301_Div_9','test.test_table_201301_Div_10'],
    	ARRAY ['ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test','ts_test']
    );

...и так каждую из 66 созданных секций.
Итого имеем 1 базовую таблицу, 66 секций по периодам и каждая из них еще секционирована по 11 секциям по отделениям. Итого, pathman_partition_list выдает 792 секции, что правильно 66 + 66*11 = 792.

Теперь пишем такой скрипт и запускаем (на самом деле это упрощенная вырезка из тела функции, но суть от этого не меняется и ошибка полностью воспроизводится):

DO
$$
DECLARE
    _f_division int = 1;
    _n_period   int = 201301;
BEGIN
DROP TABLE IF EXISTS Test;
	
CREATE TEMP TABLE Test AS
SELECT
	Tr.F_Division,
        _n_period AS N_Period
FROM test.test_table Tr
WHERE 1 = 0;
	
LOOP
  IF NOT (_N_Period < 201810)
  THEN
    EXIT;
  END IF;
	    
  RAISE NOTICE 'N_Period -> %', _n_period;
	    
  INSERT INTO Test
  SELECT
    Tr.F_Division,
    _n_period AS N_Period
  FROM test.test_table Tr
  WHERE Tr.f_division = _f_division
  AND Tr.N_Period = _n_period;
		  
  _N_Period := CASE WHEN _N_Period % 100 = 12 THEN (_N_Period / 100 + 1) * 100 + 1 ELSE _N_Period + 1 END;
END LOOP;
END;
$$;

Данный скрипт отваливается с ошибкой:

[2018-12-18 22:05:27] [XX000] ОШИБКА: variable not found in subplan target list
[2018-12-18 22:05:27] Где: SQL-оператор: "INSERT INTO Test
[2018-12-18 22:05:27] 	    SELECT
[2018-12-18 22:05:27] 			Tr.F_Division,
[2018-12-18 22:05:27] 	         _n_period AS N_Period
[2018-12-18 22:05:27] 		FROM test.test_table Tr
[2018-12-18 22:05:27] 		WHERE Tr.f_division = _f_division
[2018-12-18 22:05:27] 		   AND Tr.N_Period = _n_period"
[2018-12-18 22:05:27] функция PL/pgSQL inline_code_block, строка 23, оператор SQL-оператор
[2018-12-18 22:05:27] [00000] таблица "test" не существует, пропускается
[2018-12-18 22:05:27] [00000] N_Period -> 201301
[2018-12-18 22:05:27] [00000] N_Period -> 201302
[2018-12-18 22:05:27] [00000] N_Period -> 201303
[2018-12-18 22:05:27] [00000] N_Period -> 201304
[2018-12-18 22:05:27] [00000] N_Period -> 201305
[2018-12-18 22:05:27] [00000] N_Period -> 201306

Обратите внимание, что цикл сломался на 6й итерации когда N_Period достиг 201306.
Теперь немного магии:

  1. Если в коде исправить IF NOT (_N_Period < 201810) на IF NOT (_N_Period < 201306) то есть 5 циклов - все работает! Один циклом больше - ошибка!
  2. Если в коде исправить Tr.f_division = _f_division на Tr.f_division = 1 то есть жестко захардкодить отделение - все работает!
  3. Если в коде написать Join на таблицу отделений - тоже все работает. Вот так:
    Заменить код
INSERT INTO Test
SELECT
    Tr.F_Division,
     _n_period AS N_Period 
FROM test.test_table Tr
WHERE Tr.f_division = _f_division
     AND Tr.N_Period = _n_period;

на этот

INSERT INTO Test
SELECT
    Tr.F_Division,
     _n_period AS N_Period
FROM test.test_table Tr
INNER JOIN test.divisions sd
              ON sd.link = tr.f_division
WHERE Tr.f_division = _f_division
     AND Tr.N_Period = _n_period;

То есть по сути просто добавили бесполезное соединение - начинает работать!
Похоже где-то ошибка в планировщике.

Environment

SELECT * FROM pg_extension;

extname extowner extnamespace extrelocatable extversion extconfig extcondition
plpgsql 10 11 f 1.0
file_fdw 10 2200 t 1.0
pg_buffercache 10 2200 t 1.3
pg_stat_statements 10 2200 t 1.5
pg_variables 10 2200 t 1.1
tablefunc 10 2200 t 1.0
uuid-ossp 10 2200 t 1.1
pg_pathman 16384 2200 f 1.5 {119515229,119515240} {"",""}
tds_fdw 16384 2200 t 2.0.0-alpha.2
plv8 16384 11 f 2.3.8
pg_query_state 16384 2200 t 1.1
bloom 16384 2200 t 1.0

SELECT version();

version
PostgreSQL 11.1 on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0, 64-bit

SELECT pgpro_version();

pgpro_version
PostgresPro 11.1.1 on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0, 64-bit

SELECT pgpro_edition();

pgpro_edition
standard

select pathman_version();

pathman_version
1.5.3
@arssher
Copy link
Contributor

arssher commented Jun 7, 2019

Скорее всего, это было починено в ветке master, точнее
ba30201

@Fenoman Fenoman closed this as completed Apr 1, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants