[Bug] Inconsistent result for inheritance and FOR UPDATE. - Mailing list pgsql-hackers

From Kyotaro HORIGUCHI
Subject [Bug] Inconsistent result for inheritance and FOR UPDATE.
Date
Msg-id 20141211.192938.41300495.horiguchi.kyotaro@lab.ntt.co.jp
Whole thread Raw
Responses Re: [Bug] Inconsistent result for inheritance and FOR UPDATE.  (Tom Lane <tgl@sss.pgh.pa.us>)
List pgsql-hackers
Hello, this is about the second issue.

SELECT FROM <inheritance parent> WHERE <cond> FOR UPDATE may
return results which does not match the <cond>. The following
steps will reproduce the problematic behavior (A and B are
individual sessions) on master and back to 9.0 but 8.4 gives
correct result. I haven't checked on 8.3.

- Reproducing the symptom

A=# SET enable_seqscan TO false;
A=# SET enable_bitmapscan TO false;
A=# DROP TABLE IF EXISTS p CASCADE;
A=# CREATE TABLE p (id text, a text, b text, c text);
A=# CREATE INDEX p_i1 ON p (a, b, c) WHERE b IN ('0', '1') AND c = '0';
A=# CREATE TABLE c1 (LIKE p INCLUDING INDEXES) INHERITS (p);
A=# CREATE TABLE c2 (LIKE p INCLUDING INDEXES) INHERITS (p);
A=# CREATE TABLE c3 (LIKE p INCLUDING INDEXES) INHERITS (p);
A=# INSERT INTO c1 (SELECT  1 + a, 0, a % 4, 0 FROM generate_series(0, 7) a);
A=# INSERT INTO c2 (SELECT 11 + a, 1, a % 4, 0 FROM generate_series(0, 7) a);
A=# INSERT INTO c3 (SELECT 21 + a, 2, a % 4, 0 FROM generate_series(0, 7) a);
A=# ANALYZE;
A=# BEGIN;
A=# CREATE TEMP TABLE tt1 AS
A=# SELECT id FROM p WHERE b IN ('0', '1') AND c = '0' ORDER BY id LIMIT 1 FOR UPDATE;
A=# UPDATE p SET b = -1 WHERE id IN (SELECT id FROM tt1) RETURNING id;
A=# DROP TABLE tt1;
A=# SET enable_seqscan TO false;
A=# SET enable_bitmapscan TO false;
B=# SELECT tableoid, ctid, * FROM p WHERE b IN ('0', '1') AND c = '0' ORDER BY id LIMIT 1 FOR UPDATE;
A=# COMMIT;

On session B.

|  tableoid | ctid  | id | a | b  | c 
| ----------+-------+----+---+----+---
|     34316 | (0,9) | 1  | 0 | -1 | 0


b = -1 apparently contradicts the WHERE clause.

The plan for the query is as following. The part "b IN ('0',
'1')" in the WHERE clause is omitted even though required by EPQ
recheck.
Limit ->  LockRows  ->  Sort       Sort Key: p.id   ->  Result    ->  Append     ->  Index Scan using p_i1 on p
Index Cond: (c = '0'::text)     ->  Index Scan using c1_a_b_c_idx on c1          Index Cond: (c = '0'::text)     ->
IndexScan using c2_a_b_c_idx on c2          Index Cond: (c = '0'::text)     ->  Index Scan using c3_a_b_c_idx on c3
    Index Cond: (c = '0'::text)
 


- Analysys and solution

This is caused by that IndexRecheck examines the test tuple with
a qual "c = '0'" without "b IN ('0', '1')". The part has been
removed in create_indexscan_plan. It decieds whether to remove a
qual or not using get_parse_rowmark(root->parse(->rowMarks)) and
predicate_implied_by(). But the former always says no (NULL) for
child relations even if the parent has rowMarks.

On the other hand, rowmarks on children is already distributed at
the time by expand_inherited_rtentry() into root->rowMarks.

So I replaced the get_parse_rowmark() with get_plan_rowmark() as
the attached patch and the problem disappeared.


By the way, get_plan_rowmark() has the comment like this,

> * This probably ought to be elsewhere, but there's no very good place

I haven't moved it anywhere but createplan.c might be the good plance.

regards,

-- 
Kyotaro Horiguchi
NTT Open Source Software Center

pgsql-hackers by date:

Previous
From: Kyotaro HORIGUCHI
Date:
Subject: [Bug] Duplicate results for inheritance and FOR UPDATE.
Next
From: Etsuro Fujita
Date:
Subject: Re: inherit support for foreign tables