YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

/Users/deen/code/yugabyte-db/src/postgres/src/backend/commands/typecmds.c
Line
Count
Source (jump to first uncovered line)
1
/*-------------------------------------------------------------------------
2
 *
3
 * typecmds.c
4
 *    Routines for SQL commands that manipulate types (and domains).
5
 *
6
 * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
7
 * Portions Copyright (c) 1994, Regents of the University of California
8
 *
9
 *
10
 * IDENTIFICATION
11
 *    src/backend/commands/typecmds.c
12
 *
13
 * DESCRIPTION
14
 *    The "DefineFoo" routines take the parse tree and pick out the
15
 *    appropriate arguments/flags, passing the results to the
16
 *    corresponding "FooDefine" routines (in src/catalog) that do
17
 *    the actual catalog-munging.  These routines also verify permission
18
 *    of the user to execute the command.
19
 *
20
 * NOTES
21
 *    These things must be defined and committed in the following order:
22
 *    "create function":
23
 *        input/output, recv/send functions
24
 *    "create type":
25
 *        type
26
 *    "create operator":
27
 *        operators
28
 *
29
 *
30
 *-------------------------------------------------------------------------
31
 */
32
#include "postgres.h"
33
34
#include "access/htup_details.h"
35
#include "access/xact.h"
36
#include "catalog/binary_upgrade.h"
37
#include "catalog/catalog.h"
38
#include "catalog/heap.h"
39
#include "catalog/objectaccess.h"
40
#include "catalog/pg_am.h"
41
#include "catalog/pg_authid.h"
42
#include "catalog/pg_collation.h"
43
#include "catalog/pg_constraint.h"
44
#include "catalog/pg_depend.h"
45
#include "catalog/pg_enum.h"
46
#include "catalog/pg_language.h"
47
#include "catalog/pg_namespace.h"
48
#include "catalog/pg_proc.h"
49
#include "catalog/pg_range.h"
50
#include "catalog/pg_type.h"
51
#include "commands/defrem.h"
52
#include "commands/extension.h"
53
#include "commands/tablecmds.h"
54
#include "commands/typecmds.h"
55
#include "executor/executor.h"
56
#include "miscadmin.h"
57
#include "nodes/makefuncs.h"
58
#include "optimizer/var.h"
59
#include "parser/parse_coerce.h"
60
#include "parser/parse_collate.h"
61
#include "parser/parse_expr.h"
62
#include "parser/parse_func.h"
63
#include "parser/parse_type.h"
64
#include "utils/builtins.h"
65
#include "utils/fmgroids.h"
66
#include "utils/lsyscache.h"
67
#include "utils/memutils.h"
68
#include "utils/rel.h"
69
#include "utils/ruleutils.h"
70
#include "utils/snapmgr.h"
71
#include "utils/syscache.h"
72
73
/*  YB includes. */
74
#include "pg_yb_utils.h"
75
76
/* result structure for get_rels_with_domain() */
77
typedef struct
78
{
79
  Relation  rel;      /* opened and locked relation */
80
  int     natts;      /* number of attributes of interest */
81
  int      *atts;     /* attribute numbers */
82
  /* atts[] is of allocated length RelationGetNumberOfAttributes(rel) */
83
} RelToCheck;
84
85
/* Potentially set by pg_upgrade_support functions */
86
Oid     binary_upgrade_next_array_pg_type_oid = InvalidOid;
87
88
static void makeRangeConstructors(const char *name, Oid namespace,
89
            Oid rangeOid, Oid subtype);
90
static Oid  findTypeInputFunction(List *procname, Oid typeOid);
91
static Oid  findTypeOutputFunction(List *procname, Oid typeOid);
92
static Oid  findTypeReceiveFunction(List *procname, Oid typeOid);
93
static Oid  findTypeSendFunction(List *procname, Oid typeOid);
94
static Oid  findTypeTypmodinFunction(List *procname);
95
static Oid  findTypeTypmodoutFunction(List *procname);
96
static Oid  findTypeAnalyzeFunction(List *procname, Oid typeOid);
97
static Oid  findRangeSubOpclass(List *opcname, Oid subtype);
98
static Oid  findRangeCanonicalFunction(List *procname, Oid typeOid);
99
static Oid  findRangeSubtypeDiffFunction(List *procname, Oid subtype);
100
static void validateDomainConstraint(Oid domainoid, char *ccbin);
101
static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
102
static void checkEnumOwner(HeapTuple tup);
103
static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
104
          Oid baseTypeOid,
105
          int typMod, Constraint *constr,
106
          const char *domainName, ObjectAddress *constrAddr);
107
static Node *replace_domain_constraint_value(ParseState *pstate,
108
                ColumnRef *cref);
109
110
111
/*
112
 * DefineType
113
 *    Registers a new base type.
114
 */
115
ObjectAddress
116
DefineType(ParseState *pstate, List *names, List *parameters)
117
201
{
118
201
  char     *typeName;
119
201
  Oid     typeNamespace;
120
201
  int16   internalLength = -1;  /* default: variable-length */
121
201
  List     *inputName = NIL;
122
201
  List     *outputName = NIL;
123
201
  List     *receiveName = NIL;
124
201
  List     *sendName = NIL;
125
201
  List     *typmodinName = NIL;
126
201
  List     *typmodoutName = NIL;
127
201
  List     *analyzeName = NIL;
128
201
  char    category = TYPCATEGORY_USER;
129
201
  bool    preferred = false;
130
201
  char    delimiter = DEFAULT_TYPDELIM;
131
201
  Oid     elemType = InvalidOid;
132
201
  char     *defaultValue = NULL;
133
201
  bool    byValue = false;
134
201
  char    alignment = 'i';  /* default alignment */
135
201
  char    storage = 'p';  /* default TOAST storage method */
136
201
  Oid     collation = InvalidOid;
137
201
  DefElem    *likeTypeEl = NULL;
138
201
  DefElem    *internalLengthEl = NULL;
139
201
  DefElem    *inputNameEl = NULL;
140
201
  DefElem    *outputNameEl = NULL;
141
201
  DefElem    *receiveNameEl = NULL;
142
201
  DefElem    *sendNameEl = NULL;
143
201
  DefElem    *typmodinNameEl = NULL;
144
201
  DefElem    *typmodoutNameEl = NULL;
145
201
  DefElem    *analyzeNameEl = NULL;
146
201
  DefElem    *categoryEl = NULL;
147
201
  DefElem    *preferredEl = NULL;
148
201
  DefElem    *delimiterEl = NULL;
149
201
  DefElem    *elemTypeEl = NULL;
150
201
  DefElem    *defaultValueEl = NULL;
151
201
  DefElem    *byValueEl = NULL;
152
201
  DefElem    *alignmentEl = NULL;
153
201
  DefElem    *storageEl = NULL;
154
201
  DefElem    *collatableEl = NULL;
155
201
  Oid     inputOid;
156
201
  Oid     outputOid;
157
201
  Oid     receiveOid = InvalidOid;
158
201
  Oid     sendOid = InvalidOid;
159
201
  Oid     typmodinOid = InvalidOid;
160
201
  Oid     typmodoutOid = InvalidOid;
161
201
  Oid     analyzeOid = InvalidOid;
162
201
  char     *array_type;
163
201
  Oid     array_oid;
164
201
  Oid     typoid;
165
201
  Oid     resulttype;
166
201
  ListCell   *pl;
167
201
  ObjectAddress address;
168
169
  /*
170
   * As of Postgres 8.4, we require superuser privilege to create a base
171
   * type.  This is simple paranoia: there are too many ways to mess up the
172
   * system with an incorrect type definition (for instance, representation
173
   * parameters that don't match what the C code expects).  In practice it
174
   * takes superuser privilege to create the I/O functions, and so the
175
   * former requirement that you own the I/O functions pretty much forced
176
   * superuserness anyway.  We're just making doubly sure here.
177
   *
178
   * XXX re-enable NOT_USED code sections below if you remove this test.
179
   *
180
   * In YB mode, we allow users with the yb_extension role who are in the
181
   * midst of creating an extension to create a base type.
182
   */
183
201
  if (!(IsYbExtensionUser(GetUserId()) && creating_extension) && 
!superuser()179
)
184
201
    ereport(ERROR,
185
201
        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
186
201
         errmsg("must be superuser to create a base type")));
187
188
  /* Convert list of names to a name and namespace */
189
201
  typeNamespace = QualifiedNameGetCreationNamespace(names, &typeName);
190
191
#ifdef NOT_USED
192
  /* XXX this is unnecessary given the superuser check above */
193
  /* Check we have creation rights in target namespace */
194
  aclresult = pg_namespace_aclcheck(typeNamespace, GetUserId(), ACL_CREATE);
195
  if (aclresult != ACLCHECK_OK)
196
    aclcheck_error(aclresult, OBJECT_SCHEMA,
197
             get_namespace_name(typeNamespace));
198
#endif
199
200
  /*
201
   * Look to see if type already exists (presumably as a shell; if not,
202
   * TypeCreate will complain).
203
   */
204
201
  typoid = GetSysCacheOid2(TYPENAMENSP,
205
201
               CStringGetDatum(typeName),
206
201
               ObjectIdGetDatum(typeNamespace));
207
208
  /*
209
   * If it's not a shell, see if it's an autogenerated array type, and if so
210
   * rename it out of the way.
211
   */
212
201
  if (OidIsValid(typoid) && 
get_typisdefined(typoid)105
)
213
10
  {
214
10
    if (moveArrayTypeName(typoid, typeName, typeNamespace))
215
0
      typoid = InvalidOid;
216
10
  }
217
218
  /*
219
   * If it doesn't exist, create it as a shell, so that the OID is known for
220
   * use in the I/O function definitions.
221
   */
222
201
  if (!OidIsValid(typoid))
223
96
  {
224
96
    address = TypeShellMake(typeName, typeNamespace, GetUserId());
225
96
    typoid = address.objectId;
226
    /* Make new shell type visible for modification below */
227
96
    CommandCounterIncrement();
228
229
    /*
230
     * If the command was a parameterless CREATE TYPE, we're done ---
231
     * creating the shell type was all we're supposed to do.
232
     */
233
96
    if (parameters == NIL)
234
66
      return address;
235
96
  }
236
105
  else
237
105
  {
238
    /* Complain if dummy CREATE TYPE and entry already exists */
239
105
    if (parameters == NIL)
240
105
      ereport(ERROR,
241
105
          (errcode(ERRCODE_DUPLICATE_OBJECT),
242
105
           errmsg("type \"%s\" already exists", typeName)));
243
105
  }
244
245
  /* Extract the parameters from the parameter list */
246
135
  foreach(pl, parameters)
247
527
  {
248
527
    DefElem    *defel = (DefElem *) lfirst(pl);
249
527
    DefElem   **defelp;
250
251
527
    if (strcmp(defel->defname, "like") == 0)
252
30
      defelp = &likeTypeEl;
253
497
    else if (strcmp(defel->defname, "internallength") == 0)
254
65
      defelp = &internalLengthEl;
255
432
    else if (strcmp(defel->defname, "input") == 0)
256
105
      defelp = &inputNameEl;
257
327
    else if (strcmp(defel->defname, "output") == 0)
258
105
      defelp = &outputNameEl;
259
222
    else if (strcmp(defel->defname, "receive") == 0)
260
6
      defelp = &receiveNameEl;
261
216
    else if (strcmp(defel->defname, "send") == 0)
262
6
      defelp = &sendNameEl;
263
210
    else if (strcmp(defel->defname, "typmod_in") == 0)
264
14
      defelp = &typmodinNameEl;
265
196
    else if (strcmp(defel->defname, "typmod_out") == 0)
266
14
      defelp = &typmodoutNameEl;
267
182
    else if (strcmp(defel->defname, "analyze") == 0 ||
268
182
         strcmp(defel->defname, "analyse") == 0)
269
0
      defelp = &analyzeNameEl;
270
182
    else if (strcmp(defel->defname, "category") == 0)
271
15
      defelp = &categoryEl;
272
167
    else if (strcmp(defel->defname, "preferred") == 0)
273
11
      defelp = &preferredEl;
274
156
    else if (strcmp(defel->defname, "delimiter") == 0)
275
1
      defelp = &delimiterEl;
276
155
    else if (strcmp(defel->defname, "element") == 0)
277
20
      defelp = &elemTypeEl;
278
135
    else if (strcmp(defel->defname, "default") == 0)
279
20
      defelp = &defaultValueEl;
280
115
    else if (strcmp(defel->defname, "passedbyvalue") == 0)
281
11
      defelp = &byValueEl;
282
104
    else if (strcmp(defel->defname, "alignment") == 0)
283
36
      defelp = &alignmentEl;
284
68
    else if (strcmp(defel->defname, "storage") == 0)
285
3
      defelp = &storageEl;
286
65
    else if (strcmp(defel->defname, "collatable") == 0)
287
5
      defelp = &collatableEl;
288
60
    else
289
60
    {
290
      /* WARNING, not ERROR, for historical backwards-compatibility */
291
60
      ereport(WARNING,
292
60
          (errcode(ERRCODE_SYNTAX_ERROR),
293
60
           errmsg("type attribute \"%s\" not recognized",
294
60
              defel->defname),
295
60
           parser_errposition(pstate, defel->location)));
296
60
      continue;
297
60
    }
298
467
    if (*defelp != NULL)
299
467
      ereport(ERROR,
300
467
          (errcode(ERRCODE_SYNTAX_ERROR),
301
467
           errmsg("conflicting or redundant options"),
302
467
           parser_errposition(pstate, defel->location)));
303
467
    *defelp = defel;
304
467
  }
305
306
  /*
307
   * Now interpret the options; we do this separately so that LIKE can be
308
   * overridden by other options regardless of the ordering in the parameter
309
   * list.
310
   */
311
135
  if (likeTypeEl)
312
30
  {
313
30
    Type    likeType;
314
30
    Form_pg_type likeForm;
315
316
30
    likeType = typenameType(NULL, defGetTypeName(likeTypeEl), NULL);
317
30
    likeForm = (Form_pg_type) GETSTRUCT(likeType);
318
30
    internalLength = likeForm->typlen;
319
30
    byValue = likeForm->typbyval;
320
30
    alignment = likeForm->typalign;
321
30
    storage = likeForm->typstorage;
322
30
    ReleaseSysCache(likeType);
323
30
  }
324
135
  if (internalLengthEl)
325
65
    internalLength = defGetTypeLength(internalLengthEl);
326
135
  if (inputNameEl)
327
105
    inputName = defGetQualifiedName(inputNameEl);
328
135
  if (outputNameEl)
329
105
    outputName = defGetQualifiedName(outputNameEl);
330
135
  if (receiveNameEl)
331
6
    receiveName = defGetQualifiedName(receiveNameEl);
332
135
  if (sendNameEl)
333
6
    sendName = defGetQualifiedName(sendNameEl);
334
135
  if (typmodinNameEl)
335
14
    typmodinName = defGetQualifiedName(typmodinNameEl);
336
135
  if (typmodoutNameEl)
337
14
    typmodoutName = defGetQualifiedName(typmodoutNameEl);
338
135
  if (analyzeNameEl)
339
0
    analyzeName = defGetQualifiedName(analyzeNameEl);
340
135
  if (categoryEl)
341
15
  {
342
15
    char     *p = defGetString(categoryEl);
343
344
15
    category = p[0];
345
    /* restrict to non-control ASCII */
346
15
    if (category < 32 || category > 126)
347
15
      ereport(ERROR,
348
15
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
349
15
           errmsg("invalid type category \"%s\": must be simple ASCII",
350
15
              p)));
351
15
  }
352
135
  if (preferredEl)
353
11
    preferred = defGetBoolean(preferredEl);
354
135
  if (delimiterEl)
355
1
  {
356
1
    char     *p = defGetString(delimiterEl);
357
358
1
    delimiter = p[0];
359
    /* XXX shouldn't we restrict the delimiter? */
360
1
  }
361
135
  if (elemTypeEl)
362
20
  {
363
20
    elemType = typenameTypeId(NULL, defGetTypeName(elemTypeEl));
364
    /* disallow arrays of pseudotypes */
365
20
    if (get_typtype(elemType) == TYPTYPE_PSEUDO)
366
20
      ereport(ERROR,
367
20
          (errcode(ERRCODE_DATATYPE_MISMATCH),
368
20
           errmsg("array element type cannot be %s",
369
20
              format_type_be(elemType))));
370
20
  }
371
135
  if (defaultValueEl)
372
20
    defaultValue = defGetString(defaultValueEl);
373
135
  if (byValueEl)
374
11
    byValue = defGetBoolean(byValueEl);
375
135
  if (alignmentEl)
376
36
  {
377
36
    char     *a = defGetString(alignmentEl);
378
379
    /*
380
     * Note: if argument was an unquoted identifier, parser will have
381
     * applied translations to it, so be prepared to recognize translated
382
     * type names as well as the nominal form.
383
     */
384
36
    if (pg_strcasecmp(a, "double") == 0 ||
385
36
      
pg_strcasecmp(a, "float8") == 023
||
386
36
      
pg_strcasecmp(a, "pg_catalog.float8") == 023
)
387
13
      alignment = 'd';
388
23
    else if (pg_strcasecmp(a, "int4") == 0 ||
389
23
         
pg_strcasecmp(a, "pg_catalog.int4") == 02
)
390
21
      alignment = 'i';
391
2
    else if (pg_strcasecmp(a, "int2") == 0 ||
392
2
         pg_strcasecmp(a, "pg_catalog.int2") == 0)
393
0
      alignment = 's';
394
2
    else if (pg_strcasecmp(a, "char") == 0 ||
395
2
         pg_strcasecmp(a, "pg_catalog.bpchar") == 0)
396
2
      alignment = 'c';
397
0
    else
398
0
      ereport(ERROR,
399
36
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
400
36
           errmsg("alignment \"%s\" not recognized", a)));
401
36
  }
402
135
  if (storageEl)
403
3
  {
404
3
    char     *a = defGetString(storageEl);
405
406
3
    if (pg_strcasecmp(a, "plain") == 0)
407
2
      storage = 'p';
408
1
    else if (pg_strcasecmp(a, "external") == 0)
409
0
      storage = 'e';
410
1
    else if (pg_strcasecmp(a, "extended") == 0)
411
1
      storage = 'x';
412
0
    else if (pg_strcasecmp(a, "main") == 0)
413
0
      storage = 'm';
414
0
    else
415
0
      ereport(ERROR,
416
3
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
417
3
           errmsg("storage \"%s\" not recognized", a)));
418
3
  }
419
135
  if (collatableEl)
420
5
    collation = defGetBoolean(collatableEl) ? 
DEFAULT_COLLATION_OID4
:
InvalidOid1
;
421
422
  /*
423
   * make sure we have our required definitions
424
   */
425
135
  if (inputName == NIL)
426
135
    ereport(ERROR,
427
135
        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
428
135
         errmsg("type input function must be specified")));
429
135
  if (outputName == NIL)
430
135
    ereport(ERROR,
431
135
        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
432
135
         errmsg("type output function must be specified")));
433
434
135
  if (typmodinName == NIL && 
typmodoutName != 91
NIL91
)
435
135
    ereport(ERROR,
436
135
        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
437
135
         errmsg("type modifier output function is useless without a type modifier input function")));
438
439
  /*
440
   * Convert I/O proc names to OIDs
441
   */
442
135
  inputOid = findTypeInputFunction(inputName, typoid);
443
135
  outputOid = findTypeOutputFunction(outputName, typoid);
444
135
  if (receiveName)
445
6
    receiveOid = findTypeReceiveFunction(receiveName, typoid);
446
135
  if (sendName)
447
6
    sendOid = findTypeSendFunction(sendName, typoid);
