Coverage Report

Created: 2025-09-27 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/access/common/tupdesc.c
Line
Count
Source
1
/*-------------------------------------------------------------------------
2
 *
3
 * tupdesc.c
4
 *    POSTGRES tuple descriptor support code
5
 *
6
 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7
 * Portions Copyright (c) 1994, Regents of the University of California
8
 *
9
 *
10
 * IDENTIFICATION
11
 *    src/backend/access/common/tupdesc.c
12
 *
13
 * NOTES
14
 *    some of the executor utility code such as "ExecTypeFromTL" should be
15
 *    moved here.
16
 *
17
 *-------------------------------------------------------------------------
18
 */
19
20
#include "postgres.h"
21
22
#include "access/htup_details.h"
23
#include "access/toast_compression.h"
24
#include "access/tupdesc_details.h"
25
#include "catalog/catalog.h"
26
#include "catalog/pg_collation.h"
27
#include "catalog/pg_type.h"
28
#include "common/hashfn.h"
29
#include "utils/builtins.h"
30
#include "utils/datum.h"
31
#include "utils/resowner.h"
32
#include "utils/syscache.h"
33
34
/* ResourceOwner callbacks to hold tupledesc references  */
35
static void ResOwnerReleaseTupleDesc(Datum res);
36
static char *ResOwnerPrintTupleDesc(Datum res);
37
38
static const ResourceOwnerDesc tupdesc_resowner_desc =
39
{
40
  .name = "tupdesc reference",
41
  .release_phase = RESOURCE_RELEASE_AFTER_LOCKS,
42
  .release_priority = RELEASE_PRIO_TUPDESC_REFS,
43
  .ReleaseResource = ResOwnerReleaseTupleDesc,
44
  .DebugPrint = ResOwnerPrintTupleDesc
45
};
46
47
/* Convenience wrappers over ResourceOwnerRemember/Forget */
48
static inline void
49
ResourceOwnerRememberTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
50
0
{
51
0
  ResourceOwnerRemember(owner, PointerGetDatum(tupdesc), &tupdesc_resowner_desc);
52
0
}
53
54
static inline void
55
ResourceOwnerForgetTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
56
0
{
57
0
  ResourceOwnerForget(owner, PointerGetDatum(tupdesc), &tupdesc_resowner_desc);
58
0
}
59
60
/*
61
 * populate_compact_attribute_internal
62
 *    Helper function for populate_compact_attribute()
63
 */
64
static inline void
65
populate_compact_attribute_internal(Form_pg_attribute src,
66
                  CompactAttribute *dst)
67
0
{
68
0
  memset(dst, 0, sizeof(CompactAttribute));
69
70
0
  dst->attcacheoff = -1;
71
0
  dst->attlen = src->attlen;
72
73
0
  dst->attbyval = src->attbyval;
74
0
  dst->attispackable = (src->attstorage != TYPSTORAGE_PLAIN);
75
0
  dst->atthasmissing = src->atthasmissing;
76
0
  dst->attisdropped = src->attisdropped;
77
0
  dst->attgenerated = (src->attgenerated != '\0');
78
79
  /*
80
   * Assign nullability status for this column.  Assuming that a constraint
81
   * exists, at this point we don't know if a not-null constraint is valid,
82
   * so we assign UNKNOWN unless the table is a catalog, in which case we
83
   * know it's valid.
84
   */
85
0
  dst->attnullability = !src->attnotnull ? ATTNULLABLE_UNRESTRICTED :
86
0
    IsCatalogRelationOid(src->attrelid) ? ATTNULLABLE_VALID :
87
0
    ATTNULLABLE_UNKNOWN;
88
89
0
  switch (src->attalign)
90
0
  {
91
0
    case TYPALIGN_INT:
92
0
      dst->attalignby = ALIGNOF_INT;
93
0
      break;
94
0
    case TYPALIGN_CHAR:
95
0
      dst->attalignby = sizeof(char);
96
0
      break;
97
0
    case TYPALIGN_DOUBLE:
98
0
      dst->attalignby = ALIGNOF_DOUBLE;
99
0
      break;
100
0
    case TYPALIGN_SHORT:
101
0
      dst->attalignby = ALIGNOF_SHORT;
102
0
      break;
103
0
    default:
104
0
      dst->attalignby = 0;
105
0
      elog(ERROR, "invalid attalign value: %c", src->attalign);
106
0
      break;
107
0
  }
108
0
}
109
110
/*
111
 * populate_compact_attribute
112
 *    Fill in the corresponding CompactAttribute element from the
113
 *    Form_pg_attribute for the given attribute number.  This must be called
114
 *    whenever a change is made to a Form_pg_attribute in the TupleDesc.
115
 */
116
void
117
populate_compact_attribute(TupleDesc tupdesc, int attnum)
118
0
{
119
0
  Form_pg_attribute src = TupleDescAttr(tupdesc, attnum);
120
0
  CompactAttribute *dst;
121
122
  /*
123
   * Don't use TupleDescCompactAttr to prevent infinite recursion in assert
124
   * builds.
125
   */
126
0
  dst = &tupdesc->compact_attrs[attnum];
127
128
0
  populate_compact_attribute_internal(src, dst);
129
0
}
130
131
/*
132
 * verify_compact_attribute
133
 *    In Assert enabled builds, we verify that the CompactAttribute is
134
 *    populated correctly.  This helps find bugs in places such as ALTER
135
 *    TABLE where code makes changes to the FormData_pg_attribute but
136
 *    forgets to call populate_compact_attribute().
137
 *
138
 * This is used in TupleDescCompactAttr(), but declared here to allow access
139
 * to populate_compact_attribute_internal().
140
 */
