Skip to content

Commit 720adc8

Browse files
author
Sofia Kopikova
committed
replace UNKNOWNOID to TEXTOID
1 parent dbc3c83 commit 720adc8

7 files changed

+220
-19
lines changed

expected/pg_variables.out

+46-8
Original file line numberDiff line numberDiff line change
@@ -773,8 +773,8 @@ CLOSE r1_cur;
773773
COMMIT; -- warning
774774
RESET client_min_messages;
775775
-- Clean memory after unsuccessful creation of a variable
776-
SELECT pgv_insert('vars4', 'r1', row('str1', 'str1')); -- fail
777-
ERROR: could not identify a hash function for type unknown
776+
SELECT pgv_insert('vars4', 'r1', row (('str1'::text, 'str1'::text))); -- fail
777+
ERROR: could not identify a hash function for type record
778778
SELECT package FROM pgv_stats() WHERE package = 'vars4';
779779
package
780780
---------
@@ -954,28 +954,32 @@ SELECT pgv_select('vars', 'r1');
954954
(1,str1,str2)
955955
(1 row)
956956

957-
SELECT pgv_insert('vars', 'r2', row(1, 'str1'));
957+
SELECT pgv_insert('vars', 'r2', row(1, 'str1')); -- ok, UNKNOWNOID of 'str1' converts to TEXTOID
958+
pgv_insert
959+
------------
960+
961+
(1 row)
962+
963+
SELECT pgv_insert('vars', 'r2', foo) FROM foo; -- ok
958964
pgv_insert
959965
------------
960966

961967
(1 row)
962968

963-
SELECT pgv_insert('vars', 'r2', foo) FROM foo;
964-
ERROR: new record attribute type for attribute number 2 differs from variable "r2" structure.
965-
HINT: You may need explicit type casts.
966969
SELECT pgv_select('vars', 'r2');
967970
pgv_select
968971
------------
969972
(1,str1)
970-
(1 row)
973+
(0,str00)
974+
(2 rows)
971975

972976
SELECT pgv_insert('vars', 'r3', row(1, 'str1'::text));
973977
pgv_insert
974978
------------
975979

976980
(1 row)
977981

978-
SELECT pgv_insert('vars', 'r3', foo) FROM foo;
982+
SELECT pgv_insert('vars', 'r3', foo) FROM foo; -- ok, no conversions
979983
pgv_insert
980984
------------
981985

@@ -988,3 +992,37 @@ SELECT pgv_select('vars', 'r3');
988992
(0,str00)
989993
(2 rows)
990994

995+
SELECT pgv_insert('vars', 'r4', row(1, 2::int));
996+
pgv_insert
997+
------------
998+
999+
(1 row)
1000+
1001+
SELECT pgv_insert('vars', 'r4', row(0, 'str1')); -- fail, UNKNOWNOID of 'str1' can't be converted to int
1002+
ERROR: new record attribute type for attribute number 2 differs from variable "r4" structure.
1003+
HINT: You may need explicit type casts.
1004+
SELECT pgv_select('vars', 'r4');
1005+
pgv_select
1006+
------------
1007+
(1,2)
1008+
(1 row)
1009+
1010+
SELECT pgv_insert('vars', 'r5', foo) FROM foo; -- types: int, text
1011+
pgv_insert
1012+
------------
1013+
1014+
(1 row)
1015+
1016+
SELECT pgv_insert('vars', 'r5', row(1, 'str1')); -- ok, UNKNOWNOID of 'str1' converts to TEXTOID
1017+
pgv_insert
1018+
------------
1019+
1020+
(1 row)
1021+
1022+
SELECT pgv_select('vars', 'r5');
1023+
pgv_select
1024+
------------
1025+
(1,str1)
1026+
(0,str00)
1027+
(2 rows)
1028+

expected/pg_variables_trans.out

+2-2
Original file line numberDiff line numberDiff line change
@@ -1858,8 +1858,8 @@ SELECT pgv_insert('package', 'errs',row(1), true);
18581858
(1 row)
18591859

18601860
-- Variable should not exists in case when error occurs during creation
1861-
SELECT pgv_insert('vars4', 'r1', row('str1', 'str1'));
1862-
ERROR: could not identify a hash function for type unknown
1861+
SELECT pgv_insert('vars4', 'r1', row (('str1'::text, 'str1'::text)));
1862+
ERROR: could not identify a hash function for type record
18631863
SELECT pgv_select('vars4', 'r1', 0);
18641864
ERROR: unrecognized package "vars4"
18651865
-- If variable created and removed in same transaction level,

