88 *-------------------------------------------------------------------------
99 */
1010#include "postgres.h"
11+ #include "funcapi.h"
1112
1213#include "access/htup_details.h"
14+ #include "access/tuptoaster.h"
1315#include "catalog/pg_collation.h"
1416#include "catalog/pg_type.h"
1517#include "utils/builtins.h"
@@ -134,7 +136,11 @@ init_record(RecordVar *record, TupleDesc tupdesc, Variable *variable)
134136#endif
135137
136138 oldcxt = MemoryContextSwitchTo (record -> hctx );
137- record -> tupdesc = CreateTupleDescCopyConstr (tupdesc );
139+ record -> tupdesc = CreateTupleDescCopy (tupdesc );
140+ record -> tupdesc -> tdhasoid = false;
141+ record -> tupdesc -> tdtypeid = RECORDOID ;
142+ record -> tupdesc -> tdtypmod = -1 ;
143+ record -> tupdesc = BlessTupleDesc (record -> tupdesc );
138144
139145 /* Initialize hash table. */
140146 ctl .keysize = sizeof (HashRecordKey );
@@ -153,47 +159,6 @@ init_record(RecordVar *record, TupleDesc tupdesc, Variable *variable)
153159 MemoryContextSwitchTo (oldcxt );
154160}
155161
156- /*
157- * Copy record using src_tuple.
158- */
159- void
160- copy_record (RecordVar * dest_record , HeapTuple src_tuple , Variable * variable )
161- {
162- HeapTuple tuple ;
163- Datum value ;
164- bool isnull ;
165- HashRecordKey k ;
166- HashRecordEntry * item ;
167- bool found ;
168- MemoryContext oldcxt ;
169-
170- oldcxt = MemoryContextSwitchTo (dest_record -> hctx );
171-
172- /* Inserting a new record into dest_record */
173- tuple = heap_copytuple (src_tuple );
174- value = fastgetattr (tuple , 1 , dest_record -> tupdesc , & isnull );
175-
176- k .value = value ;
177- k .is_null = isnull ;
178- k .hash_proc = & dest_record -> hash_proc ;
179- k .cmp_proc = & dest_record -> cmp_proc ;
180-
181- item = (HashRecordEntry * ) hash_search (dest_record -> rhash , & k ,
182- HASH_ENTER , & found );
183- if (found )
184- {
185- heap_freetuple (tuple );
186- MemoryContextSwitchTo (oldcxt );
187- ereport (ERROR ,
188- (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
189- errmsg ("there is a record in the variable \"%s\" with same "
190- "key" , GetName (variable ))));
191- }
192- item -> tuple = tuple ;
193-
194- MemoryContextSwitchTo (oldcxt );
195- }
196-
197162/*
198163 * New record structure should be the same as the first record.
199164 */
@@ -247,15 +212,78 @@ check_record_key(Variable *variable, Oid typid)
247212 "key type" , GetName (variable ))));
248213}
249214
215+ static Datum
216+ copy_record_tuple (RecordVar * record , HeapTupleHeader tupleHeader )
217+ {
218+ TupleDesc tupdesc ;
219+ HeapTupleHeader result ;
220+ int tuple_len ;
221+
222+ tupdesc = record -> tupdesc ;
223+
224+ /*
225+ * If the tuple contains any external TOAST pointers, we have to inline
226+ * those fields to meet the conventions for composite-type Datums.
227+ */
228+ if (HeapTupleHeaderHasExternal (tupleHeader ))
229+ return toast_flatten_tuple_to_datum (tupleHeader ,
230+ HeapTupleHeaderGetDatumLength (tupleHeader ),
231+ tupdesc );
232+
233+ /*
234+ * Fast path for easy case: just make a palloc'd copy and insert the
235+ * correct composite-Datum header fields (since those may not be set if
236+ * the given tuple came from disk, rather than from heap_form_tuple).
237+ */
238+ tuple_len = HeapTupleHeaderGetDatumLength (tupleHeader );
239+ result = (HeapTupleHeader ) palloc (tuple_len );
240+ memcpy ((char * ) result , (char * ) tupleHeader , tuple_len );
241+
242+ HeapTupleHeaderSetDatumLength (result , tuple_len );
243+ HeapTupleHeaderSetTypeId (result , tupdesc -> tdtypeid );
244+ HeapTupleHeaderSetTypMod (result , tupdesc -> tdtypmod );
245+
246+ return PointerGetDatum (result );
247+ }
248+
249+ static Datum
250+ get_record_key (Datum tuple , TupleDesc tupdesc , bool * isnull )
251+ {
252+ HeapTupleHeader th = (HeapTupleHeader ) DatumGetPointer (tuple );
253+ bool hasnulls = th -> t_infomask & HEAP_HASNULL ;
254+ bits8 * bp = th -> t_bits ; /* ptr to null bitmap in tuple */
255+ char * tp ; /* ptr to tuple data */
256+ long off ; /* offset in tuple data */
257+ int keyatt = 0 ;
258+ Form_pg_attribute attr = GetTupleDescAttr (tupdesc , keyatt );
259+
260+ if (hasnulls && att_isnull (keyatt , bp ))
261+ {
262+ * isnull = true;
263+ return (Datum ) NULL ;
264+ }
265+
266+ tp = (char * ) th + th -> t_hoff ;
267+ off = 0 ;
268+ if (attr -> attlen == -1 )
269+ off = att_align_pointer (off , attr -> attalign , -1 , tp + off );
270+ else
271+ {
272+ /* not varlena, so safe to use att_align_nominal */
273+ off = att_align_nominal (off , attr -> attalign );
274+ }
275+
276+ * isnull = false;
277+ return fetchatt (attr , tp + off );
278+ }
279+
250280/*
251281 * Insert a new record. New record key should be unique in the variable.
252282 */
253283void
254284insert_record (Variable * variable , HeapTupleHeader tupleHeader )
255285{
256- TupleDesc tupdesc ;
257- HeapTuple tuple ;
258- int tuple_len ;
286+ Datum tuple ;
259287 Datum value ;
260288 bool isnull ;
261289 RecordVar * record ;
@@ -270,20 +298,10 @@ insert_record(Variable *variable, HeapTupleHeader tupleHeader)
270298
271299 oldcxt = MemoryContextSwitchTo (record -> hctx );
272300
273- tupdesc = record -> tupdesc ;
274-
275- /* Build a HeapTuple control structure */
276- tuple_len = HeapTupleHeaderGetDatumLength (tupleHeader );
277-
278- tuple = (HeapTuple ) palloc (HEAPTUPLESIZE + tuple_len );
279- tuple -> t_len = tuple_len ;
280- ItemPointerSetInvalid (& (tuple -> t_self ));
281- tuple -> t_tableOid = InvalidOid ;
282- tuple -> t_data = (HeapTupleHeader ) ((char * ) tuple + HEAPTUPLESIZE );
283- memcpy ((char * ) tuple -> t_data , (char * ) tupleHeader , tuple_len );
301+ tuple = copy_record_tuple (record , tupleHeader );
284302
285303 /* Inserting a new record */
286- value = fastgetattr (tuple , 1 , tupdesc , & isnull );
304+ value = get_record_key (tuple , record -> tupdesc , & isnull );
287305 /* First, check if there is a record with same key */
288306 k .value = value ;
289307 k .is_null = isnull ;
@@ -294,7 +312,7 @@ insert_record(Variable *variable, HeapTupleHeader tupleHeader)
294312 HASH_ENTER , & found );
295313 if (found )
296314 {
297- heap_freetuple ( tuple );
315+ pfree ( DatumGetPointer ( tuple ) );
298316 MemoryContextSwitchTo (oldcxt );
299317 ereport (ERROR ,
300318 (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
@@ -313,9 +331,7 @@ insert_record(Variable *variable, HeapTupleHeader tupleHeader)
313331bool
314332update_record (Variable * variable , HeapTupleHeader tupleHeader )
315333{
316- TupleDesc tupdesc ;
317- HeapTuple tuple ;
318- int tuple_len ;
334+ Datum tuple ;
319335 Datum value ;
320336 bool isnull ;
321337 RecordVar * record ;
@@ -330,20 +346,10 @@ update_record(Variable *variable, HeapTupleHeader tupleHeader)
330346
331347 oldcxt = MemoryContextSwitchTo (record -> hctx );
332348
333- tupdesc = record -> tupdesc ;
334-
335- /* Build a HeapTuple control structure */
336- tuple_len = HeapTupleHeaderGetDatumLength (tupleHeader );
337-
338- tuple = (HeapTuple ) palloc (HEAPTUPLESIZE + tuple_len );
339- tuple -> t_len = tuple_len ;
340- ItemPointerSetInvalid (& (tuple -> t_self ));
341- tuple -> t_tableOid = InvalidOid ;
342- tuple -> t_data = (HeapTupleHeader ) ((char * ) tuple + HEAPTUPLESIZE );
343- memcpy ((char * ) tuple -> t_data , (char * ) tupleHeader , tuple_len );
349+ tuple = copy_record_tuple (record , tupleHeader );
344350
345351 /* Update a record */
346- value = fastgetattr (tuple , 1 , tupdesc , & isnull );
352+ value = get_record_key (tuple , record -> tupdesc , & isnull );
347353 k .value = value ;
348354 k .is_null = isnull ;
349355 k .hash_proc = & record -> hash_proc ;
@@ -353,13 +359,13 @@ update_record(Variable *variable, HeapTupleHeader tupleHeader)
353359 HASH_FIND , & found );
354360 if (!found )
355361 {
356- heap_freetuple ( tuple );
362+ pfree ( DatumGetPointer ( tuple ) );
357363 MemoryContextSwitchTo (oldcxt );
358364 return false;
359365 }
360366
361367 /* Release old tuple */
362- heap_freetuple ( item -> tuple );
368+ pfree ( DatumGetPointer ( item -> tuple ) );
363369 item -> tuple = tuple ;
364370
365371 MemoryContextSwitchTo (oldcxt );
@@ -387,7 +393,49 @@ delete_record(Variable *variable, Datum value, bool is_null)
387393 item = (HashRecordEntry * ) hash_search (record -> rhash , & k ,
388394 HASH_REMOVE , & found );
389395 if (found )
390- heap_freetuple ( item -> tuple );
396+ pfree ( DatumGetPointer ( item -> tuple ) );
391397
392398 return found ;
393399}
400+
401+ /*
402+ * Copy record using src_tuple.
403+ */
404+ void
405+ insert_record_copy (RecordVar * dest_record , Datum src_tuple , Variable * variable )
406+ {
407+ Datum tuple ;
408+ Datum value ;
409+ bool isnull ;
410+ HashRecordKey k ;
411+ HashRecordEntry * item ;
412+ bool found ;
413+ MemoryContext oldcxt ;
414+
415+ oldcxt = MemoryContextSwitchTo (dest_record -> hctx );
416+
417+ /* Inserting a new record into dest_record */
418+ tuple = copy_record_tuple (dest_record ,
419+ (HeapTupleHeader ) DatumGetPointer (src_tuple ));
420+ value = get_record_key (tuple , dest_record -> tupdesc , & isnull );
421+
422+ k .value = value ;
423+ k .is_null = isnull ;
424+ k .hash_proc = & dest_record -> hash_proc ;
425+ k .cmp_proc = & dest_record -> cmp_proc ;
426+
427+ item = (HashRecordEntry * ) hash_search (dest_record -> rhash , & k ,
428+ HASH_ENTER , & found );
429+ if (found )
430+ {
431+ pfree (DatumGetPointer (tuple ));
432+ MemoryContextSwitchTo (oldcxt );
433+ ereport (ERROR ,
434+ (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
435+ errmsg ("there is a record in the variable \"%s\" with same "
436+ "key" , GetName (variable ))));
437+ }
438+ item -> tuple = tuple ;
439+
440+ MemoryContextSwitchTo (oldcxt );
441+ }
0 commit comments