141
void
142
verify_compact_attribute(TupleDesc tupdesc, int attnum)
143
0
{
144
#ifdef USE_ASSERT_CHECKING
145
  CompactAttribute cattr;
146
  Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum);
147
  CompactAttribute tmp;
148
149
  /*
150
   * Make a temp copy of the TupleDesc's CompactAttribute.  This may be a
151
   * shared TupleDesc and the attcacheoff might get changed by another
152
   * backend.
153
   */
154
  memcpy(&cattr, &tupdesc->compact_attrs[attnum], sizeof(CompactAttribute));
155
156
  /*
157
   * Populate the temporary CompactAttribute from the corresponding
158
   * Form_pg_attribute
159
   */
160
  populate_compact_attribute_internal(attr, &tmp);
161
162
  /*
163
   * Make the attcacheoff match since it's been reset to -1 by
164
   * populate_compact_attribute_internal.  Same with attnullability.
165
   */
166
  tmp.attcacheoff = cattr.attcacheoff;
167
  tmp.attnullability = cattr.attnullability;
168
169
  /* Check the freshly populated CompactAttribute matches the TupleDesc's */
170
  Assert(memcmp(&tmp, &cattr, sizeof(CompactAttribute)) == 0);
171
#endif
172
0
}
173
174
/*
175
 * CreateTemplateTupleDesc
176
 *    This function allocates an empty tuple descriptor structure.
177
 *
178
 * Tuple type ID information is initially set for an anonymous record type;
179
 * caller can overwrite this if needed.
180
 */
181
TupleDesc
182
CreateTemplateTupleDesc(int natts)
183
0
{
184
0
  TupleDesc desc;
185
186
  /*
187
   * sanity checks
188
   */
189
0
  Assert(natts >= 0);
190
191
  /*
192
   * Allocate enough memory for the tuple descriptor, the CompactAttribute
193
   * array and also an array of FormData_pg_attribute.
194
   *
195
   * Note: the FormData_pg_attribute array stride is
196
   * sizeof(FormData_pg_attribute), since we declare the array elements as
197
   * FormData_pg_attribute for notational convenience.  However, we only
198
   * guarantee that the first ATTRIBUTE_FIXED_PART_SIZE bytes of each entry
199
   * are valid; most code that copies tupdesc entries around copies just
200
   * that much.  In principle that could be less due to trailing padding,
201
   * although with the current definition of pg_attribute there probably
202
   * isn't any padding.
203
   */
204
0
  desc = (TupleDesc) palloc(offsetof(struct TupleDescData, compact_attrs) +
205
0
                natts * sizeof(CompactAttribute) +
206
0
                natts * sizeof(FormData_pg_attribute));
207
208
  /*
209
   * Initialize other fields of the tupdesc.
210
   */
211
0
  desc->natts = natts;
212
0
  desc->constr = NULL;
213
0
  desc->tdtypeid = RECORDOID;
214
0
  desc->tdtypmod = -1;
215
0
  desc->tdrefcount = -1;    /* assume not reference-counted */
216
217
0
  return desc;
218
0
}
219
220
/*
221
 * CreateTupleDesc
222
 *    This function allocates a new TupleDesc by copying a given
223
 *    Form_pg_attribute array.
224
 *
225
 * Tuple type ID information is initially set for an anonymous record type;
226
 * caller can overwrite this if needed.
227
 */
228
TupleDesc
229
CreateTupleDesc(int natts, Form_pg_attribute *attrs)
230
0
{
231
0
  TupleDesc desc;
232
0
  int     i;
233
234
0
  desc = CreateTemplateTupleDesc(natts);
235
236
0
  for (i = 0; i < natts; ++i)
237
0
  {
238
0
    memcpy(TupleDescAttr(desc, i), attrs[i], ATTRIBUTE_FIXED_PART_SIZE);
239
0
    populate_compact_attribute(desc, i);
240
0
  }
241
0
  return desc;
242
0
}
243
244
/*
245
 * CreateTupleDescCopy
246
 *    This function creates a new TupleDesc by copying from an existing
247
 *    TupleDesc.
248
 *
249
 * !!! Constraints and defaults are not copied !!!
250
 */
251
TupleDesc
252
CreateTupleDescCopy(TupleDesc tupdesc)
253
0
{
254
0
  TupleDesc desc;
255
0
  int     i;
256
257
0
  desc = CreateTemplateTupleDesc(tupdesc->natts);
258
259
  /* Flat-copy the attribute array */
260
0
  memcpy(TupleDescAttr(desc, 0),
261
0
       TupleDescAttr(tupdesc, 0),
262
0
       desc->natts * sizeof(FormData_pg_attribute));
263
264
  /*
265
   * Since we're not copying constraints and defaults, clear fields
266
   * associated with them.
267
   */
268
0
  for (i = 0; i < desc->natts; i++)
269
0
  {
270
0
    Form_pg_attribute att = TupleDescAttr(desc, i);
271
272
0
    att->attnotnull = false;
273
0
    att->atthasdef = false;
274
0
    att->atthasmissing = false;
275
0
    att->attidentity = '\0';
276
0
    att->attgenerated = '\0';
277
278
0
    populate_compact_attribute(desc, i);
279
0
  }
280
281
  /* We can copy the tuple type identification, too */
282
0
  desc->tdtypeid = tupdesc->tdtypeid;
283
0
  desc->tdtypmod = tupdesc->tdtypmod;
284
285
0
  return desc;
286
0
}
287
288
/*
289
 * CreateTupleDescTruncatedCopy
290
 *    This function creates a new TupleDesc with only the first 'natts'
291
 *    attributes from an existing TupleDesc
292
 *
293
 * !!! Constraints and defaults are not copied !!!
294
 */