448
449
  /*
450
   * Verify that I/O procs return the expected thing.  If we see OPAQUE,
451
   * complain and change it to the correct type-safe choice.
452
   */
453
135
  resulttype = get_func_rettype(inputOid);
454
135
  if (resulttype != typoid)
455
10
  {
456
10
    if (resulttype == OPAQUEOID)
457
10
    {
458
      /* backwards-compatibility hack */
459
10
      ereport(WARNING,
460
10
          (errmsg("changing return type of function %s from %s to %s",
461
10
              NameListToString(inputName), "opaque", typeName)));
462
10
      SetFunctionReturnType(inputOid, typoid);
463
10
    }
464
0
    else
465
10
      ereport(ERROR,
466
10
          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
467
10
           errmsg("type input function %s must return type %s",
468
10
              NameListToString(inputName), typeName)));
469
10
  }
470
135
  resulttype = get_func_rettype(outputOid);
471
135
  if (resulttype != CSTRINGOID)
472
10
  {
473
10
    if (resulttype == OPAQUEOID)
474
10
    {
475
      /* backwards-compatibility hack */
476
10
      ereport(WARNING,
477
10
          (errmsg("changing return type of function %s from %s to %s",
478
10
              NameListToString(outputName), "opaque", "cstring")));
479
10
      SetFunctionReturnType(outputOid, CSTRINGOID);
480
10
    }
481
0
    else
482
10
      ereport(ERROR,
483
10
          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
484
10
           errmsg("type output function %s must return type %s",
485
10
              NameListToString(outputName), "cstring")));
486
10
  }
487
135
  if (receiveOid)
488
6
  {
489
6
    resulttype = get_func_rettype(receiveOid);
490
6
    if (resulttype != typoid)
491
6
      ereport(ERROR,
492
6
          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
493
6
           errmsg("type receive function %s must return type %s",
494
6
              NameListToString(receiveName), typeName)));
495
6
  }
496
135
  if (sendOid)
497
6
  {
498
6
    resulttype = get_func_rettype(sendOid);
499
6
    if (resulttype != BYTEAOID)
500
6
      ereport(ERROR,
501
6
          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
502
6
           errmsg("type send function %s must return type %s",
503
6
              NameListToString(sendName), "bytea")));
504
6
  }
505
506
  /*
507
   * Convert typmodin/out function proc names to OIDs.
508
   */
509
135
  if (typmodinName)
510
14
    typmodinOid = findTypeTypmodinFunction(typmodinName);
511
135
  if (typmodoutName)
512
14
    typmodoutOid = findTypeTypmodoutFunction(typmodoutName);
513
514
  /*
515
   * Convert analysis function proc name to an OID. If no analysis function
516
   * is specified, we'll use zero to select the built-in default algorithm.
517
   */
518
135
  if (analyzeName)
519
0
    analyzeOid = findTypeAnalyzeFunction(analyzeName, typoid);
520
521
  /*
522
   * Check permissions on functions.  We choose to require the creator/owner
523
   * of a type to also own the underlying functions.  Since creating a type
524
   * is tantamount to granting public execute access on the functions, the
525
   * minimum sane check would be for execute-with-grant-option.  But we
526
   * don't have a way to make the type go away if the grant option is
527
   * revoked, so ownership seems better.
528
   */
529
#ifdef NOT_USED
530
  /* XXX this is unnecessary given the superuser check above */
531
  if (inputOid && !pg_proc_ownercheck(inputOid, GetUserId()))
532
    aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
533
             NameListToString(inputName));
534
  if (outputOid && !pg_proc_ownercheck(outputOid, GetUserId()))
535
    aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
536
             NameListToString(outputName));
537
  if (receiveOid && !pg_proc_ownercheck(receiveOid, GetUserId()))
538
    aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
539
             NameListToString(receiveName));
540
  if (sendOid && !pg_proc_ownercheck(sendOid, GetUserId()))
541
    aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
542
             NameListToString(sendName));
543
  if (typmodinOid && !pg_proc_ownercheck(typmodinOid, GetUserId()))
544
    aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
545
             NameListToString(typmodinName));
546
  if (typmodoutOid && !pg_proc_ownercheck(typmodoutOid, GetUserId()))
547
    aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
548
             NameListToString(typmodoutName));
549
  if (analyzeOid && !pg_proc_ownercheck(analyzeOid, GetUserId()))
550
    aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
551
             NameListToString(analyzeName));
552
#endif
553
554
  /*
555
   * Print warnings if any of the type's I/O functions are marked volatile.
556
   * There is a general assumption that I/O functions are stable or
557
   * immutable; this allows us for example to mark record_in/record_out
558
   * stable rather than volatile.  Ideally we would throw errors not just
559
   * warnings here; but since this check is new as of 9.5, and since the
560
   * volatility marking might be just an error-of-omission and not a true
561
   * indication of how the function behaves, we'll let it pass as a warning
562
   * for now.
563
   */
564
135
  if (inputOid && 
func_volatile(inputOid) == 95
PROVOLATILE_VOLATILE95
)
565
135
    ereport(WARNING,
566
135
        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
567
135
         errmsg("type input function %s should not be volatile",
568
135
            NameListToString(inputName))));
569
135
  if (outputOid && 
func_volatile(outputOid) == 95
PROVOLATILE_VOLATILE95
)
570
135
    ereport(WARNING,
571
135
        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
572
135
         errmsg("type output function %s should not be volatile",
573
135
            NameListToString(outputName))));
574
135
  if (receiveOid && 
func_volatile(receiveOid) == 6
PROVOLATILE_VOLATILE6
)
575
135
    ereport(WARNING,
576
135
        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
577
135
         errmsg("type receive function %s should not be volatile",
578
135
            NameListToString(receiveName))));
579
135
  if (sendOid && 
func_volatile(sendOid) == 6
PROVOLATILE_VOLATILE6
)
580
135
    ereport(WARNING,
581
135
        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
582
135
         errmsg("type send function %s should not be volatile",
583
135
            NameListToString(sendName))));
584
135
  if (typmodinOid && 
func_volatile(typmodinOid) == 14
PROVOLATILE_VOLATILE14
)
585
135
    ereport(WARNING,
586
135
        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
587
135
         errmsg("type modifier input function %s should not be volatile",
588
135
            NameListToString(typmodinName))));
589
135
  if (typmodoutOid && 
func_volatile(typmodoutOid) == 14
PROVOLATILE_VOLATILE14
)
590
135
    ereport(WARNING,
591
135
        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
592
135
         errmsg("type modifier output function %s should not be volatile",
593
135
            NameListToString(typmodoutName))));
594
595
  /*
596
   * OK, we're done checking, time to make the type.  We must assign the
597
   * array type OID ahead of calling TypeCreate, since the base type and
598
   * array type each refer to the other.
599
   */
600
135
  array_oid = AssignTypeArrayOid();
601
602
  /*
603
   * now have TypeCreate do all the real work.
604
   *
605
   * Note: the pg_type.oid is stored in user tables as array elements (base
606
   * types) in ArrayType and in composite types in DatumTupleFields.  This
607
   * oid must be preserved by binary upgrades.
608
   */
609
135
  address =
610
135
    TypeCreate(InvalidOid, /* no predetermined type OID */
611
135
           typeName,  /* type name */
612
135
           typeNamespace, /* namespace */
613
135
           InvalidOid, /* relation oid (n/a here) */
614
135
           0,     /* relation kind (ditto) */
615
135
           GetUserId(), /* owner's ID */
616
135
           internalLength,  /* internal size */
617
135
           TYPTYPE_BASE, /* type-type (base type) */
618
135
           category,  /* type-category */
619
135
           preferred, /* is it a preferred type? */
620
135
           delimiter, /* array element delimiter */
621
135
           inputOid,  /* input procedure */
622
135
           outputOid, /* output procedure */
623
135
           receiveOid,  /* receive procedure */
624
135
           sendOid,   /* send procedure */
625
135
           typmodinOid, /* typmodin procedure */
626
135
           typmodoutOid,  /* typmodout procedure */
627
135
           analyzeOid,  /* analyze procedure */
628
135
           elemType,  /* element type ID */
629
135
           false,   /* this is not an array type */
630
135
           array_oid, /* array type we are about to create */
631
135
           InvalidOid, /* base type ID (only for domains) */
632
135
           defaultValue,  /* default type value */
633
135
           NULL,    /* no binary form available */
634
135
           byValue,   /* passed by value */
635
135
           alignment, /* required alignment */
636
135
           storage,   /* TOAST strategy */
637
135
           -1,      /* typMod (Domains only) */
638
135
           0,     /* Array Dimensions of typbasetype */
639
135
           false,   /* Type NOT NULL */
640
135
           collation, /* type's collation */
641
135
           false);    /* whether relation is shared (n/a here) */
642
135
  Assert(typoid == address.objectId);
643
644
  /*
645
   * Create the array type that goes with it.
646
   */
647
135
  array_type = makeArrayTypeName(typeName, typeNamespace);
648
649
  /* alignment must be 'i' or 'd' for arrays */
650
135
  alignment = (alignment == 'd') ? 
'd'27
:
'i'108
;
651
652
135
  TypeCreate(array_oid,   /* force assignment of this type OID */
653
135
         array_type,    /* type name */
654
135
         typeNamespace, /* namespace */
655
135
         InvalidOid,   /* relation oid (n/a here) */
656
135
         0,       /* relation kind (ditto) */
657
135
         GetUserId(),   /* owner's ID */
658
135
         -1,        /* internal size (always varlena) */
659
135
         TYPTYPE_BASE, /* type-type (base type) */
660
135
         TYPCATEGORY_ARRAY, /* type-category (array) */
661
135
         false,     /* array types are never preferred */
662
135
         delimiter,   /* array element delimiter */
663
135
         F_ARRAY_IN,   /* input procedure */
664
135
         F_ARRAY_OUT,   /* output procedure */
665
135
         F_ARRAY_RECV, /* receive procedure */
666
135
         F_ARRAY_SEND, /* send procedure */
667
135
         typmodinOid,   /* typmodin procedure */
668
135
         typmodoutOid,  /* typmodout procedure */
669
135
         F_ARRAY_TYPANALYZE, /* analyze procedure */
670
135
         typoid,      /* element type ID */
671
135
         true,      /* yes this is an array type */
672
135
         InvalidOid,   /* no further array type */
673
135
         InvalidOid,   /* base type ID */
674
135
         NULL,      /* never a default type value */
675
135
         NULL,      /* binary default isn't sent either */
676
135
         false,     /* never passed by value */
677
135
         alignment,   /* see above */
678
135
         'x',       /* ARRAY is always toastable */
679
135
         -1,        /* typMod (Domains only) */
680
135
         0,       /* Array dimensions of typbasetype */
681
135
         false,     /* Type NOT NULL */
682
135
         collation,   /* type's collation */
683
135
         false);      /* whether relation is shared (n/a here) */
684
685
135
  pfree(array_type);
686
687
135
  return address;
688
135
}
689
690
/*
691
 * Guts of type deletion.
692
 */
693
void
694
RemoveTypeById(Oid typeOid)
695
8.74k
{
696
8.74k
  Relation  relation;
697
8.74k
  HeapTuple tup;
698
699
8.74k
  relation = heap_open(TypeRelationId, RowExclusiveLock);
700
701
8.74k
  tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeOid));
702
8.74k
  if (!HeapTupleIsValid(tup))
703
0
    elog(ERROR, "cache lookup failed for type %u", typeOid);
704
705
8.74k
  CatalogTupleDelete(relation, tup);
706
707
  /*
708
   * If it is an enum, delete the pg_enum entries too; we don't bother with
709
   * making dependency entries for those, so it has to be done "by hand"
710
   * here.
711
   */
712
8.74k
  if (((Form_pg_type) GETSTRUCT(tup))->typtype == TYPTYPE_ENUM)
713
27
    EnumValuesDelete(typeOid);
714
715
  /*
716
   * If it is a range type, delete the pg_range entry too; we don't bother
717
   * with making a dependency entry for that, so it has to be done "by hand"
718
   * here.
719
   */
720
8.74k
  if (((Form_pg_type) GETSTRUCT(tup))->typtype == TYPTYPE_RANGE)
721
14
    RangeDelete(typeOid);
722
723
8.74k
  ReleaseSysCache(tup);
724
725
8.74k
  heap_close(relation, RowExclusiveLock);
726
8.74k
}
727
728
729
/*
730
 * DefineDomain
731
 *    Registers a new domain.
732
 */
733
ObjectAddress
734
DefineDomain(CreateDomainStmt *stmt)
735
58
{
736
58
  char     *domainName;
737
58
  char     *domainArrayName;
738
58
  Oid     domainNamespace;
739
58
  AclResult aclresult;
740
58
  int16   internalLength;
741
58
  Oid     inputProcedure;
742
58
  Oid     outputProcedure;
743
58
  Oid     receiveProcedure;
744
58
  Oid     sendProcedure;
745
58
  Oid     analyzeProcedure;
746
58
  bool    byValue;
747
58
  char    category;
748
58
  char    delimiter;
749
58
  char    alignment;
750
58
  char    storage;
751
58
  char    typtype;
752
58
  Datum   datum;
753
58
  bool    isnull;
754
58
  char     *defaultValue = NULL;
755
58
  char     *defaultValueBin = NULL;
756
58
  bool    saw_default = false;
757
58
  bool    typNotNull = false;
758
58
  bool    nullDefined = false;
759
58
  int32   typNDims = list_length(stmt->typeName->arrayBounds);
760
58
  HeapTuple typeTup;
761
58
  List     *schema = stmt->constraints;
762
58
  ListCell   *listptr;
763
58
  Oid     basetypeoid;
764
58
  Oid     old_type_oid;
765
58
  Oid     domaincoll;
766
58
  Oid     domainArrayOid;
767
58
  Form_pg_type baseType;
768
58
  int32   basetypeMod;
769
58
  Oid     baseColl;
770
58
  ObjectAddress address;
771
772
  /* Convert list of names to a name and namespace */
773
58
  domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname,
774
58
                            &domainName);
775
776
  /* Check we have creation rights in target namespace */
777
58
  aclresult = pg_namespace_aclcheck(domainNamespace, GetUserId(),
778
58
                    ACL_CREATE);
779
58
  if (aclresult != ACLCHECK_OK)
780
0
    aclcheck_error(aclresult, OBJECT_SCHEMA,
781
0
             get_namespace_name(domainNamespace));
782
783
  /*
784
   * Check for collision with an existing type name.  If there is one and
785
   * it's an autogenerated array, we can rename it out of the way.
786
   */
787
58
  old_type_oid = GetSysCacheOid2(TYPENAMENSP,
788
58
                   CStringGetDatum(domainName),
789
58
                   ObjectIdGetDatum(domainNamespace));
790
58
  if (OidIsValid(old_type_oid))
791
2
  {
792
2
    if (!moveArrayTypeName(old_type_oid, domainName, domainNamespace))
793
2
      ereport(ERROR,
794
2
          (errcode(ERRCODE_DUPLICATE_OBJECT),
795
2
           errmsg("type \"%s\" already exists", domainName)));
796
2
  }
797
798
  /*
799
   * Look up the base type.
800
   */
801
58
  typeTup = typenameType(NULL, stmt->typeName, &basetypeMod);
802
58
  baseType = (Form_pg_type) GETSTRUCT(typeTup);
803
58
  basetypeoid = HeapTupleGetOid(typeTup);
804
805
  /*
806
   * Base type must be a plain base type, a composite type, another domain,
807
   * an enum or a range type.  Domains over pseudotypes would create a
808
   * security hole.  (It would be shorter to code this to just check for
809
   * pseudotypes; but it seems safer to call out the specific typtypes that
810
   * are supported, rather than assume that all future typtypes would be
811
   * automatically supported.)
812
   */
813
58
  typtype = baseType->typtype;
814
58
  if (typtype != TYPTYPE_BASE &&
815
58
    
typtype != 6
TYPTYPE_COMPOSITE6
&&
816
58
    
typtype != 4
TYPTYPE_DOMAIN4
&&
817
58
    
typtype != 2
TYPTYPE_ENUM2
&&
818
58
    
typtype != 1
TYPTYPE_RANGE1
)
819
58
    ereport(ERROR,
820
58
        (errcode(ERRCODE_DATATYPE_MISMATCH),
821
58
         errmsg("\"%s\" is not a valid base type for a domain",
822
58
            TypeNameToString(stmt->typeName))));
823
824
58
  aclresult = pg_type_aclcheck(basetypeoid, GetUserId(), ACL_USAGE);
825
58
  if (aclresult != ACLCHECK_OK)
826
1
    aclcheck_error_type(aclresult, basetypeoid);
827
828
  /*
829
   * Identify the collation if any
830
   */
831
58
  baseColl = baseType->typcollation;
832
58
  if (stmt->collClause)
833
5
    domaincoll = get_collation_oid(stmt->collClause->collname, false);
834
53
  else
835
53
    domaincoll = baseColl;
836
837
  /* Complain if COLLATE is applied to an uncollatable type */
838
58
  if (OidIsValid(domaincoll) && 
!16
OidIsValid16
(baseColl))
839
58
    ereport(ERROR,
840
58
        (errcode(ERRCODE_DATATYPE_MISMATCH),
841
58
         errmsg("collations are not supported by type %s",
842
58
            format_type_be(basetypeoid))));
843
844
  /* passed by value */
845
58
  byValue = baseType->typbyval;
846
847
  /* Required Alignment */
848
58
  alignment = baseType->typalign;
849
850
  /* TOAST Strategy */
851
58
  storage = baseType->typstorage;
852
853
  /* Storage Length */
854
58
  internalLength = baseType->typlen;
855
856
  /* Type Category */
857
58
  category = baseType->typcategory;
858
859
  /* Array element Delimiter */
860
58
  delimiter = baseType->typdelim;
861
862
  /* I/O Functions */
863
58
  inputProcedure = F_DOMAIN_IN;
864
58
  outputProcedure = baseType->typoutput;
865
58
  receiveProcedure = F_DOMAIN_RECV;
866
58
  sendProcedure = baseType->typsend;
867
868
  /* Domains never accept typmods, so no typmodin/typmodout needed */
869
870
  /* Analysis function */
871
58
  analyzeProcedure = baseType->typanalyze;
872
873
  /* Inherited default value */
874
58
  datum = SysCacheGetAttr(TYPEOID, typeTup,
875
58
              Anum_pg_type_typdefault, &isnull);
876
58
  if (!isnull)
877
0
    defaultValue = TextDatumGetCString(datum);
878
879
  /* Inherited default binary value */
880
58
  datum = SysCacheGetAttr(TYPEOID, typeTup,
881
58
              Anum_pg_type_typdefaultbin, &isnull);
882
58
  if (!isnull)
883
0
    defaultValueBin = TextDatumGetCString(datum);
884
885
  /*
886
   * Run through constraints manually to avoid the additional processing
887
   * conducted by DefineRelation() and friends.
888
   */
889
58
  foreach(listptr, schema)