pg_variables.c

+32-2
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ do { \
114114
} while(0)
115115
#endif /* PG_VERSION_NUM */
116116

117+
/* User controlled GUCs */
118+
bool convert_unknownoid_guc;
119+
bool convert_unknownoid;
120+
117121
static HTAB *packagesHash = NULL;
118122
static MemoryContext ModuleContext = NULL;
119123

@@ -701,6 +705,14 @@ variable_insert(PG_FUNCTION_ARGS)
701705
/*
702706
* This is the first record for the var_name. Initialize record.
703707
*/
708+
/* Convert UNKNOWNOID to TEXTOID if needed
709+
* tupdesc may be changed
710+
*/
711+
if (convert_unknownoid)
712+
{
713+
coerce_unknown_first_record(&tupdesc, &rec);
714+
}
715+
704716
init_record(record, tupdesc, variable);
705717
variable->is_deleted = false;
706718
}
@@ -709,8 +721,11 @@ variable_insert(PG_FUNCTION_ARGS)
709721
/*
710722
* We need to check attributes of the new row if this is a transient
711723
* record type or if last record has different id.
724+
* Also we convert UNKNOWNOID to TEXTOID if needed.
725+
* tupdesc may be changed
712726
*/
713-
check_attributes(variable, tupdesc);
727+
check_attributes(variable, &rec, tupdesc);
728+
714729
}
715730

716731
insert_record(variable, rec);
@@ -791,7 +806,11 @@ variable_update(PG_FUNCTION_ARGS)
791806
tupTypmod = HeapTupleHeaderGetTypMod(rec);
792807

793808
tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
794-
check_attributes(variable, tupdesc);
809+
/*
810+
* Convert UNKNOWNOID to TEXTOID if needed
811+
* tupdesc may be changed
812+
*/
813+
check_attributes(variable, &rec, tupdesc);
795814
ReleaseTupleDesc(tupdesc);
796815

797816
res = update_record(variable, rec);
@@ -2622,6 +2641,17 @@ freeStatsLists(void)
26222641
void
26232642
_PG_init(void)
26242643
{
2644+
DefineCustomBoolVariable("pg_variables.convert_unknownoid",
2645+
"Use \'TEXT\' format for all values of \'UNKNOWNOID\', default is true.",
2646+
NULL,
2647+
&convert_unknownoid,
2648+
true,
2649+
PGC_USERSET,
2650+
0, /* FLAGS??? */
2651+
NULL,
2652+
NULL,
2653+
NULL);
2654+
26252655
RegisterXactCallback(pgvTransCallback, NULL);
26262656
RegisterSubXactCallback(pgvSubTransCallback, NULL);
26272657

pg_variables.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "access/tupdesc.h"
1717
#include "datatype/timestamp.h"
1818
#include "utils/date.h"
19+
#include "utils/guc.h"
1920
#include "utils/hsearch.h"
2021
#include "utils/numeric.h"
2122
#include "utils/jsonb.h"
@@ -155,8 +156,12 @@ typedef struct ChangesStackNode
155156
MemoryContext ctx;
156157
} ChangesStackNode;
157158

159+
/* pg_variables.c */
160+
extern bool convert_unknownoid;
161+
158162
extern void init_record(RecordVar *record, TupleDesc tupdesc, Variable *variable);
159-
extern void check_attributes(Variable *variable, TupleDesc tupdesc);
163+
extern void check_attributes(Variable *variable, HeapTupleHeader *rec, TupleDesc tupdesc);
164+
extern void coerce_unknown_first_record(TupleDesc *tupdesc, HeapTupleHeader * rec);
160165
extern void check_record_key(Variable *variable, Oid typid);
161166

162167
extern void insert_record(Variable *variable, HeapTupleHeader tupleHeader);

pg_variables_record.c

+121-1
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,11 @@
2525

2626
#include "catalog/pg_collation.h"
2727
#include "catalog/pg_type.h"
28+
#include "parser/parse_type.h"
2829
#include "utils/builtins.h"
2930
#include "utils/datum.h"
31+
#include "utils/lsyscache.h"
32+
#include "utils/syscache.h"
3033
#include "utils/memutils.h"
3134
#include "utils/typcache.h"
3235

@@ -172,14 +175,118 @@ init_record(RecordVar *record, TupleDesc tupdesc, Variable *variable)
172175
MemoryContextSwitchTo(oldcxt);
173176
}
174177