295
TupleDesc
296
CreateTupleDescTruncatedCopy(TupleDesc tupdesc, int natts)
297
0
{
298
0
  TupleDesc desc;
299
0
  int     i;
300
301
0
  Assert(natts <= tupdesc->natts);
302
303
0
  desc = CreateTemplateTupleDesc(natts);
304
305
  /* Flat-copy the attribute array */
306
0
  memcpy(TupleDescAttr(desc, 0),
307
0
       TupleDescAttr(tupdesc, 0),
308
0
       desc->natts * sizeof(FormData_pg_attribute));
309
310
  /*
311
   * Since we're not copying constraints and defaults, clear fields
312
   * associated with them.
313
   */
314
0
  for (i = 0; i < desc->natts; i++)
315
0
  {
316
0
    Form_pg_attribute att = TupleDescAttr(desc, i);
317
318
0
    att->attnotnull = false;
319
0
    att->atthasdef = false;
320
0
    att->atthasmissing = false;
321
0
    att->attidentity = '\0';
322
0
    att->attgenerated = '\0';
323
324
0
    populate_compact_attribute(desc, i);
325
0
  }
326
327
  /* We can copy the tuple type identification, too */
328
0
  desc->tdtypeid = tupdesc->tdtypeid;
329
0
  desc->tdtypmod = tupdesc->tdtypmod;
330
331
0
  return desc;
332
0
}
333
334
/*
335
 * CreateTupleDescCopyConstr
336
 *    This function creates a new TupleDesc by copying from an existing
337
 *    TupleDesc (including its constraints and defaults).
338
 */
339
TupleDesc
340
CreateTupleDescCopyConstr(TupleDesc tupdesc)
341
0
{
342
0
  TupleDesc desc;
343
0
  TupleConstr *constr = tupdesc->constr;
344
0
  int     i;
345
346
0
  desc = CreateTemplateTupleDesc(tupdesc->natts);
347
348
  /* Flat-copy the attribute array */
349
0
  memcpy(TupleDescAttr(desc, 0),
350
0
       TupleDescAttr(tupdesc, 0),
351
0
       desc->natts * sizeof(FormData_pg_attribute));
352
353
0
  for (i = 0; i < desc->natts; i++)
354
0
  {
355
0
    populate_compact_attribute(desc, i);
356
357
0
    TupleDescCompactAttr(desc, i)->attnullability =
358
0
      TupleDescCompactAttr(tupdesc, i)->attnullability;
359
0
  }
360
361
  /* Copy the TupleConstr data structure, if any */
362
0
  if (constr)
363
0
  {
364
0
    TupleConstr *cpy = (TupleConstr *) palloc0(sizeof(TupleConstr));
365
366
0
    cpy->has_not_null = constr->has_not_null;
367
0
    cpy->has_generated_stored = constr->has_generated_stored;
368
0
    cpy->has_generated_virtual = constr->has_generated_virtual;
369
370
0
    if ((cpy->num_defval = constr->num_defval) > 0)
371
0
    {
372
0
      cpy->defval = (AttrDefault *) palloc(cpy->num_defval * sizeof(AttrDefault));
373
0
      memcpy(cpy->defval, constr->defval, cpy->num_defval * sizeof(AttrDefault));
374
0
      for (i = cpy->num_defval - 1; i >= 0; i--)
375
0
        cpy->defval[i].adbin = pstrdup(constr->defval[i].adbin);
376
0
    }
377
378
0
    if (constr->missing)
379
0
    {
380
0
      cpy->missing = (AttrMissing *) palloc(tupdesc->natts * sizeof(AttrMissing));
381
0
      memcpy(cpy->missing, constr->missing, tupdesc->natts * sizeof(AttrMissing));
382
0
      for (i = tupdesc->natts - 1; i >= 0; i--)
383
0
      {
384
0
        if (constr->missing[i].am_present)
385
0
        {
386
0
          CompactAttribute *attr = TupleDescCompactAttr(tupdesc, i);
387
388
0
          cpy->missing[i].am_value = datumCopy(constr->missing[i].am_value,
389
0
                             attr->attbyval,
390
0
                             attr->attlen);
391
0
        }
392
0
      }
393
0
    }
394
395
0
    if ((cpy->num_check = constr->num_check) > 0)
396
0
    {
397
0
      cpy->check = (ConstrCheck *) palloc(cpy->num_check * sizeof(ConstrCheck));
398
0
      memcpy(cpy->check, constr->check, cpy->num_check * sizeof(ConstrCheck));
399
0
      for (i = cpy->num_check - 1; i >= 0; i--)
400
0
      {
401
0
        cpy->check[i].ccname = pstrdup(constr->check[i].ccname);
402
0
        cpy->check[i].ccbin = pstrdup(constr->check[i].ccbin);
403
0
        cpy->check[i].ccenforced = constr->check[i].ccenforced;
404
0
        cpy->check[i].ccvalid = constr->check[i].ccvalid;
405
0
        cpy->check[i].ccnoinherit = constr->check[i].ccnoinherit;
406
0
      }
407
0
    }
408
409
0
    desc->constr = cpy;
410
0
  }
411
412
  /* We can copy the tuple type identification, too */
413
0
  desc->tdtypeid = tupdesc->tdtypeid;
414
0
  desc->tdtypmod = tupdesc->tdtypmod;
415
416
0
  return desc;
417
0
}
418
419
/*
420
 * TupleDescCopy
421
 *    Copy a tuple descriptor into caller-supplied memory.
422
 *    The memory may be shared memory mapped at any address, and must
423
 *    be sufficient to hold TupleDescSize(src) bytes.
424
 *
425
 * !!! Constraints and defaults are not copied !!!
426
 */