890
37
  {
891
37
    Constraint *constr = lfirst(listptr);
892
893
37
    if (!IsA(constr, Constraint))
894
0
      elog(ERROR, "unrecognized node type: %d",
895
37
         (int) nodeTag(constr));
896
37
    switch (constr->contype)
897
37
    {
898
5
      case CONSTR_DEFAULT:
899
900
        /*
901
         * The inherited default value may be overridden by the user
902
         * with the DEFAULT <expr> clause ... but only once.
903
         */
904
5
        if (saw_default)
905
5
          ereport(ERROR,
906
5
              (errcode(ERRCODE_SYNTAX_ERROR),
907
5
               errmsg("multiple default expressions")));
908
5
        saw_default = true;
909
910
5
        if (constr->raw_expr)
911
5
        {
912
5
          ParseState *pstate;
913
5
          Node     *defaultExpr;
914
915
          /* Create a dummy ParseState for transformExpr */
916
5
          pstate = make_parsestate(NULL);
917
918
          /*
919
           * Cook the constr->raw_expr into an expression. Note:
920
           * name is strictly for error message
921
           */
922
5
          defaultExpr = cookDefault(pstate, constr->raw_expr,
923
5
                        basetypeoid,
924
5
                        basetypeMod,
925
5
                        domainName);
926
927
          /*
928
           * If the expression is just a NULL constant, we treat it
929
           * like not having a default.
930
           *
931
           * Note that if the basetype is another domain, we'll see
932
           * a CoerceToDomain expr here and not discard the default.
933
           * This is critical because the domain default needs to be
934
           * retained to override any default that the base domain
935
           * might have.
936
           */
937
5
          if (defaultExpr == NULL ||
938
5
            (IsA(defaultExpr, Const) &&
939
5
             
((Const *) defaultExpr)->constisnull3
))
940
0
          {
941
0
            defaultValue = NULL;
942
0
            defaultValueBin = NULL;
943
0
          }
944
5
          else
945
5
          {
946
            /*
947
             * Expression must be stored as a nodeToString result,
948
             * but we also require a valid textual representation
949
             * (mainly to make life easier for pg_dump).
950
             */
951
5
            defaultValue =
952
5
              deparse_expression(defaultExpr,
953
5
                         NIL, false, false);
954
5
            defaultValueBin = nodeToString(defaultExpr);
955
5
          }
956
5
        }
957
0
        else
958
0
        {
959
          /* No default (can this still happen?) */
960
0
          defaultValue = NULL;
961
0
          defaultValueBin = NULL;
962
0
        }
963
5
        break;
964
965
8
      case CONSTR_NOTNULL:
966
8
        if (nullDefined && 
!typNotNull0
)
967
8
          ereport(ERROR,
968
8
              (errcode(ERRCODE_SYNTAX_ERROR),
969
8
               errmsg("conflicting NULL/NOT NULL constraints")));
970
8
        typNotNull = true;
971
8
        nullDefined = true;
972
8
        break;
973
974
0
      case CONSTR_NULL:
975
0
        if (nullDefined && typNotNull)
976
0
          ereport(ERROR,
977
0
              (errcode(ERRCODE_SYNTAX_ERROR),
978
0
               errmsg("conflicting NULL/NOT NULL constraints")));
979
0
        typNotNull = false;
980
0
        nullDefined = true;
981
0
        break;
982
983
24
      case CONSTR_CHECK:
984
985
        /*
986
         * Check constraints are handled after domain creation, as
987
         * they require the Oid of the domain; at this point we can
988
         * only check that they're not marked NO INHERIT, because that
989
         * would be bogus.
990
         */
991
24
        if (constr->is_no_inherit)
992
24
          ereport(ERROR,
993
24
              (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
994
24
               errmsg("check constraints for domains cannot be marked NO INHERIT")));
995
24
        break;
996
997
        /*
998
         * All else are error cases
999
         */
1000
24
      case CONSTR_UNIQUE:
1001
0
        ereport(ERROR,
1002
0
            (errcode(ERRCODE_SYNTAX_ERROR),
1003
0
             errmsg("unique constraints not possible for domains")));
1004
0
        break;
1005
1006
0
      case CONSTR_PRIMARY:
1007
0
        ereport(ERROR,
1008
0
            (errcode(ERRCODE_SYNTAX_ERROR),
1009
0
             errmsg("primary key constraints not possible for domains")));
1010
0
        break;
1011
1012
0
      case CONSTR_EXCLUSION:
1013
0
        ereport(ERROR,
1014
0
            (errcode(ERRCODE_SYNTAX_ERROR),
1015
0
             errmsg("exclusion constraints not possible for domains")));
1016
0
        break;
1017
1018
0
      case CONSTR_FOREIGN:
1019
0
        ereport(ERROR,
1020
0
            (errcode(ERRCODE_SYNTAX_ERROR),
1021
0
             errmsg("foreign key constraints not possible for domains")));
1022
0
        break;
1023
1024
0
      case CONSTR_ATTR_DEFERRABLE:
1025
0
      case CONSTR_ATTR_NOT_DEFERRABLE:
1026
0
      case CONSTR_ATTR_DEFERRED:
1027
0
      case CONSTR_ATTR_IMMEDIATE:
1028
0
        ereport(ERROR,
1029
0
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1030
0
             errmsg("specifying constraint deferrability not supported for domains")));
1031
0
        break;
1032
1033
0
      default:
1034
0
        elog(ERROR, "unrecognized constraint subtype: %d",
1035
0
           (int) constr->contype);
1036
0
        break;
1037
37
    }
1038
37
  }
1039
1040
  /* Allocate OID for array type */
1041
58
  domainArrayOid = AssignTypeArrayOid();
1042
1043
  /*
1044
   * Have TypeCreate do all the real work.
1045
   */
1046
58
  address =
1047
58
    TypeCreate(InvalidOid, /* no predetermined type OID */
1048
58
           domainName,  /* type name */
1049
58
           domainNamespace, /* namespace */
1050
58
           InvalidOid, /* relation oid (n/a here) */
1051
58
           0,     /* relation kind (ditto) */
1052
58
           GetUserId(), /* owner's ID */
1053
58
           internalLength,  /* internal size */
1054
58
           TYPTYPE_DOMAIN, /* type-type (domain type) */
1055
58
           category,  /* type-category */
1056
58
           false,   /* domain types are never preferred */
1057
58
           delimiter, /* array element delimiter */
1058
58
           inputProcedure,  /* input procedure */
1059
58
           outputProcedure, /* output procedure */
1060
58
           receiveProcedure,  /* receive procedure */
1061
58
           sendProcedure, /* send procedure */
1062
58
           InvalidOid, /* typmodin procedure - none */
1063
58
           InvalidOid, /* typmodout procedure - none */
1064
58
           analyzeProcedure,  /* analyze procedure */
1065
58
           InvalidOid, /* no array element type */
1066
58
           false,   /* this isn't an array */
1067
58
           domainArrayOid,  /* array type we are about to create */
1068
58
           basetypeoid, /* base type ID */
1069
58
           defaultValue,  /* default type value (text) */
1070
58
           defaultValueBin, /* default type value (binary) */
1071
58
           byValue,   /* passed by value */
1072
58
           alignment, /* required alignment */
1073
58
           storage,   /* TOAST strategy */
1074
58
           basetypeMod, /* typeMod value */
1075
58
           typNDims,  /* Array dimensions for base type */
1076
58
           typNotNull,  /* Type NOT NULL */
1077
58
           domaincoll,  /* type's collation */
1078
58
           false);    /* whether relation is shared (n/a here) */
1079
1080
  /*
1081
   * Create the array type that goes with it.
1082
   */
1083
58
  domainArrayName = makeArrayTypeName(domainName, domainNamespace);
1084
1085
  /* alignment must be 'i' or 'd' for arrays */
1086
58
  alignment = (alignment == 'd') ? 
'd'7
:
'i'51
;
1087
1088
58
  TypeCreate(domainArrayOid,  /* force assignment of this type OID */
1089
58
         domainArrayName, /* type name */
1090
58
         domainNamespace, /* namespace */
1091
58
         InvalidOid,   /* relation oid (n/a here) */
1092
58
         0,       /* relation kind (ditto) */
1093
58
         GetUserId(),   /* owner's ID */
1094
58
         -1,        /* internal size (always varlena) */
1095
58
         TYPTYPE_BASE, /* type-type (base type) */
1096
58
         TYPCATEGORY_ARRAY, /* type-category (array) */
1097
58
         false,     /* array types are never preferred */
1098
58
         delimiter,   /* array element delimiter */
1099
58
         F_ARRAY_IN,   /* input procedure */
1100
58
         F_ARRAY_OUT,   /* output procedure */
1101
58
         F_ARRAY_RECV, /* receive procedure */
1102
58
         F_ARRAY_SEND, /* send procedure */
1103
58
         InvalidOid,   /* typmodin procedure - none */
1104
58
         InvalidOid,   /* typmodout procedure - none */
1105
58
         F_ARRAY_TYPANALYZE, /* analyze procedure */
1106
58
         address.objectId,  /* element type ID */
1107
58
         true,      /* yes this is an array type */
1108
58
         InvalidOid,   /* no further array type */
1109
58
         InvalidOid,   /* base type ID */
1110
58
         NULL,      /* never a default type value */
1111
58
         NULL,      /* binary default isn't sent either */
1112
58
         false,     /* never passed by value */
1113
58
         alignment,   /* see above */
1114
58
         'x',       /* ARRAY is always toastable */
1115
58
         -1,        /* typMod (Domains only) */
1116
58
         0,       /* Array dimensions of typbasetype */
1117
58
         false,     /* Type NOT NULL */
1118
58
         domaincoll,    /* type's collation */
1119
58
         false);      /* whether relation is shared (n/a here) */
1120
1121
58
  pfree(domainArrayName);
1122
1123
  /*
1124
   * Process constraints which refer to the domain ID returned by TypeCreate
1125
   */
1126
58
  foreach(listptr, schema)
1127
37
  {
1128
37
    Constraint *constr = lfirst(listptr);
1129
1130
    /* it must be a Constraint, per check above */
1131
1132
37
    switch (constr->contype)
1133
37
    {
1134
24
      case CONSTR_CHECK:
1135
24
        domainAddConstraint(address.objectId, domainNamespace,
1136
24
                  basetypeoid, basetypeMod,
1137
24
                  constr, domainName, NULL);
1138
24
        break;
1139
1140
        /* Other constraint types were fully processed above */
1141
1142
13
      default:
1143
13
        break;
1144
37
    }
1145
1146
    /* CCI so we can detect duplicate constraint names */
1147
37
    CommandCounterIncrement();
1148
37
  }
1149
1150
  /*
1151
   * Now we can clean up.
1152
   */
1153
58
  ReleaseSysCache(typeTup);
1154
1155
58
  return address;
1156
58
}
1157
1158
1159
/*
1160
 * DefineEnum
1161
 *    Registers a new enum.
1162
 */
1163
ObjectAddress
1164
DefineEnum(CreateEnumStmt *stmt)
1165
29
{
1166
29
  char     *enumName;
1167
29
  char     *enumArrayName;
1168
29
  Oid     enumNamespace;
1169
29
  AclResult aclresult;
1170
29
  Oid     old_type_oid;
1171
29
  Oid     enumArrayOid;
1172
29
  ObjectAddress enumTypeAddr;
1173
1174
  /* Convert list of names to a name and namespace */
1175
29
  enumNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
1176
29
                            &enumName);
1177
1178
  /* Check we have creation rights in target namespace */
1179
29
  aclresult = pg_namespace_aclcheck(enumNamespace, GetUserId(), ACL_CREATE);
1180
29
  if (aclresult != ACLCHECK_OK)
1181
0
    aclcheck_error(aclresult, OBJECT_SCHEMA,
1182
0
             get_namespace_name(enumNamespace));
1183
1184
  /*
1185
   * Check for collision with an existing type name.  If there is one and
1186
   * it's an autogenerated array, we can rename it out of the way.
1187
   */
1188
29
  old_type_oid = GetSysCacheOid2(TYPENAMENSP,
1189
29
                   CStringGetDatum(enumName),
1190
29
                   ObjectIdGetDatum(enumNamespace));
1191
29
  if (OidIsValid(old_type_oid))
1192
1
  {
1193
1
    if (!moveArrayTypeName(old_type_oid, enumName, enumNamespace))
1194
1
      ereport(ERROR,
1195
1
          (errcode(ERRCODE_DUPLICATE_OBJECT),
1196
1
           errmsg("type \"%s\" already exists", enumName)));
1197
1
  }
1198
1199
  /* Allocate OID for array type */
1200
29
  enumArrayOid = AssignTypeArrayOid();
1201
1202
  /* Create the pg_type entry */
1203
29
  enumTypeAddr =
1204
29
    TypeCreate(InvalidOid, /* no predetermined type OID */
1205
29
           enumName,  /* type name */
1206
29
           enumNamespace, /* namespace */
1207
29
           InvalidOid, /* relation oid (n/a here) */
1208
29
           0,     /* relation kind (ditto) */
1209
29
           GetUserId(), /* owner's ID */
1210
29
           sizeof(Oid), /* internal size */
1211
29
           TYPTYPE_ENUM, /* type-type (enum type) */
1212
29
           TYPCATEGORY_ENUM, /* type-category (enum type) */
1213
29
           false,   /* enum types are never preferred */
1214
29
           DEFAULT_TYPDELIM, /* array element delimiter */
1215
29
           F_ENUM_IN, /* input procedure */
1216
29
           F_ENUM_OUT, /* output procedure */
1217
29
           F_ENUM_RECV, /* receive procedure */
1218
29
           F_ENUM_SEND, /* send procedure */
1219
29
           InvalidOid, /* typmodin procedure - none */
1220
29
           InvalidOid, /* typmodout procedure - none */
1221
29
           InvalidOid, /* analyze procedure - default */
1222
29
           InvalidOid, /* element type ID */
1223
29
           false,   /* this is not an array type */
1224
29
           enumArrayOid,  /* array type we are about to create */
1225
29
           InvalidOid, /* base type ID (only for domains) */
1226
29
           NULL,    /* never a default type value */
1227
29
           NULL,    /* binary default isn't sent either */
1228
29
           true,    /* always passed by value */
1229
29
           'i',     /* int alignment */
1230
29
           'p',     /* TOAST strategy always plain */
1231
29
           -1,      /* typMod (Domains only) */
1232
29
           0,     /* Array dimensions of typbasetype */
1233
29
           false,   /* Type NOT NULL */
1234
29
           InvalidOid, /* type's collation */
1235
29
           false);    /* whether relation is shared (n/a here) */
1236
1237
  /* Enter the enum's values into pg_enum */
1238
29
  EnumValuesCreate(enumTypeAddr.objectId, stmt->vals);
1239
1240
  /*
1241
   * Create the array type that goes with it.
1242
   */
1243
29
  enumArrayName = makeArrayTypeName(enumName, enumNamespace);
1244
1245
29
  TypeCreate(enumArrayOid,  /* force assignment of this type OID */
1246
29
         enumArrayName, /* type name */
1247
29
         enumNamespace, /* namespace */
1248
29
         InvalidOid,   /* relation oid (n/a here) */
1249
29
         0,       /* relation kind (ditto) */
1250
29
         GetUserId(),   /* owner's ID */
1251
29
         -1,        /* internal size (always varlena) */
1252
29
         TYPTYPE_BASE, /* type-type (base type) */
1253
29
         TYPCATEGORY_ARRAY, /* type-category (array) */
1254
29
         false,     /* array types are never preferred */
1255
29
         DEFAULT_TYPDELIM, /* array element delimiter */
1256
29
         F_ARRAY_IN,   /* input procedure */
1257
29
         F_ARRAY_OUT,   /* output procedure */
1258
29
         F_ARRAY_RECV, /* receive procedure */
1259
29
         F_ARRAY_SEND, /* send procedure */
1260
29
         InvalidOid,   /* typmodin procedure - none */
1261
29
         InvalidOid,   /* typmodout procedure - none */
1262
29
         F_ARRAY_TYPANALYZE, /* analyze procedure */
1263
29
         enumTypeAddr.objectId, /* element type ID */
1264
29
         true,      /* yes this is an array type */
1265
29
         InvalidOid,   /* no further array type */
1266
29
         InvalidOid,   /* base type ID */
1267
29
         NULL,      /* never a default type value */
1268
29
         NULL,      /* binary default isn't sent either */
1269
29
         false,     /* never passed by value */
1270
29
         'i',       /* enums have align i, so do their arrays */
1271
29
         'x',       /* ARRAY is always toastable */
1272
29
         -1,        /* typMod (Domains only) */
1273
29
         0,       /* Array dimensions of typbasetype */
1274
29
         false,     /* Type NOT NULL */
1275
29
         InvalidOid,   /* type's collation */
1276
29
         false);      /* whether relation is shared (n/a here) */
1277
1278
29
  pfree(enumArrayName);
1279
1280
29
  return enumTypeAddr;
1281
29
}
1282
1283
/*
1284
 * AlterEnum
1285
 *    Adds a new label to an existing enum.
1286
 */
1287
ObjectAddress
1288
AlterEnum(AlterEnumStmt *stmt)
1289
50
{
1290
50
  Oid     enum_type_oid;
1291
50
  TypeName   *typename;
1292
50
  HeapTuple tup;
1293
50
  ObjectAddress address;
1294
1295
  /* Make a TypeName so we can use standard type lookup machinery */
1296
50
  typename = makeTypeNameFromNameList(stmt->typeName);
1297
50
  enum_type_oid = typenameTypeId(NULL, typename);
1298
1299
50
  tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(enum_type_oid));
1300
50
  if (!HeapTupleIsValid(tup))
1301
0
    elog(ERROR, "cache lookup failed for type %u", enum_type_oid);
1302
1303
  /* Check it's an enum and check user has permission to ALTER the enum */
1304
50
  checkEnumOwner(tup);
1305
1306
50
  ReleaseSysCache(tup);
1307
1308
50
  if (stmt->oldVal)
1309
4
  {
1310
    /* Rename an existing label */
1311
4
    RenameEnumLabel(enum_type_oid, stmt->oldVal, stmt->newVal);
1312
4
  }
1313
46
  else
1314
46
  {
1315
    /* Add a new label */
1316
46
    AddEnumLabel(enum_type_oid, stmt->newVal,
1317
46
           stmt->newValNeighbor, stmt->newValIsAfter,
1318
46
           stmt->skipIfNewValExists);
1319
46
  }
1320
1321
50
  InvokeObjectPostAlterHook(TypeRelationId, enum_type_oid, 0);
1322
1323
50
  ObjectAddressSet(address, TypeRelationId, enum_type_oid);
1324
1325
50
  return address;
1326
50
}
1327
1328
1329
/*
1330
 * checkEnumOwner
1331
 *
1332
 * Check that the type is actually an enum and that the current user
1333
 * has permission to do ALTER TYPE on it.  Throw an error if not.
1334
 */
1335
static void
1336
checkEnumOwner(HeapTuple tup)
1337
50
{
1338
50
  Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup);
1339
1340
  /* Check that this is actually an enum */
1341
50
  if (typTup->typtype != TYPTYPE_ENUM)
1342
50
    ereport(ERROR,
1343
50
        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1344
50
         errmsg("%s is not an enum",
1345
50
            format_type_be(HeapTupleGetOid(tup)))));
1346
1347
  /* Permission check: must own type */
1348
50
  if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId()))
1349
0
    aclcheck_error_type(ACLCHECK_NOT_OWNER, HeapTupleGetOid(tup));
1350
50
}
1351
1352
1353
/*
1354
 * DefineRange
1355
 *    Registers a new range type.
1356
 */
1357
ObjectAddress
1358
DefineRange(CreateRangeStmt *stmt)
1359
15
{
1360
15
  char     *typeName;
1361
15
  Oid     typeNamespace;
1362
15
  Oid     typoid;
1363
15
  char     *rangeArrayName;
1364
15
  Oid     rangeArrayOid;
1365
15
  Oid     rangeSubtype = InvalidOid;
1366
15
  List     *rangeSubOpclassName = NIL;
1367
15
  List     *rangeCollationName = NIL;
1368
15
  List     *rangeCanonicalName = NIL;
1369
15
  List     *rangeSubtypeDiffName = NIL;
1370
15
  Oid     rangeSubOpclass;
1371
15
  Oid     rangeCollation;
1372
15
  regproc   rangeCanonical;
1373
15
  regproc   rangeSubtypeDiff;
1374
15
  int16   subtyplen;
1375
15
  bool    subtypbyval;
1376
15
  char    subtypalign;
1377
15
  char    alignment;
1378
15
  AclResult aclresult;
1379
15
  ListCell   *lc;
1380
15
  ObjectAddress address;
1381
1382
  /* Convert list of names to a name and namespace */
1383
15
  typeNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
1384
15
                            &typeName);
