Skip to content

Commit f11fbe3

Browse files
author
Nikita Malakhov
committed
Multiple TOAST tables support for TOAST API
TOAST mechanics supposes that all TOASTed columns of single table use one TOAST table entity for TOASTed records. This TOAST table has strictly defined structure. This is fine for reference TOAST mechanics but TOAST API supposes that custom Toasters could use TOAST tables with different structure along with different columns in a table could have different Toasters assigned. This requires that regular table should have possibility to access several (different) TOAST tables. Using Toasters with TOAST tables structure different from regular (generic) on one table leads to segmentation fault. This patch is adressed to solve this problem. This patch provides multiple TOAST tables support. Multiple TOAST tables support consists of 2 new pg_class columns - reltoasterids and reltoastrelids. These two columns contain arrays of Toaster OIDs and TOAST relation OIDs used by table columns. New pg_class columns affect a lot of source code. Author: Teodor Sigaev <teodor@sigaev.ru> Author: Oleg Bartunov <obartunov@postgrespro.ru> Author: Nikita Glukhov <n.gluhov@postgrespro.ru> Author: Nikita Malakhov <n.malakhov@postgrespro.ru>
1 parent 1f954e7 commit f11fbe3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+1309
-551
lines changed

contrib/amcheck/t/001_verify_heapam.pl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ sub get_toast_for
9797
SELECT 'pg_toast.' || t.relname
9898
FROM pg_catalog.pg_class c, pg_catalog.pg_class t
9999
WHERE c.relname = '$relname'
100-
AND c.reltoastrelid = t.oid));
100+
AND t.oid = ANY(c.reltoastrelids)));
101101
}
102102

103103
# (Re)create and populate a test table of the given name.

contrib/amcheck/verify_heapam.c

Lines changed: 80 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,13 @@ typedef struct HeapCheckContext
114114
Relation toast_rel;
115115
Relation *toast_indexes;
116116
Relation valid_toast_index;
117-
int num_toast_indexes;
117+
struct ToastRelContext
118+
{
119+
Relation toast_rel;
120+
Relation *toast_indexes;
121+
Relation valid_toast_index;
122+
int num_toast_indexes;
123+
} *toast_rels;
118124

119125
/* Values for iterating over pages in the relation */
120126
BlockNumber blkno;
@@ -154,6 +160,7 @@ typedef struct HeapCheckContext
154160
/* Internal implementation */
155161
static void check_tuple(HeapCheckContext *ctx);
156162
static void check_toast_tuple(HeapTuple toasttup, HeapCheckContext *ctx,
163+
struct ToastRelContext *toast_rel,
157164
ToastedAttribute *ta, int32 *expected_chunk_seq,
158165
uint32 extsize);
159166

@@ -364,29 +371,48 @@ verify_heapam(PG_FUNCTION_ARGS)
364371
last_block = (BlockNumber) lb;
365372
}
366373

374+
/* XXX spaces */
367375
/* Optionally open the toast relation, if any. */
368-
if (ctx.rel->rd_rel->reltoastrelid && check_toast)
376+
if (ctx.rel->rd_ntoasters > 0 && check_toast)
369377
{
370-
int offset;
371-
372-
/* Main relation has associated toast relation */
373-
ctx.toast_rel = table_open(ctx.rel->rd_rel->reltoastrelid,
374-
AccessShareLock);
375-
offset = toast_open_indexes(ctx.toast_rel,
376-
AccessShareLock,
377-
&(ctx.toast_indexes),
378-
&(ctx.num_toast_indexes));
379-
ctx.valid_toast_index = ctx.toast_indexes[offset];
378+
ctx.toast_rels = palloc(sizeof(*ctx.toast_rels) * ctx.rel->rd_ntoasters);
379+
380+
for (int i = 0; i < ctx.rel->rd_ntoasters; i++)
381+
{
382+
Oid toastrelid = ctx.rel->rd_toastrelids[i];
383+
384+
if (OidIsValid(toastrelid))
385+
{
386+
int offset;
387+
388+
/* Main relation has associated toast relation */
389+
ctx.toast_rels[i].toast_rel =
390+
table_open(toastrelid, AccessShareLock);
391+
392+
offset = toast_open_indexes(ctx.toast_rels[i].toast_rel,
393+
AccessShareLock,
394+
&(ctx.toast_rels[i].toast_indexes),
395+
&(ctx.toast_rels[i].num_toast_indexes));
396+
397+
ctx.toast_rels[i].valid_toast_index =
398+
ctx.toast_rels[i].toast_indexes[offset];
399+
}
400+
else
401+
{
402+
ctx.toast_rels[i].toast_rel = NULL;
403+
ctx.toast_rels[i].toast_indexes = NULL;
404+
ctx.toast_rels[i].num_toast_indexes = 0;
405+
ctx.toast_rels[i].valid_toast_index = 0;
406+
}
407+
}
380408
}
381409
else
382410
{
383411
/*
384412
* Main relation has no associated toast relation, or we're
385413
* intentionally skipping it.
386414
*/
387-
ctx.toast_rel = NULL;
388-
ctx.toast_indexes = NULL;
389-
ctx.num_toast_indexes = 0;
415+
ctx.toast_rels = NULL;
390416
}
391417