427
void
428
TupleDescCopy(TupleDesc dst, TupleDesc src)
429
0
{
430
0
  int     i;
431
432
  /* Flat-copy the header and attribute arrays */
433
0
  memcpy(dst, src, TupleDescSize(src));
434
435
  /*
436
   * Since we're not copying constraints and defaults, clear fields
437
   * associated with them.
438
   */
439
0
  for (i = 0; i < dst->natts; i++)
440
0
  {
441
0
    Form_pg_attribute att = TupleDescAttr(dst, i);
442
443
0
    att->attnotnull = false;
444
0
    att->atthasdef = false;
445
0
    att->atthasmissing = false;
446
0
    att->attidentity = '\0';
447
0
    att->attgenerated = '\0';
448
449
0
    populate_compact_attribute(dst, i);
450
0
  }
451
0
  dst->constr = NULL;
452
453
  /*
454
   * Also, assume the destination is not to be ref-counted.  (Copying the
455
   * source's refcount would be wrong in any case.)
456
   */
457
0
  dst->tdrefcount = -1;
458
0
}
459
460
/*
461
 * TupleDescCopyEntry
462
 *    This function copies a single attribute structure from one tuple
463
 *    descriptor to another.
464
 *
465
 * !!! Constraints and defaults are not copied !!!
466
 */
467
void
468
TupleDescCopyEntry(TupleDesc dst, AttrNumber dstAttno,
469
           TupleDesc src, AttrNumber srcAttno)
470
0
{
471
0
  Form_pg_attribute dstAtt = TupleDescAttr(dst, dstAttno - 1);
472
0
  Form_pg_attribute srcAtt = TupleDescAttr(src, srcAttno - 1);
473
474
  /*
475
   * sanity checks
476
   */
477
0
  Assert(src);
478
0
  Assert(dst);
479
0
  Assert(srcAttno >= 1);
480
0
  Assert(srcAttno <= src->natts);
481
0
  Assert(dstAttno >= 1);
482
0
  Assert(dstAttno <= dst->natts);
483
484
0
  memcpy(dstAtt, srcAtt, ATTRIBUTE_FIXED_PART_SIZE);
485
486
0
  dstAtt->attnum = dstAttno;
487
488
  /* since we're not copying constraints or defaults, clear these */
489
0
  dstAtt->attnotnull = false;
490
0
  dstAtt->atthasdef = false;
491
0
  dstAtt->atthasmissing = false;
492
0
  dstAtt->attidentity = '\0';
493
0
  dstAtt->attgenerated = '\0';
494
495
0
  populate_compact_attribute(dst, dstAttno - 1);
496
0
}
497
498
/*
499
 * Free a TupleDesc including all substructure
500
 */
501
void
502
FreeTupleDesc(TupleDesc tupdesc)
503
0
{
504
0
  int     i;
505
506
  /*
507
   * Possibly this should assert tdrefcount == 0, to disallow explicit
508
   * freeing of un-refcounted tupdescs?
509
   */
510
0
  Assert(tupdesc->tdrefcount <= 0);
511
512
0
  if (tupdesc->constr)
513
0
  {
514
0
    if (tupdesc->constr->num_defval > 0)
515
0
    {
516
0
      AttrDefault *attrdef = tupdesc->constr->defval;
517
518
0
      for (i = tupdesc->constr->num_defval - 1; i >= 0; i--)
519
0
        pfree(attrdef[i].adbin);
520
0
      pfree(attrdef);
521
0
    }
522
0
    if (tupdesc->constr->missing)
523
0
    {
524
0
      AttrMissing *attrmiss = tupdesc->constr->missing;
525
526
0
      for (i = tupdesc->natts - 1; i >= 0; i--)
527
0
      {
528
0
        if (attrmiss[i].am_present
529
0
          && !TupleDescAttr(tupdesc, i)->attbyval)
530
0
          pfree(DatumGetPointer(attrmiss[i].am_value));
531
0
      }
532
0
      pfree(attrmiss);
533
0
    }
534
0
    if (tupdesc->constr->num_check > 0)
535
0
    {
536
0
      ConstrCheck *check = tupdesc->constr->check;
537
538
0
      for (i = tupdesc->constr->num_check - 1; i >= 0; i--)
539
0
      {
540
0
        pfree(check[i].ccname);
541
0
        pfree(check[i].ccbin);
542
0
      }
543
0
      pfree(check);
544
0
    }
545
0
    pfree(tupdesc->constr);
546
0
  }
547
548
0
  pfree(tupdesc);
549
0
}
550
551
/*
552
 * Increment the reference count of a tupdesc, and log the reference in
553
 * CurrentResourceOwner.
554
 *
555
 * Do not apply this to tupdescs that are not being refcounted.  (Use the
556
 * macro PinTupleDesc for tupdescs of uncertain status.)
557
 */
558
void
559
IncrTupleDescRefCount(TupleDesc tupdesc)
560
0
{
561
0
  Assert(tupdesc->tdrefcount >= 0);
562
563
0
  ResourceOwnerEnlarge(CurrentResourceOwner);
564
0
  tupdesc->tdrefcount++;
565
0
  ResourceOwnerRememberTupleDesc(CurrentResourceOwner, tupdesc);
566
0
}
567
568
/*
569
 * Decrement the reference count of a tupdesc, remove the corresponding
570
 * reference from CurrentResourceOwner, and free the tupdesc if no more
571
 * references remain.
572
 *
573
 * Do not apply this to tupdescs that are not being refcounted.  (Use the
574
 * macro ReleaseTupleDesc for tupdescs of uncertain status.)
575
 */
