Commit 853a0af
committed
[BACKPORT 2024.2.7][#30104] YSQL: Use pg_index tuple as truth source for indpred and indexprs
Summary:
===Cherrypick Notes (2024.2 --> 2024.2.7)===
Clean cherrypick, no conflicts
===Backport Notes (2025.1 --> 2024.2)===
- Lint related conflicts in ybcplan.c. Resolve by keeping style as in 2024.2
- File pg_yb_index_check-test.cc not present in 2024.2. Resolve by adding file with boilerplate code needed to run test.
===Backport Notes (2025.2 --> 2025.1)===
Clean merge, no conflicts
===Backport Notes (master --> 2025.2)===
Clean merge, no conflicts
===Bug ===
Consider the following schema and data:
```
CREATE TABLE test (k INT PRIMARY KEY, v1 INT, v2 INT, boolcol BOOL);
CREATE UNIQUE INDEX test_v1_idx ON test (v1) WHERE boolcol = true;
INSERT INTO test VALUES (1, 1, 1, true);
```
The following query produces an index consistency on `test_v1_idx` when run in a separate connection (that has not queried table `test` yet):
```
INSERT INTO test VALUES (1, 1, 1, true) ON CONFLICT (k) DO UPDATE SET boolcol = false;
```
Output:
```
yugabyte=# INSERT INTO test VALUES (1, 1, 1, true) ON CONFLICT (k) DO UPDATE SET boolcol = false;
INSERT 0 1
yugabyte=# SELECT yb_index_check('test_v1_idx'::regclass);
ERROR: XX002: index contains spurious row
DETAIL: index: 'test_v1_idx', ybbasectid: '\x47121048800000012121'
```
Since a table row corresponding to (k=1) exists, the above query executes the DO UPDATE clause which causes the index row corresponding to v1=1 to no longer satisfy the predicate. Therefore, it is expected that the the index row `v1=1` is deleted. However, this operation is incorrectly skipped, leading to a spurious row in the index.
===Cause ===
The Yugabyte planner has an optimization ([D34040](https://phorge.dev.yugabyte.com/D34040) / [63f471a](https://phorge.dev.yugabyte.com/rYBDB63f471a02c128ab8fb79b6f97a742cf0580cfb32))[1] to skip updating an index on `INSERT ... ON CONFLICT ... DO UPDATE` when it determines that the contents of the index row remain unmodified by the UPDATE. For partial indexes, this involves inspection of both the columns (key and non-key) in the index as well as its index predicate. This is done by reading various attributes in the index's `Relation` object.
Vanilla postgres has a plan-time optimization[2] that skips the multi-step planning process for trivial INSERT queries. Such queries touch a single relation, and the values to be inserted are fetched from a Result scan. Such queries may optionally involve an ON CONFLICT clause. A side effect of this optimization is that the relation's catalog information (its attributes, indexes, constraints) are not loaded into the planner's data structures (RelOptInfo) which in turn causes the index's expression trees to not be loaded into the `rd_indpred` and `rd_indexprs` fields of index's `Relation`. Instead, these expression trees are lazily loaded during query execution. This lazily loading ensures that the second instance of the query in a given backend/connection will already have the expressions loaded at planning time.
As a result of this postgres optimization [2], in this specific example, `rd_indpred` and `rd_indexprs` are not populated (pointer is NULL) when the planner inspects[1] the index for columns that are involved in the update. Since no other columns in the index are modified by the query, the planner[1] incorrectly concludes that the index is unmodified and skips updating it. The skipped updates lead to "spurious" or "missing" rows in case of partial indexes and "binary mismatch" in expression indexes. This bug is similar in nature to D47590 / 2b3e502.
===Fix===
This revision switches to using the pg_index tuple of the index as a source of truth; consulting (`pg_index.indpred`, `pg_index.indexprs`) instead of the cached `Relation.rd_indpred` and `Relation.rd_indexprs` fields.
[1] - [ybplan.c --> func YbUpdateComputeIndexColumnReferences](https://github.com/yugabyte/yugabyte-db/blob/master/src/postgres/src/backend/optimizer/util/ybplan.c)
[2] - [planmain.c --> func query_planner](https://github.com/yugabyte/yugabyte-db/blob/master/src/postgres/src/backend/optimizer/plan/planmain.c)
Backport-to: 2025.2, 2025.1, 2024.2
Original commit: 51ffdaa / D49910
Test Plan:
Run the following test:
```
./yb_build.sh --cxx-test pgwrapper_pg_yb_index_check-test --gtest-filter 'PgYbIndexCheckTest.YbPartialExpressionIndexNewConnUpdate'
```
Reviewers: jason, sanketh, myang, #db-approvers
Reviewed By: myang, #db-approvers
Subscribers: mihnea, svc_phabricator, yql, smishra
Tags: #jenkins-ready
Differential Revision: https://phorge.dev.yugabyte.com/D500601 parent 63c7dc6 commit 853a0af
2 files changed
Lines changed: 85 additions & 17 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
881 | 881 | | |
882 | 882 | | |
883 | 883 | | |
| 884 | + | |
| 885 | + | |
| 886 | + | |
| 887 | + | |
| 888 | + | |
884 | 889 | | |
885 | 890 | | |
886 | 891 | | |
887 | | - | |
888 | | - | |
889 | | - | |
890 | | - | |
891 | | - | |
892 | | - | |
893 | | - | |
894 | | - | |
895 | | - | |
896 | | - | |
897 | | - | |
898 | | - | |
| 892 | + | |
| 893 | + | |
899 | 894 | | |
900 | | - | |
901 | | - | |
902 | | - | |
903 | | - | |
904 | | - | |
| 895 | + | |
| 896 | + | |
905 | 897 | | |
906 | 898 | | |
907 | 899 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
0 commit comments