Skip to content

Commit be634f0

Browse files
committed
improved support for rowmarks (select for share; update; delete; etc)
1 parent 62e4952 commit be634f0

File tree

7 files changed

+234
-185
lines changed

7 files changed

+234
-185
lines changed

expected/pathman_rowmarks.out

+142
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,148 @@ FOR SHARE;
168168
6
169169
(1 row)
170170

171+
/* Check updates (plan) */
172+
EXPLAIN (COSTS OFF)
173+
UPDATE rowmarks.second SET id = 2
174+
WHERE rowmarks.second.id IN (SELECT id FROM rowmarks.first WHERE id = 1);
175+
QUERY PLAN
176+
---------------------------------------------
177+
Update on second
178+
-> Nested Loop Semi Join
179+
-> Seq Scan on second
180+
Filter: (id = 1)
181+
-> Materialize
182+
-> Append
183+
-> Seq Scan on first_0
184+
Filter: (id = 1)
185+
(8 rows)
186+
187+
EXPLAIN (COSTS OFF)
188+
UPDATE rowmarks.second SET id = 2
189+
WHERE rowmarks.second.id IN (SELECT id FROM rowmarks.first WHERE id < 1);
190+
QUERY PLAN
191+
---------------------------------------------------
192+
Update on second
193+
-> Hash Join
194+
Hash Cond: (second.id = first_0.id)
195+
-> Seq Scan on second
196+
-> Hash
197+
-> HashAggregate
198+
Group Key: first_0.id
199+
-> Append
200+
-> Seq Scan on first_0
201+
Filter: (id < 1)
202+
-> Seq Scan on first_1
203+
Filter: (id < 1)
204+
-> Seq Scan on first_2
205+
Filter: (id < 1)
206+
-> Seq Scan on first_3
207+
Filter: (id < 1)
208+
-> Seq Scan on first_4
209+
Filter: (id < 1)
210+
(18 rows)
211+
212+
EXPLAIN (COSTS OFF)
213+
UPDATE rowmarks.second SET id = 2
214+
WHERE rowmarks.second.id IN (SELECT id FROM rowmarks.first WHERE id = 1 OR id = 2);
215+
QUERY PLAN
216+
---------------------------------------------
217+
Update on second
218+
-> Hash Semi Join
219+
Hash Cond: (second.id = first_0.id)
220+
-> Seq Scan on second
221+
-> Hash
222+
-> Append
223+
-> Seq Scan on first_0
224+
Filter: (id = 1)
225+
-> Seq Scan on first_1
226+
Filter: (id = 2)
227+
(10 rows)
228+
229+
EXPLAIN (COSTS OFF)
230+
UPDATE rowmarks.second SET id = 2
231+
WHERE rowmarks.second.id IN (SELECT id FROM rowmarks.first WHERE id = 1)
232+
RETURNING *, tableoid::regclass;
233+
QUERY PLAN
234+
---------------------------------------------
235+
Update on second
236+
-> Nested Loop Semi Join
237+
-> Seq Scan on second
238+
Filter: (id = 1)
239+
-> Materialize
240+
-> Append
241+
-> Seq Scan on first_0
242+
Filter: (id = 1)
243+
(8 rows)
244+
245+
/* Check updates (execution) */
246+
UPDATE rowmarks.second SET id = 1
247+
WHERE rowmarks.second.id IN (SELECT id FROM rowmarks.first WHERE id = 1 OR id = 2)
248+
RETURNING *, tableoid::regclass;
249+
id | tableoid
250+
----+-----------------
251+
1 | rowmarks.second
252+
1 | rowmarks.second
253+
(2 rows)
254+
255+
/* Check deletes (plan) */
256+
EXPLAIN (COSTS OFF)
257+
DELETE FROM rowmarks.second
258+
WHERE rowmarks.second.id IN (SELECT id FROM rowmarks.first WHERE id = 1);
259+
QUERY PLAN
260+
---------------------------------------------
261+
Delete on second
262+
-> Nested Loop Semi Join
263+
-> Seq Scan on second
264+
Filter: (id = 1)
265+
-> Materialize
266+
-> Append
267+
-> Seq Scan on first_0
268+
Filter: (id = 1)
269+
(8 rows)
270+
271+
EXPLAIN (COSTS OFF)
272+
DELETE FROM rowmarks.second
273+
WHERE rowmarks.second.id IN (SELECT id FROM rowmarks.first WHERE id < 1);
274+
QUERY PLAN
275+
---------------------------------------------------
276+
Delete on second
277+
-> Hash Join
278+
Hash Cond: (second.id = first_0.id)
279+
-> Seq Scan on second
280+
-> Hash
281+
-> HashAggregate
282+
Group Key: first_0.id
283+
-> Append
284+
-> Seq Scan on first_0
285+
Filter: (id < 1)
286+
-> Seq Scan on first_1
287+
Filter: (id < 1)
288+
-> Seq Scan on first_2
289+
Filter: (id < 1)
290+
-> Seq Scan on first_3
291+
Filter: (id < 1)
292+
-> Seq Scan on first_4
293+
Filter: (id < 1)
294+
(18 rows)
295+
296+
EXPLAIN (COSTS OFF)
297+
DELETE FROM rowmarks.second
298+
WHERE rowmarks.second.id IN (SELECT id FROM rowmarks.first WHERE id = 1 OR id = 2);
299+
QUERY PLAN
300+
---------------------------------------------
301+
Delete on second
302+
-> Hash Semi Join
303+
Hash Cond: (second.id = first_0.id)
304+
-> Seq Scan on second
305+
-> Hash
306+
-> Append
307+
-> Seq Scan on first_0
308+
Filter: (id = 1)
309+
-> Seq Scan on first_1
310+
Filter: (id = 2)
311+
(10 rows)
312+
171313
DROP SCHEMA rowmarks CASCADE;
172314
NOTICE: drop cascades to 7 other objects
173315
DETAIL: drop cascades to table rowmarks.first