576
void
577
DecrTupleDescRefCount(TupleDesc tupdesc)
578
0
{
579
0
  Assert(tupdesc->tdrefcount > 0);
580
581
0
  ResourceOwnerForgetTupleDesc(CurrentResourceOwner, tupdesc);
582
0
  if (--tupdesc->tdrefcount == 0)
583
0
    FreeTupleDesc(tupdesc);
584
0
}
585
586
/*
587
 * Compare two TupleDesc structures for logical equality
588
 */
589
bool
590
equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
591
0
{
592
0
  int     i,
593
0
        n;
594
595
0
  if (tupdesc1->natts != tupdesc2->natts)
596
0
    return false;
597
0
  if (tupdesc1->tdtypeid != tupdesc2->tdtypeid)
598
0
    return false;
599
600
  /* tdtypmod and tdrefcount are not checked */
601
602
0
  for (i = 0; i < tupdesc1->natts; i++)
603
0
  {
604
0
    Form_pg_attribute attr1 = TupleDescAttr(tupdesc1, i);
605
0
    Form_pg_attribute attr2 = TupleDescAttr(tupdesc2, i);
606
607
    /*
608
     * We do not need to check every single field here: we can disregard
609
     * attrelid and attnum (which were used to place the row in the attrs
610
     * array in the first place).  It might look like we could dispense
611
     * with checking attlen/attbyval/attalign, since these are derived
612
     * from atttypid; but in the case of dropped columns we must check
613
     * them (since atttypid will be zero for all dropped columns) and in
614
     * general it seems safer to check them always.
615
     *
616
     * We intentionally ignore atthasmissing, since that's not very
617
     * relevant in tupdescs, which lack the attmissingval field.
618
     */
619
0
    if (strcmp(NameStr(attr1->attname), NameStr(attr2->attname)) != 0)
620
0
      return false;
621
0
    if (attr1->atttypid != attr2->atttypid)
622
0
      return false;
623
0
    if (attr1->attlen != attr2->attlen)
624
0
      return false;
625
0
    if (attr1->attndims != attr2->attndims)
626
0
      return false;
627
0
    if (attr1->atttypmod != attr2->atttypmod)
628
0
      return false;
629
0
    if (attr1->attbyval != attr2->attbyval)
630
0
      return false;
631
0
    if (attr1->attalign != attr2->attalign)
632
0
      return false;
633
0
    if (attr1->attstorage != attr2->attstorage)
634
0
      return false;
635
0
    if (attr1->attcompression != attr2->attcompression)
636
0
      return false;
637
0
    if (attr1->attnotnull != attr2->attnotnull)
638
0
      return false;
639
640
    /*
641
     * When the column has a not-null constraint, we also need to consider
642
     * its validity aspect, which only manifests in CompactAttribute->
643
     * attnullability, so verify that.
644
     */
645
0
    if (attr1->attnotnull)
646
0
    {
647
0
      CompactAttribute *cattr1 = TupleDescCompactAttr(tupdesc1, i);
648
0
      CompactAttribute *cattr2 = TupleDescCompactAttr(tupdesc2, i);
649
650
0
      Assert(cattr1->attnullability != ATTNULLABLE_UNKNOWN);
651
0
      Assert((cattr1->attnullability == ATTNULLABLE_UNKNOWN) ==
652
0
           (cattr2->attnullability == ATTNULLABLE_UNKNOWN));
653
654
0
      if (cattr1->attnullability != cattr2->attnullability)
655
0
        return false;
656
0
    }
657
0
    if (attr1->atthasdef != attr2->atthasdef)
658
0
      return false;
659
0
    if (attr1->attidentity != attr2->attidentity)
660
0
      return false;
661
0
    if (attr1->attgenerated != attr2->attgenerated)
662
0
      return false;
663
0
    if (attr1->attisdropped != attr2->attisdropped)
664
0
      return false;
665
0
    if (attr1->attislocal != attr2->attislocal)
666
0
      return false;
667
0
    if (attr1->attinhcount != attr2->attinhcount)
668
0
      return false;
669
0
    if (attr1->attcollation != attr2->attcollation)
670
0
      return false;
671
    /* variable-length fields are not even present... */
672
0
  }
673
674
0
  if (tupdesc1->constr != NULL)
675
0
  {
676
0
    TupleConstr *constr1 = tupdesc1->constr;
677
0
    TupleConstr *constr2 = tupdesc2->constr;
678
679
0
    if (constr2 == NULL)
680
0
      return false;
681
0
    if (constr1->has_not_null != constr2->has_not_null)
682
0
      return false;
683
0
    if (constr1->has_generated_stored != constr2->has_generated_stored)
684
0
      return false;
685
0
    if (constr1->has_generated_virtual != constr2->has_generated_virtual)
686
0
      return false;
687
0
    n = constr1->num_defval;
688
0
    if (n != (int) constr2->num_defval)
689
0
      return false;
690
    /* We assume here that both AttrDefault arrays are in adnum order */
691
0
    for (i = 0; i < n; i++)
692
0
    {
693
0
      AttrDefault *defval1 = constr1->defval + i;
694
0
      AttrDefault *defval2 = constr2->defval + i;
695
696
0
      if (defval1->adnum != defval2->adnum)
697
0
        return false;
698
0
      if (strcmp(defval1->adbin, defval2->adbin) != 0)
699
0
        return false;
700
0
    }
701
0
    if (constr1->missing)
702
0
    {
703
0
      if (!constr2->missing)
704
0
        return false;
705
0
      for (i = 0; i < tupdesc1->natts; i++)
706
0
      {
707
0
        AttrMissing *missval1 = constr1->missing + i;
708
0
        AttrMissing *missval2 = constr2->missing + i;
709
710
0
        if (missval1->am_present != missval2->am_present)
711
0
          return false;
712
0
        if (missval1->am_present)
713
0
        {
714
0
          CompactAttribute *missatt1 = TupleDescCompactAttr(tupdesc1, i);
715
716
0
          if (!datumIsEqual(missval1->am_value, missval2->am_value,
717
0
                    missatt1->attbyval, missatt1->attlen))
718
0
            return false;
719
0
        }
720
0
      }
721
0
    }
722
0
    else if (constr2->missing)
723
0
      return false;
724
0
    n = constr1->num_check;
725
0
    if (n != (int) constr2->num_check)
726
0
      return false;
727
728
    /*
729
     * Similarly, we rely here on the ConstrCheck entries being sorted by
730
     * name.  If there are duplicate names, the outcome of the comparison
731
     * is uncertain, but that should not happen.
732
     */
733
0
    for (i = 0; i < n; i++)
734
0
    {
735
0
      ConstrCheck *check1 = constr1->check + i;
736
0
      ConstrCheck *check2 = constr2->check + i;
737
738
0
      if (!(strcmp(check1->ccname, check2->ccname) == 0 &&
739
0
          strcmp(check1->ccbin, check2->ccbin) == 0 &&
740
0
          check1->ccenforced == check2->ccenforced &&
741
0
          check1->ccvalid == check2->ccvalid &&
742
0
          check1->ccnoinherit == check2->ccnoinherit))
743
0
        return false;
744
0
    }
745
0
  }
746
0
  else if (tupdesc2->constr != NULL)
747
0
    return false;
748
0
  return true;
749
0
}
750
751
/*
752
 * equalRowTypes
753
 *
754
 * This determines whether two tuple descriptors have equal row types.  This
755
 * only checks those fields in pg_attribute that are applicable for row types,
756
 * while ignoring those fields that define the physical row storage or those
757
 * that define table column metadata.
758
 *
759
 * Specifically, this checks:
760
 *
761
 * - same number of attributes
762
 * - same composite type ID (but could both be zero)
763
 * - corresponding attributes (in order) have same the name, type, typmod,
764
 *   collation
765
 *
766
 * This is used to check whether two record types are compatible, whether
767
 * function return row types are the same, and other similar situations.
768
 *
769
 * (XXX There was some discussion whether attndims should be checked here, but
770
 * for now it has been decided not to.)
771
 *
772
 * Note: We deliberately do not check the tdtypmod field.  This allows
773
 * typcache.c to use this routine to see if a cached record type matches a
774
 * requested type.
775
 */
