Skip to content

Commit e62a1e2

Browse files
committed
[#24078] xCluster: support user-defined composite types in automatic mode
Summary: This extends the automatic mode OID preserving machinery to also preserve the following OIDs: * pg_type OID for shell types * pg_type OID for single-range types * pg_type OID for user-defined composite types * pg_type OID for user-defined base types This allows user-defined composite types, single-range types, and user-defined based types to be created in automatic mode while replication is running. This diff does not preserve OIDs for multi-range types but we do not currently support storing those in tables anyways. Note that table row types are also considered composite types; this diff does not preserve their OIDs. Also not preserved: pg_type OIDs for array types and domain types. Because enum pg_type OID's are now preserved I was able to remove the kludge where we ignored the enum pg_type OID when locating enum labels. Did some refactoring to allow combining the new type OID map with the existing sequence OID map since they have the same fields. Fixes #24078 Jira: DB-12971 Test Plan: Added a new regression test for preserving pg_type OIDs for user-defined composite types, non-array-based types, enums, ranges, and shell types: ``` ybd release --cxx-test xcluster_ddl_replication_pgregress-test --gtest_filter '*.PgRegressCreateDropType' ``` The same preservation test is applied to the other regression tests: ``` ybd release --cxx-test xcluster_ddl_replication_pgregress-test ``` For reviewer convenience, the following is from the logs running the new test: ``` I0327 12:35:45.720230 4215 xcluster_ddl_replication_pgregress-test.cc:148] pg_type OIDs that must match on both sides are: pg_catalog, aclitem, 1033, b, NULL pg_catalog, bit, 1560, b, NULL pg_catalog, bool, 16, b, NULL pg_catalog, box, 603, b, NULL pg_catalog, bpchar, 1042, b, NULL pg_catalog, bytea, 17, b, NULL pg_catalog, char, 18, b, NULL pg_catalog, cid, 29, b, NULL pg_catalog, cidr, 650, b, NULL pg_catalog, circle, 718, b, NULL pg_catalog, date, 1082, b, NULL pg_catalog, daterange, 3912, r, NULL pg_catalog, float4, 700, b, NULL pg_catalog, float8, 701, b, NULL pg_catalog, gtsvector, 3642, b, NULL pg_catalog, inet, 869, b, NULL pg_catalog, int2, 21, b, NULL pg_catalog, int4, 23, b, NULL pg_catalog, int4range, 3904, r, NULL pg_catalog, int8, 20, b, NULL pg_catalog, int8range, 3926, r, NULL pg_catalog, interval, 1186, b, NULL pg_catalog, json, 114, b, NULL pg_catalog, jsonb, 3802, b, NULL pg_catalog, jsonpath, 8003, b, NULL pg_catalog, line, 628, b, NULL pg_catalog, lseg, 601, b, NULL pg_catalog, macaddr, 829, b, NULL pg_catalog, macaddr8, 774, b, NULL pg_catalog, money, 790, b, NULL pg_catalog, name, 19, b, NULL pg_catalog, numeric, 1700, b, NULL pg_catalog, numrange, 3906, r, NULL pg_catalog, oid, 26, b, NULL pg_catalog, path, 602, b, NULL pg_catalog, pg_brin_bloom_summary, 4600, b, NULL pg_catalog, pg_brin_minmax_multi_summary, 4601, b, NULL pg_catalog, pg_dependencies, 3402, b, NULL pg_catalog, pg_lsn, 3220, b, NULL pg_catalog, pg_mcv_list, 5017, b, NULL pg_catalog, pg_ndistinct, 3361, b, NULL pg_catalog, pg_node_tree, 194, b, NULL pg_catalog, pg_snapshot, 5038, b, NULL pg_catalog, point, 600, b, NULL pg_catalog, polygon, 604, b, NULL pg_catalog, refcursor, 1790, b, NULL pg_catalog, regclass, 2205, b, NULL pg_catalog, regcollation, 4191, b, NULL pg_catalog, regconfig, 3734, b, NULL pg_catalog, regdictionary, 3769, b, NULL pg_catalog, regnamespace, 4089, b, NULL pg_catalog, regoper, 2203, b, NULL pg_catalog, regoperator, 2204, b, NULL pg_catalog, regproc, 24, b, NULL pg_catalog, regprocedure, 2202, b, NULL pg_catalog, regrole, 4096, b, NULL pg_catalog, regtype, 2206, b, NULL pg_catalog, text, 25, b, NULL pg_catalog, tid, 27, b, NULL pg_catalog, time, 1083, b, NULL pg_catalog, timestamp, 1114, b, NULL pg_catalog, timestamptz, 1184, b, NULL pg_catalog, timetz, 1266, b, NULL pg_catalog, tsquery, 3615, b, NULL pg_catalog, tsrange, 3908, r, NULL pg_catalog, tstzrange, 3910, r, NULL pg_catalog, tsvector, 3614, b, NULL pg_catalog, txid_snapshot, 2970, b, NULL pg_catalog, uuid, 2950, b, NULL pg_catalog, varbit, 1562, b, NULL pg_catalog, varchar, 1043, b, NULL pg_catalog, xid, 28, b, NULL pg_catalog, xid8, 5069, b, NULL pg_catalog, xml, 142, b, NULL public, bogon, 16473, e, NULL public, bogon2, 16480, e, NULL public, bogon3, 16488, e, NULL public, colors, 16496, e, NULL public, empty_enum, 16518, e, NULL public, huge_label, 16520, e, NULL public, paint_color, 16510, e, NULL public, planets, 16432, e, NULL public, rainbow, 16418, e, NULL schema1, enum_in_schema, 16527, c, c schema2, enum_in_schema, 16530, c, c ``` Reviewers: xCluster, jhe Reviewed By: jhe Subscribers: ybase, yql Differential Revision: https://phorge.dev.yugabyte.com/D42819
1 parent 56e2af1 commit e62a1e2

File tree

13 files changed

+511
-191
lines changed

13 files changed

+511
-191
lines changed

src/postgres/src/backend/catalog/pg_type.c

+11
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939

4040
/* YB includes */
4141
#include "catalog/catalog.h"
42+
#include "catalog/yb_oid_assignment.h"
4243
#include "pg_yb_utils.h"
4344

4445
static char *makeUniqueTypeName(const char *typeName, Oid typeNamespace,
@@ -142,6 +143,11 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
142143
typoid = binary_upgrade_next_pg_type_oid;
143144
binary_upgrade_next_pg_type_oid = InvalidOid;
144145
}
146+
else if (YbUsingTypeOidAssignment())
147+
{
148+
typoid = YbLookupOidAssignmentForType(get_namespace_name(typeNamespace),
149+
typeName);
150+
}
145151
else
146152
{
147153
typoid = GetNewOidWithIndex(pg_type_desc, TypeOidIndexId,
@@ -524,6 +530,11 @@ TypeCreate(Oid newTypeOid,
524530
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
525531
errmsg("system relations must have an explicit type OID!")));
526532
}
533+
else if (YbUsingTypeOidAssignment())
534+
{
535+
typeObjectId = YbLookupOidAssignmentForType(get_namespace_name(typeNamespace),
536+
typeName);
537+
}
527538
/* else allow system to assign oid */
528539
else
529540
{

src/postgres/src/backend/catalog/yb_catalog/yb_oid_assignment.c

+151-108
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
*--------------------------------------------------------------------------------------------------
2222
*/
2323

24-
2524
#include "postgres.h"
2625

2726
#include "utils/builtins.h"
@@ -32,7 +31,8 @@
3231
static HTAB *yb_enum_label_assignment_map = NULL;
3332
static bool yb_enum_label_assignment_exists = false;
3433

35-
static HTAB *yb_sequence_oid_assignment_map = NULL;
34+
static HTAB *yb_oid_assignment_map = NULL;
35+
static bool yb_type_oid_assignment_exists = false;
3636
static bool yb_sequence_oid_assignment_exists = false;
3737

3838
/*
@@ -66,12 +66,6 @@ YbClearEnumLabelMap(void)
6666
static void
6767
YbCreateEnumLabelMapKey(Oid enum_oid, const char *label, char *key_buffer)
6868
{
69-
/*
70-
* For now ignore enum OID field. See YbLookupOidAssignmentForEnumLabel
71-
* for why.
72-
*/
73-
enum_oid = 42;
74-
7569
int written_bytes = snprintf(key_buffer,
7670
YB_ENUM_LABEL_ASSIGNMENT_MAP_KEY_SIZE, "%u.%s",
7771
enum_oid, label);
@@ -114,74 +108,78 @@ YbLookupOidForEnumLabel(Oid enum_oid, const char *label)
114108
return InvalidOid;
115109
}
116110

111+
#define YB_OID_KIND_TYPE "type"
112+
#define YB_OID_KIND_SEQUENCE "sequence"
113+
117114
/*
118-
* yb_sequence_oid_assignment_map key format is <schema>.<name>\0
115+
* yb_oid_assignment_map key format is <oid_kind>.<schema>.<name>\0
116+
* <oid_kind>\0 is guaranteed to fit in 20 characters.
119117
* <identifier>\0 is guaranteed to fit in NAMEDATALEN characters.
120118
*/
121-
#define YB_SEQUENCE_OID_ASSIGNMENT_MAP_KEY_SIZE (NAMEDATALEN + NAMEDATALEN)
119+
#define YB_OID_ASSIGNMENT_MAP_KEY_SIZE (20 + NAMEDATALEN + NAMEDATALEN)
122120

123-
typedef struct YbSequenceOidAssignmentMapEntry {
124-
/* encodes schema, name */
125-
char key[YB_SEQUENCE_OID_ASSIGNMENT_MAP_KEY_SIZE];
121+
typedef struct YbOidAssignmentMapEntry {
122+
/* encodes oid_kind, schema, name */
123+
char key[YB_OID_ASSIGNMENT_MAP_KEY_SIZE];
126124
Oid oid;
127-
} YbSequenceOidAssignmentMapEntry;
125+
} YbOidAssignmentMapEntry;
128126

129127
static void
130-
YbClearSequenceOidMap(void)
128+
YbClearOidMap(void)
131129
{
132130
HASHCTL ctl;
133131

134-
if (yb_sequence_oid_assignment_map != NULL)
135-
hash_destroy(yb_sequence_oid_assignment_map);
132+
if (yb_oid_assignment_map != NULL)
133+
hash_destroy(yb_oid_assignment_map);
136134
memset(&ctl, 0, sizeof(ctl));
137-
ctl.keysize = YB_SEQUENCE_OID_ASSIGNMENT_MAP_KEY_SIZE;
138-
ctl.entrysize = sizeof(YbSequenceOidAssignmentMapEntry);
139-
yb_sequence_oid_assignment_map = hash_create("YB sequence OIDs map",
140-
/* initial size */ 20, &ctl,
141-
HASH_ELEM | HASH_STRINGS);
135+
ctl.keysize = YB_OID_ASSIGNMENT_MAP_KEY_SIZE;
136+
ctl.entrysize = sizeof(YbOidAssignmentMapEntry);
137+
yb_oid_assignment_map = hash_create("YB OIDs map",
138+
/* initial size */ 20, &ctl,
139+
HASH_ELEM | HASH_STRINGS);
142140
}
143141

144142
static void
145-
YbCreateSequenceOidMapKey(const char *schema, const char *name, char *key_buffer)
143+
YbCreateOidMapKey(const char *oid_kind, const char *schema, const char *name,
144+
char *key_buffer)
146145
{
147-
int written_bytes = snprintf(key_buffer,
148-
YB_SEQUENCE_OID_ASSIGNMENT_MAP_KEY_SIZE,
149-
"%s.%s", schema, name);
150-
if (written_bytes >= YB_SEQUENCE_OID_ASSIGNMENT_MAP_KEY_SIZE)
146+
int written_bytes = snprintf(key_buffer, YB_OID_ASSIGNMENT_MAP_KEY_SIZE,
147+
"%s.%s.%s", oid_kind, schema, name);
148+
if (written_bytes >= YB_OID_ASSIGNMENT_MAP_KEY_SIZE)
151149
elog(ERROR,
152150
"unexpectedly large schema/name in OID assignment (schema '%s', "
153151
"name '%s')",
154152
schema, name);
155153
}
156154

157155
static void
158-
YbInsertSequenceOid(const char *schema, const char *name, Oid sequence_oid)
156+
YbInsertOid(const char *oid_kind, const char *schema, const char *name, Oid oid)
159157
{
160-
char key[YB_SEQUENCE_OID_ASSIGNMENT_MAP_KEY_SIZE];
161-
YbCreateSequenceOidMapKey(schema, name, key);
158+
char key[YB_OID_ASSIGNMENT_MAP_KEY_SIZE];
159+
YbCreateOidMapKey(oid_kind, schema, name, key);
162160

163161
bool found;
164-
YbSequenceOidAssignmentMapEntry *entry =
165-
hash_search(yb_sequence_oid_assignment_map, key, HASH_ENTER, &found);
162+
YbOidAssignmentMapEntry *entry =
163+
hash_search(yb_oid_assignment_map, key, HASH_ENTER, &found);
166164
if (!found)
167-
entry->oid = sequence_oid;
168-
else if (entry->oid != sequence_oid)
165+
entry->oid = oid;
166+
else if (entry->oid != oid)
169167
elog(ERROR,
170-
"attempt to provide multiple OIDs for sequence %s.%s: %u vs "
168+
"attempt to provide multiple OIDs for %s %s.%s: %u vs "
171169
"%u",
172-
schema, name, entry->oid, sequence_oid);
170+
oid_kind, schema, name, entry->oid, oid);
173171
}
174172