1385
1386
  /* Check we have creation rights in target namespace */
1387
15
  aclresult = pg_namespace_aclcheck(typeNamespace, GetUserId(), ACL_CREATE);
1388
15
  if (aclresult != ACLCHECK_OK)
1389
0
    aclcheck_error(aclresult, OBJECT_SCHEMA,
1390
0
             get_namespace_name(typeNamespace));
1391
1392
  /*
1393
   * Look to see if type already exists.
1394
   */
1395
15
  typoid = GetSysCacheOid2(TYPENAMENSP,
1396
15
               CStringGetDatum(typeName),
1397
15
               ObjectIdGetDatum(typeNamespace));
1398
1399
  /*
1400
   * If it's not a shell, see if it's an autogenerated array type, and if so
1401
   * rename it out of the way.
1402
   */
1403
15
  if (OidIsValid(typoid) && 
get_typisdefined(typoid)0
)
1404
0
  {
1405
0
    if (moveArrayTypeName(typoid, typeName, typeNamespace))
1406
0
      typoid = InvalidOid;
1407
0
    else
1408
0
      ereport(ERROR,
1409
0
          (errcode(ERRCODE_DUPLICATE_OBJECT),
1410
0
           errmsg("type \"%s\" already exists", typeName)));
1411
0
  }
1412
1413
  /*
1414
   * If it doesn't exist, create it as a shell, so that the OID is known for
1415
   * use in the range function definitions.
1416
   */
1417
15
  if (!OidIsValid(typoid))
1418
15
  {
1419
15
    address = TypeShellMake(typeName, typeNamespace, GetUserId());
1420
15
    typoid = address.objectId;
1421
    /* Make new shell type visible for modification below */
1422
15
    CommandCounterIncrement();
1423
15
  }
1424
1425
  /* Extract the parameters from the parameter list */
1426
15
  foreach(lc, stmt->params)
1427
22
  {
1428
22
    DefElem    *defel = (DefElem *) lfirst(lc);
1429
1430
22
    if (strcmp(defel->defname, "subtype") == 0)
1431
15
    {
1432
15
      if (OidIsValid(rangeSubtype))
1433
15
        ereport(ERROR,
1434
15
            (errcode(ERRCODE_SYNTAX_ERROR),
1435
15
             errmsg("conflicting or redundant options")));
1436
      /* we can look up the subtype name immediately */
1437
15
      rangeSubtype = typenameTypeId(NULL, defGetTypeName(defel));
1438
15
    }
1439
7
    else if (strcmp(defel->defname, "subtype_opclass") == 0)
1440
0
    {
1441
0
      if (rangeSubOpclassName != NIL)
1442
0
        ereport(ERROR,
1443
0
            (errcode(ERRCODE_SYNTAX_ERROR),
1444
0
             errmsg("conflicting or redundant options")));
1445
0
      rangeSubOpclassName = defGetQualifiedName(defel);
1446
0
    }
1447
7
    else if (strcmp(defel->defname, "collation") == 0)
1448
5
    {
1449
5
      if (rangeCollationName != NIL)
1450
5
        ereport(ERROR,
1451
5
            (errcode(ERRCODE_SYNTAX_ERROR),
1452
5
             errmsg("conflicting or redundant options")));
1453
5
      rangeCollationName = defGetQualifiedName(defel);
1454
5
    }
1455
2
    else if (strcmp(defel->defname, "canonical") == 0)
1456
0
    {
1457
0
      if (rangeCanonicalName != NIL)
1458
0
        ereport(ERROR,
1459
0
            (errcode(ERRCODE_SYNTAX_ERROR),
1460
0
             errmsg("conflicting or redundant options")));
1461
0
      rangeCanonicalName = defGetQualifiedName(defel);
1462
0
    }
1463
2
    else if (strcmp(defel->defname, "subtype_diff") == 0)
1464
2
    {
1465
2
      if (rangeSubtypeDiffName != NIL)
1466
2
        ereport(ERROR,
1467
2
            (errcode(ERRCODE_SYNTAX_ERROR),
1468
2
             errmsg("conflicting or redundant options")));
1469
2
      rangeSubtypeDiffName = defGetQualifiedName(defel);
1470
2
    }
1471
0
    else
1472
0
      ereport(ERROR,
1473
22
          (errcode(ERRCODE_SYNTAX_ERROR),
1474
22
           errmsg("type attribute \"%s\" not recognized",
1475
22
              defel->defname)));
1476
22
  }
1477
1478
  /* Must have a subtype */
1479
15
  if (!OidIsValid(rangeSubtype))
1480
15
    ereport(ERROR,
1481
15
        (errcode(ERRCODE_SYNTAX_ERROR),
1482
15
         errmsg("type attribute \"subtype\" is required")));
1483
  /* disallow ranges of pseudotypes */
1484
15
  if (get_typtype(rangeSubtype) == TYPTYPE_PSEUDO)
1485
15
    ereport(ERROR,
1486
15
        (errcode(ERRCODE_DATATYPE_MISMATCH),
1487
15
         errmsg("range subtype cannot be %s",
1488
15
            format_type_be(rangeSubtype))));
1489
1490
  /* Identify subopclass */
1491
15
  rangeSubOpclass = findRangeSubOpclass(rangeSubOpclassName, rangeSubtype);
1492
1493
  /* Identify collation to use, if any */
1494
15
  if (type_is_collatable(rangeSubtype))
1495
5
  {
1496
5
    if (rangeCollationName != NIL)
1497
5
      rangeCollation = get_collation_oid(rangeCollationName, false);
1498
0
    else
1499
0
      rangeCollation = get_typcollation(rangeSubtype);
1500
5
  }
1501
10
  else
1502
10
  {
1503
10
    if (rangeCollationName != NIL)
1504
10
      ereport(ERROR,
1505
10
          (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1506
10
           errmsg("range collation specified but subtype does not support collation")));
1507
10
    rangeCollation = InvalidOid;
1508
10
  }
1509
1510
  /* Identify support functions, if provided */
1511
15
  if (rangeCanonicalName != NIL)
1512
0
    rangeCanonical = findRangeCanonicalFunction(rangeCanonicalName,
1513
0
                          typoid);
1514
15
  else
1515
15
    rangeCanonical = InvalidOid;
1516
1517
15
  if (rangeSubtypeDiffName != NIL)
1518
2
    rangeSubtypeDiff = findRangeSubtypeDiffFunction(rangeSubtypeDiffName,
1519
2
                            rangeSubtype);
1520
13
  else
1521
13
    rangeSubtypeDiff = InvalidOid;
1522
1523
15
  get_typlenbyvalalign(rangeSubtype,
1524
15
             &subtyplen, &subtypbyval, &subtypalign);
1525
1526
  /* alignment must be 'i' or 'd' for ranges */
1527
15
  alignment = (subtypalign == 'd') ? 
'd'4
:
'i'11
;
1528
1529
  /* Allocate OID for array type */
1530
15
  rangeArrayOid = AssignTypeArrayOid();
1531
1532
  /* Create the pg_type entry */
1533
15
  address =
1534
15
    TypeCreate(InvalidOid, /* no predetermined type OID */
1535
15
           typeName,  /* type name */
1536
15
           typeNamespace, /* namespace */
1537
15
           InvalidOid, /* relation oid (n/a here) */
1538
15
           0,     /* relation kind (ditto) */
1539
15
           GetUserId(), /* owner's ID */
1540
15
           -1,      /* internal size (always varlena) */
1541
15
           TYPTYPE_RANGE, /* type-type (range type) */
1542
15
           TYPCATEGORY_RANGE, /* type-category (range type) */
1543
15
           false,   /* range types are never preferred */
1544
15
           DEFAULT_TYPDELIM, /* array element delimiter */
1545
15
           F_RANGE_IN, /* input procedure */
1546
15
           F_RANGE_OUT, /* output procedure */
1547
15
           F_RANGE_RECV, /* receive procedure */
1548
15
           F_RANGE_SEND, /* send procedure */
1549
15
           InvalidOid, /* typmodin procedure - none */
1550
15
           InvalidOid, /* typmodout procedure - none */
1551
15
           F_RANGE_TYPANALYZE, /* analyze procedure */
1552
15
           InvalidOid, /* element type ID - none */
1553
15
           false,   /* this is not an array type */
1554
15
           rangeArrayOid, /* array type we are about to create */
1555
15
           InvalidOid, /* base type ID (only for domains) */
1556
15
           NULL,    /* never a default type value */
1557
15
           NULL,    /* no binary form available either */
1558
15
           false,   /* never passed by value */
1559
15
           alignment, /* alignment */
1560
15
           'x',     /* TOAST strategy (always extended) */
1561
15
           -1,      /* typMod (Domains only) */
1562
15
           0,     /* Array dimensions of typbasetype */
1563
15
           false,   /* Type NOT NULL */
1564
15
           InvalidOid, /* type's collation (ranges never have one) */
1565
15
           false);    /* whether relation is shared (n/a here) */
1566
15
  Assert(typoid == address.objectId);
1567
1568
  /* Create the entry in pg_range */
1569
15
  RangeCreate(typoid, rangeSubtype, rangeCollation, rangeSubOpclass,
1570
15
        rangeCanonical, rangeSubtypeDiff);
1571
1572
  /*
1573
   * Create the array type that goes with it.
1574
   */
1575
15
  rangeArrayName = makeArrayTypeName(typeName, typeNamespace);
1576
1577
15
  TypeCreate(rangeArrayOid, /* force assignment of this type OID */
1578
15
         rangeArrayName,  /* type name */
1579
15
         typeNamespace, /* namespace */
1580
15
         InvalidOid,   /* relation oid (n/a here) */
1581
15
         0,       /* relation kind (ditto) */
1582
15
         GetUserId(),   /* owner's ID */
1583
15
         -1,        /* internal size (always varlena) */
1584
15
         TYPTYPE_BASE, /* type-type (base type) */
1585
15
         TYPCATEGORY_ARRAY, /* type-category (array) */
1586
15
         false,     /* array types are never preferred */
1587
15
         DEFAULT_TYPDELIM, /* array element delimiter */
1588
15
         F_ARRAY_IN,   /* input procedure */
1589
15
         F_ARRAY_OUT,   /* output procedure */
1590
15
         F_ARRAY_RECV, /* receive procedure */
1591
15
         F_ARRAY_SEND, /* send procedure */
1592
15
         InvalidOid,   /* typmodin procedure - none */
1593
15
         InvalidOid,   /* typmodout procedure - none */
1594
15
         F_ARRAY_TYPANALYZE, /* analyze procedure */
1595
15
         typoid,      /* element type ID */
1596
15
         true,      /* yes this is an array type */
1597
15
         InvalidOid,   /* no further array type */
1598
15
         InvalidOid,   /* base type ID */
1599
15
         NULL,      /* never a default type value */
1600
15
         NULL,      /* binary default isn't sent either */
1601
15
         false,     /* never passed by value */
1602
15
         alignment,   /* alignment - same as range's */
1603
15
         'x',       /* ARRAY is always toastable */
1604
15
         -1,        /* typMod (Domains only) */
1605
15
         0,       /* Array dimensions of typbasetype */
1606
15
         false,     /* Type NOT NULL */
1607
15
         InvalidOid,   /* typcollation */
1608
15
         false);      /* whether relation is shared (n/a here) */
1609
1610
15
  pfree(rangeArrayName);
1611
1612
  /* And create the constructor functions for this range type */
1613
15
  makeRangeConstructors(typeName, typeNamespace, typoid, rangeSubtype);
1614
1615
15
  return address;
1616
15
}
1617
1618
/*
1619
 * Because there may exist several range types over the same subtype, the
1620
 * range type can't be uniquely determined from the subtype.  So it's
1621
 * impossible to define a polymorphic constructor; we have to generate new
1622
 * constructor functions explicitly for each range type.
1623
 *
1624
 * We actually define 4 functions, with 0 through 3 arguments.  This is just
1625
 * to offer more convenience for the user.
1626
 */
1627
static void
1628
makeRangeConstructors(const char *name, Oid namespace,
1629
            Oid rangeOid, Oid subtype)
1630
14
{
1631
14
  static const char *const prosrc[2] = {"range_constructor2",
1632
14
  "range_constructor3"};
1633
14
  static const int pronargs[2] = {2, 3};
1634
1635
14
  Oid     constructorArgTypes[3];
1636
14
  ObjectAddress myself,
1637
14
        referenced;
1638
14
  int     i;
1639
1640
14
  constructorArgTypes[0] = subtype;
1641
14
  constructorArgTypes[1] = subtype;
1642
14
  constructorArgTypes[2] = TEXTOID;
1643
1644
14
  referenced.classId = TypeRelationId;
1645
14
  referenced.objectId = rangeOid;
1646
14
  referenced.objectSubId = 0;
1647
1648
42
  for (i = 0; i < lengthof(prosrc); 
i++28
)
1649
28
  {
1650
28
    oidvector  *constructorArgTypesVector;
1651
1652
28
    constructorArgTypesVector = buildoidvector(constructorArgTypes,
1653
28
                           pronargs[i]);
1654
1655
28
    myself = ProcedureCreate(name,  /* name: same as range type */
1656
28
                 namespace, /* namespace */
1657
28
                 false, /* replace */
1658
28
                 false, /* returns set */
1659
28
                 rangeOid,  /* return type */
1660
28
                 BOOTSTRAP_SUPERUSERID, /* proowner */
1661
28
                 INTERNALlanguageId, /* language */
1662
28
                 F_FMGR_INTERNAL_VALIDATOR, /* language validator */
1663
28
                 prosrc[i], /* prosrc */
1664
28
                 NULL,  /* probin */
1665
28
                 PROKIND_FUNCTION,
1666
28
                 false, /* security_definer */
1667
28
                 false, /* leakproof */
1668
28
                 false, /* isStrict */
1669
28
                 PROVOLATILE_IMMUTABLE, /* volatility */
1670
28
                 PROPARALLEL_SAFE, /* parallel safety */
1671
28
                 constructorArgTypesVector, /* parameterTypes */
1672
28
                 PointerGetDatum(NULL), /* allParameterTypes */
1673
28
                 PointerGetDatum(NULL), /* parameterModes */
1674
28
                 PointerGetDatum(NULL), /* parameterNames */
1675
28
                 NIL, /* parameterDefaults */
1676
28
                 PointerGetDatum(NULL), /* trftypes */
1677
28
                 PointerGetDatum(NULL), /* proconfig */
1678
28
                 1.0, /* procost */
1679
28
                 0.0);  /* prorows */
1680
1681
    /*
1682
     * Make the constructors internally-dependent on the range type so
1683
     * that they go away silently when the type is dropped.  Note that
1684
     * pg_dump depends on this choice to avoid dumping the constructors.
1685
     */
1686
28
    recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
1687
28
  }
1688
14
}
1689
1690
1691
/*
1692
 * Find suitable I/O functions for a type.
1693
 *
1694
 * typeOid is the type's OID (which will already exist, if only as a shell
1695
 * type).
1696
 */
1697
1698
static Oid
1699
findTypeInputFunction(List *procname, Oid typeOid)
1700
105
{
1701
105
  Oid     argList[3];
1702
105
  Oid     procOid;
1703
1704
  /*
1705
   * Input functions can take a single argument of type CSTRING, or three
1706
   * arguments (string, typioparam OID, typmod).
1707
   *
1708
   * For backwards compatibility we allow OPAQUE in place of CSTRING; if we
1709
   * see this, we issue a warning and fix up the pg_proc entry.
1710
   */
1711
105
  argList[0] = CSTRINGOID;
1712
1713
105
  procOid = LookupFuncName(procname, 1, argList, true);
1714
105
  if (OidIsValid(procOid))
1715
91
    return procOid;
1716
1717
14
  argList[1] = OIDOID;
1718
14
  argList[2] = INT4OID;
1719
1720
14
  procOid = LookupFuncName(procname, 3, argList, true);
1721
14
  if (OidIsValid(procOid))
1722
14
    return procOid;
1723
1724
  /* No luck, try it with OPAQUE */
1725
0
  argList[0] = OPAQUEOID;
1726
1727
0
  procOid = LookupFuncName(procname, 1, argList, true);
1728
1729
0
  if (!OidIsValid(procOid))
1730
0
  {
1731
0
    argList[1] = OIDOID;
1732
0
    argList[2] = INT4OID;
1733
1734
0
    procOid = LookupFuncName(procname, 3, argList, true);
1735
0
  }
1736
1737
0
  if (OidIsValid(procOid))
1738
0
  {
1739
    /* Found, but must complain and fix the pg_proc entry */
1740
0
    ereport(WARNING,
1741
0
        (errmsg("changing argument type of function %s from \"opaque\" to \"cstring\"",
1742
0
            NameListToString(procname))));
1743
0
    SetFunctionArgType(procOid, 0, CSTRINGOID);
1744
1745
    /*
1746
     * Need CommandCounterIncrement since DefineType will likely try to
1747
     * alter the pg_proc tuple again.
1748
     */
1749
0
    CommandCounterIncrement();
1750
1751
0
    return procOid;
1752
0
  }
1753
1754
  /* Use CSTRING (preferred) in the error message */
1755
0
  argList[0] = CSTRINGOID;
1756
1757
0
  ereport(ERROR,
1758
0
      (errcode(ERRCODE_UNDEFINED_FUNCTION),
1759
0
       errmsg("function %s does not exist",
1760
0
          func_signature_string(procname, 1, NIL, argList))));
1761
1762
0
  return InvalidOid;     /* keep compiler quiet */
1763
0
}
1764
1765
static Oid
1766
findTypeOutputFunction(List *procname, Oid typeOid)
1767
105
{
1768
105
  Oid     argList[1];
1769
105
  Oid     procOid;
1770
1771
  /*
1772
   * Output functions can take a single argument of the type.
1773
   *
1774
   * For backwards compatibility we allow OPAQUE in place of the actual type
1775
   * name; if we see this, we issue a warning and fix up the pg_proc entry.
1776
   */
1777
105
  argList[0] = typeOid;
1778
1779
105
  procOid = LookupFuncName(procname, 1, argList, true);
1780
105
  if (OidIsValid(procOid))
1781
85
    return procOid;
1782
1783
  /* No luck, try it with OPAQUE */
1784
20
  argList[0] = OPAQUEOID;
1785
1786
20
  procOid = LookupFuncName(procname, 1, argList, true);
1787
1788
20
  if (OidIsValid(procOid))
1789
10
  {
1790
    /* Found, but must complain and fix the pg_proc entry */
1791
10
    ereport(WARNING,
1792
10
        (errmsg("changing argument type of function %s from \"opaque\" to %s",
1793
10
            NameListToString(procname), format_type_be(typeOid))));
1794
10
    SetFunctionArgType(procOid, 0, typeOid);
1795
1796
    /*
1797
     * Need CommandCounterIncrement since DefineType will likely try to
1798
     * alter the pg_proc tuple again.
1799
     */
1800
10
    CommandCounterIncrement();
1801
1802
10
    return procOid;
1803
10
  }
1804
1805
  /* Use type name, not OPAQUE, in the failure message. */
1806
10
  argList[0] = typeOid;
1807
1808
10
  ereport(ERROR,
1809
10
      (errcode(ERRCODE_UNDEFINED_FUNCTION),
1810
10
       errmsg("function %s does not exist",
1811
10
          func_signature_string(procname, 1, NIL, argList))));
1812
1813
10
  return InvalidOid;     /* keep compiler quiet */
1814
10
}
1815
1816
static Oid
1817
findTypeReceiveFunction(List *procname, Oid typeOid)
1818
6
{
1819
6
  Oid     argList[3];
1820
6
  Oid     procOid;
1821
1822
  /*
1823
   * Receive functions can take a single argument of type INTERNAL, or three
1824
   * arguments (internal, typioparam OID, typmod).
1825
   */
1826
6
  argList[0] = INTERNALOID;
1827
1828
6
  procOid = LookupFuncName(procname, 1, argList, true);
1829
6
  if (OidIsValid(procOid))
1830
2
    return procOid;
1831
1832
4
  argList[1] = OIDOID;
1833
4
  argList[2] = INT4OID;
1834
1835
4
  procOid = LookupFuncName(procname, 3, argList, true);
1836
4
  if (OidIsValid(procOid))
1837
4
    return procOid;
1838
1839
0
  ereport(ERROR,
1840
0
      (errcode(ERRCODE_UNDEFINED_FUNCTION),
1841
0
       errmsg("function %s does not exist",
1842
0
          func_signature_string(procname, 1, NIL, argList))));
1843
1844
0
  return InvalidOid;     /* keep compiler quiet */
1845
0
}
1846
1847
static Oid
1848
findTypeSendFunction(List *procname, Oid typeOid)
1849
6
{
1850
6
  Oid     argList[1];
1851
6
  Oid     procOid;
1852
1853
  /*
1854
   * Send functions can take a single argument of the type.
1855
   */
1856
6
  argList[0] = typeOid;
1857
1858
6
  procOid = LookupFuncName(procname, 1, argList, true);
1859
6
  if (OidIsValid(procOid))
1860
6
    return procOid;
1861
1862
0
  ereport(ERROR,
1863
0
      (errcode(ERRCODE_UNDEFINED_FUNCTION),
1864
0
       errmsg("function %s does not exist",
1865
0
          func_signature_string(procname, 1, NIL, argList))));
1866
1867
0
  return InvalidOid;     /* keep compiler quiet */
1868
0
}
1869
1870
static Oid
1871
findTypeTypmodinFunction(List *procname)
1872
14
{
1873
14
  Oid     argList[1];
1874
14
  Oid     procOid;
1875
1876
  /*
1877
   * typmodin functions always take one cstring[] argument and return int4.
1878
   */
1879
14
  argList[0] = CSTRINGARRAYOID;
1880
1881
14
  procOid = LookupFuncName(procname, 1, argList, true);
1882
14
  if (!OidIsValid(procOid))
1883
14
    ereport(ERROR,
1884
14
        (errcode(ERRCODE_UNDEFINED_FUNCTION),
1885
14
         errmsg("function %s does not exist",
1886
14
            func_signature_string(procname, 1, NIL, argList))));
1887
1888
14
  if (get_func_rettype(procOid) != INT4OID)
1889
14
    ereport(ERROR,
1890
14
        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1891
14
         errmsg("typmod_in function %s must return type %s",
1892
14
            NameListToString(procname), "integer")));