776
bool
777
equalRowTypes(TupleDesc tupdesc1, TupleDesc tupdesc2)
778
0
{
779
0
  if (tupdesc1->natts != tupdesc2->natts)
780
0
    return false;
781
0
  if (tupdesc1->tdtypeid != tupdesc2->tdtypeid)
782
0
    return false;
783
784
0
  for (int i = 0; i < tupdesc1->natts; i++)
785
0
  {
786
0
    Form_pg_attribute attr1 = TupleDescAttr(tupdesc1, i);
787
0
    Form_pg_attribute attr2 = TupleDescAttr(tupdesc2, i);
788
789
0
    if (strcmp(NameStr(attr1->attname), NameStr(attr2->attname)) != 0)
790
0
      return false;
791
0
    if (attr1->atttypid != attr2->atttypid)
792
0
      return false;
793
0
    if (attr1->atttypmod != attr2->atttypmod)
794
0
      return false;
795
0
    if (attr1->attcollation != attr2->attcollation)
796
0
      return false;
797
798
    /* Record types derived from tables could have dropped fields. */
799
0
    if (attr1->attisdropped != attr2->attisdropped)
800
0
      return false;
801
0
  }
802
803
0
  return true;
804
0
}
805
806
/*
807
 * hashRowType
808
 *
809
 * If two tuple descriptors would be considered equal by equalRowTypes()
810
 * then their hash value will be equal according to this function.
811
 */
812
uint32
813
hashRowType(TupleDesc desc)
814
0
{
815
0
  uint32    s;
816
0
  int     i;
817
818
0
  s = hash_combine(0, hash_bytes_uint32(desc->natts));
819
0
  s = hash_combine(s, hash_bytes_uint32(desc->tdtypeid));
820
0
  for (i = 0; i < desc->natts; ++i)
821
0
    s = hash_combine(s, hash_bytes_uint32(TupleDescAttr(desc, i)->atttypid));
822
823
0
  return s;
824
0
}
825
826
/*
827
 * TupleDescInitEntry
828
 *    This function initializes a single attribute structure in
829
 *    a previously allocated tuple descriptor.
830
 *
831
 * If attributeName is NULL, the attname field is set to an empty string
832
 * (this is for cases where we don't know or need a name for the field).
833
 * Also, some callers use this function to change the datatype-related fields
834
 * in an existing tupdesc; they pass attributeName = NameStr(att->attname)
835
 * to indicate that the attname field shouldn't be modified.
836
 *
837
 * Note that attcollation is set to the default for the specified datatype.
838
 * If a nondefault collation is needed, insert it afterwards using
839
 * TupleDescInitEntryCollation.
840
 */