175173
/* Returns InvalidOid on not found. */
176174
static Oid
177-
YbLookupOidForSequence(const char *schema, const char *name)
175+
YbLookupOid(const char *oid_kind, const char *schema, const char *name)
178176
{
179-
char key[YB_SEQUENCE_OID_ASSIGNMENT_MAP_KEY_SIZE];
180-
YbCreateSequenceOidMapKey(schema, name, key);
177+
char key[YB_OID_ASSIGNMENT_MAP_KEY_SIZE];
178+
YbCreateOidMapKey(oid_kind, schema, name, key);
181179

182180
bool found;
183-
YbSequenceOidAssignmentMapEntry *entry =
184-
hash_search(yb_sequence_oid_assignment_map, key, HASH_FIND, &found);
181+
YbOidAssignmentMapEntry *entry =
182+
hash_search(yb_oid_assignment_map, key, HASH_FIND, &found);
185183
if (found)
186184
return entry->oid;
187185
return InvalidOid;
@@ -201,6 +199,76 @@ YbGetOidFromText(const text *input)
201199
return result;
202200
}
203201

202+
static void
203+
YbExtractEnumLabelMap(text *json_text, char *map_key, bool *found)
204+
{
205+
text *map_json = json_get_value(json_text, map_key);
206+
if (!map_json)
207+
{
208+
*found = false;
209+
return;
210+
}
211+
*found = true;
212+
213+
int length = get_json_array_length(map_json);
214+
for (int i = 0; i < length; i++)
215+
{
216+
text *label_info_entry = get_json_array_element(map_json, i);
217+
char *label = text_to_cstring(json_get_denormalized_value(label_info_entry,
218+
"label"));
219+
text *label_oid_text = json_get_value(label_info_entry,
220+
"label_oid");
221+
text *enum_oid_text = json_get_value(label_info_entry,
222+
"enum_oid");
223+
224+
Oid label_oid = YbGetOidFromText(label_oid_text);
225+
Oid enum_oid = YbGetOidFromText(enum_oid_text);
226+
if (label_oid == InvalidOid || enum_oid == InvalidOid)
227+
{
228+
elog(ERROR,
229+
"invalid JSON passed to "
230+
"yb_xcluster_set_next_oid_assignments: '%s'",
231+
text_to_cstring(json_text));
232+
}
233+
234+
YbInsertEnumLabel(enum_oid, label, label_oid);
235+
}
236+
}
237+
238+
static void
239+
YbExtractNameToOidMap(text *json_text, char *map_key, const char *oid_kind,
240+
bool *found)
241+
{
242+
text *map_json = json_get_value(json_text, map_key);
243+
if (!map_json)
244+
{
245+
*found = false;
246+
return;
247+
}
248+
*found = true;
249+
250+
int length = get_json_array_length(map_json);
251+
for (int i = 0; i < length; i++)
252+
{
253+
text *type_info_entry = get_json_array_element(map_json, i);
254+
char *schema = text_to_cstring(json_get_denormalized_value(type_info_entry,
255+
"schema"));
256+
char *name = text_to_cstring(json_get_denormalized_value(type_info_entry,
257+
"name"));
258+
text *oid_text = json_get_value(type_info_entry, "oid");
259+
Oid oid = YbGetOidFromText(oid_text);
260+
if (oid == InvalidOid)
261+
{
262+
elog(ERROR,
263+
"invalid JSON passed to "
264+
"yb_xcluster_set_next_oid_assignments: '%s'",
265+
text_to_cstring(json_text));
266+
}
267+
268+
YbInsertOid(oid_kind, schema, name, oid);
269+
}
270+
}
271+
204272
PG_FUNCTION_INFO_V1(yb_xcluster_set_next_oid_assignments);
205273