178+
/* Check if any attributes of type UNKNOWNOID are in given tupdesc */
179+
static int
180+
is_unknownoid_in_tupdesc(TupleDesc tupdesc)
181+
{
182+
int i = 0;
183+
for (i = 0; i < tupdesc->natts; i++)
184+
{
185+
Form_pg_attribute attr = GetTupleDescAttr(tupdesc, i);
186+
187+
if (attr->atttypid == UNKNOWNOID)
188+
return true;
189+
190+
}
191+
return false;
192+
}
193+
194+
/* Replace all attributes of type UNKNOWNOID to TEXTOID in given tupdesc */
195+
static void
196+
coerce_unknown_rewrite_tupdesc(TupleDesc old_tupdesc, TupleDesc *return_tupdesc)
197+
{
198+
int i;
199+
200+
(*return_tupdesc) = CreateTupleDescCopy(old_tupdesc);
201+
202+
for (i = 0; i < old_tupdesc->natts; i++)
203+
{
204+
Form_pg_attribute attr = GetTupleDescAttr(old_tupdesc, i);
205+
206+
if (attr->atttypid == UNKNOWNOID)
207+
{
208+
FormData_pg_attribute new_attr = *attr;
209+
210+
new_attr.atttypid = TEXTOID;
211+
new_attr.attlen = -1;
212+
new_attr.atttypmod = -1;
213+
memcpy(TupleDescAttr((*return_tupdesc), i), &new_attr, sizeof(FormData_pg_attribute));
214+
}
215+
}
216+
}
217+
218+
/*
219+
* Deform tuple with old_tupdesc, coerce values of type UNKNOWNOID to TEXTOID, form tuple with new_tupdesc.
220+
* new_tupdesc must have the same attributes as old_tupdesc except such of types UNKNOWNOID -- they must be of TEXTOID type
221+
*/
222+
static void
223+
reconstruct_tuple(TupleDesc old_tupdesc, TupleDesc new_tupdesc, HeapTupleHeader *rec)
224+
{
225+
HeapTupleData tuple;
226+
HeapTuple newtup;
227+
Datum *values = (Datum*)palloc(old_tupdesc->natts * sizeof(Datum));
228+
bool *isnull = (bool*)palloc(old_tupdesc->natts * sizeof(bool));
229+
Oid baseTypeId = UNKNOWNOID;
230+
int32 baseTypeMod = -1;
231+
int32 inputTypeMod = -1;
232+
Type baseType = NULL;
233+
int i;
234+
235+
baseTypeId = getBaseTypeAndTypmod(TEXTOID, &baseTypeMod);
236+
baseType = typeidType(baseTypeId);
237+
/* Build a temporary HeapTuple control structure */
238+
tuple.t_len = HeapTupleHeaderGetDatumLength(*rec);
239+
tuple.t_data = *rec;
240+
heap_deform_tuple(&tuple, old_tupdesc, values, isnull);
241+
242+
for (i = 0; i < old_tupdesc->natts; i++)
243+
{
244+
Form_pg_attribute attr = GetTupleDescAttr(old_tupdesc, i);
245+
246+
if (attr->atttypid == UNKNOWNOID)
247+
{
248+
values[i] = stringTypeDatum(baseType,
249+
DatumGetCString(values[i]),
250+
inputTypeMod);
251+
}
252+
}
253+
254+
newtup = heap_form_tuple(new_tupdesc, values, isnull);
255+
(*rec) = newtup->t_data;
256+
pfree(isnull);
257+
pfree(values);
258+
ReleaseSysCache(baseType);
259+
}
260+
261+
/*
262+
* Used in pg_variables.c insert_record for coercing types in first record in variable.
263+
* If there are UNKNOWNOIDs in tupdesc, rewrites it and reconstructs tuple with new tupdesc.
264+
* Replaces given tupdesc with the new one.
265+
*/
266+
void
267+
coerce_unknown_first_record(TupleDesc *tupdesc, HeapTupleHeader *rec)
268+
{
269+
TupleDesc new_tupdesc = NULL;
270+
271+
if (!is_unknownoid_in_tupdesc(*tupdesc))
272+
return;
273+
274+
coerce_unknown_rewrite_tupdesc(*tupdesc, &new_tupdesc);
275+
reconstruct_tuple(*tupdesc, new_tupdesc, rec);
276+
277+
ReleaseTupleDesc(*tupdesc);
278+
(*tupdesc) = new_tupdesc;
279+
}
280+
175281
/*
176282
* New record structure should be the same as the first record.
177283
*/
178284
void
179-
check_attributes(Variable *variable, TupleDesc tupdesc)
285+
check_attributes(Variable *variable, HeapTupleHeader *rec, TupleDesc tupdesc)
180286
{
181287
int i;
182288
RecordVar *record;
289+
bool unknowns = false;
183290

184291
Assert(variable->typid == RECORDOID);
185292

@@ -198,6 +305,16 @@ check_attributes(Variable *variable, TupleDesc tupdesc)
198305
Form_pg_attribute attr1 = GetTupleDescAttr(record->tupdesc, i),
199306
attr2 = GetTupleDescAttr(tupdesc, i);
200307

308+
/*
309+
* For the sake of convenience, we consider all the unknown types are to be
310+
* a text type.
311+
*/
312+
if (convert_unknownoid && (attr1->atttypid == TEXTOID) && (attr2->atttypid == UNKNOWNOID))
313+
{
314+
unknowns = true;
315+
continue;
316+
}
317+
201318
if ((attr1->atttypid != attr2->atttypid)
202319
|| (attr1->attndims != attr2->attndims)
203320
|| (attr1->atttypmod != attr2->atttypmod))
@@ -208,6 +325,9 @@ check_attributes(Variable *variable, TupleDesc tupdesc)
208325
i + 1, GetName(variable)),
209326
errhint("You may need explicit type casts.")));
210327
}
328+
329+
if (unknowns)
330+
reconstruct_tuple(tupdesc, record->tupdesc, rec);
211331
}
212332