1893
1894
14
  return procOid;
1895
14
}
1896
1897
static Oid
1898
findTypeTypmodoutFunction(List *procname)
1899
14
{
1900
14
  Oid     argList[1];
1901
14
  Oid     procOid;
1902
1903
  /*
1904
   * typmodout functions always take one int4 argument and return cstring.
1905
   */
1906
14
  argList[0] = INT4OID;
1907
1908
14
  procOid = LookupFuncName(procname, 1, argList, true);
1909
14
  if (!OidIsValid(procOid))
1910
14
    ereport(ERROR,
1911
14
        (errcode(ERRCODE_UNDEFINED_FUNCTION),
1912
14
         errmsg("function %s does not exist",
1913
14
            func_signature_string(procname, 1, NIL, argList))));
1914
1915
14
  if (get_func_rettype(procOid) != CSTRINGOID)
1916
14
    ereport(ERROR,
1917
14
        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1918
14
         errmsg("typmod_out function %s must return type %s",
1919
14
            NameListToString(procname), "cstring")));
1920
1921
14
  return procOid;
1922
14
}
1923
1924
static Oid
1925
findTypeAnalyzeFunction(List *procname, Oid typeOid)
1926
0
{
1927
0
  Oid     argList[1];
1928
0
  Oid     procOid;
1929
1930
  /*
1931
   * Analyze functions always take one INTERNAL argument and return bool.
1932
   */
1933
0
  argList[0] = INTERNALOID;
1934
1935
0
  procOid = LookupFuncName(procname, 1, argList, true);
1936
0
  if (!OidIsValid(procOid))
1937
0
    ereport(ERROR,
1938
0
        (errcode(ERRCODE_UNDEFINED_FUNCTION),
1939
0
         errmsg("function %s does not exist",
1940
0
            func_signature_string(procname, 1, NIL, argList))));
1941
1942
0
  if (get_func_rettype(procOid) != BOOLOID)
1943
0
    ereport(ERROR,
1944
0
        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1945
0
         errmsg("type analyze function %s must return type %s",
1946
0
            NameListToString(procname), "boolean")));
1947
1948
0
  return procOid;
1949
0
}
1950
1951
/*
1952
 * Find suitable support functions and opclasses for a range type.
1953
 */
1954
1955
/*
1956
 * Find named btree opclass for subtype, or default btree opclass if
1957
 * opcname is NIL.
1958
 */
1959
static Oid
1960
findRangeSubOpclass(List *opcname, Oid subtype)
1961
15
{
1962
15
  Oid     opcid;
1963
15
  Oid     opInputType;
1964
1965
15
  if (opcname != NIL)
1966
0
  {
1967
0
    opcid = get_opclass_oid(IsYugaByteEnabled() ? LSM_AM_OID : BTREE_AM_OID, opcname, false);
1968
1969
    /*
1970
     * Verify that the operator class accepts this datatype. Note we will
1971
     * accept binary compatibility.
1972
     */
1973
0
    opInputType = get_opclass_input_type(opcid);
1974
0
    if (!IsBinaryCoercible(subtype, opInputType))
1975
0
      ereport(ERROR,
1976
0
          (errcode(ERRCODE_DATATYPE_MISMATCH),
1977
0
           errmsg("operator class \"%s\" does not accept data type %s",
1978
0
              NameListToString(opcname),
1979
0
              format_type_be(subtype))));
1980
0
  }
1981
15
  else
1982
15
  {
1983
15
    opcid = GetDefaultOpClass(subtype, IsYugaByteEnabled() ? LSM_AM_OID : 
BTREE_AM_OID0
);
1984
15
    if (!OidIsValid(opcid))
1985
0
    {
1986
      /* We spell the error message identically to ResolveOpClass */
1987
0
      ereport(ERROR,
1988
0
          (errcode(ERRCODE_UNDEFINED_OBJECT),
1989
0
           errmsg("data type %s has no default operator class for access method \"%s\"",
1990
0
              format_type_be(subtype), "btree"),
1991
0
           errhint("You must specify an operator class for the range type or define a default operator class for the subtype.")));
1992
0
    }
1993
15
  }
1994
1995
15
  return opcid;
1996
15
}
1997
1998
static Oid
1999
findRangeCanonicalFunction(List *procname, Oid typeOid)
2000
0
{
2001
0
  Oid     argList[1];
2002
0
  Oid     procOid;
2003
0
  AclResult aclresult;
2004
2005
  /*
2006
   * Range canonical functions must take and return the range type, and must
2007
   * be immutable.
2008
   */
2009
0
  argList[0] = typeOid;
2010
2011
0
  procOid = LookupFuncName(procname, 1, argList, true);
2012
2013
0
  if (!OidIsValid(procOid))
2014
0
    ereport(ERROR,
2015
0
        (errcode(ERRCODE_UNDEFINED_FUNCTION),
2016
0
         errmsg("function %s does not exist",
2017
0
            func_signature_string(procname, 1, NIL, argList))));
2018
2019
0
  if (get_func_rettype(procOid) != typeOid)
2020
0
    ereport(ERROR,
2021
0
        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2022
0
         errmsg("range canonical function %s must return range type",
2023
0
            func_signature_string(procname, 1, NIL, argList))));
2024
2025
0
  if (func_volatile(procOid) != PROVOLATILE_IMMUTABLE)
2026
0
    ereport(ERROR,
2027
0
        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2028
0
         errmsg("range canonical function %s must be immutable",
2029
0
            func_signature_string(procname, 1, NIL, argList))));
2030
2031
  /* Also, range type's creator must have permission to call function */
2032
0
  aclresult = pg_proc_aclcheck(procOid, GetUserId(), ACL_EXECUTE);
2033
0
  if (aclresult != ACLCHECK_OK)
2034
0
    aclcheck_error(aclresult, OBJECT_FUNCTION, get_func_name(procOid));
2035
2036
0
  return procOid;
2037
0
}
2038
2039
static Oid
2040
findRangeSubtypeDiffFunction(List *procname, Oid subtype)
2041
2
{
2042
2
  Oid     argList[2];
2043
2
  Oid     procOid;
2044
2
  AclResult aclresult;
2045
2046
  /*
2047
   * Range subtype diff functions must take two arguments of the subtype,
2048
   * must return float8, and must be immutable.
2049
   */
2050
2
  argList[0] = subtype;
2051
2
  argList[1] = subtype;
2052
2053
2
  procOid = LookupFuncName(procname, 2, argList, true);
2054
2055
2
  if (!OidIsValid(procOid))
2056
2
    ereport(ERROR,
2057
2
        (errcode(ERRCODE_UNDEFINED_FUNCTION),
2058
2
         errmsg("function %s does not exist",
2059
2
            func_signature_string(procname, 2, NIL, argList))));
2060
2061
2
  if (get_func_rettype(procOid) != FLOAT8OID)
2062
2
    ereport(ERROR,
2063
2
        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2064
2
         errmsg("range subtype diff function %s must return type %s",
2065
2
            func_signature_string(procname, 2, NIL, argList),
2066
2
            "double precision")));
2067
2068
2
  if (func_volatile(procOid) != PROVOLATILE_IMMUTABLE)
2069
2
    ereport(ERROR,
2070
2
        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2071
2
         errmsg("range subtype diff function %s must be immutable",
2072
2
            func_signature_string(procname, 2, NIL, argList))));
2073
2074
  /* Also, range type's creator must have permission to call function */
2075
2
  aclresult = pg_proc_aclcheck(procOid, GetUserId(), ACL_EXECUTE);
2076
2
  if (aclresult != ACLCHECK_OK)
2077
0
    aclcheck_error(aclresult, OBJECT_FUNCTION, get_func_name(procOid));
2078
2079
2
  return procOid;
2080
2
}
2081
2082
/*
2083
 *  AssignTypeArrayOid
2084
 *
2085
 *  Pre-assign the type's array OID for use in pg_type.typarray
2086
 */
2087
Oid
2088
AssignTypeArrayOid(void)
2089
4.86k
{
2090
4.86k
  Oid     type_array_oid;
2091
2092
  /* Use binary-upgrade override for pg_type.typarray? */
2093
4.86k
  if (IsBinaryUpgrade || yb_binary_restore)
2094
31
  {
2095
31
    if (!OidIsValid(binary_upgrade_next_array_pg_type_oid))
2096
31
      ereport(ERROR,
2097
31
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2098
31
           errmsg("pg_type array OID value not set when in binary upgrade mode")));
2099
2100
31
    type_array_oid = binary_upgrade_next_array_pg_type_oid;
2101
31
    binary_upgrade_next_array_pg_type_oid = InvalidOid;
2102
31
  }
2103
4.83k
  else
2104
4.83k
  {
2105
4.83k
    Relation  pg_type = heap_open(TypeRelationId, AccessShareLock);
2106
2107
4.83k
    type_array_oid = GetNewOid(pg_type);
2108
4.83k
    heap_close(pg_type, AccessShareLock);
2109
4.83k
  }
2110
2111
4.86k
  return type_array_oid;
2112
4.86k
}
2113
2114
2115
/*-------------------------------------------------------------------
2116
 * DefineCompositeType
2117
 *
2118
 * Create a Composite Type relation.
2119
 * `DefineRelation' does all the work, we just provide the correct
2120
 * arguments!
2121
 *
2122
 * If the relation already exists, then 'DefineRelation' will abort
2123
 * the xact...
2124
 *
2125
 * Return type is the new type's object address.
2126
 *-------------------------------------------------------------------
2127
 */
2128
ObjectAddress
2129
DefineCompositeType(RangeVar *typevar, List *coldeflist)
2130
82
{
2131
82
  CreateStmt *createStmt = makeNode(CreateStmt);
2132
0
  Oid     old_type_oid;
2133
82
  Oid     typeNamespace;
2134
82
  ObjectAddress address;
2135
2136
  /*
2137
   * now set the parameters for keys/inheritance etc. All of these are
2138
   * uninteresting for composite types...
2139
   */
2140
82
  createStmt->relation = typevar;
2141
82
  createStmt->tableElts = coldeflist;
2142
82
  createStmt->inhRelations = NIL;
2143
82
  createStmt->constraints = NIL;
2144
82
  createStmt->options = NIL;
2145
82
  createStmt->oncommit = ONCOMMIT_NOOP;
2146
82
  createStmt->tablespacename = NULL;
2147
82
  createStmt->if_not_exists = false;
2148
2149
  /*
2150
   * Check for collision with an existing type name. If there is one and
2151
   * it's an autogenerated array, we can rename it out of the way.  This
2152
   * check is here mainly to get a better error message about a "type"
2153
   * instead of below about a "relation".
2154
   */
2155
82
  typeNamespace = RangeVarGetAndCheckCreationNamespace(createStmt->relation,
2156
82
                             NoLock, NULL);
2157
82
  RangeVarAdjustRelationPersistence(createStmt->relation, typeNamespace);
2158
82
  old_type_oid =
2159
82
    GetSysCacheOid2(TYPENAMENSP,
2160
82
            CStringGetDatum(createStmt->relation->relname),
2161
82
            ObjectIdGetDatum(typeNamespace));
2162
82
  if (OidIsValid(old_type_oid))
2163
0
  {
2164
0
    if (!moveArrayTypeName(old_type_oid, createStmt->relation->relname, typeNamespace))
2165
0
      ereport(ERROR,
2166
0
          (errcode(ERRCODE_DUPLICATE_OBJECT),
2167
0
           errmsg("type \"%s\" already exists", createStmt->relation->relname)));
2168
0
  }
2169
2170
  /*
2171
   * Finally create the relation.  This also creates the type.
2172
   */
2173
82
  DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, &address,
2174
82
           NULL);
2175
2176
82
  return address;
2177
82
}
2178
2179
/*
2180
 * AlterDomainDefault
2181
 *
2182
 * Routine implementing ALTER DOMAIN SET/DROP DEFAULT statements.
2183
 *
2184
 * Returns ObjectAddress of the modified domain.
2185
 */
2186
ObjectAddress
2187
AlterDomainDefault(List *names, Node *defaultRaw)
2188
4
{
2189
4
  TypeName   *typename;
2190
4
  Oid     domainoid;
2191
4
  HeapTuple tup;
2192
4
  ParseState *pstate;
2193
4
  Relation  rel;
2194
4
  char     *defaultValue;
2195
4
  Node     *defaultExpr = NULL; /* NULL if no default specified */
2196
4
  Acl      *typacl;
2197
4
  Datum   aclDatum;
2198
4
  bool    isNull;
2199
4
  Datum   new_record[Natts_pg_type];
2200
4
  bool    new_record_nulls[Natts_pg_type];
2201
4
  bool    new_record_repl[Natts_pg_type];
2202
4
  HeapTuple newtuple;
2203
4
  Form_pg_type typTup;
2204
4
  ObjectAddress address;
2205
2206
  /* Make a TypeName so we can use standard type lookup machinery */
2207
4
  typename = makeTypeNameFromNameList(names);
2208
4
  domainoid = typenameTypeId(NULL, typename);
2209
2210
  /* Look up the domain in the type table */
2211
4
  rel = heap_open(TypeRelationId, RowExclusiveLock);
2212
2213
4
  tup = SearchSysCacheCopy1(TYPEOID, ObjectIdGetDatum(domainoid));
2214
4
  if (!HeapTupleIsValid(tup))
2215
0
    elog(ERROR, "cache lookup failed for type %u", domainoid);
2216
4
  typTup = (Form_pg_type) GETSTRUCT(tup);
2217
2218
  /* Check it's a domain and check user has permission for ALTER DOMAIN */
2219
4
  checkDomainOwner(tup);
2220
2221
  /* Setup new tuple */
2222
4
  MemSet(new_record, (Datum) 0, sizeof(new_record));
2223
4
  MemSet(new_record_nulls, false, sizeof(new_record_nulls));
2224
4
  MemSet(new_record_repl, false, sizeof(new_record_repl));
2225
2226
  /* Store the new default into the tuple */
2227
4
  if (defaultRaw)
2228
2
  {
2229
    /* Create a dummy ParseState for transformExpr */
2230
2
    pstate = make_parsestate(NULL);
2231
2232
    /*
2233
     * Cook the colDef->raw_expr into an expression. Note: Name is
2234
     * strictly for error message
2235
     */
2236
2
    defaultExpr = cookDefault(pstate, defaultRaw,
2237
2
                  typTup->typbasetype,
2238
2
                  typTup->typtypmod,
2239
2
                  NameStr(typTup->typname));
2240
2241
    /*
2242
     * If the expression is just a NULL constant, we treat the command
2243
     * like ALTER ... DROP DEFAULT.  (But see note for same test in
2244
     * DefineDomain.)
2245
     */
2246
2
    if (defaultExpr == NULL ||
2247
2
      (IsA(defaultExpr, Const) &&((Const *) defaultExpr)->constisnull))
2248
0
    {
2249
      /* Default is NULL, drop it */
2250
0
      new_record_nulls[Anum_pg_type_typdefaultbin - 1] = true;
2251
0
      new_record_repl[Anum_pg_type_typdefaultbin - 1] = true;
2252
0
      new_record_nulls[Anum_pg_type_typdefault - 1] = true;
2253
0
      new_record_repl[Anum_pg_type_typdefault - 1] = true;
2254
0
    }
2255
2
    else
2256
2
    {
2257
      /*
2258
       * Expression must be stored as a nodeToString result, but we also
2259
       * require a valid textual representation (mainly to make life
2260
       * easier for pg_dump).
2261
       */
2262
2
      defaultValue = deparse_expression(defaultExpr,
2263
2
                        NIL, false, false);
2264
2265
      /*
2266
       * Form an updated tuple with the new default and write it back.
2267
       */
2268
2
      new_record[Anum_pg_type_typdefaultbin - 1] = CStringGetTextDatum(nodeToString(defaultExpr));
2269
2270
2
      new_record_repl[Anum_pg_type_typdefaultbin - 1] = true;
2271
2
      new_record[Anum_pg_type_typdefault - 1] = CStringGetTextDatum(defaultValue);
2272
2
      new_record_repl[Anum_pg_type_typdefault - 1] = true;
2273
2
    }
2274
2
  }
2275
2
  else
2276
2
  {
2277
    /* ALTER ... DROP DEFAULT */
2278
2
    new_record_nulls[Anum_pg_type_typdefaultbin - 1] = true;
2279
2
    new_record_repl[Anum_pg_type_typdefaultbin - 1] = true;
2280
2
    new_record_nulls[Anum_pg_type_typdefault - 1] = true;
2281
2
    new_record_repl[Anum_pg_type_typdefault - 1] = true;
2282
2
  }
2283
2284
4
  newtuple = heap_modify_tuple(tup, RelationGetDescr(rel),
2285
4
                 new_record, new_record_nulls,
2286
4
                 new_record_repl);
2287
2288
4
  CatalogTupleUpdate(rel, &tup->t_self, newtuple);
2289
2290
  /* Must extract ACL for use of GenerateTypeDependencies */
2291
4
  aclDatum = heap_getattr(newtuple, Anum_pg_type_typacl,
2292
4
              RelationGetDescr(rel), &isNull);
2293
4
  if (isNull)
2294
4
    typacl = NULL;
2295
0
  else
2296
0
    typacl = DatumGetAclPCopy(aclDatum);
2297
2298
  /* Rebuild dependencies */
2299
4
  GenerateTypeDependencies(domainoid,
2300
4
               (Form_pg_type) GETSTRUCT(newtuple),
2301
4
               defaultExpr,
2302
4
               typacl,
2303
4
               0, /* relation kind is n/a */
2304
4
               false, /* a domain isn't an implicit array */
2305
4
               false, /* nor is it any kind of dependent type */
2306
4
               true, /* We do need to rebuild dependencies */
2307
4
               false, /* not a system relation rowtype */
2308
4
               false); /* not a shared relation rowtype */
2309
2310
4
  InvokeObjectPostAlterHook(TypeRelationId, domainoid, 0);
2311
2312
4
  ObjectAddressSet(address, TypeRelationId, domainoid);
2313
2314
  /* Clean up */
2315
4
  heap_close(rel, NoLock);
2316
4
  heap_freetuple(newtuple);
2317
2318
4
  return address;
2319
4
}
2320
2321
/*
2322
 * AlterDomainNotNull
2323
 *
2324
 * Routine implementing ALTER DOMAIN SET/DROP NOT NULL statements.
2325
 *
2326
 * Returns ObjectAddress of the modified domain.
2327
 */