sql/pathman_rowmarks.sql

+34
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
CREATE EXTENSION pg_pathman;
22
CREATE SCHEMA rowmarks;
33

4+
5+
46
CREATE TABLE rowmarks.first(id int NOT NULL);
57
CREATE TABLE rowmarks.second(id int NOT NULL);
68

@@ -56,6 +58,38 @@ WHERE id = (SELECT id FROM rowmarks.second
5658
FOR UPDATE)
5759
FOR SHARE;
5860

61+
/* Check updates (plan) */
62+
EXPLAIN (COSTS OFF)
63+
UPDATE rowmarks.second SET id = 2
64+
WHERE rowmarks.second.id IN (SELECT id FROM rowmarks.first WHERE id = 1);
65+
EXPLAIN (COSTS OFF)
66+
UPDATE rowmarks.second SET id = 2
67+
WHERE rowmarks.second.id IN (SELECT id FROM rowmarks.first WHERE id < 1);
68+
EXPLAIN (COSTS OFF)
69+
UPDATE rowmarks.second SET id = 2
70+
WHERE rowmarks.second.id IN (SELECT id FROM rowmarks.first WHERE id = 1 OR id = 2);
71+
EXPLAIN (COSTS OFF)
72+
UPDATE rowmarks.second SET id = 2
73+
WHERE rowmarks.second.id IN (SELECT id FROM rowmarks.first WHERE id = 1)
74+
RETURNING *, tableoid::regclass;
75+
76+
/* Check updates (execution) */
77+
UPDATE rowmarks.second SET id = 1
78+
WHERE rowmarks.second.id IN (SELECT id FROM rowmarks.first WHERE id = 1 OR id = 2)
79+
RETURNING *, tableoid::regclass;
80+
81+
/* Check deletes (plan) */
82+
EXPLAIN (COSTS OFF)
83+
DELETE FROM rowmarks.second
84+
WHERE rowmarks.second.id IN (SELECT id FROM rowmarks.first WHERE id = 1);
85+
EXPLAIN (COSTS OFF)
86+
DELETE FROM rowmarks.second
87+
WHERE rowmarks.second.id IN (SELECT id FROM rowmarks.first WHERE id < 1);
88+
EXPLAIN (COSTS OFF)
89+
DELETE FROM rowmarks.second
90+
WHERE rowmarks.second.id IN (SELECT id FROM rowmarks.first WHERE id = 1 OR id = 2);
91+
92+
5993