841
void
842
TupleDescInitEntry(TupleDesc desc,
843
           AttrNumber attributeNumber,
844
           const char *attributeName,
845
           Oid oidtypeid,
846
           int32 typmod,
847
           int attdim)
848
0
{
849
0
  HeapTuple tuple;
850
0
  Form_pg_type typeForm;
851
0
  Form_pg_attribute att;
852
853
  /*
854
   * sanity checks
855
   */
856
0
  Assert(desc);
857
0
  Assert(attributeNumber >= 1);
858
0
  Assert(attributeNumber <= desc->natts);
859
0
  Assert(attdim >= 0);
860
0
  Assert(attdim <= PG_INT16_MAX);
861
862
  /*
863
   * initialize the attribute fields
864
   */
865
0
  att = TupleDescAttr(desc, attributeNumber - 1);
866
867
0
  att->attrelid = 0;      /* dummy value */
868
869
  /*
870
   * Note: attributeName can be NULL, because the planner doesn't always
871
   * fill in valid resname values in targetlists, particularly for resjunk
872
   * attributes. Also, do nothing if caller wants to re-use the old attname.
873
   */
874
0
  if (attributeName == NULL)
875
0
    MemSet(NameStr(att->attname), 0, NAMEDATALEN);
876
0
  else if (attributeName != NameStr(att->attname))
877
0
    namestrcpy(&(att->attname), attributeName);
878
879
0
  att->atttypmod = typmod;
880
881
0
  att->attnum = attributeNumber;
882
0
  att->attndims = attdim;
883
884
0
  att->attnotnull = false;
885
0
  att->atthasdef = false;
886
0
  att->atthasmissing = false;
887
0
  att->attidentity = '\0';
888
0
  att->attgenerated = '\0';
889
0
  att->attisdropped = false;
890
0
  att->attislocal = true;
891
0
  att->attinhcount = 0;
892
  /* variable-length fields are not present in tupledescs */
893
894
0
  tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(oidtypeid));
895
0
  if (!HeapTupleIsValid(tuple))
896
0
    elog(ERROR, "cache lookup failed for type %u", oidtypeid);
897
0
  typeForm = (Form_pg_type) GETSTRUCT(tuple);
898
899
0
  att->atttypid = oidtypeid;
900
0
  att->attlen = typeForm->typlen;
901
0
  att->attbyval = typeForm->typbyval;
902
0
  att->attalign = typeForm->typalign;
903
0
  att->attstorage = typeForm->typstorage;
904
0
  att->attcompression = InvalidCompressionMethod;
905
0
  att->attcollation = typeForm->typcollation;
906
907
0
  populate_compact_attribute(desc, attributeNumber - 1);
908
909
0
  ReleaseSysCache(tuple);
910
0
}
911
912
/*
913
 * TupleDescInitBuiltinEntry
914
 *    Initialize a tuple descriptor without catalog access.  Only
915
 *    a limited range of builtin types are supported.
916
 */
917
void
918
TupleDescInitBuiltinEntry(TupleDesc desc,
919
              AttrNumber attributeNumber,
920
              const char *attributeName,
921
              Oid oidtypeid,
922
              int32 typmod,
923
              int attdim)
924
0
{
925
0
  Form_pg_attribute att;
926
927
  /* sanity checks */
928
0
  Assert(desc);
929
0
  Assert(attributeNumber >= 1);
930
0
  Assert(attributeNumber <= desc->natts);
931
0
  Assert(attdim >= 0);
932
0
  Assert(attdim <= PG_INT16_MAX);
933
934
  /* initialize the attribute fields */
935
0
  att = TupleDescAttr(desc, attributeNumber - 1);
936
0
  att->attrelid = 0;      /* dummy value */
937
938
  /* unlike TupleDescInitEntry, we require an attribute name */
939
0
  Assert(attributeName != NULL);
940
0
  namestrcpy(&(att->attname), attributeName);
941
942
0
  att->atttypmod = typmod;
943
944
0
  att->attnum = attributeNumber;
945
0
  att->attndims = attdim;
946
947
0
  att->attnotnull = false;
948
0
  att->atthasdef = false;
949
0
  att->atthasmissing = false;
950
0
  att->attidentity = '\0';
951
0
  att->attgenerated = '\0';
952
0
  att->attisdropped = false;
953
0
  att->attislocal = true;
954
0
  att->attinhcount = 0;
955
  /* variable-length fields are not present in tupledescs */
956
957
0
  att->atttypid = oidtypeid;
958
959
  /*
960
   * Our goal here is to support just enough types to let basic builtin
961
   * commands work without catalog access - e.g. so that we can do certain
962
   * things even in processes that are not connected to a database.
963
   */
964
0
  switch (oidtypeid)
965
0
  {
966
0
    case TEXTOID:
967
0
    case TEXTARRAYOID:
968
0
      att->attlen = -1;
969
0
      att->attbyval = false;
970
0
      att->attalign = TYPALIGN_INT;
971
0
      att->attstorage = TYPSTORAGE_EXTENDED;
972
0
      att->attcompression = InvalidCompressionMethod;
973
0
      att->attcollation = DEFAULT_COLLATION_OID;
974
0
      break;
975
976
0
    case BOOLOID:
977
0
      att->attlen = 1;
978
0
      att->attbyval = true;
979
0
      att->attalign = TYPALIGN_CHAR;
980
0
      att->attstorage = TYPSTORAGE_PLAIN;
981
0
      att->attcompression = InvalidCompressionMethod;
982
0
      att->attcollation = InvalidOid;
983
0
      break;
984
985
0
    case INT4OID:
986
0
      att->attlen = 4;
987
0
      att->attbyval = true;
988
0
      att->attalign = TYPALIGN_INT;
989
0
      att->attstorage = TYPSTORAGE_PLAIN;
990
0
      att->attcompression = InvalidCompressionMethod;
991
0
      att->attcollation = InvalidOid;
992
0
      break;
993
994
0
    case INT8OID:
995
0
      att->attlen = 8;
996
0
      att->attbyval = true;
997
0
      att->attalign = TYPALIGN_DOUBLE;
998
0
      att->attstorage = TYPSTORAGE_PLAIN;
999
0
      att->attcompression = InvalidCompressionMethod;
1000
0
      att->attcollation = InvalidOid;
1001
0
      break;
1002
1003
0
    case OIDOID:
1004
0
      att->attlen = 4;
1005
0
      att->attbyval = true;
1006
0
      att->attalign = TYPALIGN_INT;
1007
0
      att->attstorage = TYPSTORAGE_PLAIN;
1008
0
      att->attcompression = InvalidCompressionMethod;
1009
0
      att->attcollation = InvalidOid;
1010
0
      break;
1011
1012
0
    default:
1013
0
      elog(ERROR, "unsupported type %u", oidtypeid);
1014
0
  }
1015
1016
0
  populate_compact_attribute(desc, attributeNumber - 1);
1017
0
}
1018
1019
/*
1020
 * TupleDescInitEntryCollation
1021
 *
1022
 * Assign a nondefault collation to a previously initialized tuple descriptor
1023
 * entry.
1024
 */