206274
/*
@@ -231,6 +299,24 @@ PG_FUNCTION_INFO_V1(yb_xcluster_set_next_oid_assignments);
231299
*
232300
* Example:
233301
* SELECT pg_catalog.yb_xcluster_set_next_oid_assignments(
302+
* '{"type_info":[' ||
303+
* '{"schema":"public","name":"my_range","oid":16406}' ||
304+
* ']}');
305+
*
306+
* This indicates that the type named my_range in schema public should
307+
* be assigned the OID 16406.
308+
*
309+
* The type_info key is optional; if it is present then all types created
310+
* directly via CREATE TYPE until the assignment is changed are expected to be
311+
* covered by the assignment. In the example this means that if the DDL
312+
* attempts to create a range not_my_range then an error will occur. It is
313+
* not an error if the DDL does not create all the types mentioned in the
314+
* assignment. Multi-ranges are not covered here as they are not directly
315+
* created via CREATE TYPE.
316+
*
317+
*
318+
* Example:
319+
* SELECT pg_catalog.yb_xcluster_set_next_oid_assignments(
234320
* '{"sequence_info":[' ||
235321
* '{"schema":"public","name":"my_sequence","oid":16406}' ||
236322
* ']}');
@@ -263,62 +349,14 @@ yb_xcluster_set_next_oid_assignments(PG_FUNCTION_ARGS)
263349
text *json_text = PG_GETARG_TEXT_P(0);
264350

265351
YbClearEnumLabelMap();
266-
yb_enum_label_assignment_exists = false;
267-
text *enum_label_info = json_get_value(json_text, "enum_label_info");
268-
if (enum_label_info != NULL)
269-
{
270-
yb_enum_label_assignment_exists = true;
271-
int length = get_json_array_length(enum_label_info);
272-
for (int i = 0; i < length; i++)
273-
{
274-
text *label_info_entry = get_json_array_element(enum_label_info, i);
275-
char *label = text_to_cstring(json_get_denormalized_value(label_info_entry, "label"));
276-
text *label_oid_text = json_get_value(label_info_entry,
277-
"label_oid");
278-
text *enum_oid_text = json_get_value(label_info_entry,
279-
"enum_oid");
280-
281-
Oid label_oid = YbGetOidFromText(label_oid_text);
282-
Oid enum_oid = YbGetOidFromText(enum_oid_text);
283-
if (label_oid == InvalidOid || enum_oid == InvalidOid)
284-
{
285-
elog(ERROR,
286-
"corrupted JSON passed to "
287-
"yb_xcluster_set_next_oid_assignments: '%s'",
288-
text_to_cstring(json_text));
289-
}
290-
291-
YbInsertEnumLabel(enum_oid, label, label_oid);
292-
}
293-
}
352+
YbExtractEnumLabelMap(json_text, "enum_label_info",
353+
&yb_enum_label_assignment_exists);
294354

295-
YbClearSequenceOidMap();
296-
yb_sequence_oid_assignment_exists = false;
297-
text *sequence_info = json_get_value(json_text, "sequence_info");
298-
if (sequence_info != NULL)
299-
{
300-
yb_sequence_oid_assignment_exists = true;
301-
int length = get_json_array_length(sequence_info);
302-
for (int i = 0; i < length; i++)
303-
{
304-
text *sequence_info_entry = get_json_array_element(sequence_info, i);
305-
char *schema = text_to_cstring(json_get_denormalized_value(sequence_info_entry,
306-
"schema"));
307-
char *name = text_to_cstring(json_get_denormalized_value(sequence_info_entry,
308-
"name"));
309-
text *oid_text = json_get_value(sequence_info_entry, "oid");
310-
Oid sequence_oid = YbGetOidFromText(oid_text);
311-
if (sequence_oid == InvalidOid)
312-
{
313-
elog(ERROR,
314-
"corrupted JSON passed to "
315-
"yb_xcluster_set_next_oid_assignments: '%s'",
316-
text_to_cstring(json_text));
317-
}
318-
319-
YbInsertSequenceOid(schema, name, sequence_oid);
320-
}
321-
}
355+
YbClearOidMap();
356+
YbExtractNameToOidMap(json_text, "type_info", YB_OID_KIND_TYPE,
357+
&yb_type_oid_assignment_exists);
358+
YbExtractNameToOidMap(json_text, "sequence_info", YB_OID_KIND_SEQUENCE,
359+
&yb_sequence_oid_assignment_exists);
322360

323361
PG_RETURN_VOID();
324362
}
@@ -332,24 +370,29 @@ YbUsingEnumLabelOidAssignment(void)
332370
Oid
333371
YbLookupOidAssignmentForEnumLabel(Oid enum_oid, const char *label)
334372
{
335-
/*----------
336-
* Currently we do not ensure that enums have the same *pg_type*
337-
* OIDs. We will fix that later, but in the meantime we take
338-
* advantage of the fact that we currently never have a
339-
* replicating DDL that refers to two different enums to ignore
340-
* the actual enum OID field when comparing labels.
341-
*
342-
* See YbCreateEnumLabelMapKey for the temporary code to ignore the enum
343-
* OID field.
344-
*----------
345-
*/
346373
Oid label_oid = YbLookupOidForEnumLabel(enum_oid, label);
347374
if (label_oid == InvalidOid)
348375
elog(ERROR, "no OID assignment for enum label %u.%s in OID assignment",
349376
enum_oid, label);
350377
return label_oid;
351378
}
352379

380+
bool
381+
YbUsingTypeOidAssignment(void)
382+
{
383+
return yb_type_oid_assignment_exists;
384+
}
385+
386+
Oid
387+
YbLookupOidAssignmentForType(const char *schema, const char *name)
388+
{
389+
Oid type_oid = YbLookupOid(YB_OID_KIND_TYPE, schema, name);
390+
if (type_oid == InvalidOid)
391+
elog(ERROR, "no OID assignment for type %s.%s in OID assignment",
392+
schema, name);
393+
return type_oid;
394+
}
395+
353396
bool
354397
YbUsingSequenceOidAssignment(void)
355398
{
@@ -359,7 +402,7 @@ YbUsingSequenceOidAssignment(void)
359402
Oid
360403
YbLookupOidAssignmentForSequence(const char *schema, const char *name)
361404
{
362-
Oid sequence_oid = YbLookupOidForSequence(schema, name);
405+
Oid sequence_oid = YbLookupOid(YB_OID_KIND_SEQUENCE, schema, name);
363406
if (sequence_oid == InvalidOid)
364407
elog(ERROR, "no OID assignment for sequence %s.%s in OID assignment",
365408
schema, name);

0 commit comments

Comments
 (0)