2328
ObjectAddress
2329
AlterDomainNotNull(List *names, bool notNull)
2330
0
{
2331
0
  TypeName   *typename;
2332
0
  Oid     domainoid;
2333
0
  Relation  typrel;
2334
0
  HeapTuple tup;
2335
0
  Form_pg_type typTup;
2336
0
  ObjectAddress address = InvalidObjectAddress;
2337
2338
  /* Make a TypeName so we can use standard type lookup machinery */
2339
0
  typename = makeTypeNameFromNameList(names);
2340
0
  domainoid = typenameTypeId(NULL, typename);
2341
2342
  /* Look up the domain in the type table */
2343
0
  typrel = heap_open(TypeRelationId, RowExclusiveLock);
2344
2345
0
  tup = SearchSysCacheCopy1(TYPEOID, ObjectIdGetDatum(domainoid));
2346
0
  if (!HeapTupleIsValid(tup))
2347
0
    elog(ERROR, "cache lookup failed for type %u", domainoid);
2348
0
  typTup = (Form_pg_type) GETSTRUCT(tup);
2349
2350
  /* Check it's a domain and check user has permission for ALTER DOMAIN */
2351
0
  checkDomainOwner(tup);
2352
2353
  /* Is the domain already set to the desired constraint? */
2354
0
  if (typTup->typnotnull == notNull)
2355
0
  {
2356
0
    heap_close(typrel, RowExclusiveLock);
2357
0
    return address;
2358
0
  }
2359
2360
  /* Adding a NOT NULL constraint requires checking existing columns */
2361
0
  if (notNull)
2362
0
  {
2363
0
    List     *rels;
2364
0
    ListCell   *rt;
2365
2366
    /* Fetch relation list with attributes based on this domain */
2367
    /* ShareLock is sufficient to prevent concurrent data changes */
2368
2369
0
    rels = get_rels_with_domain(domainoid, ShareLock);
2370
2371
0
    foreach(rt, rels)
2372
0
    {
2373
0
      RelToCheck *rtc = (RelToCheck *) lfirst(rt);
2374
0
      Relation  testrel = rtc->rel;
2375
0
      TupleDesc tupdesc = RelationGetDescr(testrel);
2376
0
      HeapScanDesc scan;
2377
0
      HeapTuple tuple;
2378
0
      Snapshot  snapshot;
2379
2380
      /* Scan all tuples in this relation */
2381
0
      snapshot = RegisterSnapshot(GetLatestSnapshot());
2382
0
      scan = heap_beginscan(testrel, snapshot, 0, NULL);
2383
0
      while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
2384
0
      {
2385
0
        int     i;
2386
2387
        /* Test attributes that are of the domain */
2388
0
        for (i = 0; i < rtc->natts; i++)
2389
0
        {
2390
0
          int     attnum = rtc->atts[i];
2391
0
          Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
2392
2393
0
          if (heap_attisnull(tuple, attnum, tupdesc))
2394
0
          {
2395
            /*
2396
             * In principle the auxiliary information for this
2397
             * error should be errdatatype(), but errtablecol()
2398
             * seems considerably more useful in practice.  Since
2399
             * this code only executes in an ALTER DOMAIN command,
2400
             * the client should already know which domain is in
2401
             * question.
2402
             */
2403
0
            ereport(ERROR,
2404
0
                (errcode(ERRCODE_NOT_NULL_VIOLATION),
2405
0
                 errmsg("column \"%s\" of table \"%s\" contains null values",
2406
0
                    NameStr(attr->attname),
2407
0
                    RelationGetRelationName(testrel)),
2408
0
                 errtablecol(testrel, attnum)));
2409
0
          }
2410
0
        }
2411
0
      }
2412
0
      heap_endscan(scan);
2413
0
      UnregisterSnapshot(snapshot);
2414
2415
      /* Close each rel after processing, but keep lock */
2416
0
      heap_close(testrel, NoLock);
2417
0
    }
2418
0
  }
2419
2420
  /*
2421
   * Okay to update pg_type row.  We can scribble on typTup because it's a
2422
   * copy.
2423
   */
2424
0
  typTup->typnotnull = notNull;
2425
2426
0
  CatalogTupleUpdate(typrel, &tup->t_self, tup);
2427
2428
0
  InvokeObjectPostAlterHook(TypeRelationId, domainoid, 0);
2429
2430
0
  ObjectAddressSet(address, TypeRelationId, domainoid);
2431
2432
  /* Clean up */
2433
0
  heap_freetuple(tup);
2434
0
  heap_close(typrel, RowExclusiveLock);
2435
2436
0
  return address;
2437
0
}
2438
2439
/*
2440
 * AlterDomainDropConstraint
2441
 *
2442
 * Implements the ALTER DOMAIN DROP CONSTRAINT statement
2443
 *
2444
 * Returns ObjectAddress of the modified domain.
2445
 */
2446
ObjectAddress
2447
AlterDomainDropConstraint(List *names, const char *constrName,
2448
              DropBehavior behavior, bool missing_ok)
2449
0
{
2450
0
  TypeName   *typename;
2451
0
  Oid     domainoid;
2452
0
  HeapTuple tup;
2453
0
  Relation  rel;
2454
0
  Relation  conrel;
2455
0
  SysScanDesc conscan;
2456
0
  ScanKeyData skey[3];
2457
0
  HeapTuple contup;
2458
0
  bool    found = false;
2459
0
  ObjectAddress address;
2460
2461
  /* Make a TypeName so we can use standard type lookup machinery */
2462
0
  typename = makeTypeNameFromNameList(names);
2463
0
  domainoid = typenameTypeId(NULL, typename);
2464
2465
  /* Look up the domain in the type table */
2466
0
  rel = heap_open(TypeRelationId, RowExclusiveLock);
2467
2468
0
  tup = SearchSysCacheCopy1(TYPEOID, ObjectIdGetDatum(domainoid));
2469
0
  if (!HeapTupleIsValid(tup))
2470
0
    elog(ERROR, "cache lookup failed for type %u", domainoid);
2471
2472
  /* Check it's a domain and check user has permission for ALTER DOMAIN */
2473
0
  checkDomainOwner(tup);
2474
2475
  /* Grab an appropriate lock on the pg_constraint relation */
2476
0
  conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
2477
2478
  /* Find and remove the target constraint */
2479
0
  ScanKeyInit(&skey[0],
2480
0
        Anum_pg_constraint_conrelid,
2481
0
        BTEqualStrategyNumber, F_OIDEQ,
2482
0
        ObjectIdGetDatum(InvalidOid));
2483
0
  ScanKeyInit(&skey[1],
2484
0
        Anum_pg_constraint_contypid,
2485
0
        BTEqualStrategyNumber, F_OIDEQ,
2486
0
        ObjectIdGetDatum(domainoid));
2487
0
  ScanKeyInit(&skey[2],
2488
0
        Anum_pg_constraint_conname,
2489
0
        BTEqualStrategyNumber, F_NAMEEQ,
2490
0
        CStringGetDatum(constrName));
2491
2492
0
  conscan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId, true,
2493
0
                 NULL, 3, skey);
2494
2495
  /* There can be at most one matching row */
2496
0
  if ((contup = systable_getnext(conscan)) != NULL)
2497
0
  {
2498
0
    ObjectAddress conobj;
2499
2500
0
    conobj.classId = ConstraintRelationId;
2501
0
    conobj.objectId = HeapTupleGetOid(contup);
2502
0
    conobj.objectSubId = 0;
2503
2504
0
    performDeletion(&conobj, behavior, 0);
2505
0
    found = true;
2506
0
  }
2507
2508
  /* Clean up after the scan */
2509
0
  systable_endscan(conscan);
2510
0
  heap_close(conrel, RowExclusiveLock);
2511
2512
0
  heap_close(rel, NoLock);
2513
2514
0
  if (!found)
2515
0
  {
2516
0
    if (!missing_ok)
2517
0
      ereport(ERROR,
2518
0
          (errcode(ERRCODE_UNDEFINED_OBJECT),
2519
0
           errmsg("constraint \"%s\" of domain \"%s\" does not exist",
2520
0
              constrName, TypeNameToString(typename))));
2521
0
    else
2522
0
      ereport(NOTICE,
2523
0
          (errmsg("constraint \"%s\" of domain \"%s\" does not exist, skipping",
2524
0
              constrName, TypeNameToString(typename))));
2525
0
  }
2526
2527
0
  ObjectAddressSet(address, TypeRelationId, domainoid);
2528
2529
0
  return address;
2530
0
}
2531
2532
/*
2533
 * AlterDomainAddConstraint
2534
 *
2535
 * Implements the ALTER DOMAIN .. ADD CONSTRAINT statement.
2536
 */
2537
ObjectAddress
2538
AlterDomainAddConstraint(List *names, Node *newConstraint,
2539
             ObjectAddress *constrAddr)
2540
0
{
2541
0
  TypeName   *typename;
2542
0
  Oid     domainoid;
2543
0
  Relation  typrel;
2544
0
  HeapTuple tup;
2545
0
  Form_pg_type typTup;
2546
0
  Constraint *constr;
2547
0
  char     *ccbin;
2548
0
  ObjectAddress address;
2549
2550
  /* Make a TypeName so we can use standard type lookup machinery */
2551
0
  typename = makeTypeNameFromNameList(names);
2552
0
  domainoid = typenameTypeId(NULL, typename);
2553
2554
  /* Look up the domain in the type table */
2555
0
  typrel = heap_open(TypeRelationId, RowExclusiveLock);
2556
2557
0
  tup = SearchSysCacheCopy1(TYPEOID, ObjectIdGetDatum(domainoid));
2558
0
  if (!HeapTupleIsValid(tup))
2559
0
    elog(ERROR, "cache lookup failed for type %u", domainoid);
2560
0
  typTup = (Form_pg_type) GETSTRUCT(tup);
2561
2562
  /* Check it's a domain and check user has permission for ALTER DOMAIN */
2563
0
  checkDomainOwner(tup);
2564
2565
0
  if (!IsA(newConstraint, Constraint))
2566
0
    elog(ERROR, "unrecognized node type: %d",
2567
0
       (int) nodeTag(newConstraint));
2568
2569
0
  constr = (Constraint *) newConstraint;
2570
2571
0
  switch (constr->contype)
2572
0
  {
2573
0
    case CONSTR_CHECK:
2574
      /* processed below */
2575
0
      break;
2576
2577
0
    case CONSTR_UNIQUE:
2578
0
      ereport(ERROR,
2579
0
          (errcode(ERRCODE_SYNTAX_ERROR),
2580
0
           errmsg("unique constraints not possible for domains")));
2581
0
      break;
2582
2583
0
    case CONSTR_PRIMARY:
2584
0
      ereport(ERROR,
2585
0
          (errcode(ERRCODE_SYNTAX_ERROR),
2586
0
           errmsg("primary key constraints not possible for domains")));
2587
0
      break;
2588
2589
0
    case CONSTR_EXCLUSION:
2590
0
      ereport(ERROR,
2591
0
          (errcode(ERRCODE_SYNTAX_ERROR),
2592
0
           errmsg("exclusion constraints not possible for domains")));
2593
0
      break;
2594
2595
0
    case CONSTR_FOREIGN:
2596
0
      ereport(ERROR,
2597
0
          (errcode(ERRCODE_SYNTAX_ERROR),
2598
0
           errmsg("foreign key constraints not possible for domains")));
2599
0
      break;
2600
2601
0
    case CONSTR_ATTR_DEFERRABLE:
2602
0
    case CONSTR_ATTR_NOT_DEFERRABLE:
2603
0
    case CONSTR_ATTR_DEFERRED:
2604
0
    case CONSTR_ATTR_IMMEDIATE:
2605
0
      ereport(ERROR,
2606
0
          (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2607
0
           errmsg("specifying constraint deferrability not supported for domains")));
2608
0
      break;
2609
2610
0
    default:
2611
0
      elog(ERROR, "unrecognized constraint subtype: %d",
2612
0
         (int) constr->contype);
2613
0
      break;
2614
0
  }
2615
2616
  /*
2617
   * Since all other constraint types throw errors, this must be a check
2618
   * constraint.  First, process the constraint expression and add an entry
2619
   * to pg_constraint.
2620
   */
2621
2622
0
  ccbin = domainAddConstraint(domainoid, typTup->typnamespace,
2623
0
                typTup->typbasetype, typTup->typtypmod,
2624
0
                constr, NameStr(typTup->typname), constrAddr);
2625
2626
  /*
2627
   * If requested to validate the constraint, test all values stored in the
2628
   * attributes based on the domain the constraint is being added to.
2629
   */
2630
0
  if (!constr->skip_validation)
2631
0
    validateDomainConstraint(domainoid, ccbin);
2632
2633
0
  ObjectAddressSet(address, TypeRelationId, domainoid);
2634
2635
  /* Clean up */
2636
0
  heap_close(typrel, RowExclusiveLock);
2637
2638
0
  return address;
2639
0
}
2640
2641
/*
2642
 * AlterDomainValidateConstraint
2643
 *
2644
 * Implements the ALTER DOMAIN .. VALIDATE CONSTRAINT statement.
2645
 */
2646
ObjectAddress
2647
AlterDomainValidateConstraint(List *names, const char *constrName)
2648
0
{
2649
0
  TypeName   *typename;
2650
0
  Oid     domainoid;
2651
0
  Relation  typrel;
2652
0
  Relation  conrel;
2653
0
  HeapTuple tup;
2654
0
  Form_pg_constraint con;
2655
0
  Form_pg_constraint copy_con;
2656
0
  char     *conbin;
2657
0
  SysScanDesc scan;
2658
0
  Datum   val;
2659
0
  bool    isnull;
2660
0
  HeapTuple tuple;
2661
0
  HeapTuple copyTuple;
2662
0
  ScanKeyData skey[3];
2663
0
  ObjectAddress address;
2664
2665
  /* Make a TypeName so we can use standard type lookup machinery */
2666
0
  typename = makeTypeNameFromNameList(names);
2667
0
  domainoid = typenameTypeId(NULL, typename);
2668
2669
  /* Look up the domain in the type table */
2670
0
  typrel = heap_open(TypeRelationId, AccessShareLock);
2671
2672
0
  tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(domainoid));
2673
0
  if (!HeapTupleIsValid(tup))
2674
0
    elog(ERROR, "cache lookup failed for type %u", domainoid);
2675
2676
  /* Check it's a domain and check user has permission for ALTER DOMAIN */
2677
0
  checkDomainOwner(tup);
2678
2679
  /*
2680
   * Find and check the target constraint
2681
   */
2682
0
  conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
2683
2684
0
  ScanKeyInit(&skey[0],
2685
0
        Anum_pg_constraint_conrelid,
2686
0
        BTEqualStrategyNumber, F_OIDEQ,
2687
0
        ObjectIdGetDatum(InvalidOid));
2688
0
  ScanKeyInit(&skey[1],
2689
0
        Anum_pg_constraint_contypid,
2690
0
        BTEqualStrategyNumber, F_OIDEQ,
2691
0
        ObjectIdGetDatum(domainoid));
2692
0
  ScanKeyInit(&skey[2],
2693
0
        Anum_pg_constraint_conname,
2694
0
        BTEqualStrategyNumber, F_NAMEEQ,
2695
0
        CStringGetDatum(constrName));
2696
2697
0
  scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId, true,
2698
0
                NULL, 3, skey);
2699
2700
  /* There can be at most one matching row */
2701
0
  if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
2702
0
    ereport(ERROR,
2703
0
        (errcode(ERRCODE_UNDEFINED_OBJECT),
2704
0
         errmsg("constraint \"%s\" of domain \"%s\" does not exist",
2705
0
            constrName, TypeNameToString(typename))));
2706
2707
0
  con = (Form_pg_constraint) GETSTRUCT(tuple);
2708
0
  if (con->contype != CONSTRAINT_CHECK)
2709
0
    ereport(ERROR,
2710
0
        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2711
0
         errmsg("constraint \"%s\" of domain \"%s\" is not a check constraint",
2712
0
            constrName, TypeNameToString(typename))));
2713
2714
0
  val = SysCacheGetAttr(CONSTROID, tuple,
2715
0
              Anum_pg_constraint_conbin,
2716
0
              &isnull);
2717
0
  if (isnull)
2718
0
    elog(ERROR, "null conbin for constraint %u",
2719
0
       HeapTupleGetOid(tuple));
2720
0
  conbin = TextDatumGetCString(val);
2721
2722
0
  validateDomainConstraint(domainoid, conbin);
2723
2724
  /*
2725
   * Now update the catalog, while we have the door open.
2726
   */
2727
0
  copyTuple = heap_copytuple(tuple);
2728
0
  copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
2729
0
  copy_con->convalidated = true;
2730
0
  CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
2731
2732
0
  InvokeObjectPostAlterHook(ConstraintRelationId,
2733
0
                HeapTupleGetOid(copyTuple), 0);
2734
2735
0
  ObjectAddressSet(address, TypeRelationId, domainoid);
2736
2737
0
  heap_freetuple(copyTuple);
2738
2739
0
  systable_endscan(scan);
2740
2741
0
  heap_close(typrel, AccessShareLock);
2742
0
  heap_close(conrel, RowExclusiveLock);
2743
2744
0
  ReleaseSysCache(tup);
2745
2746
0
  return address;