1025
void
1026
TupleDescInitEntryCollation(TupleDesc desc,
1027
              AttrNumber attributeNumber,
1028
              Oid collationid)
1029
0
{
1030
  /*
1031
   * sanity checks
1032
   */
1033
0
  Assert(desc);
1034
0
  Assert(attributeNumber >= 1);
1035
0
  Assert(attributeNumber <= desc->natts);
1036
1037
0
  TupleDescAttr(desc, attributeNumber - 1)->attcollation = collationid;
1038
0
}
1039
1040
/*
1041
 * BuildDescFromLists
1042
 *
1043
 * Build a TupleDesc given lists of column names (as String nodes),
1044
 * column type OIDs, typmods, and collation OIDs.
1045
 *
1046
 * No constraints are generated.
1047
 *
1048
 * This is for use with functions returning RECORD.
1049
 */
1050
TupleDesc
1051
BuildDescFromLists(const List *names, const List *types, const List *typmods, const List *collations)
1052
0
{
1053
0
  int     natts;
1054
0
  AttrNumber  attnum;
1055
0
  ListCell   *l1;
1056
0
  ListCell   *l2;
1057
0
  ListCell   *l3;
1058
0
  ListCell   *l4;
1059
0
  TupleDesc desc;
1060
1061
0
  natts = list_length(names);
1062
0
  Assert(natts == list_length(types));
1063
0
  Assert(natts == list_length(typmods));
1064
0
  Assert(natts == list_length(collations));
1065
1066
  /*
1067
   * allocate a new tuple descriptor
1068
   */
1069
0
  desc = CreateTemplateTupleDesc(natts);
1070
1071
0
  attnum = 0;
1072
0
  forfour(l1, names, l2, types, l3, typmods, l4, collations)
1073
0
  {
1074
0
    char     *attname = strVal(lfirst(l1));
1075
0
    Oid     atttypid = lfirst_oid(l2);
1076
0
    int32   atttypmod = lfirst_int(l3);
1077
0
    Oid     attcollation = lfirst_oid(l4);
1078
1079
0
    attnum++;
1080
1081
0
    TupleDescInitEntry(desc, attnum, attname, atttypid, atttypmod, 0);
1082
0
    TupleDescInitEntryCollation(desc, attnum, attcollation);
1083
0
  }
1084
1085
0
  return desc;
1086
0
}
1087
1088
/*
1089
 * Get default expression (or NULL if none) for the given attribute number.
1090
 */
1091
Node *
1092
TupleDescGetDefault(TupleDesc tupdesc, AttrNumber attnum)
1093
0
{
1094
0
  Node     *result = NULL;
1095
1096
0
  if (tupdesc->constr)
1097
0
  {
1098
0
    AttrDefault *attrdef = tupdesc->constr->defval;
1099
1100
0
    for (int i = 0; i < tupdesc->constr->num_defval; i++)
1101
0
    {
1102
0
      if (attrdef[i].adnum == attnum)
1103
0
      {
1104
0
        result = stringToNode(attrdef[i].adbin);
1105
0
        break;
1106
0
      }
1107
0
    }
1108
0
  }
1109
1110
0
  return result;
1111
0
}
1112
1113
/* ResourceOwner callbacks */
1114
1115
static void
1116
ResOwnerReleaseTupleDesc(Datum res)
1117
0
{
1118
0
  TupleDesc tupdesc = (TupleDesc) DatumGetPointer(res);
1119
1120
  /* Like DecrTupleDescRefCount, but don't call ResourceOwnerForget() */
1121
0
  Assert(tupdesc->tdrefcount > 0);
1122
0
  if (--tupdesc->tdrefcount == 0)
1123
0
    FreeTupleDesc(tupdesc);
1124
0
}
1125
1126
static char *
1127
ResOwnerPrintTupleDesc(Datum res)
1128
0
{
1129
0
  TupleDesc tupdesc = (TupleDesc) DatumGetPointer(res);
1130
1131
0
  return psprintf("TupleDesc %p (%u,%d)",
1132
0
          tupdesc, tupdesc->tdtypeid, tupdesc->tdtypmod);
1133
0
}