392418
update_cached_xid_range(&ctx);
@@ -535,12 +561,21 @@ verify_heapam(PG_FUNCTION_ARGS)
535561
ReleaseBuffer(vmbuffer);
536562

537563
/* Close the associated toast table and indexes, if any. */
538-
if (ctx.toast_indexes)
539-
toast_close_indexes(ctx.toast_indexes, ctx.num_toast_indexes,
540-
AccessShareLock);
541-
if (ctx.toast_rel)
542-
table_close(ctx.toast_rel, AccessShareLock);
543-
564+
if (ctx.toast_rels)
565+
{
566+
for (int i = 0; i < ctx.rel->rd_ntoasters; i++)
567+
{
568+
if (ctx.toast_rels[i].toast_indexes)
569+
toast_close_indexes(ctx.toast_rels[i].toast_indexes,
570+
ctx.toast_rels[i].num_toast_indexes,
571+
AccessShareLock);
572+
573+
if (ctx.toast_rels[i].toast_rel)
574+
table_close(ctx.toast_rels[i].toast_rel, AccessShareLock);
575+
}
576+
577+
pfree(ctx.toast_rels);
578+
}
544579
/* Close the main relation */
545580
relation_close(ctx.rel, AccessShareLock);
546581

@@ -1157,6 +1192,7 @@ check_tuple_visibility(HeapCheckContext *ctx)
11571192
*/
11581193
static void
11591194
check_toast_tuple(HeapTuple toasttup, HeapCheckContext *ctx,
1195+
struct ToastRelContext *toast_rel,
11601196
ToastedAttribute *ta, int32 *expected_chunk_seq,
11611197
uint32 extsize)
11621198
{
@@ -1169,7 +1205,7 @@ check_toast_tuple(HeapTuple toasttup, HeapCheckContext *ctx,
11691205

11701206
/* Sanity-check the sequence number. */
11711207
chunk_seq = DatumGetInt32(fastgetattr(toasttup, 2,
1172-
ctx->toast_rel->rd_att, &isnull));
1208+
toast_rel->toast_rel->rd_att, &isnull));
11731209
if (isnull)
11741210
{
11751211
report_toast_corruption(ctx, ta,
@@ -1189,7 +1225,7 @@ check_toast_tuple(HeapTuple toasttup, HeapCheckContext *ctx,
11891225

11901226
/* Sanity-check the chunk data. */
11911227
chunk = DatumGetPointer(fastgetattr(toasttup, 3,
1192-
ctx->toast_rel->rd_att, &isnull));
1228+
toast_rel->toast_rel->rd_att, &isnull));
11931229
if (isnull)
11941230
{
11951231
report_toast_corruption(ctx, ta,
@@ -1328,7 +1364,7 @@ check_tuple_attribute(HeapCheckContext *ctx)
13281364
{
13291365
uint8 va_tag = VARTAG_EXTERNAL(tp + ctx->offset);
13301366

1331-
if (va_tag != VARTAG_ONDISK)
1367+
if (va_tag != VARTAG_ONDISK && va_tag != VARTAG_CUSTOM)
13321368
{
13331369
report_corruption(ctx,
13341370
psprintf("toasted attribute has unexpected TOAST tag %u",
@@ -1422,7 +1458,7 @@ check_tuple_attribute(HeapCheckContext *ctx)
14221458
}
14231459

14241460
/* The relation better have a toast table */
1425-
if (!ctx->rel->rd_rel->reltoastrelid)
1461+
if (ctx->rel->rd_ntoasters <= 0)
14261462
{
14271463
report_corruption(ctx,
14281464
psprintf("toast value %u is external but relation has no toast relation",
@@ -1431,7 +1467,7 @@ check_tuple_attribute(HeapCheckContext *ctx)
14311467
}
14321468

14331469
/* If we were told to skip toast checking, then we're done. */
1434-
if (ctx->toast_rel == NULL)
1470+
if (ctx->toast_rels == NULL)
14351471
return true;
14361472

14371473
/*
@@ -1472,6 +1508,7 @@ check_toasted_attribute(HeapCheckContext *ctx, ToastedAttribute *ta)
14721508
uint32 extsize;
14731509
int32 expected_chunk_seq = 0;
14741510
int32 last_chunk_seq;
1511+
struct ToastRelContext *toast_rel = NULL;
14751512

14761513
extsize = VARATT_EXTERNAL_GET_EXTSIZE(ta->toast_pointer);
14771514
last_chunk_seq = (extsize - 1) / TOAST_MAX_CHUNK_SIZE;
@@ -1484,13 +1521,27 @@ check_toasted_attribute(HeapCheckContext *ctx, ToastedAttribute *ta)
14841521
BTEqualStrategyNumber, F_OIDEQ,
14851522
ObjectIdGetDatum(ta->toast_pointer.va_valueid));
14861523

1524+
for (int i = 0; i < ctx->rel->rd_ntoasters; i++)
1525+
{
1526+
if (ta->toast_pointer.va_toastrelid == ctx->rel->rd_toastrelids[i])
1527+
{
1528+
toast_rel = &ctx->toast_rels[i];
1529+
break;
1530+
}
1531+
}
1532+
1533+
if (!toast_rel)
1534+
report_corruption(ctx,
1535+
psprintf("toast pointer's va_toastrelid %u does not contain in reltoastrelids",
1536+
ta->toast_pointer.va_toastrelid));
1537+
14871538
/*
14881539
* Check if any chunks for this toasted object exist in the toast table,
14891540
* accessible via the index.
14901541
*/
14911542
init_toast_snapshot(&SnapshotToast);
1492-
toastscan = systable_beginscan_ordered(ctx->toast_rel,
1493-
ctx->valid_toast_index,
1543+
toastscan = systable_beginscan_ordered(toast_rel->toast_rel,
1544+
toast_rel->valid_toast_index,
14941545
&SnapshotToast, 1,
14951546
&toastkey);
14961547
found_toasttup = false;
@@ -1499,7 +1550,7 @@ check_toasted_attribute(HeapCheckContext *ctx, ToastedAttribute *ta)
14991550
ForwardScanDirection)) != NULL)
15001551
{
15011552
found_toasttup = true;
1502-
check_toast_tuple(toasttup, ctx, ta, &expected_chunk_seq, extsize);
1553+
check_toast_tuple(toasttup, ctx, toast_rel, ta, &expected_chunk_seq, extsize);
15031554
}
15041555
systable_endscan_ordered(toastscan);
15051556

contrib/pg_visibility/expected/pg_visibility.out

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,22 @@ SAVEPOINT q; SELECT * FROM pg_visibility_map(:oid); ROLLBACK TO q;
1111
ERROR: XX000
1212
-- ERROR: could not open relation with OID 16xxx
1313
SAVEPOINT q; SELECT 1; ROLLBACK TO q;
14-
?column?
14+
?column?
1515
----------
1616
1
1717
(1 row)
1818

1919
SAVEPOINT q; SELECT 1; ROLLBACK TO q;
20-
?column?
20+
?column?
2121
----------
2222
1
2323
(1 row)
2424

2525
SELECT pg_relation_size(:oid), pg_relation_filepath(:oid),
2626
has_table_privilege(:oid, 'SELECT');
27-
pg_relation_size | pg_relation_filepath | has_table_privilege
27+
pg_relation_size | pg_relation_filepath | has_table_privilege
2828
------------------+----------------------+---------------------
29-
| |
29+
| |
3030
(1 row)
3131

3232
SELECT * FROM pg_visibility_map(:oid);
@@ -132,42 +132,42 @@ alter table regular_table alter column b set storage external;
132132
insert into regular_table values (1, repeat('one', 1000)), (2, repeat('two', 1000));
133133
vacuum (disable_page_skipping) regular_table;
134134
select count(*) > 0 from pg_visibility('regular_table');
135-
?column?
135+
?column?
136136
----------
137137
t
138138
(1 row)
139139

140-
select count(*) > 0 from pg_visibility((select reltoastrelid from pg_class where relname = 'regular_table'));
141-
?column?
140+
select count(*) > 0 from pg_visibility((select reltoastrelids[1] from pg_class where relname = 'regular_table'));
141+
?column?
142142
----------
143143
t
144144
(1 row)
145145

146146
truncate regular_table;
147147
select count(*) > 0 from pg_visibility('regular_table');
148-
?column?
148+
?column?
149149
----------
150150
f
151151
(1 row)
152152

153-
select count(*) > 0 from pg_visibility((select reltoastrelid from pg_class where relname = 'regular_table'));
154-
?column?
153+
select count(*) > 0 from pg_visibility((select reltoastrelids[1] from pg_class where relname = 'regular_table'));
154+
?column?
155155
----------
156156
f
157157
(1 row)
158158

159159
create materialized view matview_visibility_test as select * from regular_table;
160160
vacuum (disable_page_skipping) matview_visibility_test;
161161
select count(*) > 0 from pg_visibility('matview_visibility_test');
162-
?column?
162+
?column?
163163
----------
164164
f
165165
(1 row)
166166

167167
insert into regular_table values (1), (2);
168168
refresh materialized view matview_visibility_test;
169169
select count(*) > 0 from pg_visibility('matview_visibility_test');
170-
?column?
170+
?column?
171171
----------
172172
t
173173
(1 row)
@@ -176,32 +176,32 @@ select count(*) > 0 from pg_visibility('matview_visibility_test');
176176
insert into test_partition values (1);
177177
vacuum (disable_page_skipping) test_partition;
178178
select count(*) > 0 from pg_visibility('test_partition', 0);
179-
?column?
179+
?column?
180180
----------
181181
t
182182
(1 row)
183183

184184
select count(*) > 0 from pg_visibility_map('test_partition');
185-
?column?
185+
?column?
186186
----------
187187
t
188188
(1 row)
189189

190190
select count(*) > 0 from pg_visibility_map_summary('test_partition');
191-
?column?
191+
?column?
192192
----------
193193
t
194194
(1 row)
195195

196196
select * from pg_check_frozen('test_partition'); -- hopefully none
197-
t_ctid
197+
t_ctid
198198
--------
199199
(0 rows)
200200

201201
select pg_truncate_visibility_map('test_partition');
202-
pg_truncate_visibility_map
202+
pg_truncate_visibility_map
203203
----------------------------
204-
204+
205205
(1 row)
206206

207207
-- test copy freeze
@@ -213,15 +213,15 @@ truncate copyfreeze;
213213
copy copyfreeze from stdin freeze;
214214
commit;
215215
select * from pg_visibility_map('copyfreeze');
216-
blkno | all_visible | all_frozen
216+
blkno | all_visible | all_frozen
217217
-------+-------------+------------
218218
0 | t | t
219219
1 | t | t
220220
2 | t | t
221221
(3 rows)
222222

223223
select * from pg_check_frozen('copyfreeze');
224-
t_ctid
224+
t_ctid
225225
--------
226226
(0 rows)
227227

@@ -235,15 +235,15 @@ copy copyfreeze from stdin;
235235
copy copyfreeze from stdin freeze;
236236
commit;
237237
select * from pg_visibility_map('copyfreeze');
238-
blkno | all_visible | all_frozen
238+
blkno | all_visible | all_frozen
239239
-------+-------------+------------
240240
0 | f | f
241241
1 | f | f
242242
2 | t | t
243243
(3 rows)
244244

245245
select * from pg_check_frozen('copyfreeze');
246-
t_ctid
246+
t_ctid
247247
--------
248248
(0 rows)
249249

@@ -255,15 +255,15 @@ copy copyfreeze from stdin;
255255
copy copyfreeze from stdin freeze;
256256
commit;
257257
select * from pg_visibility_map('copyfreeze');
258-
blkno | all_visible | all_frozen
258+
blkno | all_visible | all_frozen
259259
-------+-------------+------------
260260
0 | t | t
261261
1 | f | f
262262
2 | t | t
263263
(3 rows)
264264

265265
select * from pg_check_frozen('copyfreeze');
266-
t_ctid
266+
t_ctid
267267
--------
268268
(0 rows)
269269

0 commit comments

Comments
 (0)