2747
0
}
2748
2749
static void
2750
validateDomainConstraint(Oid domainoid, char *ccbin)
2751
0
{
2752
0
  Expr     *expr = (Expr *) stringToNode(ccbin);
2753
0
  List     *rels;
2754
0
  ListCell   *rt;
2755
0
  EState     *estate;
2756
0
  ExprContext *econtext;
2757
0
  ExprState  *exprstate;
2758
2759
  /* Need an EState to run ExecEvalExpr */
2760
0
  estate = CreateExecutorState();
2761
0
  econtext = GetPerTupleExprContext(estate);
2762
2763
  /* build execution state for expr */
2764
0
  exprstate = ExecPrepareExpr(expr, estate);
2765
2766
  /* Fetch relation list with attributes based on this domain */
2767
  /* ShareLock is sufficient to prevent concurrent data changes */
2768
2769
0
  rels = get_rels_with_domain(domainoid, ShareLock);
2770
2771
0
  foreach(rt, rels)
2772
0
  {
2773
0
    RelToCheck *rtc = (RelToCheck *) lfirst(rt);
2774
0
    Relation  testrel = rtc->rel;
2775
0
    TupleDesc tupdesc = RelationGetDescr(testrel);
2776
0
    HeapScanDesc scan;
2777
0
    HeapTuple tuple;
2778
0
    Snapshot  snapshot;
2779
2780
    /* Scan all tuples in this relation */
2781
0
    snapshot = RegisterSnapshot(GetLatestSnapshot());
2782
0
    scan = heap_beginscan(testrel, snapshot, 0, NULL);
2783
0
    while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
2784
0
    {
2785
0
      int     i;
2786
2787
      /* Test attributes that are of the domain */
2788
0
      for (i = 0; i < rtc->natts; i++)
2789
0
      {
2790
0
        int     attnum = rtc->atts[i];
2791
0
        Datum   d;
2792
0
        bool    isNull;
2793
0
        Datum   conResult;
2794
0
        Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
2795
2796
0
        d = heap_getattr(tuple, attnum, tupdesc, &isNull);
2797
2798
0
        econtext->domainValue_datum = d;
2799
0
        econtext->domainValue_isNull = isNull;
2800
2801
0
        conResult = ExecEvalExprSwitchContext(exprstate,
2802
0
                            econtext,
2803
0
                            &isNull);
2804
2805
0
        if (!isNull && !DatumGetBool(conResult))
2806
0
        {
2807
          /*
2808
           * In principle the auxiliary information for this error
2809
           * should be errdomainconstraint(), but errtablecol()
2810
           * seems considerably more useful in practice.  Since this
2811
           * code only executes in an ALTER DOMAIN command, the
2812
           * client should already know which domain is in question,
2813
           * and which constraint too.
2814
           */
2815
0
          ereport(ERROR,
2816
0
              (errcode(ERRCODE_CHECK_VIOLATION),
2817
0
               errmsg("column \"%s\" of table \"%s\" contains values that violate the new constraint",
2818
0
                  NameStr(attr->attname),
2819
0
                  RelationGetRelationName(testrel)),
2820
0
               errtablecol(testrel, attnum)));
2821
0
        }
2822
0
      }
2823
2824
0
      ResetExprContext(econtext);
2825
0
    }
2826
0
    heap_endscan(scan);
2827
0
    UnregisterSnapshot(snapshot);
2828
2829
    /* Hold relation lock till commit (XXX bad for concurrency) */
2830
0
    heap_close(testrel, NoLock);
2831
0
  }
2832
2833
0
  FreeExecutorState(estate);
2834
0
}
2835
2836
/*
2837
 * get_rels_with_domain
2838
 *
2839
 * Fetch all relations / attributes which are using the domain
2840
 *
2841
 * The result is a list of RelToCheck structs, one for each distinct
2842
 * relation, each containing one or more attribute numbers that are of
2843
 * the domain type.  We have opened each rel and acquired the specified lock
2844
 * type on it.
2845
 *
2846
 * We support nested domains by including attributes that are of derived
2847
 * domain types.  Current callers do not need to distinguish between attributes
2848
 * that are of exactly the given domain and those that are of derived domains.
2849
 *
2850
 * XXX this is completely broken because there is no way to lock the domain
2851
 * to prevent columns from being added or dropped while our command runs.
2852
 * We can partially protect against column drops by locking relations as we
2853
 * come across them, but there is still a race condition (the window between
2854
 * seeing a pg_depend entry and acquiring lock on the relation it references).
2855
 * Also, holding locks on all these relations simultaneously creates a non-
2856
 * trivial risk of deadlock.  We can minimize but not eliminate the deadlock
2857
 * risk by using the weakest suitable lock (ShareLock for most callers).
2858
 *
2859
 * XXX the API for this is not sufficient to support checking domain values
2860
 * that are inside container types, such as composite types, arrays, or
2861
 * ranges.  Currently we just error out if a container type containing the
2862
 * target domain is stored anywhere.
2863
 *
2864
 * Generally used for retrieving a list of tests when adding
2865
 * new constraints to a domain.
2866
 */
2867
static List *
2868
get_rels_with_domain(Oid domainOid, LOCKMODE lockmode)
2869
0
{
2870
0
  List     *result = NIL;
2871
0
  char     *domainTypeName = format_type_be(domainOid);
2872
0
  Relation  depRel;
2873
0
  ScanKeyData key[2];
2874
0
  SysScanDesc depScan;
2875
0
  HeapTuple depTup;
2876
2877
0
  Assert(lockmode != NoLock);
2878
2879
  /* since this function recurses, it could be driven to stack overflow */
2880
0
  check_stack_depth();
2881
2882
  /*
2883
   * We scan pg_depend to find those things that depend on the domain. (We
2884
   * assume we can ignore refobjsubid for a domain.)
2885
   */
2886
0
  depRel = heap_open(DependRelationId, AccessShareLock);
2887
2888
0
  ScanKeyInit(&key[0],
2889
0
        Anum_pg_depend_refclassid,
2890
0
        BTEqualStrategyNumber, F_OIDEQ,
2891
0
        ObjectIdGetDatum(TypeRelationId));
2892
0
  ScanKeyInit(&key[1],
2893
0
        Anum_pg_depend_refobjid,
2894
0
        BTEqualStrategyNumber, F_OIDEQ,
2895
0
        ObjectIdGetDatum(domainOid));
2896
2897
0
  depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
2898
0
                 NULL, 2, key);
2899
2900
0
  while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
2901
0
  {
2902
0
    Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
2903
0
    RelToCheck *rtc = NULL;
2904
0
    ListCell   *rellist;
2905
0
    Form_pg_attribute pg_att;
2906
0
    int     ptr;
2907
2908
    /* Check for directly dependent types */
2909
0
    if (pg_depend->classid == TypeRelationId)
2910
0
    {
2911
0
      if (get_typtype(pg_depend->objid) == TYPTYPE_DOMAIN)
2912
0
      {
2913
        /*
2914
         * This is a sub-domain, so recursively add dependent columns
2915
         * to the output list.  This is a bit inefficient since we may
2916
         * fail to combine RelToCheck entries when attributes of the
2917
         * same rel have different derived domain types, but it's
2918
         * probably not worth improving.
2919
         */
2920
0
        result = list_concat(result,
2921
0
                   get_rels_with_domain(pg_depend->objid,
2922
0
                              lockmode));
2923
0
      }
2924
0
      else
2925
0
      {
2926
        /*
2927
         * Otherwise, it is some container type using the domain, so
2928
         * fail if there are any columns of this type.
2929
         */
2930
0
        find_composite_type_dependencies(pg_depend->objid,
2931
0
                         NULL,
2932
0
                         domainTypeName);
2933
0
      }
2934
0
      continue;
2935
0
    }
2936
2937
    /* Else, ignore dependees that aren't user columns of relations */
2938
    /* (we assume system columns are never of domain types) */
2939
0
    if (pg_depend->classid != RelationRelationId ||
2940
0
      pg_depend->objsubid <= 0)
2941
0
      continue;
2942
2943
    /* See if we already have an entry for this relation */
2944
0
    foreach(rellist, result)
2945
0
    {
2946
0
      RelToCheck *rt = (RelToCheck *) lfirst(rellist);
2947
2948
0
      if (RelationGetRelid(rt->rel) == pg_depend->objid)
2949
0
      {
2950
0
        rtc = rt;
2951
0
        break;
2952
0
      }
2953
0
    }
2954
2955
0
    if (rtc == NULL)
2956
0
    {
2957
      /* First attribute found for this relation */
2958
0
      Relation  rel;
2959
2960
      /* Acquire requested lock on relation */
2961
0
      rel = relation_open(pg_depend->objid, lockmode);
2962
2963
      /*
2964
       * Check to see if rowtype is stored anyplace as a composite-type
2965
       * column; if so we have to fail, for now anyway.
2966
       */
2967
0
      if (OidIsValid(rel->rd_rel->reltype))
2968
0
        find_composite_type_dependencies(rel->rd_rel->reltype,
2969
0
                         NULL,
2970
0
                         domainTypeName);
2971
2972
      /*
2973
       * Otherwise, we can ignore relations except those with both
2974
       * storage and user-chosen column types.
2975
       *
2976
       * XXX If an index-only scan could satisfy "col::some_domain" from
2977
       * a suitable expression index, this should also check expression
2978
       * index columns.
2979
       */
2980
0
      if (rel->rd_rel->relkind != RELKIND_RELATION &&
2981
0
        rel->rd_rel->relkind != RELKIND_MATVIEW)
2982
0
      {
2983
0
        relation_close(rel, lockmode);
2984
0
        continue;
2985
0
      }
2986
2987
      /* Build the RelToCheck entry with enough space for all atts */
2988
0
      rtc = (RelToCheck *) palloc(sizeof(RelToCheck));
2989
0
      rtc->rel = rel;
2990
0
      rtc->natts = 0;
2991
0
      rtc->atts = (int *) palloc(sizeof(int) * RelationGetNumberOfAttributes(rel));
2992
0
      result = lcons(rtc, result);
2993
0
    }
2994
2995
    /*
2996
     * Confirm column has not been dropped, and is of the expected type.
2997
     * This defends against an ALTER DROP COLUMN occurring just before we
2998
     * acquired lock ... but if the whole table were dropped, we'd still
2999
     * have a problem.
3000
     */
3001
0
    if (pg_depend->objsubid > RelationGetNumberOfAttributes(rtc->rel))
3002
0
      continue;
3003
0
    pg_att = TupleDescAttr(rtc->rel->rd_att, pg_depend->objsubid - 1);
3004
0
    if (pg_att->attisdropped || pg_att->atttypid != domainOid)
3005
0
      continue;
3006
3007
    /*
3008
     * Okay, add column to result.  We store the columns in column-number
3009
     * order; this is just a hack to improve predictability of regression
3010
     * test output ...
3011
     */
3012
0
    Assert(rtc->natts < RelationGetNumberOfAttributes(rtc->rel));
3013
3014
0
    ptr = rtc->natts++;
3015
0
    while (ptr > 0 && rtc->atts[ptr - 1] > pg_depend->objsubid)
3016
0
    {
3017
0
      rtc->atts[ptr] = rtc->atts[ptr - 1];
3018
0
      ptr--;
3019
0
    }
3020
0
    rtc->atts[ptr] = pg_depend->objsubid;
3021
0
  }
3022
3023
0
  systable_endscan(depScan);
3024
3025
0
  relation_close(depRel, AccessShareLock);
3026
3027
0
  return result;
3028
0
}
3029
3030
/*
3031
 * checkDomainOwner
3032
 *
3033
 * Check that the type is actually a domain and that the current user
3034
 * has permission to do ALTER DOMAIN on it.  Throw an error if not.
3035
 */
3036
void
3037
checkDomainOwner(HeapTuple tup)
3038
4
{
3039
4
  Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup);
3040
3041
  /* Check that this is actually a domain */
3042
4
  if (typTup->typtype != TYPTYPE_DOMAIN)
3043
4
    ereport(ERROR,
3044
4
        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3045
4
         errmsg("%s is not a domain",
3046
4
            format_type_be(HeapTupleGetOid(tup)))));
3047
3048
  /* Permission check: must own type */
3049
4
  if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId()))
3050
0
    aclcheck_error_type(ACLCHECK_NOT_OWNER, HeapTupleGetOid(tup));
3051
4
}
3052
3053
/*
3054
 * domainAddConstraint - code shared between CREATE and ALTER DOMAIN
3055
 */
3056
static char *
3057
domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
3058
          int typMod, Constraint *constr,
3059
          const char *domainName, ObjectAddress *constrAddr)
3060
24
{
3061
24
  Node     *expr;
3062
24
  char     *ccsrc;
3063
24
  char     *ccbin;
3064
24
  ParseState *pstate;
3065
24
  CoerceToDomainValue *domVal;
3066
24
  Oid     ccoid;
3067
3068
  /*
3069
   * Assign or validate constraint name
3070
   */
3071
24
  if (constr->conname)
3072
8
  {
3073
8
    if (ConstraintNameIsUsed(CONSTRAINT_DOMAIN,
3074
8
                 domainOid,
3075
8
                 constr->conname))
3076
8
      ereport(ERROR,
3077
8
          (errcode(ERRCODE_DUPLICATE_OBJECT),
3078
8
           errmsg("constraint \"%s\" for domain \"%s\" already exists",
3079
8
              constr->conname, domainName)));
3080
8
  }
3081
16
  else
3082
16
    constr->conname = ChooseConstraintName(domainName,
3083
16
                         NULL,
3084
16
                         "check",
3085
16
                         domainNamespace,
3086
16
                         NIL);
3087
3088
  /*
3089
   * Convert the A_EXPR in raw_expr into an EXPR
3090
   */
3091
24
  pstate = make_parsestate(NULL);
3092
3093
  /*
3094
   * Set up a CoerceToDomainValue to represent the occurrence of VALUE in
3095
   * the expression.  Note that it will appear to have the type of the base
3096
   * type, not the domain.  This seems correct since within the check
3097
   * expression, we should not assume the input value can be considered a
3098
   * member of the domain.
3099
   */
3100
24
  domVal = makeNode(CoerceToDomainValue);
3101
0
  domVal->typeId = baseTypeOid;
3102
24
  domVal->typeMod = typMod;
3103
24
  domVal->collation = get_typcollation(baseTypeOid);
3104
24
  domVal->location = -1;    /* will be set when/if used */
3105
3106
24
  pstate->p_pre_columnref_hook = replace_domain_constraint_value;
3107
24
  pstate->p_ref_hook_state = (void *) domVal;
3108
3109
24
  expr = transformExpr(pstate, constr->raw_expr, EXPR_KIND_DOMAIN_CHECK);
3110
3111
  /*
3112
   * Make sure it yields a boolean result.
3113
   */
3114
24
  expr = coerce_to_boolean(pstate, expr, "CHECK");
3115
3116
  /*
3117
   * Fix up collation information.
3118
   */
3119
24
  assign_expr_collations(pstate, expr);
3120
3121
  /*
3122
   * Domains don't allow variables (this is probably dead code now that
3123
   * add_missing_from is history, but let's be sure).
3124
   */
3125
24
  if (list_length(pstate->p_rtable) != 0 ||
3126
24
    contain_var_clause(expr))
3127
24
    ereport(ERROR,
3128
24
        (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
3129
24
         errmsg("cannot use table references in domain check constraint")));
3130
3131
  /*
3132
   * Convert to string form for storage.
3133
   */
3134
24
  ccbin = nodeToString(expr);
3135
3136
  /*
3137
   * Deparse it to produce text for consrc.
3138
   */
3139
24
  ccsrc = deparse_expression(expr,
3140
24
                 NIL, false, false);
3141
3142
  /*
3143
   * Store the constraint in pg_constraint
3144
   */
3145
24
  ccoid =
3146
24
    CreateConstraintEntry(constr->conname,  /* Constraint Name */
3147
24
                domainNamespace,  /* namespace */
3148
24
                CONSTRAINT_CHECK, /* Constraint Type */
3149
24
                false,  /* Is Deferrable */
3150
24
                false,  /* Is Deferred */
3151
24
                !constr->skip_validation, /* Is Validated */
3152
24
                InvalidOid, /* no parent constraint */
3153
24
                InvalidOid, /* not a relation constraint */
3154
24
                NULL,
3155
24
                0,
3156
24
                0,
3157
24
                domainOid,  /* domain constraint */
3158
24
                InvalidOid, /* no associated index */
3159
24
                InvalidOid, /* Foreign key fields */
3160
24
                NULL,
3161
24
                NULL,
3162
24
                NULL,
3163
24
                NULL,
3164
24
                0,
3165
24
                ' ',
3166
24
                ' ',
3167
24
                ' ',
3168
24
                NULL, /* not an exclusion constraint */
3169
24
                expr, /* Tree form of check constraint */
3170
24
                ccbin,  /* Binary form of check constraint */
3171
24
                ccsrc,  /* Source form of check constraint */
3172
24
                true, /* is local */
3173
24
                0,  /* inhcount */
3174
24
                false,  /* connoinherit */
3175
24
                false); /* is_internal */
3176
24
  if (constrAddr)
3177
24
    ObjectAddressSet(*constrAddr, ConstraintRelationId, ccoid);
3178
3179
  /*
3180
   * Return the compiled constraint expression so the calling routine can
3181
   * perform any additional required tests.
3182
   */
3183
24
  return ccbin;
3184
24
}
3185
3186
/* Parser pre_columnref_hook for domain CHECK constraint parsing */
3187
static Node *
3188
replace_domain_constraint_value(ParseState *pstate, ColumnRef *cref)
3189
27
{
3190
  /*
3191
   * Check for a reference to "value", and if that's what it is, replace
3192
   * with a CoerceToDomainValue as prepared for us by domainAddConstraint.
3193
   * (We handle VALUE as a name, not a keyword, to avoid breaking a lot of
3194
   * applications that have used VALUE as a column name in the past.)
3195
   */
3196
27
  if (list_length(cref->fields) == 1)
3197
27
  {
3198
27
    Node     *field1 = (Node *) linitial(cref->fields);
3199
27
    char     *colname;
3200
3201
27
    Assert(IsA(field1, String));
3202
27
    colname = strVal(field1);
3203
27
    if (strcmp(colname, "value") == 0)
3204
27
    {
3205
27
      CoerceToDomainValue *domVal = copyObject(pstate->p_ref_hook_state);
3206
3207
      /* Propagate location knowledge, if any */
3208
27
      domVal->location = cref->location;
3209
27
      return (Node *) domVal;
3210
27
    }
3211
27
  }
3212
0
  return NULL;
3213
27
}
3214
3215
3216
/*
3217
 * Execute ALTER TYPE RENAME
3218
 */
3219
ObjectAddress
3220
RenameType(RenameStmt *stmt)
3221
8
{
3222
8
  List     *names = castNode(List, stmt->object);
3223
8
  const char *newTypeName = stmt->newname;
3224
8
  TypeName   *typename;
3225
8
  Oid     typeOid;
3226
8
  Relation  rel;
3227
8
  HeapTuple tup;
3228
8
  Form_pg_type typTup;
3229
8
  ObjectAddress address;
3230
3231
  /* Make a TypeName so we can use standard type lookup machinery */
3232
8
  typename = makeTypeNameFromNameList(names);
3233
8
  typeOid = typenameTypeId(NULL, typename);
3234
3235
  /* Look up the type in the type table */
3236
8
  rel = heap_open(TypeRelationId, RowExclusiveLock);
3237
3238
8
  tup = SearchSysCacheCopy1(TYPEOID, ObjectIdGetDatum(typeOid));
3239
8
  if (!HeapTupleIsValid(tup))
3240
0
    elog(ERROR, "cache lookup failed for type %u", typeOid);
3241
8
  typTup = (Form_pg_type) GETSTRUCT(tup);
3242
3243
  /* check permissions on type */
3244
8
  if (!pg_type_ownercheck(typeOid, GetUserId()))
3245
0
    aclcheck_error_type(ACLCHECK_NOT_OWNER, typeOid);
3246
3247
  /* ALTER DOMAIN used on a non-domain? */
3248
8
  if (stmt->renameType == OBJECT_DOMAIN && 
typTup->typtype != 2
TYPTYPE_DOMAIN2
)
3249
8
    ereport(ERROR,
3250
8
        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3251
8
         errmsg("%s is not a domain",
3252
8
            format_type_be(typeOid))));
3253
3254
  /*
3255
   * If it's a composite type, we need to check that it really is a
3256
   * free-standing composite type, and not a table's rowtype. We want people
3257
   * to use ALTER TABLE not ALTER TYPE for that case.
3258
   */
