Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Поиск в btree не возвращает результаты для mvarchar/mchar #6

Open
@darthunix

Description

@darthunix

Тестовая схема + данные

pg1c.sql.txt

Описание проблемы

В патче pg для работы с 1С наблюдается некорректное поведение при поиске в btree индексе поля типа mvarchar и mchar подстроки, содержащей букву "ё" (возможно, проблема более общая). На определенных наборах данных поиск в индексе не возвращает никаких результатов, хотя поиск по таблице успешно возвращает нужные строки (если скрыть индекс от планировщика тем же plantuner). При этом, если посмотреть индекс через pageinspect, то ключ, указывающий на искомую строку таблицы, в него успешно попадает (но не находится). Проблема проявляется на всех сборках postgresql с патчем 1С из репозитория postgres pro.

Воспроизведение бага

Инициализация базы

createdb pg1c
psql -f pg1c.sql.txt -1 pg1c
  1. Проверяем, что в индексе мы не можем найти 'Зё Г. В.' по подстроке, а в таблице - можем.

Ищем в индексе

explain analyze select * from t where val like 'зё%';
                                                    QUERY PLAN                                                    
------------------------------------------------------------------------------------------------------------------
 Index Only Scan using t_idx on t  (cost=0.42..8.44 rows=2268 width=29) (actual time=0.009..0.009 rows=0 loops=1)
   Index Cond: ((val >= 'зё'::mvarchar COLLATE "default") AND (val < 'зє'::mvarchar COLLATE "default"))
   Filter: (val ~~ 'зё%'::mvarchar)
   Heap Fetches: 0
 Planning time: 1.251 ms
 Execution time: 0.028 ms
(6 строк)

select * from t where val like 'зё%';
 val 
-----
(0 строк)

Ищем в таблице

load 'plantuner';
set plantuner.disable_index = 't_idx';
explain analyze select * from t where val like 'зё%';
                                             QUERY PLAN                                             
----------------------------------------------------------------------------------------------------
 Seq Scan on t  (cost=0.00..9072.00 rows=2268 width=29) (actual time=69.260..70.382 rows=2 loops=1)
   Filter: (val ~~ 'зё%'::mvarchar)
   Rows Removed by Filter: 453519
 Planning time: 0.241 ms
 Execution time: 70.397 ms
(5 строк)

select * from t where val like 'зё%';
    val     
------------
 Зёма Л. Ф.
 Зё Г. В.
(2 строки)

Проверим, что проблема не в like, но сохраняется и для поиска подстроки по диапазону (так переписывается план запроса вида "like" в индексе)

Ищем в индексе

explain analyze select * from t where (val >= 'зё'::mvarchar) AND (val < 'зє'::mvarchar);
                                                  QUERY PLAN                                                   
---------------------------------------------------------------------------------------------------------------
 Index Only Scan using t_idx on t  (cost=0.42..8.44 rows=1 width=29) (actual time=0.049..0.049 rows=0 loops=1)
   Index Cond: ((val >= 'зё'::mvarchar) AND (val < 'зє'::mvarchar))
   Heap Fetches: 0
 Planning time: 0.170 ms
 Execution time: 0.068 ms
(5 строк)

select * from t where (val >= 'зё'::mvarchar) AND (val < 'зє'::mvarchar);
 val 
-----
(0 строк)

Ищем в таблице

load 'plantuner';
set plantuner.disable_index = 't_idx';
explain analyze select * from t where (val >= 'зё'::mvarchar) AND (val < 'зє'::mvarchar);
                                            QUERY PLAN                                            
--------------------------------------------------------------------------------------------------
 Seq Scan on t  (cost=0.00..10205.80 rows=1 width=29) (actual time=95.307..96.933 rows=2 loops=1)
   Filter: ((val >= 'зё'::mvarchar) AND (val < 'зє'::mvarchar))
   Rows Removed by Filter: 453519
 Planning time: 0.084 ms
 Execution time: 96.948 ms
(5 строк)

select * from t where (val >= 'зё'::mvarchar) AND (val < 'зє'::mvarchar);
    val     
------------
 Зёма Л. Ф.
 Зё Г. В.
(2 строки)
  1. Проверим, что "Зё Г. В." содержится в индексе и в таблице (и бинарные данные там одинаковые)
create extension pageinspect;


select g.g as page, i.* from generate_series(1,2823) as g, lateral bt_page_items('t_idx', g.g) as i
where regexp_replace(data,' ','','g') like '%'||right(mvarchar_send('Зё Г. В.')::text, -2)||'%';
page | itemoffset |   ctid    | itemlen | nulls | vars |                                  data                                   
------+------------+-----------+---------+-------+------+-------------------------------------------------------------------------
  822 |        115 | (3402,13) |      32 | f     | t    | 23 17 04 51 04 20 00 13 04 2e 00 20 00 12 04 2e 00 00 00 00 00 00 00 00
(1 строка)


select * from heap_page_items(get_raw_page('t', 3402)) where lp = 13;
 lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax | t_field3 |  t_ctid   | t_infomask2 | t_infomask | t_hoff | t_bits | t_oid |                t_data                
----+--------+----------+--------+--------+--------+----------+-----------+-------------+------------+--------+--------+-------+--------------------------------------
 13 |   7504 |        1 |     41 |   1018 |      0 |      463 | (3402,13) |           1 |       2306 |     24 |        |       | \x2317045104200013042e00200012042e00
(1 строка)

Итого

  1. В кортежах индекса и таблицы для "Зё Г. В." лежат одинаковые бинарные данные типа mvarchar.
  2. На одинаковом наборе операторов класса mvarchar поиск из таблицы возвращает верные значения, а из индекса - нет.
  3. Проблема воспроизводится не на всех наборах данных.
  4. Проблема имеет место для mvarchar и mchar.
  5. Листовые страницы индекса имеют предка root и не висят в воздухе (должны находиться)

Версии

Ключ попадает не на ту страницу индекса

  1. проблема в опорной функции
  2. не корректно сравнивается значение искомого ключа с наибольшим значением в странице

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions