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)
172175MemoryContextSwitchTo (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 */
178284void
179- check_attributes (Variable * variable ,TupleDesc tupdesc )
285+ check_attributes (Variable * variable ,HeapTupleHeader * rec , TupleDesc tupdesc )
180286{
181287int i ;
182288RecordVar * record ;
289+ bool unknowns = false;
183290
184291Assert (variable -> typid == RECORDOID );
185292
@@ -198,6 +305,16 @@ check_attributes(Variable *variable, TupleDesc tupdesc)
198305Form_pg_attribute attr1 = GetTupleDescAttr (record -> tupdesc ,i ),
199306attr2 = 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+
201318if ((attr1 -> atttypid != attr2 -> atttypid )
202319|| (attr1 -> attndims != attr2 -> attndims )
203320|| (attr1 -> atttypmod != attr2 -> atttypmod ))
@@ -208,6 +325,9 @@ check_attributes(Variable *variable, TupleDesc tupdesc)
208325i + 1 ,GetName (variable )),
209326errhint ("You may need explicit type casts." )));
210327}
328+
329+ if (unknowns )
330+ reconstruct_tuple (tupdesc ,record -> tupdesc ,rec );
211331}
212332
213333/*