3259
8
  if (typTup->typtype == TYPTYPE_COMPOSITE &&
3260
8
    
get_rel_relkind(typTup->typrelid) != 0
RELKIND_COMPOSITE_TYPE0
)
3261
8
    ereport(ERROR,
3262
8
        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3263
8
         errmsg("%s is a table's row type",
3264
8
            format_type_be(typeOid)),
3265
8
         errhint("Use ALTER TABLE instead.")));
3266
3267
  /* don't allow direct alteration of array types, either */
3268
8
  if (OidIsValid(typTup->typelem) &&
3269
8
    
get_array_type(typTup->typelem) == typeOid0
)
3270
8
    ereport(ERROR,
3271
8
        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3272
8
         errmsg("cannot alter array type %s",
3273
8
            format_type_be(typeOid)),
3274
8
         errhint("You can alter type %s, which will alter the array type as well.",
3275
8
             format_type_be(typTup->typelem))));
3276
3277
  /*
3278
   * If type is composite we need to rename associated pg_class entry too.
3279
   * RenameRelationInternal will call RenameTypeInternal automatically.
3280
   */
3281
8
  if (typTup->typtype == TYPTYPE_COMPOSITE)
3282
0
    RenameRelationInternal(typTup->typrelid, newTypeName, false);
3283
8
  else
3284
8
    RenameTypeInternal(typeOid, newTypeName,
3285
8
               typTup->typnamespace);
3286
3287
8
  ObjectAddressSet(address, TypeRelationId, typeOid);
3288
  /* Clean up */
3289
8
  heap_close(rel, RowExclusiveLock);
3290
3291
8
  return address;
3292
8
}
3293
3294
/*
3295
 * Change the owner of a type.
3296
 */
3297
ObjectAddress
3298
AlterTypeOwner(List *names, Oid newOwnerId, ObjectType objecttype)
3299
0
{
3300
0
  TypeName   *typename;
3301
0
  Oid     typeOid;
3302
0
  Relation  rel;
3303
0
  HeapTuple tup;
3304
0
  HeapTuple newtup;
3305
0
  Form_pg_type typTup;
3306
0
  AclResult aclresult;
3307
0
  ObjectAddress address;
3308
3309
0
  rel = heap_open(TypeRelationId, RowExclusiveLock);
3310
3311
  /* Make a TypeName so we can use standard type lookup machinery */
3312
0
  typename = makeTypeNameFromNameList(names);
3313
3314
  /* Use LookupTypeName here so that shell types can be processed */
3315
0
  tup = LookupTypeName(NULL, typename, NULL, false);
3316
0
  if (tup == NULL)
3317
0
    ereport(ERROR,
3318
0
        (errcode(ERRCODE_UNDEFINED_OBJECT),
3319
0
         errmsg("type \"%s\" does not exist",
3320
0
            TypeNameToString(typename))));
3321
0
  typeOid = typeTypeId(tup);
3322
3323
  /* Copy the syscache entry so we can scribble on it below */
3324
0
  newtup = heap_copytuple(tup);
3325
0
  ReleaseSysCache(tup);
3326
0
  tup = newtup;
3327
0
  typTup = (Form_pg_type) GETSTRUCT(tup);
3328
3329
  /* Don't allow ALTER DOMAIN on a type */
3330
0
  if (objecttype == OBJECT_DOMAIN && typTup->typtype != TYPTYPE_DOMAIN)
3331
0
    ereport(ERROR,
3332
0
        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3333
0
         errmsg("%s is not a domain",
3334
0
            format_type_be(typeOid))));
3335
3336
  /*
3337
   * If it's a composite type, we need to check that it really is a
3338
   * free-standing composite type, and not a table's rowtype. We want people
3339
   * to use ALTER TABLE not ALTER TYPE for that case.
3340
   */
3341
0
  if (typTup->typtype == TYPTYPE_COMPOSITE &&
3342
0
    get_rel_relkind(typTup->typrelid) != RELKIND_COMPOSITE_TYPE)
3343
0
    ereport(ERROR,
3344
0
        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3345
0
         errmsg("%s is a table's row type",
3346
0
            format_type_be(typeOid)),
3347
0
         errhint("Use ALTER TABLE instead.")));
3348
3349
  /* don't allow direct alteration of array types, either */
3350
0
  if (OidIsValid(typTup->typelem) &&
3351
0
    get_array_type(typTup->typelem) == typeOid)
3352
0
    ereport(ERROR,
3353
0
        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3354
0
         errmsg("cannot alter array type %s",
3355
0
            format_type_be(typeOid)),
3356
0
         errhint("You can alter type %s, which will alter the array type as well.",
3357
0
             format_type_be(typTup->typelem))));
3358
3359
  /*
3360
   * If the new owner is the same as the existing owner, consider the
3361
   * command to have succeeded.  This is for dump restoration purposes.
3362
   */
3363
0
  if (typTup->typowner != newOwnerId)
3364
0
  {
3365
    /* Superusers can always do it */
3366
0
    if (!superuser())
3367
0
    {
3368
      /* Otherwise, must be owner of the existing object */
3369
0
      if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId()))
3370
0
        aclcheck_error_type(ACLCHECK_NOT_OWNER, HeapTupleGetOid(tup));
3371
3372
      /* Must be able to become new owner */
3373
0
      check_is_member_of_role(GetUserId(), newOwnerId);
3374
3375
      /* New owner must have CREATE privilege on namespace */
3376
0
      aclresult = pg_namespace_aclcheck(typTup->typnamespace,
3377
0
                        newOwnerId,
3378
0
                        ACL_CREATE);
3379
0
      if (aclresult != ACLCHECK_OK)
3380
0
        aclcheck_error(aclresult, OBJECT_SCHEMA,
3381
0
                 get_namespace_name(typTup->typnamespace));
3382
0
    }
3383
3384
0
    AlterTypeOwner_oid(typeOid, newOwnerId, true);
3385
0
  }
3386
3387
0
  ObjectAddressSet(address, TypeRelationId, typeOid);
3388
3389
  /* Clean up */
3390
0
  heap_close(rel, RowExclusiveLock);
3391
3392
0
  return address;
3393
0
}
3394
3395
/*
3396
 * AlterTypeOwner_oid - change type owner unconditionally
3397
 *
3398
 * This function recurses to handle a pg_class entry, if necessary.  It
3399
 * invokes any necessary access object hooks.  If hasDependEntry is true, this
3400
 * function modifies the pg_shdepend entry appropriately (this should be
3401
 * passed as false only for table rowtypes and array types).
3402
 *
3403
 * This is used by ALTER TABLE/TYPE OWNER commands, as well as by REASSIGN
3404
 * OWNED BY.  It assumes the caller has done all needed check.
3405
 */
3406
void
3407
AlterTypeOwner_oid(Oid typeOid, Oid newOwnerId, bool hasDependEntry)
3408
0
{
3409
0
  Relation  rel;
3410
0
  HeapTuple tup;
3411
0
  Form_pg_type typTup;
3412
3413
0
  rel = heap_open(TypeRelationId, RowExclusiveLock);
3414
3415
0
  tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeOid));
3416
0
  if (!HeapTupleIsValid(tup))
3417
0
    elog(ERROR, "cache lookup failed for type %u", typeOid);
3418
0
  typTup = (Form_pg_type) GETSTRUCT(tup);
3419
3420
  /*
3421
   * If it's a composite type, invoke ATExecChangeOwner so that we fix up
3422
   * the pg_class entry properly.  That will call back to
3423
   * AlterTypeOwnerInternal to take care of the pg_type entry(s).
3424
   */
3425
0
  if (typTup->typtype == TYPTYPE_COMPOSITE)
3426
0
    ATExecChangeOwner(typTup->typrelid, newOwnerId, true, AccessExclusiveLock);
3427
0
  else
3428
0
    AlterTypeOwnerInternal(typeOid, newOwnerId);
3429
3430
  /* Update owner dependency reference */
3431
0
  if (hasDependEntry)
3432
0
    changeDependencyOnOwner(TypeRelationId, typeOid, newOwnerId);
3433
3434
0
  InvokeObjectPostAlterHook(TypeRelationId, typeOid, 0);
3435
3436
0
  ReleaseSysCache(tup);
3437
0
  heap_close(rel, RowExclusiveLock);
3438
0
}
3439
3440
/*
3441
 * AlterTypeOwnerInternal - bare-bones type owner change.
3442
 *
3443
 * This routine simply modifies the owner of a pg_type entry, and recurses
3444
 * to handle a possible array type.
3445
 */
3446
void
3447
AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId)
3448
163
{
3449
163
  Relation  rel;
3450
163
  HeapTuple tup;
3451
163
  Form_pg_type typTup;
3452
163
  Datum   repl_val[Natts_pg_type];
3453
163
  bool    repl_null[Natts_pg_type];
3454
163
  bool    repl_repl[Natts_pg_type];
3455
163
  Acl      *newAcl;
3456
163
  Datum   aclDatum;
3457
163
  bool    isNull;
3458
3459
163
  rel = heap_open(TypeRelationId, RowExclusiveLock);
3460
3461
163
  tup = SearchSysCacheCopy1(TYPEOID, ObjectIdGetDatum(typeOid));
3462
163
  if (!HeapTupleIsValid(tup))
3463
0
    elog(ERROR, "cache lookup failed for type %u", typeOid);
3464
163
  typTup = (Form_pg_type) GETSTRUCT(tup);
3465
3466
163
  memset(repl_null, false, sizeof(repl_null));
3467
163
  memset(repl_repl, false, sizeof(repl_repl));
3468
3469
163
  repl_repl[Anum_pg_type_typowner - 1] = true;
3470
163
  repl_val[Anum_pg_type_typowner - 1] = ObjectIdGetDatum(newOwnerId);
3471
3472
163
  aclDatum = heap_getattr(tup,
3473
163
              Anum_pg_type_typacl,
3474
163
              RelationGetDescr(rel),
3475
163
              &isNull);
3476
  /* Null ACLs do not require changes */
3477
163
  if (!isNull)
3478
0
  {
3479
0
    newAcl = aclnewowner(DatumGetAclP(aclDatum),
3480
0
               typTup->typowner, newOwnerId);
3481
0
    repl_repl[Anum_pg_type_typacl - 1] = true;
3482
0
    repl_val[Anum_pg_type_typacl - 1] = PointerGetDatum(newAcl);
3483
0
  }
3484
3485
163
  tup = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null,
3486
163
              repl_repl);
3487
3488
163
  CatalogTupleUpdate(rel, &tup->t_self, tup);
3489
3490
  /* If it has an array type, update that too */
3491
163
  if (OidIsValid(typTup->typarray))
3492
80
    AlterTypeOwnerInternal(typTup->typarray, newOwnerId);
3493
3494
  /* Clean up */
3495
163
  heap_close(rel, RowExclusiveLock);
3496
163
}
3497
3498
/*
3499
 * Execute ALTER TYPE SET SCHEMA
3500
 */
3501
ObjectAddress
3502
AlterTypeNamespace(List *names, const char *newschema, ObjectType objecttype,
3503
           Oid *oldschema)
3504
0
{
3505
0
  TypeName   *typename;
3506
0
  Oid     typeOid;
3507
0
  Oid     nspOid;
3508
0
  Oid     oldNspOid;
3509
0
  ObjectAddresses *objsMoved;
3510
0
  ObjectAddress myself;
3511
3512
  /* Make a TypeName so we can use standard type lookup machinery */
3513
0
  typename = makeTypeNameFromNameList(names);
3514
0
  typeOid = typenameTypeId(NULL, typename);
3515
3516
  /* Don't allow ALTER DOMAIN on a type */
3517
0
  if (objecttype == OBJECT_DOMAIN && get_typtype(typeOid) != TYPTYPE_DOMAIN)
3518
0
    ereport(ERROR,
3519
0
        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3520
0
         errmsg("%s is not a domain",
3521
0
            format_type_be(typeOid))));
3522
3523
  /* get schema OID and check its permissions */
3524
0
  nspOid = LookupCreationNamespace(newschema);
3525
3526
0
  objsMoved = new_object_addresses();
3527
0
  oldNspOid = AlterTypeNamespace_oid(typeOid, nspOid, objsMoved);
3528
0
  free_object_addresses(objsMoved);
3529
3530
0
  if (oldschema)
3531
0
    *oldschema = oldNspOid;
3532
3533
0
  ObjectAddressSet(myself, TypeRelationId, typeOid);
3534
3535
0
  return myself;
3536
0
}
3537
3538
Oid
3539
AlterTypeNamespace_oid(Oid typeOid, Oid nspOid, ObjectAddresses *objsMoved)
3540
0
{
3541
0
  Oid     elemOid;
3542
3543
  /* check permissions on type */
3544
0
  if (!pg_type_ownercheck(typeOid, GetUserId()))
3545
0
    aclcheck_error_type(ACLCHECK_NOT_OWNER, typeOid);
3546
3547
  /* don't allow direct alteration of array types */
3548
0
  elemOid = get_element_type(typeOid);
3549
0
  if (OidIsValid(elemOid) && get_array_type(elemOid) == typeOid)
3550
0
    ereport(ERROR,
3551
0
        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3552
0
         errmsg("cannot alter array type %s",
3553
0
            format_type_be(typeOid)),
3554
0
         errhint("You can alter type %s, which will alter the array type as well.",
3555
0
             format_type_be(elemOid))));
3556
3557
  /* and do the work */
3558
0
  return AlterTypeNamespaceInternal(typeOid, nspOid, false, true, objsMoved);
3559
0
}
3560
3561
/*
3562
 * Move specified type to new namespace.
3563
 *
3564
 * Caller must have already checked privileges.
3565
 *
3566
 * The function automatically recurses to process the type's array type,
3567
 * if any.  isImplicitArray should be true only when doing this internal
3568
 * recursion (outside callers must never try to move an array type directly).
3569
 *
3570
 * If errorOnTableType is true, the function errors out if the type is
3571
 * a table type.  ALTER TABLE has to be used to move a table to a new
3572
 * namespace.
3573
 *
3574
 * Returns the type's old namespace OID.
3575
 */
3576
Oid
3577
AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
3578
               bool isImplicitArray,
3579
               bool errorOnTableType,
3580
               ObjectAddresses *objsMoved)
3581
2
{
3582
2
  Relation  rel;
3583
2
  HeapTuple tup;
3584
2
  Form_pg_type typform;
3585
2
  Oid     oldNspOid;
3586
2
  Oid     arrayOid;
3587
2
  bool    isCompositeType;
3588
2
  ObjectAddress thisobj;
3589
3590
  /*
3591
   * Make sure we haven't moved this object previously.
3592
   */
3593
2
  thisobj.classId = TypeRelationId;
3594
2
  thisobj.objectId = typeOid;
3595
2
  thisobj.objectSubId = 0;
3596
3597
2
  if (object_address_present(&thisobj, objsMoved))
3598
0
    return InvalidOid;
3599
3600
2
  rel = heap_open(TypeRelationId, RowExclusiveLock);
3601
3602
2
  tup = SearchSysCacheCopy1(TYPEOID, ObjectIdGetDatum(typeOid));
3603
2
  if (!HeapTupleIsValid(tup))
3604
0
    elog(ERROR, "cache lookup failed for type %u", typeOid);
3605
2
  typform = (Form_pg_type) GETSTRUCT(tup);
3606
3607
2
  oldNspOid = typform->typnamespace;
3608
2
  arrayOid = typform->typarray;
3609
3610
  /* If the type is already there, we scan skip these next few checks. */
3611
2
  if (oldNspOid != nspOid)
3612
2
  {
3613
    /* common checks on switching namespaces */
3614
2
    CheckSetNamespace(oldNspOid, nspOid);
3615
3616
    /* check for duplicate name (more friendly than unique-index failure) */
3617
2
    if (SearchSysCacheExists2(TYPENAMENSP,
3618
2
                  NameGetDatum(&typform->typname),
3619
2
                  ObjectIdGetDatum(nspOid)))
3620
2
      ereport(ERROR,
3621
2
          (errcode(ERRCODE_DUPLICATE_OBJECT),
3622
2
           errmsg("type \"%s\" already exists in schema \"%s\"",
3623
2
              NameStr(typform->typname),
3624
2
              get_namespace_name(nspOid))));
3625
2
  }
3626
3627
  /* Detect whether type is a composite type (but not a table rowtype) */
3628
2
  isCompositeType =
3629
2
    (typform->typtype == TYPTYPE_COMPOSITE &&
3630
2
     
get_rel_relkind(typform->typrelid) == 1
RELKIND_COMPOSITE_TYPE1
);
3631
3632
  /* Enforce not-table-type if requested */
3633
2
  if (typform->typtype == TYPTYPE_COMPOSITE && 
!isCompositeType1
&&
3634
2
    
errorOnTableType1
)
3635
2
    ereport(ERROR,
3636
2
        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3637
2
         errmsg("%s is a table's row type",
3638
2
            format_type_be(typeOid)),
3639
2
         errhint("Use ALTER TABLE instead.")));
3640
3641
2
  if (oldNspOid != nspOid)
3642
2
  {
3643
    /* OK, modify the pg_type row */
3644
3645
    /* tup is a copy, so we can scribble directly on it */
3646
2
    typform->typnamespace = nspOid;
3647
3648
2
    CatalogTupleUpdate(rel, &tup->t_self, tup);
3649
2
  }
3650
3651
  /*
3652
   * Composite types have pg_class entries.
3653
   *
3654
   * We need to modify the pg_class tuple as well to reflect the change of
3655
   * schema.
3656
   */
3657
2
  if (isCompositeType)
3658
0
  {
3659
0
    Relation  classRel;
3660
3661
0
    classRel = heap_open(RelationRelationId, RowExclusiveLock);
3662
3663
0
    AlterRelationNamespaceInternal(classRel, typform->typrelid,
3664
0
                     oldNspOid, nspOid,
3665
0
                     false, objsMoved);
3666
3667
0
    heap_close(classRel, RowExclusiveLock);
3668
3669
    /*
3670
     * Check for constraints associated with the composite type (we don't
3671
     * currently support this, but probably will someday).
3672
     */
3673
0
    AlterConstraintNamespaces(typform->typrelid, oldNspOid,
3674
0
                  nspOid, false, objsMoved);
3675
0
  }
3676
2
  else
3677
2
  {
3678
    /* If it's a domain, it might have constraints */
3679
2
    if (typform->typtype == TYPTYPE_DOMAIN)
3680
0
      AlterConstraintNamespaces(typeOid, oldNspOid, nspOid, true,
3681
0
                    objsMoved);
3682
2
  }
3683
3684
  /*
3685
   * Update dependency on schema, if any --- a table rowtype has not got
3686
   * one, and neither does an implicit array.
3687
   */
3688
2
  if (oldNspOid != nspOid &&
3689
2
    (isCompositeType || typform->typtype != TYPTYPE_COMPOSITE) &&
3690
2
    
!isImplicitArray1
)
3691
0
    if (changeDependencyFor(TypeRelationId, typeOid,
3692
0
                NamespaceRelationId, oldNspOid, nspOid) != 1)
3693
0
      elog(ERROR, "failed to change schema dependency for type %s",
3694
2
         format_type_be(typeOid));
3695
3696
2
  InvokeObjectPostAlterHook(TypeRelationId, typeOid, 0);
3697
3698
2
  heap_freetuple(tup);
3699
3700
2
  heap_close(rel, RowExclusiveLock);
3701
3702
2
  add_exact_object_address(&thisobj, objsMoved);
3703
3704
  /* Recursively alter the associated array type, if any */
3705
2
  if (OidIsValid(arrayOid))
3706
1
    AlterTypeNamespaceInternal(arrayOid, nspOid, true, true, objsMoved);
3707
3708
2
  return oldNspOid;
3709
2
}