6094
DROP SCHEMA rowmarks CASCADE;
6195
DROP EXTENSION pg_pathman;

src/compat/rowmarks_fix.c

+18-144
Original file line numberDiff line numberDiff line change
@@ -14,165 +14,39 @@
1414

1515
#include "access/sysattr.h"
1616
#include "catalog/pg_type.h"
17-
#include "nodes/relation.h"
1817
#include "nodes/nodeFuncs.h"
18+
#include "optimizer/planmain.h"
1919
#include "utils/builtins.h"
2020
#include "utils/rel.h"
2121

2222

2323
#ifndef NATIVE_PARTITIONING_ROWMARKS
2424

25-
/* Special column name for rowmarks */
26-
#define TABLEOID_STR(subst) ( "pathman_tableoid" subst )
27-
#define TABLEOID_STR_BASE_LEN ( sizeof(TABLEOID_STR("")) - 1 )
2825

29-
30-
static void lock_rows_visitor(Plan *plan, void *context);
31-
static List *get_tableoids_list(List *tlist);
32-
33-
34-
/* Final rowmark processing for partitioned tables */
3526
void
36-
postprocess_lock_rows(List *rtable, Plan *plan)
37-
{
38-
plan_tree_walker(plan, lock_rows_visitor, rtable);
39-
}
40-
41-
/*
42-
* Add missing 'TABLEOID_STR%u' junk attributes for inherited partitions
43-
*
44-
* This is necessary since preprocess_targetlist() heavily
45-
* depends on the 'inh' flag which we have to unset.
46-
*
47-
* postprocess_lock_rows() will later transform 'TABLEOID_STR:Oid'
48-
* relnames into 'tableoid:rowmarkId'.
49-
*/
50-
void
51-
rowmark_add_tableoids(Query *parse)
52-
{
53-
ListCell *lc;
54-
55-
/* Generate 'tableoid' for partitioned table rowmark */
56-
foreach (lc, parse->rowMarks)
57-
{
58-
RowMarkClause *rc = (RowMarkClause *) lfirst(lc);
59-
Oid parent = getrelid(rc->rti, parse->rtable);
60-
Var *var;
61-
TargetEntry *tle;
62-
char resname[64];
63-
64-
/* Check that table is partitioned */
65-
if (!get_pathman_relation_info(parent))
66-
continue;
67-
68-
var = makeVar(rc->rti,
69-
TableOidAttributeNumber,
70-
OIDOID,
71-
-1,
72-
InvalidOid,
73-
0);
74-
75-
/* Use parent's Oid as TABLEOID_STR's key (%u) */
76-
snprintf(resname, sizeof(resname), TABLEOID_STR("%u"), parent);
77-
78-
tle = makeTargetEntry((Expr *) var,
79-
list_length(parse->targetList) + 1,
80-
pstrdup(resname),
81-
true);
82-
83-
/* There's no problem here since new attribute is junk */
84-
parse->targetList = lappend(parse->targetList, tle);
85-
}
86-
}
87-
88-
/*
89-
* Extract target entries with resnames beginning with TABLEOID_STR
90-
* and var->varoattno == TableOidAttributeNumber
91-
*/
92-
static List *
93-
get_tableoids_list(List *tlist)
27+
append_tle_for_rowmark(PlannerInfo *root, PlanRowMark *rc)
9428
{
95-
List *result = NIL;
96-
ListCell *lc;
97-
98-
foreach (lc, tlist)
99-
{
100-
TargetEntry *te = (TargetEntry *) lfirst(lc);
101-
Var *var = (Var *) te->expr;
102-
103-
if (!IsA(var, Var))
104-
continue;
105-
106-
/* Check that column name begins with TABLEOID_STR & it's tableoid */
107-
if (var->varoattno == TableOidAttributeNumber &&
108-
(te->resname && strlen(te->resname) > TABLEOID_STR_BASE_LEN) &&
109-
0 == strncmp(te->resname, TABLEOID_STR(""), TABLEOID_STR_BASE_LEN))
110-
{
111-
result = lappend(result, te);
112-
}
113-
}
114-
115-
return result;
116-
}
117-
118-
/*
119-
* Find 'TABLEOID_STR%u' attributes that were manually
120-
* created for partitioned tables and replace Oids
121-
* (used for '%u') with expected rc->rowmarkIds
122-
*/
123-
static void
124-
lock_rows_visitor(Plan *plan, void *context)
125-
{
126-
List *rtable = (List *) context;
127-
LockRows *lock_rows = (LockRows *) plan;
128-
Plan *lock_child = outerPlan(plan);
129-
List *tableoids;
130-
ListCell *lc;
131-
132-
if (!IsA(lock_rows, LockRows))
133-
return;
134-
135-
Assert(rtable && IsA(rtable, List) && lock_child);
136-
137-
/* Select tableoid attributes that must be renamed */
138-
tableoids = get_tableoids_list(lock_child->targetlist);
139-
if (!tableoids)
140-
return; /* this LockRows has nothing to do with partitioned table */
141-
142-
foreach (lc, lock_rows->rowMarks)
143-
{
144-
PlanRowMark *rc = (PlanRowMark *) lfirst(lc);
145-
Oid parent_oid = getrelid(rc->rti, rtable);
146-
ListCell *mark_lc;
147-
List *finished_tes = NIL; /* postprocessed target entries */
148-
149-
foreach (mark_lc, tableoids)
150-
{
151-
TargetEntry *te = (TargetEntry *) lfirst(mark_lc);
152-
const char *cur_oid_str = &(te->resname[TABLEOID_STR_BASE_LEN]);
153-
Datum cur_oid_datum;
154-
155-
cur_oid_datum = DirectFunctionCall1(oidin, CStringGetDatum(cur_oid_str));
29+
Var *var;
30+
char resname[32];
31+
TargetEntry *tle;
15632

157-
if (DatumGetObjectId(cur_oid_datum) == parent_oid)
158-
{
159-
char resname[64];
33+
var = makeVar(rc->rti,
34+
TableOidAttributeNumber,
35+
OIDOID,
36+
-1,
37+
InvalidOid,
38+
0);
16039

161-
/* Replace 'TABLEOID_STR:Oid' with 'tableoid:rowmarkId' */
162-
snprintf(resname, sizeof(resname), "tableoid%u", rc->rowmarkId);
163-
te->resname = pstrdup(resname);
40+
snprintf(resname, sizeof(resname), "tableoid%u", rc->rowmarkId);
16441

165-
finished_tes = lappend(finished_tes, te);
166-
}
167-
}
42+
tle = makeTargetEntry((Expr *) var,
43+
list_length(root->processed_tlist) + 1,
44+
pstrdup(resname),
45+
true);
16846

169-
/* Remove target entries that have been processed in this step */
170-
foreach (mark_lc, finished_tes)
171-
tableoids = list_delete_ptr(tableoids, lfirst(mark_lc));
47+
root->processed_tlist = lappend(root->processed_tlist, tle);
17248

173-
if (list_length(tableoids) == 0)
174-
break; /* nothing to do */
175-
}
49+
add_vars_to_targetlist(root, list_make1(var), bms_make_singleton(0), true);
17650
}
17751

17852
#endif /* NATIVE_PARTITIONING_ROWMARKS */

0 commit comments

Comments
 (0)