213333
/*

sql/pg_variables.sql

+12-4
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ COMMIT; -- warning
222222
RESET client_min_messages;
223223

224224
-- Clean memory after unsuccessful creation of a variable
225-
SELECT pgv_insert('vars4', 'r1', row('str1', 'str1')); -- fail
225+
SELECT pgv_insert('vars4', 'r1', row (('str1'::text, 'str1'::text))); -- fail
226226
SELECT package FROM pgv_stats() WHERE package = 'vars4';
227227

228228
-- Remove package if it is empty
@@ -268,10 +268,18 @@ SELECT pgv_select('vars', 'r1');
268268
SELECT pgv_insert('vars', 'r1', foo) FROM foo;
269269
SELECT pgv_select('vars', 'r1');
270270

271-
SELECT pgv_insert('vars', 'r2', row(1, 'str1'));
272-
SELECT pgv_insert('vars', 'r2', foo) FROM foo;
271+
SELECT pgv_insert('vars', 'r2', row(1, 'str1')); -- ok, UNKNOWNOID of 'str1' converts to TEXTOID
272+
SELECT pgv_insert('vars', 'r2', foo) FROM foo; -- ok
273273
SELECT pgv_select('vars', 'r2');
274274

275275
SELECT pgv_insert('vars', 'r3', row(1, 'str1'::text));
276-
SELECT pgv_insert('vars', 'r3', foo) FROM foo;
276+
SELECT pgv_insert('vars', 'r3', foo) FROM foo; -- ok, no conversions
277277
SELECT pgv_select('vars', 'r3');
278+
279+
SELECT pgv_insert('vars', 'r4', row(1, 2::int));
280+
SELECT pgv_insert('vars', 'r4', row(0, 'str1')); -- fail, UNKNOWNOID of 'str1' can't be converted to int
281+
SELECT pgv_select('vars', 'r4');
282+
283+
SELECT pgv_insert('vars', 'r5', foo) FROM foo; -- types: int, text
284+
SELECT pgv_insert('vars', 'r5', row(1, 'str1')); -- ok, UNKNOWNOID of 'str1' converts to TEXTOID
285+
SELECT pgv_select('vars', 'r5');

sql/pg_variables_trans.sql

+1-1
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,7 @@ FROM generate_series(1,5) AS gs(n) WHERE 1.0/(n-3)<>0;
470470
SELECT pgv_insert('package', 'errs',row(1), true);
471471

472472
-- Variable should not exists in case when error occurs during creation
473-
SELECT pgv_insert('vars4', 'r1', row('str1', 'str1'));
473+
SELECT pgv_insert('vars4', 'r1', row (('str1'::text, 'str1'::text)));
474474
SELECT pgv_select('vars4', 'r1', 0);
475475

476476
-- If variable created and removed in same transaction level,

0 commit comments

Comments
 (0)