YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/postgres/src/backend/commands/extension.c
Line
Count
Source (jump to first uncovered line)
1
/*-------------------------------------------------------------------------
2
 *
3
 * extension.c
4
 *    Commands to manipulate extensions
5
 *
6
 * Extensions in PostgreSQL allow management of collections of SQL objects.
7
 *
8
 * All we need internally to manage an extension is an OID so that the
9
 * dependent objects can be associated with it.  An extension is created by
10
 * populating the pg_extension catalog from a "control" file.
11
 * The extension control file is parsed with the same parser we use for
12
 * postgresql.conf and recovery.conf.  An extension also has an installation
13
 * script file, containing SQL commands to create the extension's objects.
14
 *
15
 * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
16
 * Portions Copyright (c) 1994, Regents of the University of California
17
 *
18
 *
19
 * IDENTIFICATION
20
 *    src/backend/commands/extension.c
21
 *
22
 *-------------------------------------------------------------------------
23
 */
24
#include "postgres.h"
25
26
#include <dirent.h>
27
#include <limits.h>
28
#include <sys/file.h>
29
#include <sys/stat.h>
30
#include <unistd.h>
31
32
#include "access/htup_details.h"
33
#include "access/sysattr.h"
34
#include "access/xact.h"
35
#include "catalog/dependency.h"
36
#include "catalog/indexing.h"
37
#include "catalog/namespace.h"
38
#include "catalog/objectaccess.h"
39
#include "catalog/pg_collation.h"
40
#include "catalog/pg_depend.h"
41
#include "catalog/pg_extension.h"
42
#include "catalog/pg_namespace.h"
43
#include "catalog/pg_type.h"
44
#include "commands/alter.h"
45
#include "commands/comment.h"
46
#include "commands/defrem.h"
47
#include "commands/extension.h"
48
#include "commands/schemacmds.h"
49
#include "funcapi.h"
50
#include "mb/pg_wchar.h"
51
#include "miscadmin.h"
52
#include "nodes/makefuncs.h"
53
#include "storage/fd.h"
54
#include "tcop/utility.h"
55
#include "utils/acl.h"
56
#include "utils/builtins.h"
57
#include "utils/fmgroids.h"
58
#include "utils/lsyscache.h"
59
#include "utils/memutils.h"
60
#include "utils/rel.h"
61
#include "utils/snapmgr.h"
62
#include "utils/tqual.h"
63
#include "utils/varlena.h"
64
65
#include "pg_yb_utils.h"
66
67
68
/* Globally visible state variables */
69
bool    creating_extension = false;
70
Oid     CurrentExtensionObject = InvalidOid;
71
72
/*
73
 * Internal data structure to hold the results of parsing a control file
74
 */
75
typedef struct ExtensionControlFile
76
{
77
  char     *name;     /* name of the extension */
78
  char     *directory;    /* directory for script files */
79
  char     *default_version;  /* default install target version, if any */
80
  char     *module_pathname;  /* string to substitute for
81
                   * MODULE_PATHNAME */
82
  char     *comment;    /* comment, if any */
83
  char     *schema;     /* target schema (allowed if !relocatable) */
84
  bool    relocatable;  /* is ALTER EXTENSION SET SCHEMA supported? */
85
  bool    superuser;    /* must be superuser to install? */
86
  int     encoding;   /* encoding of the script file, or -1 */
87
  List     *requires;   /* names of prerequisite extensions */
88
} ExtensionControlFile;
89
90
/*
91
 * Internal data structure for update path information
92
 */
93
typedef struct ExtensionVersionInfo
94
{
95
  char     *name;     /* name of the starting version */
96
  List     *reachable;    /* List of ExtensionVersionInfo's */
97
  bool    installable;  /* does this version have an install script? */
98
  /* working state for Dijkstra's algorithm: */
99
  bool    distance_known; /* is distance from start known yet? */
100
  int     distance;   /* current worst-case distance estimate */
101
  struct ExtensionVersionInfo *previous;  /* current best predecessor */
102
} ExtensionVersionInfo;
103
104
/* Local functions */
105
static List *find_update_path(List *evi_list,
106
         ExtensionVersionInfo *evi_start,
107
         ExtensionVersionInfo *evi_target,
108
         bool reject_indirect,
109
         bool reinitialize);
110
static Oid get_required_extension(char *reqExtensionName,
111
             char *extensionName,
112
             char *origSchemaName,
113
             bool cascade,
114
             List *parents,
115
             bool is_create);
116
static void get_available_versions_for_extension(ExtensionControlFile *pcontrol,
117
                   Tuplestorestate *tupstore,
118
                   TupleDesc tupdesc);
119
static Datum convert_requires_to_datum(List *requires);
120
static void ApplyExtensionUpdates(Oid extensionOid,
121
            ExtensionControlFile *pcontrol,
122
            const char *initialVersion,
123
            List *updateVersions,
124
            char *origSchemaName,
125
            bool cascade,
126
            bool is_create);
127
static char *read_whole_file(const char *filename, int *length);
128
129
130
/*
131
 * get_extension_oid - given an extension name, look up the OID
132
 *
133
 * If missing_ok is false, throw an error if extension name not found.  If
134
 * true, just return InvalidOid.
135
 */
136
Oid
137
get_extension_oid(const char *extname, bool missing_ok)
138
434
{
139
434
  Oid     result;
140
434
  Relation  rel;
141
434
  SysScanDesc scandesc;
142
434
  HeapTuple tuple;
143
434
  ScanKeyData entry[1];
144
145
434
  rel = heap_open(ExtensionRelationId, AccessShareLock);
146
147
434
  ScanKeyInit(&entry[0],
148
434
        Anum_pg_extension_extname,
149
434
        BTEqualStrategyNumber, F_NAMEEQ,
150
434
        CStringGetDatum(extname));
151
152
434
  scandesc = systable_beginscan(rel, ExtensionNameIndexId, true,
153
434
                  NULL, 1, entry);
154
155
434
  tuple = systable_getnext(scandesc);
156
157
  /* We assume that there can be at most one matching tuple */
158
434
  if (HeapTupleIsValid(tuple))
159
414
    result = HeapTupleGetOid(tuple);
160
434
  else
161
20
    result = InvalidOid;
162
163
434
  systable_endscan(scandesc);
164
165
434
  heap_close(rel, AccessShareLock);
166
167
434
  if (!OidIsValid(result) && !missing_ok)
168
434
    ereport(ERROR,
169
434
        (errcode(ERRCODE_UNDEFINED_OBJECT),
170
434
         errmsg("extension \"%s\" does not exist",
171
434
            extname)));
172
173
434
  return result;
174
434
}
175
176
/*
177
 * get_extension_name - given an extension OID, look up the name
178
 *
179
 * Returns a palloc'd string, or NULL if no such extension.
180
 */
181
char *
182
get_extension_name(Oid ext_oid)
183
4
{
184
4
  char     *result;
185
4
  Relation  rel;
186
4
  SysScanDesc scandesc;
187
4
  HeapTuple tuple;
188
4
  ScanKeyData entry[1];
189
190
4
  rel = heap_open(ExtensionRelationId, AccessShareLock);
191
192
4
  ScanKeyInit(&entry[0],
193
4
        ObjectIdAttributeNumber,
194
4
        BTEqualStrategyNumber, F_OIDEQ,
195
4
        ObjectIdGetDatum(ext_oid));
196
197
4
  scandesc = systable_beginscan(rel, ExtensionOidIndexId, true,
198
4
                  NULL, 1, entry);
199
200
4
  tuple = systable_getnext(scandesc);
201
202
  /* We assume that there can be at most one matching tuple */
203
4
  if (HeapTupleIsValid(tuple))
204
4
    result = pstrdup(NameStr(((Form_pg_extension) GETSTRUCT(tuple))->extname));
205
0
  else
206
0
    result = NULL;
207
208
4
  systable_endscan(scandesc);
209
210
4
  heap_close(rel, AccessShareLock);
211
212
4
  return result;
213
4
}
214
215
/*
216
 * get_extension_schema - given an extension OID, fetch its extnamespace
217
 *
218
 * Returns InvalidOid if no such extension.
219
 */
220
static Oid
221
get_extension_schema(Oid ext_oid)
222
1
{
223
1
  Oid     result;
224
1
  Relation  rel;
225
1
  SysScanDesc scandesc;
226
1
  HeapTuple tuple;
227
1
  ScanKeyData entry[1];
228
229
1
  rel = heap_open(ExtensionRelationId, AccessShareLock);
230
231
1
  ScanKeyInit(&entry[0],
232
1
        ObjectIdAttributeNumber,
233
1
        BTEqualStrategyNumber, F_OIDEQ,
234
1
        ObjectIdGetDatum(ext_oid));
235
236
1
  scandesc = systable_beginscan(rel, ExtensionOidIndexId, true,
237
1
                  NULL, 1, entry);
238
239
1
  tuple = systable_getnext(scandesc);
240
241
  /* We assume that there can be at most one matching tuple */
242
1
  if (HeapTupleIsValid(tuple))
243
1
    result = ((Form_pg_extension) GETSTRUCT(tuple))->extnamespace;
244
0
  else
245
0
    result = InvalidOid;
246
247
1
  systable_endscan(scandesc);
248
249
1
  heap_close(rel, AccessShareLock);
250
251
1
  return result;
252
1
}
253
254
/*
255
 * Utility functions to check validity of extension and version names
256
 */
257
static void
258
check_valid_extension_name(const char *extensionname)
259
17
{
260
17
  int     namelen = strlen(extensionname);
261
262
  /*
263
   * Disallow empty names (the parser rejects empty identifiers anyway, but
264
   * let's check).
265
   */
266
17
  if (namelen == 0)
267
17
    ereport(ERROR,
268
17
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
269
17
         errmsg("invalid extension name: \"%s\"", extensionname),
270
17
         errdetail("Extension names must not be empty.")));
271
272
  /*
273
   * No double dashes, since that would make script filenames ambiguous.
274
   */
275
17
  if (strstr(extensionname, "--"))
276
17
    ereport(ERROR,
277
17
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
278
17
         errmsg("invalid extension name: \"%s\"", extensionname),
279
17
         errdetail("Extension names must not contain \"--\".")));
280
281
  /*
282
   * No leading or trailing dash either.  (We could probably allow this, but
283
   * it would require much care in filename parsing and would make filenames
284
   * visually if not formally ambiguous.  Since there's no real-world use
285
   * case, let's just forbid it.)
286
   */
287
17
  if (extensionname[0] == '-' || extensionname[namelen - 1] == '-')
288
17
    ereport(ERROR,
289
17
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
290
17
         errmsg("invalid extension name: \"%s\"", extensionname),
291
17
         errdetail("Extension names must not begin or end with \"-\".")));
292
293
  /*
294
   * No directory separators either (this is sufficient to prevent ".."
295
   * style attacks).
296
   */
297
17
  if (first_dir_separator(extensionname) != NULL)
298
17
    ereport(ERROR,
299
17
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
300
17
         errmsg("invalid extension name: \"%s\"", extensionname),
301
17
         errdetail("Extension names must not contain directory separator characters.")));
302
17
}
303
304
static void
305
check_valid_version_name(const char *versionname)
306
18
{
307
18
  int     namelen = strlen(versionname);
308
309
  /*
310
   * Disallow empty names (we could possibly allow this, but there seems
311
   * little point).
312
   */
313
18
  if (namelen == 0)
314
18
    ereport(ERROR,
315
18
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
316
18
         errmsg("invalid extension version name: \"%s\"", versionname),
317
18
         errdetail("Version names must not be empty.")));
318
319
  /*
320
   * No double dashes, since that would make script filenames ambiguous.
321
   */
322
18
  if (strstr(versionname, "--"))
323
18
    ereport(ERROR,
324
18
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
325
18
         errmsg("invalid extension version name: \"%s\"", versionname),
326
18
         errdetail("Version names must not contain \"--\".")));
327
328
  /*
329
   * No leading or trailing dash either.
330
   */
331
18
  if (versionname[0] == '-' || versionname[namelen - 1] == '-')
332
18
    ereport(ERROR,
333
18
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
334
18
         errmsg("invalid extension version name: \"%s\"", versionname),
335
18
         errdetail("Version names must not begin or end with \"-\".")));
336
337
  /*
338
   * No directory separators either (this is sufficient to prevent ".."
339
   * style attacks).
340
   */
341
18
  if (first_dir_separator(versionname) != NULL)
342
18
    ereport(ERROR,
343
18
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
344
18
         errmsg("invalid extension version name: \"%s\"", versionname),
345
18
         errdetail("Version names must not contain directory separator characters.")));
346
18
}
347
348
/*
349
 * Utility functions to handle extension-related path names
350
 */
351
static bool
352
is_extension_control_filename(const char *filename)
353
0
{
354
0
  const char *extension = strrchr(filename, '.');
355
356
0
  return (extension != NULL) && (strcmp(extension, ".control") == 0);
357
0
}
358
359
static bool
360
is_extension_script_filename(const char *filename)
361
1.63k
{
362
1.63k
  const char *extension = strrchr(filename, '.');
363
364
1.63k
  return (extension != NULL) && (strcmp(extension, ".sql") == 0);
365
1.63k
}
366
367
static char *
368
get_extension_control_directory(void)
369
82
{
370
82
  char    sharepath[MAXPGPATH];
371
82
  char     *result;
372
373
82
  get_share_path(my_exec_path, sharepath);
374
82
  result = (char *) palloc(MAXPGPATH);
375
82
  snprintf(result, MAXPGPATH, "%s/extension", sharepath);
376
377
82
  return result;
378
82
}
379
380
static char *
381
get_extension_control_filename(const char *extname)
382
18
{
383
18
  char    sharepath[MAXPGPATH];
384
18
  char     *result;
385
386
18
  get_share_path(my_exec_path, sharepath);
387
18
  result = (char *) palloc(MAXPGPATH);
388
18
  snprintf(result, MAXPGPATH, "%s/extension/%s.control",
389
18
       sharepath, extname);
390
391
18
  return result;
392
18
}
393
394
static char *
395
get_extension_script_directory(ExtensionControlFile *control)
396
82
{
397
82
  char    sharepath[MAXPGPATH];
398
82
  char     *result;
399
400
  /*
401
   * The directory parameter can be omitted, absolute, or relative to the
402
   * installation's share directory.
403
   */
404
82
  if (!control->directory)
405
82
    return get_extension_control_directory();
406
407
0
  if (is_absolute_path(control->directory))
408
0
    return pstrdup(control->directory);
409
410
0
  get_share_path(my_exec_path, sharepath);
411
0
  result = (char *) palloc(MAXPGPATH);
412
0
  snprintf(result, MAXPGPATH, "%s/%s", sharepath, control->directory);
413
414
0
  return result;
415
0
}
416
417
static char *
418
get_extension_aux_control_filename(ExtensionControlFile *control,
419
                   const char *version)
420
30
{
421
30
  char     *result;
422
30
  char     *scriptdir;
423
424
30
  scriptdir = get_extension_script_directory(control);
425
426
30
  result = (char *) palloc(MAXPGPATH);
427
30
  snprintf(result, MAXPGPATH, "%s/%s--%s.control",
428
30
       scriptdir, control->name, version);
429
430
30
  pfree(scriptdir);
431
432
30
  return result;
433
30
}
434
435
static char *
436
get_extension_script_filename(ExtensionControlFile *control,
437
                const char *from_version, const char *version)
438
45
{
439
45
  char     *result;
440
45
  char     *scriptdir;
441
442
45
  scriptdir = get_extension_script_directory(control);
443
444
45
  result = (char *) palloc(MAXPGPATH);
445
45
  if (from_version)
446
45
    snprintf(result, MAXPGPATH, "%s/%s--%s--%s.sql",
447
45
         scriptdir, control->name, from_version, version);
448
45
  else
449
45
    snprintf(result, MAXPGPATH, "%s/%s--%s.sql",
450
45
         scriptdir, control->name, version);
451
452
45
  pfree(scriptdir);
453
454
45
  return result;
455
45
}
456
457
458
/*
459
 * Parse contents of primary or auxiliary control file, and fill in
460
 * fields of *control.  We parse primary file if version == NULL,
461
 * else the optional auxiliary file for that version.
462
 *
463
 * Control files are supposed to be very short, half a dozen lines,
464
 * so we don't worry about memory allocation risks here.  Also we don't
465
 * worry about what encoding it's in; all values are expected to be ASCII.
466
 */
467
static void
468
parse_extension_control_file(ExtensionControlFile *control,
469
               const char *version)
470
48
{
471
48
  char     *filename;
472
48
  FILE     *file;
473
48
  ConfigVariable *item,
474
48
         *head = NULL,
475
48
         *tail = NULL;
476
477
  /*
478
   * Locate the file to read.  Auxiliary files are optional.
479
   */
480
48
  if (version)
481
30
    filename = get_extension_aux_control_filename(control, version);
482
18
  else
483
18
    filename = get_extension_control_filename(control->name);
484
485
48
  if ((file = AllocateFile(filename, "r")) == NULL)
486
30
  {
487
30
    if (version && errno == ENOENT)
488
30
    {
489
      /* no auxiliary file for this version */
490
30
      pfree(filename);
491
30
      return;
492
30
    }
493
0
    ereport(ERROR,
494
0
        (errcode_for_file_access(),
495
0
         errmsg("could not open extension control file \"%s\": %m",
496
0
            filename)));
497
0
  }
498
499
  /*
500
   * Parse the file content, using GUC's file parsing code.  We need not
501
   * check the return value since any errors will be thrown at ERROR level.
502
   */
503
18
  (void) ParseConfigFp(file, filename, 0, ERROR, &head, &tail);
504
505
18
  FreeFile(file);
506
507
  /*
508
   * Convert the ConfigVariable list into ExtensionControlFile entries.
509
   */
510
93
  for (item = head; item != NULL; item = item->next)
511
75
  {
512
75
    if (strcmp(item->name, "directory") == 0)
513
0
    {
514
0
      if (version)
515
0
        ereport(ERROR,
516
0
            (errcode(ERRCODE_SYNTAX_ERROR),
517
0
             errmsg("parameter \"%s\" cannot be set in a secondary extension control file",
518
0
                item->name)));
519
520
0
      control->directory = pstrdup(item->value);
521
0
    }
522
75
    else if (strcmp(item->name, "default_version") == 0)
523
18
    {
524
18
      if (version)
525
18
        ereport(ERROR,
526
18
            (errcode(ERRCODE_SYNTAX_ERROR),
527
18
             errmsg("parameter \"%s\" cannot be set in a secondary extension control file",
528
18
                item->name)));
529
530
18
      control->default_version = pstrdup(item->value);
531
18
    }
532
57
    else if (strcmp(item->name, "module_pathname") == 0)
533
18
    {
534
18
      control->module_pathname = pstrdup(item->value);
535
18
    }
536
39
    else if (strcmp(item->name, "comment") == 0)
537
18
    {
538
18
      control->comment = pstrdup(item->value);
539
18
    }
540
21
    else if (strcmp(item->name, "schema") == 0)
541
1
    {
542
1
      control->schema = pstrdup(item->value);
543
1
    }
544
20
    else if (strcmp(item->name, "relocatable") == 0)
545
18
    {
546
18
      if (!parse_bool(item->value, &control->relocatable))
547
18
        ereport(ERROR,
548
18
            (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
549
18
             errmsg("parameter \"%s\" requires a Boolean value",
550
18
                item->name)));
551
18
    }
552
2
    else if (strcmp(item->name, "superuser") == 0)
553
0
    {
554
0
      if (!parse_bool(item->value, &control->superuser))
555
0
        ereport(ERROR,
556
0
            (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
557
0
             errmsg("parameter \"%s\" requires a Boolean value",
558
0
                item->name)));
559
0
    }
560
2
    else if (strcmp(item->name, "encoding") == 0)
561
0
    {
562
0
      control->encoding = pg_valid_server_encoding(item->value);
563
0
      if (control->encoding < 0)
564
0
        ereport(ERROR,
565
0
            (errcode(ERRCODE_UNDEFINED_OBJECT),
566
0
             errmsg("\"%s\" is not a valid encoding name",
567
0
                item->value)));
568
0
    }
569
2
    else if (strcmp(item->name, "requires") == 0)
570
2
    {
571
      /* Need a modifiable copy of string */
572
2
      char     *rawnames = pstrdup(item->value);
573
574
      /* Parse string into list of identifiers */
575
2
      if (!SplitIdentifierString(rawnames, ',', &control->requires))
576
0
      {
577
        /* syntax error in name list */
578
0
        ereport(ERROR,
579
0
            (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
580
0
             errmsg("parameter \"%s\" must be a list of extension names",
581
0
                item->name)));
582
0
      }
583
2
    }
584
2
    else
585
0
      ereport(ERROR,
586
75
          (errcode(ERRCODE_SYNTAX_ERROR),
587
75
           errmsg("unrecognized parameter \"%s\" in file \"%s\"",
588
75
              item->name, filename)));
589
75
  }
590
591
18
  FreeConfigVariables(head);
592
593
18
  if (control->relocatable && control->schema != NULL)
594
18
    ereport(ERROR,
595
18
        (errcode(ERRCODE_SYNTAX_ERROR),
596
18
         errmsg("parameter \"schema\" cannot be specified when \"relocatable\" is true")));
597
598
18
  pfree(filename);
599
18
}
600
601
/*
602
 * Read the primary control file for the specified extension.
603
 */
604
static ExtensionControlFile *
605
read_extension_control_file(const char *extname)
606
18
{
607
18
  ExtensionControlFile *control;
608
609
  /*
610
   * Set up default values.  Pointer fields are initially null.
611
   */
612
18
  control = (ExtensionControlFile *) palloc0(sizeof(ExtensionControlFile));
613
18
  control->name = pstrdup(extname);
614
18
  control->relocatable = false;
615
18
  control->superuser = true;
616
18
  control->encoding = -1;
617
618
  /*
619
   * Parse the primary control file.
620
   */
621
18
  parse_extension_control_file(control, NULL);
622
623
18
  return control;
624
18
}
625
626
/*
627
 * Read the auxiliary control file for the specified extension and version.
628
 *
629
 * Returns a new modified ExtensionControlFile struct; the original struct
630
 * (reflecting just the primary control file) is not modified.
631
 */
632
static ExtensionControlFile *
633
read_extension_aux_control_file(const ExtensionControlFile *pcontrol,
634
                const char *version)
635
30
{
636
30
  ExtensionControlFile *acontrol;
637
638
  /*
639
   * Flat-copy the struct.  Pointer fields share values with original.
640
   */
641
30
  acontrol = (ExtensionControlFile *) palloc(sizeof(ExtensionControlFile));
642
30
  memcpy(acontrol, pcontrol, sizeof(ExtensionControlFile));
643
644
  /*
645
   * Parse the auxiliary control file, overwriting struct fields
646
   */
647
30
  parse_extension_control_file(acontrol, version);
648
649
30
  return acontrol;
650
30
}
651
652
/*
653
 * Read an SQL script file into a string, and convert to database encoding
654
 */
655
static char *
656
read_extension_script_file(const ExtensionControlFile *control,
657
               const char *filename)
658
28
{
659
28
  int     src_encoding;
660
28
  char     *src_str;
661
28
  char     *dest_str;
662
28
  int     len;
663
664
28
  src_str = read_whole_file(filename, &len);
665
666
  /* use database encoding if not given */
667
28
  if (control->encoding < 0)
668
28
    src_encoding = GetDatabaseEncoding();
669
0
  else
670
0
    src_encoding = control->encoding;
671
672
  /* make sure that source string is valid in the expected encoding */
673
28
  pg_verify_mbstr_len(src_encoding, src_str, len, false);
674
675
  /*
676
   * Convert the encoding to the database encoding. read_whole_file
677
   * null-terminated the string, so if no conversion happens the string is
678
   * valid as is.
679
   */
680
28
  dest_str = pg_any_to_server(src_str, len, src_encoding);
681
682
28
  return dest_str;
683
28
}
684
685
/*
686
 * Execute given SQL string.
687
 *
688
 * filename is used only to report errors.
689
 *
690
 * Note: it's tempting to just use SPI to execute the string, but that does
691
 * not work very well.  The really serious problem is that SPI will parse,
692
 * analyze, and plan the whole string before executing any of it; of course
693
 * this fails if there are any plannable statements referring to objects
694
 * created earlier in the script.  A lesser annoyance is that SPI insists
695
 * on printing the whole string as errcontext in case of any error, and that
696
 * could be very long.
697
 */
698
static void
699
execute_sql_string(const char *sql, const char *filename)
700
28
{
701
28
  List     *raw_parsetree_list;
702
28
  DestReceiver *dest;
703
28
  ListCell   *lc1;
704
705
  /*
706
   * Parse the SQL string into a list of raw parse trees.
707
   */
708
28
  raw_parsetree_list = pg_parse_query(sql);
709
710
  /* All output from SELECTs goes to the bit bucket */
711
28
  dest = CreateDestReceiver(DestNone);
712
713
  /*
714
   * Do parse analysis, rule rewrite, planning, and execution for each raw
715
   * parsetree.  We must fully execute each query before beginning parse
716
   * analysis on the next one, since there may be interdependencies.
717
   */
718
28
  foreach(lc1, raw_parsetree_list)
719
1.13k
  {
720
1.13k
    RawStmt    *parsetree = lfirst_node(RawStmt, lc1);
721
1.13k
    List     *stmt_list;
722
1.13k
    ListCell   *lc2;
723
724
    /* Be sure parser can see any DDL done so far */
725
1.13k
    CommandCounterIncrement();
726
727
1.13k
    stmt_list = pg_analyze_and_rewrite(parsetree,
728
1.13k
                       sql,
729
1.13k
                       NULL,
730
1.13k
                       0,
731
1.13k
                       NULL);
732
1.13k
    stmt_list = pg_plan_queries(stmt_list, CURSOR_OPT_PARALLEL_OK, NULL);
733
734
1.13k
    foreach(lc2, stmt_list)
735
1.13k
    {
736
1.13k
      PlannedStmt *stmt = lfirst_node(PlannedStmt, lc2);
737
738
1.13k
      CommandCounterIncrement();
739
740
1.13k
      PushActiveSnapshot(GetTransactionSnapshot());
741
742
1.13k
      if (stmt->utilityStmt == NULL)
743
6
      {
744
6
        QueryDesc  *qdesc;
745
746
6
        qdesc = CreateQueryDesc(stmt,
747
6
                    sql,
748
6
                    GetActiveSnapshot(), NULL,
749
6
                    dest, NULL, NULL, 0);
750
751
6
        ExecutorStart(qdesc, 0);
752
6
        ExecutorRun(qdesc, ForwardScanDirection, 0, true);
753
6
        ExecutorFinish(qdesc);
754
6
        ExecutorEnd(qdesc);
755
756
6
        FreeQueryDesc(qdesc);
757
6
      }
758
1.12k
      else
759
1.12k
      {
760
1.12k
        if (IsA(stmt->utilityStmt, TransactionStmt))
761
1.12k
          ereport(ERROR,
762
1.12k
              (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
763
1.12k
               errmsg("transaction control statements are not allowed within an extension script")));
764
765
1.12k
        ProcessUtility(stmt,
766
1.12k
                 sql,
767
1.12k
                 PROCESS_UTILITY_QUERY,
768
1.12k
                 NULL,
769
1.12k
                 NULL,
770
1.12k
                 dest,
771
1.12k
                 NULL);
772
1.12k
      }
773
774
1.13k
      PopActiveSnapshot();
775
1.13k
    }
776
1.13k
  }
777
778
  /* Be sure to advance the command counter after the last script command */
779
28
  CommandCounterIncrement();
780
28
}
781
782
/*
783
 * Execute the appropriate script file for installing or updating the extension
784
 *
785
 * If from_version isn't NULL, it's an update
786
 */
787
static void
788
execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
789
             const char *from_version,
790
             const char *version,
791
             List *requiredSchemas,
792
             const char *schemaName, Oid schemaOid)
793
29
{
794
29
  char     *filename;
795
29
  int     save_nestlevel;
796
29
  StringInfoData pathbuf;
797
29
  ListCell   *lc;
798
799
  /*
800
   * Enforce superuser-ness if appropriate.  We postpone this check until
801
   * here so that the flag is correctly associated with the right script(s)
802
   * if it's set in secondary control files.
803
   */
804
29
  if (!IsYbExtensionUser(GetUserId()) && (control->superuser && !superuser()))
805
1
  {
806
1
    if (from_version == NULL)
807
1
      ereport(ERROR,
808
1
          (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
809
1
           errmsg("permission denied to create extension \"%s\"",
810
1
              control->name),
811
1
           errhint("Must be superuser to create this extension.")));
812
1
    else
813
1
      ereport(ERROR,
814
1
          (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
815
1
           errmsg("permission denied to update extension \"%s\"",
816
1
              control->name),
817
1
           errhint("Must be superuser to update this extension.")));
818
1
  }
819
820
29
  filename = get_extension_script_filename(control, from_version, version);
821
822
  /*
823
   * Force client_min_messages and log_min_messages to be at least WARNING,
824
   * so that we won't spam the user with useless NOTICE messages from common
825
   * script actions like creating shell types.
826
   *
827
   * We use the equivalent of a function SET option to allow the setting to
828
   * persist for exactly the duration of the script execution.  guc.c also
829
   * takes care of undoing the setting on error.
830
   */
831
29
  save_nestlevel = NewGUCNestLevel();
832
833
29
  if (client_min_messages < WARNING)
834
28
    (void) set_config_option("client_min_messages", "warning",
835
28
                 PGC_USERSET, PGC_S_SESSION,
836
28
                 GUC_ACTION_SAVE, true, 0, false);
837
29
  if (log_min_messages < WARNING)
838
0
    (void) set_config_option("log_min_messages", "warning",
839
0
                 PGC_SUSET, PGC_S_SESSION,
840
0
                 GUC_ACTION_SAVE, true, 0, false);
841
842
  /*
843
   * Set up the search path to contain the target schema, then the schemas
844
   * of any prerequisite extensions, and nothing else.  In particular this
845
   * makes the target schema be the default creation target namespace.
846
   *
847
   * Note: it might look tempting to use PushOverrideSearchPath for this,
848
   * but we cannot do that.  We have to actually set the search_path GUC in
849
   * case the extension script examines or changes it.  In any case, the
850
   * GUC_ACTION_SAVE method is just as convenient.
851
   */
852
29
  initStringInfo(&pathbuf);
853
29
  appendStringInfoString(&pathbuf, quote_identifier(schemaName));
854
29
  foreach(lc, requiredSchemas)
855
1
  {
856
1
    Oid     reqschema = lfirst_oid(lc);
857
1
    char     *reqname = get_namespace_name(reqschema);
858
859
1
    if (reqname)
860
1
      appendStringInfo(&pathbuf, ", %s", quote_identifier(reqname));
861
1
  }
862
863
29
  (void) set_config_option("search_path", pathbuf.data,
864
29
               PGC_USERSET, PGC_S_SESSION,
865
29
               GUC_ACTION_SAVE, true, 0, false);
866
867
  /*
868
   * Set creating_extension and related variables so that
869
   * recordDependencyOnCurrentExtension and other functions do the right
870
   * things.  On failure, ensure we reset these variables.
871
   */
872
29
  creating_extension = true;
873
29
  CurrentExtensionObject = extensionOid;
874
29
  PG_TRY();
875
28
  {
876
28
    char     *c_sql = read_extension_script_file(control, filename);
877
28
    Datum   t_sql;
878
879
    /* We use various functions that want to operate on text datums */
880
28
    t_sql = CStringGetTextDatum(c_sql);
881
882
    /*
883
     * Reduce any lines beginning with "\echo" to empty.  This allows
884
     * scripts to contain messages telling people not to run them via
885
     * psql, which has been found to be necessary due to old habits.
886
     */
887
28
    t_sql = DirectFunctionCall4Coll(textregexreplace,
888
28
                    C_COLLATION_OID,
889
28
                    t_sql,
890
28
                    CStringGetTextDatum("^\\\\echo.*$"),
891
28
                    CStringGetTextDatum(""),
892
28
                    CStringGetTextDatum("ng"));
893
894
    /*
895
     * If it's not relocatable, substitute the target schema name for
896
     * occurrences of @extschema@.
897
     *
898
     * For a relocatable extension, we needn't do this.  There cannot be
899
     * any need for @extschema@, else it wouldn't be relocatable.
900
     */
901
28
    if (!control->relocatable)
902
3
    {
903
3
      const char *qSchemaName = quote_identifier(schemaName);
904
905
3
      t_sql = DirectFunctionCall3(replace_text,
906
3
                    t_sql,
907
3
                    CStringGetTextDatum("@extschema@"),
908
3
                    CStringGetTextDatum(qSchemaName));
909
3
    }
910
911
    /*
912
     * If module_pathname was set in the control file, substitute its
913
     * value for occurrences of MODULE_PATHNAME.
914
     */
915
28
    if (control->module_pathname)
916
28
    {
917
28
      t_sql = DirectFunctionCall3(replace_text,
918
28
                    t_sql,
919
28
                    CStringGetTextDatum("MODULE_PATHNAME"),
920
28
                    CStringGetTextDatum(control->module_pathname));
921
28
    }
922
923
    /* And now back to C string */
924
28
    c_sql = text_to_cstring(DatumGetTextPP(t_sql));
925
926
28
    execute_sql_string(c_sql, filename);
927
28
  }
928
28
  PG_CATCH();
929
1
  {
930
1
    creating_extension = false;
931
1
    CurrentExtensionObject = InvalidOid;
932
1
    PG_RE_THROW();
933
1
  }
934
1
  PG_END_TRY();
935
936
28
  creating_extension = false;
937
28
  CurrentExtensionObject = InvalidOid;
938
939
  /*
940
   * Restore the GUC variables we set above.
941
   */
942
28
  AtEOXact_GUC(true, save_nestlevel);
943
28
}
944
945
/*
946
 * Find or create an ExtensionVersionInfo for the specified version name
947
 *
948
 * Currently, we just use a List of the ExtensionVersionInfo's.  Searching
949
 * for them therefore uses about O(N^2) time when there are N versions of
950
 * the extension.  We could change the data structure to a hash table if
951
 * this ever becomes a bottleneck.
952
 */
953
static ExtensionVersionInfo *
954
get_ext_ver_info(const char *versionname, List **evi_list)
955
80
{
956
80
  ExtensionVersionInfo *evi;
957
80
  ListCell   *lc;
958
959
80
  foreach(lc, *evi_list)
960
239
  {
961
239
    evi = (ExtensionVersionInfo *) lfirst(lc);
962
239
    if (strcmp(evi->name, versionname) == 0)
963
40
      return evi;
964
239
  }
965
966
40
  evi = (ExtensionVersionInfo *) palloc(sizeof(ExtensionVersionInfo));
967
40
  evi->name = pstrdup(versionname);
968
40
  evi->reachable = NIL;
969
40
  evi->installable = false;
970
  /* initialize for later application of Dijkstra's algorithm */
971
40
  evi->distance_known = false;
972
40
  evi->distance = INT_MAX;
973
40
  evi->previous = NULL;
974
975
40
  *evi_list = lappend(*evi_list, evi);
976
977
40
  return evi;
978
80
}
979
980
/*
981
 * Locate the nearest unprocessed ExtensionVersionInfo
982
 *
983
 * This part of the algorithm is also about O(N^2).  A priority queue would
984
 * make it much faster, but for now there's no need.
985
 */
986
static ExtensionVersionInfo *
987
get_nearest_unprocessed_vertex(List *evi_list)
988
20
{
989
20
  ExtensionVersionInfo *evi = NULL;
990
20
  ListCell   *lc;
991
992
20
  foreach(lc, evi_list)
993
116
  {
994
116
    ExtensionVersionInfo *evi2 = (ExtensionVersionInfo *) lfirst(lc);
995
996
    /* only vertices whose distance is still uncertain are candidates */
997
116
    if (evi2->distance_known)
998
19
      continue;
999
    /* remember the closest such vertex */
1000
97
    if (evi == NULL ||
1001
77
      evi->distance > evi2->distance)
1002
35
      evi = evi2;
1003
97
  }
1004
1005
20
  return evi;
1006
20
}
1007
1008
/*
1009
 * Obtain information about the set of update scripts available for the
1010
 * specified extension.  The result is a List of ExtensionVersionInfo
1011
 * structs, each with a subsidiary list of the ExtensionVersionInfos for
1012
 * the versions that can be reached in one step from that version.
1013
 */
1014
static List *
1015
get_ext_ver_list(ExtensionControlFile *control)
1016
7
{
1017
7
  List     *evi_list = NIL;
1018
7
  int     extnamelen = strlen(control->name);
1019
7
  char     *location;
1020
7
  DIR      *dir;
1021
7
  struct dirent *de;
1022
1023
7
  location = get_extension_script_directory(control);
1024
7
  dir = AllocateDir(location);
1025
1.63k
  while ((de = ReadDir(dir, location)) != NULL)
1026
1.63k
  {
1027
1.63k
    char     *vername;
1028
1.63k
    char     *vername2;
1029
1.63k
    ExtensionVersionInfo *evi;
1030
1.63k
    ExtensionVersionInfo *evi2;
1031
1032
    /* must be a .sql file ... */
1033
1.63k
    if (!is_extension_script_filename(de->d_name))
1034
357
      continue;
1035
1036
    /* ... matching extension name followed by separator */
1037
1.27k
    if (strncmp(de->d_name, control->name, extnamelen) != 0 ||
1038
40
      de->d_name[extnamelen] != '-' ||
1039
40
      de->d_name[extnamelen + 1] != '-')
1040
1.23k
      continue;
1041
1042
    /* extract version name(s) from 'extname--something.sql' filename */
1043
40
    vername = pstrdup(de->d_name + extnamelen + 2);
1044
40
    *strrchr(vername, '.') = '\0';
1045
40
    vername2 = strstr(vername, "--");
1046
40
    if (!vername2)
1047
7
    {
1048
      /* It's an install, not update, script; record its version name */
1049
7
      evi = get_ext_ver_info(vername, &evi_list);
1050
7
      evi->installable = true;
1051
7
      continue;
1052
7
    }
1053
33
    *vername2 = '\0';   /* terminate first version */
1054
33
    vername2 += 2;      /* and point to second */
1055
1056
    /* if there's a third --, it's bogus, ignore it */
1057
33
    if (strstr(vername2, "--"))
1058
0
      continue;
1059
1060
    /* Create ExtensionVersionInfos and link them together */
1061
33
    evi = get_ext_ver_info(vername, &evi_list);
1062
33
    evi2 = get_ext_ver_info(vername2, &evi_list);
1063
33
    evi->reachable = lappend(evi->reachable, evi2);
1064
33
  }
1065
7
  FreeDir(dir);
1066
1067
7
  return evi_list;
1068
7
}
1069
1070
/*
1071
 * Given an initial and final version name, identify the sequence of update
1072
 * scripts that have to be applied to perform that update.
1073
 *
1074
 * Result is a List of names of versions to transition through (the initial
1075
 * version is *not* included).
1076
 */
1077
static List *
1078
identify_update_path(ExtensionControlFile *control,
1079
           const char *oldVersion, const char *newVersion)
1080
0
{
1081
0
  List     *result;
1082
0
  List     *evi_list;
1083
0
  ExtensionVersionInfo *evi_start;
1084
0
  ExtensionVersionInfo *evi_target;
1085
1086
  /* Extract the version update graph from the script directory */
1087
0
  evi_list = get_ext_ver_list(control);
1088
1089
  /* Initialize start and end vertices */
1090
0
  evi_start = get_ext_ver_info(oldVersion, &evi_list);
1091
0
  evi_target = get_ext_ver_info(newVersion, &evi_list);
1092
1093
  /* Find shortest path */
1094
0
  result = find_update_path(evi_list, evi_start, evi_target, false, false);
1095
1096
0
  if (result == NIL)
1097
0
    ereport(ERROR,
1098
0
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1099
0
         errmsg("extension \"%s\" has no update path from version \"%s\" to version \"%s\"",
1100
0
            control->name, oldVersion, newVersion)));
1101
1102
0
  return result;
1103
0
}
1104
1105
/*
1106
 * Apply Dijkstra's algorithm to find the shortest path from evi_start to
1107
 * evi_target.
1108
 *
1109
 * If reject_indirect is true, ignore paths that go through installable
1110
 * versions.  This saves work when the caller will consider starting from
1111
 * all installable versions anyway.
1112
 *
1113
 * If reinitialize is false, assume the ExtensionVersionInfo list has not
1114
 * been used for this before, and the initialization done by get_ext_ver_info
1115
 * is still good.  Otherwise, reinitialize all transient fields used here.
1116
 *
1117
 * Result is a List of names of versions to transition through (the initial
1118
 * version is *not* included).  Returns NIL if no such path.
1119
 */
1120
static List *
1121
find_update_path(List *evi_list,
1122
         ExtensionVersionInfo *evi_start,
1123
         ExtensionVersionInfo *evi_target,
1124
         bool reject_indirect,
1125
         bool reinitialize)
1126
7
{
1127
7
  List     *result;
1128
7
  ExtensionVersionInfo *evi;
1129
7
  ListCell   *lc;
1130
1131
  /* Caller error if start == target */
1132
7
  Assert(evi_start != evi_target);
1133
  /* Caller error if reject_indirect and target is installable */
1134
7
  Assert(!(reject_indirect && evi_target->installable));
1135
1136
7
  if (reinitialize)
1137
7
  {
1138
7
    foreach(lc, evi_list)
1139
40
    {
1140
40
      evi = (ExtensionVersionInfo *) lfirst(lc);
1141
40
      evi->distance_known = false;
1142
40
      evi->distance = INT_MAX;
1143
40
      evi->previous = NULL;
1144
40
    }
1145
7
  }
1146
1147
7
  evi_start->distance = 0;
1148
1149
20
  while ((evi = get_nearest_unprocessed_vertex(evi_list)) != NULL)
1150
20
  {
1151
20
    if (evi->distance == INT_MAX)
1152
0
      break;       /* all remaining vertices are unreachable */
1153
20
    evi->distance_known = true;
1154
20
    if (evi == evi_target)
1155
7
      break;       /* found shortest path to target */
1156
13
    foreach(lc, evi->reachable)
1157
13
    {
1158
13
      ExtensionVersionInfo *evi2 = (ExtensionVersionInfo *) lfirst(lc);
1159
13
      int     newdist;
1160
1161
      /* if reject_indirect, treat installable versions as unreachable */
1162
13
      if (reject_indirect && evi2->installable)
1163
0
        continue;
1164
13
      newdist = evi->distance + 1;
1165
13
      if (newdist < evi2->distance)
1166
13
      {
1167
13
        evi2->distance = newdist;
1168
13
        evi2->previous = evi;
1169
13
      }
1170
0
      else if (newdist == evi2->distance &&
1171
0
           evi2->previous != NULL &&
1172
0
           strcmp(evi->name, evi2->previous->name) < 0)
1173
0
      {
1174
        /*
1175
         * Break ties in favor of the version name that comes first
1176
         * according to strcmp().  This behavior is undocumented and
1177
         * users shouldn't rely on it.  We do it just to ensure that
1178
         * if there is a tie, the update path that is chosen does not
1179
         * depend on random factors like the order in which directory
1180
         * entries get visited.
1181
         */
1182
0
        evi2->previous = evi;
1183
0
      }
1184
13
    }
1185
13
  }
1186
1187
  /* Return NIL if target is not reachable from start */
1188
7
  if (!evi_target->distance_known)
1189
0
    return NIL;
1190
1191
  /* Build and return list of version names representing the update path */
1192
7
  result = NIL;
1193
20
  for (evi = evi_target; evi != evi_start; evi = evi->previous)
1194
13
    result = lcons(evi->name, result);
1195
1196
7
  return result;
1197
7
}
1198
1199
/*
1200
 * Given a target version that is not directly installable, find the
1201
 * best installation sequence starting from a directly-installable version.
1202
 *
1203
 * evi_list: previously-collected version update graph
1204
 * evi_target: member of that list that we want to reach
1205
 *
1206
 * Returns the best starting-point version, or NULL if there is none.
1207
 * On success, *best_path is set to the path from the start point.
1208
 *
1209
 * If there's more than one possible start point, prefer shorter update paths,
1210
 * and break any ties arbitrarily on the basis of strcmp'ing the starting
1211
 * versions' names.
1212
 */
1213
static ExtensionVersionInfo *
1214
find_install_path(List *evi_list, ExtensionVersionInfo *evi_target,
1215
          List **best_path)
1216
7
{
1217
7
  ExtensionVersionInfo *evi_start = NULL;
1218
7
  ListCell   *lc;
1219
1220
7
  *best_path = NIL;
1221
1222
  /*
1223
   * We don't expect to be called for an installable target, but if we are,
1224
   * the answer is easy: just start from there, with an empty update path.
1225
   */
1226
7
  if (evi_target->installable)
1227
0
    return evi_target;
1228
1229
  /* Consider all installable versions as start points */
1230
7
  foreach(lc, evi_list)
1231
40
  {
1232
40
    ExtensionVersionInfo *evi1 = (ExtensionVersionInfo *) lfirst(lc);
1233
40
    List     *path;
1234
1235
40
    if (!evi1->installable)
1236
33
      continue;
1237
1238
    /*
1239
     * Find shortest path from evi1 to evi_target; but no need to consider
1240
     * paths going through other installable versions.
1241
     */
1242
7
    path = find_update_path(evi_list, evi1, evi_target, true, true);
1243
7
    if (path == NIL)
1244
0
      continue;
1245
1246
    /* Remember best path */
1247
7
    if (evi_start == NULL ||
1248
0
      list_length(path) < list_length(*best_path) ||
1249
0
      (list_length(path) == list_length(*best_path) &&
1250
0
       strcmp(evi_start->name, evi1->name) < 0))
1251
7
    {
1252
7
      evi_start = evi1;
1253
7
      *best_path = path;
1254
7
    }
1255
7
  }
1256
1257
7
  return evi_start;
1258
7
}
1259
1260
/*
1261
 * CREATE EXTENSION worker
1262
 *
1263
 * When CASCADE is specified, CreateExtensionInternal() recurses if required
1264
 * extensions need to be installed.  To sanely handle cyclic dependencies,
1265
 * the "parents" list contains a list of names of extensions already being
1266
 * installed, allowing us to error out if we recurse to one of those.
1267
 */
1268
static ObjectAddress
1269
CreateExtensionInternal(char *extensionName,
1270
            char *schemaName,
1271
            const char *versionName,
1272
            const char *oldVersionName,
1273
            bool cascade,
1274
            List *parents,
1275
            bool is_create)
1276
17
{
1277
17
  char     *origSchemaName = schemaName;
1278
17
  Oid     schemaOid = InvalidOid;
1279
17
  Oid     extowner = GetUserId();
1280
17
  ExtensionControlFile *pcontrol;
1281
17
  ExtensionControlFile *control;
1282
17
  List     *updateVersions;
1283
17
  List     *requiredExtensions;
1284
17
  List     *requiredSchemas;
1285
17
  Oid     extensionOid;
1286
17
  ObjectAddress address;
1287
17
  ListCell   *lc;
1288
1289
  /*
1290
   * Read the primary control file.  Note we assume that it does not contain
1291
   * any non-ASCII data, so there is no need to worry about encoding at this
1292
   * point.
1293
   */
1294
17
  pcontrol = read_extension_control_file(extensionName);
1295
1296
  /*
1297
   * Determine the version to install
1298
   */
1299
17
  if (versionName == NULL)
1300
17
  {
1301
17
    if (pcontrol->default_version)
1302
17
      versionName = pcontrol->default_version;
1303
17
    else
1304
17
      ereport(ERROR,
1305
17
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1306
17
           errmsg("version to install must be specified")));
1307
17
  }
1308
17
  check_valid_version_name(versionName);
1309
1310
  /*
1311
   * Figure out which script(s) we need to run to install the desired
1312
   * version of the extension.  If we do not have a script that directly
1313
   * does what is needed, we try to find a sequence of update scripts that
1314
   * will get us there.
1315
   */
1316
17
  if (oldVersionName)
1317
0
  {
1318
    /*
1319
     * "FROM old_version" was specified, indicating that we're trying to
1320
     * update from some unpackaged version of the extension.  Locate a
1321
     * series of update scripts that will do it.
1322
     */
1323
0
    check_valid_version_name(oldVersionName);
1324
1325
0
    if (strcmp(oldVersionName, versionName) == 0)
1326
0
      ereport(ERROR,
1327
0
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1328
0
           errmsg("FROM version must be different from installation target version \"%s\"",
1329
0
              versionName)));
1330
1331
0
    updateVersions = identify_update_path(pcontrol,
1332
0
                        oldVersionName,
1333
0
                        versionName);
1334
1335
0
    if (list_length(updateVersions) == 1)
1336
0
    {
1337
      /*
1338
       * Simple case where there's just one update script to run. We
1339
       * will not need any follow-on update steps.
1340
       */
1341
0
      Assert(strcmp((char *) linitial(updateVersions), versionName) == 0);
1342
0
      updateVersions = NIL;
1343
0
    }
1344
0
    else
1345
0
    {
1346
      /*
1347
       * Multi-step sequence.  We treat this as installing the version
1348
       * that is the target of the first script, followed by successive
1349
       * updates to the later versions.
1350
       */
1351
0
      versionName = (char *) linitial(updateVersions);
1352
0
      updateVersions = list_delete_first(updateVersions);
1353
0
    }
1354
0
  }
1355
17
  else
1356
17
  {
1357
    /*
1358
     * No FROM, so we're installing from scratch.  If there is an install
1359
     * script for the desired version, we only need to run that one.
1360
     */
1361
17
    char     *filename;
1362
17
    struct stat fst;
1363
1364
17
    oldVersionName = NULL;
1365
1366
17
    filename = get_extension_script_filename(pcontrol, NULL, versionName);
1367
17
    if (stat(filename, &fst) == 0)
1368
10
    {
1369
      /* Easy, no extra scripts */
1370
10
      updateVersions = NIL;
1371
10
    }
1372
7
    else
1373
7
    {
1374
      /* Look for best way to install this version */
1375
7
      List     *evi_list;
1376
7
      ExtensionVersionInfo *evi_start;
1377
7
      ExtensionVersionInfo *evi_target;
1378
1379
      /* Extract the version update graph from the script directory */
1380
7
      evi_list = get_ext_ver_list(pcontrol);
1381
1382
      /* Identify the target version */
1383
7
      evi_target = get_ext_ver_info(versionName, &evi_list);
1384
1385
      /* Identify best path to reach target */
1386
7
      evi_start = find_install_path(evi_list, evi_target,
1387
7
                      &updateVersions);
1388
1389
      /* Fail if no path ... */
1390
7
      if (evi_start == NULL)
1391
7
        ereport(ERROR,
1392
7
            (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1393
7
             errmsg("extension \"%s\" has no installation script nor update path for version \"%s\"",
1394
7
                pcontrol->name, versionName)));
1395
1396
      /* Otherwise, install best starting point and then upgrade */
1397
7
      versionName = evi_start->name;
1398
7
    }
1399
17
  }
1400
1401
  /*
1402
   * Fetch control parameters for installation target version
1403
   */
1404
17
  control = read_extension_aux_control_file(pcontrol, versionName);
1405
1406
  /*
1407
   * Determine the target schema to install the extension into
1408
   */
1409
17
  if (schemaName)
1410
1
  {
1411
    /* If the user is giving us the schema name, it must exist already. */
1412
1
    schemaOid = get_namespace_oid(schemaName, false);
1413
1
  }
1414
1415
17
  if (control->schema != NULL)
1416
1
  {
1417
    /*
1418
     * The extension is not relocatable and the author gave us a schema
1419
     * for it.
1420
     *
1421
     * Unless CASCADE parameter was given, it's an error to give a schema
1422
     * different from control->schema if control->schema is specified.
1423
     */
1424
1
    if (schemaName && strcmp(control->schema, schemaName) != 0 &&
1425
0
      !cascade)
1426
1
      ereport(ERROR,
1427
1
          (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1428
1
           errmsg("extension \"%s\" must be installed in schema \"%s\"",
1429
1
              control->name,
1430
1
              control->schema)));
1431
1432
    /* Always use the schema from control file for current extension. */
1433
1
    schemaName = control->schema;
1434
1435
    /* Find or create the schema in case it does not exist. */
1436
1
    schemaOid = get_namespace_oid(schemaName, true);
1437
1438
1
    if (!OidIsValid(schemaOid))
1439
0
    {
1440
0
      CreateSchemaStmt *csstmt = makeNode(CreateSchemaStmt);
1441
1442
0
      csstmt->schemaname = schemaName;
1443
0
      csstmt->authrole = NULL;  /* will be created by current user */
1444
0
      csstmt->schemaElts = NIL;
1445
0
      csstmt->if_not_exists = false;
1446
0
      CreateSchemaCommand(csstmt, "(generated CREATE SCHEMA command)",
1447
0
                -1, -1);
1448
1449
      /*
1450
       * CreateSchemaCommand includes CommandCounterIncrement, so new
1451
       * schema is now visible.
1452
       */
1453
0
      schemaOid = get_namespace_oid(schemaName, false);
1454
0
    }
1455
1
  }
1456
16
  else if (!OidIsValid(schemaOid))
1457
15
  {
1458
    /*
1459
     * Neither user nor author of the extension specified schema; use the
1460
     * current default creation namespace, which is the first explicit
1461
     * entry in the search_path.
1462
     */
1463
15
    List     *search_path = fetch_search_path(false);
1464
1465
15
    if (search_path == NIL) /* nothing valid in search_path? */
1466
15
      ereport(ERROR,
1467
15
          (errcode(ERRCODE_UNDEFINED_SCHEMA),
1468
15
           errmsg("no schema has been selected to create in")));
1469
15
    schemaOid = linitial_oid(search_path);
1470
15
    schemaName = get_namespace_name(schemaOid);
1471
15
    if (schemaName == NULL) /* recently-deleted namespace? */
1472
15
      ereport(ERROR,
1473
15
          (errcode(ERRCODE_UNDEFINED_SCHEMA),
1474
15
           errmsg("no schema has been selected to create in")));
1475
1476
15
    list_free(search_path);
1477
15
  }
1478
1479
  /*
1480
   * Make note if a temporary namespace has been accessed in this
1481
   * transaction.
1482
   */
1483
17
  if (isTempNamespace(schemaOid))
1484
0
    MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;
1485
1486
  /*
1487
   * We don't check creation rights on the target namespace here.  If the
1488
   * extension script actually creates any objects there, it will fail if
1489
   * the user doesn't have such permissions.  But there are cases such as
1490
   * procedural languages where it's convenient to set schema = pg_catalog
1491
   * yet we don't want to restrict the command to users with ACL_CREATE for
1492
   * pg_catalog.
1493
   */
1494
1495
  /*
1496
   * Look up the prerequisite extensions, install them if necessary, and
1497
   * build lists of their OIDs and the OIDs of their target schemas.
1498
   */
1499
17
  requiredExtensions = NIL;
1500
17
  requiredSchemas = NIL;
1501
17
  foreach(lc, control->requires)
1502
2
  {
1503
2
    char     *curreq = (char *) lfirst(lc);
1504
2
    Oid     reqext;
1505
2
    Oid     reqschema;
1506
1507
2
    reqext = get_required_extension(curreq,
1508
2
                    extensionName,
1509
2
                    origSchemaName,
1510
2
                    cascade,
1511
2
                    parents,
1512
2
                    is_create);
1513
2
    reqschema = get_extension_schema(reqext);
1514
2
    requiredExtensions = lappend_oid(requiredExtensions, reqext);
1515
2
    requiredSchemas = lappend_oid(requiredSchemas, reqschema);
1516
2
  }
1517
1518
  /*
1519
   * Insert new tuple into pg_extension, and create dependency entries.
1520
   */
1521
17
  address = InsertExtensionTuple(control->name, extowner,
1522
17
                   schemaOid, control->relocatable,
1523
17
                   versionName,
1524
17
                   PointerGetDatum(NULL),
1525
17
                   PointerGetDatum(NULL),
1526
17
                   requiredExtensions);
1527
17
  extensionOid = address.objectId;
1528
1529
  /*
1530
   * Apply any control-file comment on extension
1531
   */
1532
17
  if (control->comment != NULL)
1533
16
    CreateComments(extensionOid, ExtensionRelationId, 0, control->comment);
1534
1535
  /*
1536
   * Execute the installation script file
1537
   */
1538
17
  execute_extension_script(extensionOid, control,
1539
17
               oldVersionName, versionName,
1540
17
               requiredSchemas,
1541
17
               schemaName, schemaOid);
1542
1543
  /*
1544
   * If additional update scripts have to be executed, apply the updates as
1545
   * though a series of ALTER EXTENSION UPDATE commands were given
1546
   */
1547
17
  ApplyExtensionUpdates(extensionOid, pcontrol,
1548
17
              versionName, updateVersions,
1549
17
              origSchemaName, cascade, is_create);
1550
1551
17
  return address;
1552
17
}
1553
1554
/*
1555
 * Get the OID of an extension listed in "requires", possibly creating it.
1556
 */
1557
static Oid
1558
get_required_extension(char *reqExtensionName,
1559
             char *extensionName,
1560
             char *origSchemaName,
1561
             bool cascade,
1562
             List *parents,
1563
             bool is_create)
1564
2
{
1565
2
  Oid     reqExtensionOid;
1566
1567
2
  reqExtensionOid = get_extension_oid(reqExtensionName, true);
1568
2
  if (!OidIsValid(reqExtensionOid))
1569
1
  {
1570
1
    if (cascade)
1571
0
    {
1572
      /* Must install it. */
1573
0
      ObjectAddress addr;
1574
0
      List     *cascade_parents;
1575
0
      ListCell   *lc;
1576
1577
      /* Check extension name validity before trying to cascade. */
1578
0
      check_valid_extension_name(reqExtensionName);
1579
1580
      /* Check for cyclic dependency between extensions. */
1581
0
      foreach(lc, parents)
1582
0
      {
1583
0
        char     *pname = (char *) lfirst(lc);
1584
1585
0
        if (strcmp(pname, reqExtensionName) == 0)
1586
0
          ereport(ERROR,
1587
0
              (errcode(ERRCODE_INVALID_RECURSION),
1588
0
               errmsg("cyclic dependency detected between extensions \"%s\" and \"%s\"",
1589
0
                  reqExtensionName, extensionName)));
1590
0
      }
1591
1592
0
      ereport(NOTICE,
1593
0
          (errmsg("installing required extension \"%s\"",
1594
0
              reqExtensionName)));
1595
1596
      /* Add current extension to list of parents to pass down. */
1597
0
      cascade_parents = lappend(list_copy(parents), extensionName);
1598
1599
      /*
1600
       * Create the required extension.  We propagate the SCHEMA option
1601
       * if any, and CASCADE, but no other options.
1602
       */
1603
0
      addr = CreateExtensionInternal(reqExtensionName,
1604
0
                       origSchemaName,
1605
0
                       NULL,
1606
0
                       NULL,
1607
0
                       cascade,
1608
0
                       cascade_parents,
1609
0
                       is_create);
1610
1611
      /* Get its newly-assigned OID. */
1612
0
      reqExtensionOid = addr.objectId;
1613
0
    }
1614
1
    else
1615
1
      ereport(ERROR,
1616
1
          (errcode(ERRCODE_UNDEFINED_OBJECT),
1617
1
           errmsg("required extension \"%s\" is not installed",
1618
1
              reqExtensionName),
1619
1
           is_create ?
1620
1
           errhint("Use CREATE EXTENSION ... CASCADE to install required extensions too.") : 0));
1621
1
  }
1622
1623
2
  return reqExtensionOid;
1624
2
}
1625
1626
/*
1627
 * CREATE EXTENSION
1628
 */
1629
ObjectAddress
1630
CreateExtension(ParseState *pstate, CreateExtensionStmt *stmt)
1631
17
{
1632
17
  DefElem    *d_schema = NULL;
1633
17
  DefElem    *d_new_version = NULL;
1634
17
  DefElem    *d_old_version = NULL;
1635
17
  DefElem    *d_cascade = NULL;
1636
17
  char     *schemaName = NULL;
1637
17
  char     *versionName = NULL;
1638
17
  char     *oldVersionName = NULL;
1639
17
  bool    cascade = false;
1640
17
  ListCell   *lc;
1641
1642
  /* Check extension name validity before any filesystem access */
1643
17
  check_valid_extension_name(stmt->extname);
1644
1645
  /*
1646
   * Check for duplicate extension name.  The unique index on
1647
   * pg_extension.extname would catch this anyway, and serves as a backstop
1648
   * in case of race conditions; but this is a friendlier error message, and
1649
   * besides we need a check to support IF NOT EXISTS.
1650
   */
1651
17
  if (get_extension_oid(stmt->extname, true) != InvalidOid)
1652
0
  {
1653
0
    if (stmt->if_not_exists)
1654
0
    {
1655
0
      ereport(NOTICE,
1656
0
          (errcode(ERRCODE_DUPLICATE_OBJECT),
1657
0
           errmsg("extension \"%s\" already exists, skipping",
1658
0
              stmt->extname)));
1659
0
      return InvalidObjectAddress;
1660
0
    }
1661
0
    else
1662
0
      ereport(ERROR,
1663
0
          (errcode(ERRCODE_DUPLICATE_OBJECT),
1664
0
           errmsg("extension \"%s\" already exists",
1665
0
              stmt->extname)));
1666
0
  }
1667
1668
  /*
1669
   * We use global variables to track the extension being created, so we can
1670
   * create only one extension at the same time.
1671
   */
1672
17
  if (creating_extension)
1673
17
    ereport(ERROR,
1674
17
        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1675
17
         errmsg("nested CREATE EXTENSION is not supported")));
1676
1677
  /* Deconstruct the statement option list */
1678
17
  foreach(lc, stmt->options)
1679
1
  {
1680
1
    DefElem    *defel = (DefElem *) lfirst(lc);
1681
1682
1
    if (strcmp(defel->defname, "schema") == 0)
1683
1
    {
1684
1
      if (d_schema)
1685
1
        ereport(ERROR,
1686
1
            (errcode(ERRCODE_SYNTAX_ERROR),
1687
1
             errmsg("conflicting or redundant options"),
1688
1
             parser_errposition(pstate, defel->location)));
1689
1
      d_schema = defel;
1690
1
      schemaName = defGetString(d_schema);
1691
1
    }
1692
0
    else if (strcmp(defel->defname, "new_version") == 0)
1693
0
    {
1694
0
      if (d_new_version)
1695
0
        ereport(ERROR,
1696
0
            (errcode(ERRCODE_SYNTAX_ERROR),
1697
0
             errmsg("conflicting or redundant options"),
1698
0
             parser_errposition(pstate, defel->location)));
1699
0
      d_new_version = defel;
1700
0
      versionName = defGetString(d_new_version);
1701
0
    }
1702
0
    else if (strcmp(defel->defname, "old_version") == 0)
1703
0
    {
1704
0
      if (d_old_version)
1705
0
        ereport(ERROR,
1706
0
            (errcode(ERRCODE_SYNTAX_ERROR),
1707
0
             errmsg("conflicting or redundant options"),
1708
0
             parser_errposition(pstate, defel->location)));
1709
0
      d_old_version = defel;
1710
0
      oldVersionName = defGetString(d_old_version);
1711
0
    }
1712
0
    else if (strcmp(defel->defname, "cascade") == 0)
1713
0
    {
1714
0
      if (d_cascade)
1715
0
        ereport(ERROR,
1716
0
            (errcode(ERRCODE_SYNTAX_ERROR),
1717
0
             errmsg("conflicting or redundant options"),
1718
0
             parser_errposition(pstate, defel->location)));
1719
0
      d_cascade = defel;
1720
0
      cascade = defGetBoolean(d_cascade);
1721
0
    }
1722
0
    else
1723
0
      elog(ERROR, "unrecognized option: %s", defel->defname);
1724
1
  }
1725
1726
  /* Call CreateExtensionInternal to do the real work. */
1727
17
  return CreateExtensionInternal(stmt->extname,
1728
17
                   schemaName,
1729
17
                   versionName,
1730
17
                   oldVersionName,
1731
17
                   cascade,
1732
17
                   NIL,
1733
17
                   true);
1734
17
}
1735
1736
/*
1737
 * InsertExtensionTuple
1738
 *
1739
 * Insert the new pg_extension row, and create extension's dependency entries.
1740
 * Return the OID assigned to the new row.
1741
 *
1742
 * This is exported for the benefit of pg_upgrade, which has to create a
1743
 * pg_extension entry (and the extension-level dependencies) without
1744
 * actually running the extension's script.
1745
 *
1746
 * extConfig and extCondition should be arrays or PointerGetDatum(NULL).
1747
 * We declare them as plain Datum to avoid needing array.h in extension.h.
1748
 */
1749
ObjectAddress
1750
InsertExtensionTuple(const char *extName, Oid extOwner,
1751
           Oid schemaOid, bool relocatable, const char *extVersion,
1752
           Datum extConfig, Datum extCondition,
1753
           List *requiredExtensions)
1754
16
{
1755
16
  Oid     extensionOid;
1756
16
  Relation  rel;
1757
16
  Datum   values[Natts_pg_extension];
1758
16
  bool    nulls[Natts_pg_extension];
1759
16
  HeapTuple tuple;
1760
16
  ObjectAddress myself;
1761
16
  ObjectAddress nsp;
1762
16
  ListCell   *lc;
1763
1764
  /*
1765
   * Build and insert the pg_extension tuple
1766
   */
1767
16
  rel = heap_open(ExtensionRelationId, RowExclusiveLock);
1768
1769
16
  memset(values, 0, sizeof(values));
1770
16
  memset(nulls, 0, sizeof(nulls));
1771
1772
16
  values[Anum_pg_extension_extname - 1] =
1773
16
    DirectFunctionCall1(namein, CStringGetDatum(extName));
1774
16
  values[Anum_pg_extension_extowner - 1] = ObjectIdGetDatum(extOwner);
1775
16
  values[Anum_pg_extension_extnamespace - 1] = ObjectIdGetDatum(schemaOid);
1776
16
  values[Anum_pg_extension_extrelocatable - 1] = BoolGetDatum(relocatable);
1777
16
  values[Anum_pg_extension_extversion - 1] = CStringGetTextDatum(extVersion);
1778
1779
16
  if (extConfig == PointerGetDatum(NULL))
1780
16
    nulls[Anum_pg_extension_extconfig - 1] = true;
1781
16
  else
1782
0
    values[Anum_pg_extension_extconfig - 1] = extConfig;
1783
1784
16
  if (extCondition == PointerGetDatum(NULL))
1785
16
    nulls[Anum_pg_extension_extcondition - 1] = true;
1786
16
  else
1787
0
    values[Anum_pg_extension_extcondition - 1] = extCondition;
1788
1789
16
  tuple = heap_form_tuple(rel->rd_att, values, nulls);
1790
1791
16
  extensionOid = CatalogTupleInsert(rel, tuple);
1792
1793
16
  heap_freetuple(tuple);
1794
16
  heap_close(rel, RowExclusiveLock);
1795
1796
  /*
1797
   * Record dependencies on owner, schema, and prerequisite extensions
1798
   */
1799
16
  recordDependencyOnOwner(ExtensionRelationId, extensionOid, extOwner);
1800
1801
16
  myself.classId = ExtensionRelationId;
1802
16
  myself.objectId = extensionOid;
1803
16
  myself.objectSubId = 0;
1804
1805
16
  nsp.classId = NamespaceRelationId;
1806
16
  nsp.objectId = schemaOid;
1807
16
  nsp.objectSubId = 0;
1808
1809
16
  recordDependencyOn(&myself, &nsp, DEPENDENCY_NORMAL);
1810
1811
16
  foreach(lc, requiredExtensions)
1812
1
  {
1813
1
    Oid     reqext = lfirst_oid(lc);
1814
1
    ObjectAddress otherext;
1815
1816
1
    otherext.classId = ExtensionRelationId;
1817
1
    otherext.objectId = reqext;
1818
1
    otherext.objectSubId = 0;
1819
1820
1
    recordDependencyOn(&myself, &otherext, DEPENDENCY_NORMAL);
1821
1
  }
1822
  /* Post creation hook for new extension */
1823
16
  InvokeObjectPostCreateHook(ExtensionRelationId, extensionOid, 0);
1824
1825
16
  return myself;
1826
16
}
1827
1828
/*
1829
 * Guts of extension deletion.
1830
 *
1831
 * All we need do here is remove the pg_extension tuple itself.  Everything
1832
 * else is taken care of by the dependency infrastructure.
1833
 */
1834
void
1835
RemoveExtensionById(Oid extId)
1836
15
{
1837
15
  Relation  rel;
1838
15
  SysScanDesc scandesc;
1839
15
  HeapTuple tuple;
1840
15
  ScanKeyData entry[1];
1841
1842
  /*
1843
   * Disallow deletion of any extension that's currently open for insertion;
1844
   * else subsequent executions of recordDependencyOnCurrentExtension()
1845
   * could create dangling pg_depend records that refer to a no-longer-valid
1846
   * pg_extension OID.  This is needed not so much because we think people
1847
   * might write "DROP EXTENSION foo" in foo's own script files, as because
1848
   * errors in dependency management in extension script files could give
1849
   * rise to cases where an extension is dropped as a result of recursing
1850
   * from some contained object.  Because of that, we must test for the case
1851
   * here, not at some higher level of the DROP EXTENSION command.
1852
   */
1853
15
  if (extId == CurrentExtensionObject)
1854
15
    ereport(ERROR,
1855
15
        (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1856
15
         errmsg("cannot drop extension \"%s\" because it is being modified",
1857
15
            get_extension_name(extId))));
1858
1859
15
  rel = heap_open(ExtensionRelationId, RowExclusiveLock);
1860
1861
15
  ScanKeyInit(&entry[0],
1862
15
        ObjectIdAttributeNumber,
1863
15
        BTEqualStrategyNumber, F_OIDEQ,
1864
15
        ObjectIdGetDatum(extId));
1865
15
  scandesc = systable_beginscan(rel, ExtensionOidIndexId, true,
1866
15
                  NULL, 1, entry);
1867
1868
15
  tuple = systable_getnext(scandesc);
1869
1870
  /* We assume that there can be at most one matching tuple */
1871
15
  if (HeapTupleIsValid(tuple))
1872
15
    CatalogTupleDelete(rel, tuple);
1873
1874
15
  systable_endscan(scandesc);
1875
1876
15
  heap_close(rel, RowExclusiveLock);
1877
15
}
1878
1879
/*
1880
 * This function lists the available extensions (one row per primary control
1881
 * file in the control directory).  We parse each control file and report the
1882
 * interesting fields.
1883
 *
1884
 * The system view pg_available_extensions provides a user interface to this
1885
 * SRF, adding information about whether the extensions are installed in the
1886
 * current DB.
1887
 */
1888
Datum
1889
pg_available_extensions(PG_FUNCTION_ARGS)
1890
0
{
1891
0
  ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
1892
0
  TupleDesc tupdesc;
1893
0
  Tuplestorestate *tupstore;
1894
0
  MemoryContext per_query_ctx;
1895
0
  MemoryContext oldcontext;
1896
0
  char     *location;
1897
0
  DIR      *dir;
1898
0
  struct dirent *de;
1899
1900
  /* check to see if caller supports us returning a tuplestore */
1901
0
  if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
1902
0
    ereport(ERROR,
1903
0
        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1904
0
         errmsg("set-valued function called in context that cannot accept a set")));
1905
0
  if (!(rsinfo->allowedModes & SFRM_Materialize))
1906
0
    ereport(ERROR,
1907
0
        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1908
0
         errmsg("materialize mode required, but it is not " \
1909
0
            "allowed in this context")));
1910
1911
  /* Build a tuple descriptor for our result type */
1912
0
  if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
1913
0
    elog(ERROR, "return type must be a row type");
1914
1915
  /* Build tuplestore to hold the result rows */
1916
0
  per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
1917
0
  oldcontext = MemoryContextSwitchTo(per_query_ctx);
1918
1919
0
  tupstore = tuplestore_begin_heap(true, false, work_mem);
1920
0
  rsinfo->returnMode = SFRM_Materialize;
1921
0
  rsinfo->setResult = tupstore;
1922
0
  rsinfo->setDesc = tupdesc;
1923
1924
0
  MemoryContextSwitchTo(oldcontext);
1925
1926
0
  location = get_extension_control_directory();
1927
0
  dir = AllocateDir(location);
1928
1929
  /*
1930
   * If the control directory doesn't exist, we want to silently return an
1931
   * empty set.  Any other error will be reported by ReadDir.
1932
   */
1933
0
  if (dir == NULL && errno == ENOENT)
1934
0
  {
1935
    /* do nothing */
1936
0
  }
1937
0
  else
1938
0
  {
1939
0
    while ((de = ReadDir(dir, location)) != NULL)
1940
0
    {
1941
0
      ExtensionControlFile *control;
1942
0
      char     *extname;
1943
0
      Datum   values[3];
1944
0
      bool    nulls[3];
1945
1946
0
      if (!is_extension_control_filename(de->d_name))
1947
0
        continue;
1948
1949
      /* extract extension name from 'name.control' filename */
1950
0
      extname = pstrdup(de->d_name);
1951
0
      *strrchr(extname, '.') = '\0';
1952
1953
      /* ignore it if it's an auxiliary control file */
1954
0
      if (strstr(extname, "--"))
1955
0
        continue;
1956
1957
0
      control = read_extension_control_file(extname);
1958
1959
0
      memset(values, 0, sizeof(values));
1960
0
      memset(nulls, 0, sizeof(nulls));
1961
1962
      /* name */
1963
0
      values[0] = DirectFunctionCall1(namein,
1964
0
                      CStringGetDatum(control->name));
1965
      /* default_version */
1966
0
      if (control->default_version == NULL)
1967
0
        nulls[1] = true;
1968
0
      else
1969
0
        values[1] = CStringGetTextDatum(control->default_version);
1970
      /* comment */
1971
0
      if (control->comment == NULL)
1972
0
        nulls[2] = true;
1973
0
      else
1974
0
        values[2] = CStringGetTextDatum(control->comment);
1975
1976
0
      tuplestore_putvalues(tupstore, tupdesc, values, nulls);
1977
0
    }
1978
1979
0
    FreeDir(dir);
1980
0
  }
1981
1982
  /* clean up and return the tuplestore */
1983
0
  tuplestore_donestoring(tupstore);
1984
1985
0
  return (Datum) 0;
1986
0
}
1987
1988
/*
1989
 * This function lists the available extension versions (one row per
1990
 * extension installation script).  For each version, we parse the related
1991
 * control file(s) and report the interesting fields.
1992
 *
1993
 * The system view pg_available_extension_versions provides a user interface
1994
 * to this SRF, adding information about which versions are installed in the
1995
 * current DB.
1996
 */
1997
Datum
1998
pg_available_extension_versions(PG_FUNCTION_ARGS)
1999
0
{
2000
0
  ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
2001
0
  TupleDesc tupdesc;
2002
0
  Tuplestorestate *tupstore;
2003
0
  MemoryContext per_query_ctx;
2004
0
  MemoryContext oldcontext;
2005
0
  char     *location;
2006
0
  DIR      *dir;
2007
0
  struct dirent *de;
2008
2009
  /* check to see if caller supports us returning a tuplestore */
2010
0
  if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
2011
0
    ereport(ERROR,
2012
0
        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2013
0
         errmsg("set-valued function called in context that cannot accept a set")));
2014
0
  if (!(rsinfo->allowedModes & SFRM_Materialize))
2015
0
    ereport(ERROR,
2016
0
        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2017
0
         errmsg("materialize mode required, but it is not " \
2018
0
            "allowed in this context")));
2019
2020
  /* Build a tuple descriptor for our result type */
2021
0
  if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
2022
0
    elog(ERROR, "return type must be a row type");
2023
2024
  /* Build tuplestore to hold the result rows */
2025
0
  per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
2026
0
  oldcontext = MemoryContextSwitchTo(per_query_ctx);
2027
2028
0
  tupstore = tuplestore_begin_heap(true, false, work_mem);
2029
0
  rsinfo->returnMode = SFRM_Materialize;
2030
0
  rsinfo->setResult = tupstore;
2031
0
  rsinfo->setDesc = tupdesc;
2032
2033
0
  MemoryContextSwitchTo(oldcontext);
2034
2035
0
  location = get_extension_control_directory();
2036
0
  dir = AllocateDir(location);
2037
2038
  /*
2039
   * If the control directory doesn't exist, we want to silently return an
2040
   * empty set.  Any other error will be reported by ReadDir.
2041
   */
2042
0
  if (dir == NULL && errno == ENOENT)
2043
0
  {
2044
    /* do nothing */
2045
0
  }
2046
0
  else
2047
0
  {
2048
0
    while ((de = ReadDir(dir, location)) != NULL)
2049
0
    {
2050
0
      ExtensionControlFile *control;
2051
0
      char     *extname;
2052
2053
0
      if (!is_extension_control_filename(de->d_name))
2054
0
        continue;
2055
2056
      /* extract extension name from 'name.control' filename */
2057
0
      extname = pstrdup(de->d_name);
2058
0
      *strrchr(extname, '.') = '\0';
2059
2060
      /* ignore it if it's an auxiliary control file */
2061
0
      if (strstr(extname, "--"))
2062
0
        continue;
2063
2064
      /* read the control file */
2065
0
      control = read_extension_control_file(extname);
2066
2067
      /* scan extension's script directory for install scripts */
2068
0
      get_available_versions_for_extension(control, tupstore, tupdesc);
2069
0
    }
2070
2071
0
    FreeDir(dir);
2072
0
  }
2073
2074
  /* clean up and return the tuplestore */
2075
0
  tuplestore_donestoring(tupstore);
2076
2077
0
  return (Datum) 0;
2078
0
}
2079
2080
/*
2081
 * Inner loop for pg_available_extension_versions:
2082
 *    read versions of one extension, add rows to tupstore
2083
 */
2084
static void
2085
get_available_versions_for_extension(ExtensionControlFile *pcontrol,
2086
                   Tuplestorestate *tupstore,
2087
                   TupleDesc tupdesc)
2088
0
{
2089
0
  List     *evi_list;
2090
0
  ListCell   *lc;
2091
2092
  /* Extract the version update graph from the script directory */
2093
0
  evi_list = get_ext_ver_list(pcontrol);
2094
2095
  /* For each installable version ... */
2096
0
  foreach(lc, evi_list)
2097
0
  {
2098
0
    ExtensionVersionInfo *evi = (ExtensionVersionInfo *) lfirst(lc);
2099
0
    ExtensionControlFile *control;
2100
0
    Datum   values[7];
2101
0
    bool    nulls[7];
2102
0
    ListCell   *lc2;
2103
2104
0
    if (!evi->installable)
2105
0
      continue;
2106
2107
    /*
2108
     * Fetch parameters for specific version (pcontrol is not changed)
2109
     */
2110
0
    control = read_extension_aux_control_file(pcontrol, evi->name);
2111
2112
0
    memset(values, 0, sizeof(values));
2113
0
    memset(nulls, 0, sizeof(nulls));
2114
2115
    /* name */
2116
0
    values[0] = DirectFunctionCall1(namein,
2117
0
                    CStringGetDatum(control->name));
2118
    /* version */
2119
0
    values[1] = CStringGetTextDatum(evi->name);
2120
    /* superuser */
2121
0
    values[2] = BoolGetDatum(control->superuser);
2122
    /* relocatable */
2123
0
    values[3] = BoolGetDatum(control->relocatable);
2124
    /* schema */
2125
0
    if (control->schema == NULL)
2126
0
      nulls[4] = true;
2127
0
    else
2128
0
      values[4] = DirectFunctionCall1(namein,
2129
0
                      CStringGetDatum(control->schema));
2130
    /* requires */
2131
0
    if (control->requires == NIL)
2132
0
      nulls[5] = true;
2133
0
    else
2134
0
      values[5] = convert_requires_to_datum(control->requires);
2135
    /* comment */
2136
0
    if (control->comment == NULL)
2137
0
      nulls[6] = true;
2138
0
    else
2139
0
      values[6] = CStringGetTextDatum(control->comment);
2140
2141
0
    tuplestore_putvalues(tupstore, tupdesc, values, nulls);
2142
2143
    /*
2144
     * Find all non-directly-installable versions that would be installed
2145
     * starting from this version, and report them, inheriting the
2146
     * parameters that aren't changed in updates from this version.
2147
     */
2148
0
    foreach(lc2, evi_list)
2149
0
    {
2150
0
      ExtensionVersionInfo *evi2 = (ExtensionVersionInfo *) lfirst(lc2);
2151
0
      List     *best_path;
2152
2153
0
      if (evi2->installable)
2154
0
        continue;
2155
0
      if (find_install_path(evi_list, evi2, &best_path) == evi)
2156
0
      {
2157
        /*
2158
         * Fetch parameters for this version (pcontrol is not changed)
2159
         */
2160
0
        control = read_extension_aux_control_file(pcontrol, evi2->name);
2161
2162
        /* name stays the same */
2163
        /* version */
2164
0
        values[1] = CStringGetTextDatum(evi2->name);
2165
        /* superuser */
2166
0
        values[2] = BoolGetDatum(control->superuser);
2167
        /* relocatable */
2168
0
        values[3] = BoolGetDatum(control->relocatable);
2169
        /* schema stays the same */
2170
        /* requires */
2171
0
        if (control->requires == NIL)
2172
0
          nulls[5] = true;
2173
0
        else
2174
0
        {
2175
0
          values[5] = convert_requires_to_datum(control->requires);
2176
0
          nulls[5] = false;
2177
0
        }
2178
        /* comment stays the same */
2179
2180
0
        tuplestore_putvalues(tupstore, tupdesc, values, nulls);
2181
0
      }
2182
0
    }
2183
0
  }
2184
0
}
2185
2186
/*
2187
 * Convert a list of extension names to a name[] Datum
2188
 */
2189
static Datum
2190
convert_requires_to_datum(List *requires)
2191
0
{
2192
0
  Datum    *datums;
2193
0
  int     ndatums;
2194
0
  ArrayType  *a;
2195
0
  ListCell   *lc;
2196
2197
0
  ndatums = list_length(requires);
2198
0
  datums = (Datum *) palloc(ndatums * sizeof(Datum));
2199
0
  ndatums = 0;
2200
0
  foreach(lc, requires)
2201
0
  {
2202
0
    char     *curreq = (char *) lfirst(lc);
2203
2204
0
    datums[ndatums++] =
2205
0
      DirectFunctionCall1(namein, CStringGetDatum(curreq));
2206
0
  }
2207
0
  a = construct_array(datums, ndatums,
2208
0
            NAMEOID,
2209
0
            NAMEDATALEN, false, 'c');
2210
0
  return PointerGetDatum(a);
2211
0
}
2212
2213
/*
2214
 * This function reports the version update paths that exist for the
2215
 * specified extension.
2216
 */
2217
Datum
2218
pg_extension_update_paths(PG_FUNCTION_ARGS)
2219
0
{
2220
0
  Name    extname = PG_GETARG_NAME(0);
2221
0
  ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
2222
0
  TupleDesc tupdesc;
2223
0
  Tuplestorestate *tupstore;
2224
0
  MemoryContext per_query_ctx;
2225
0
  MemoryContext oldcontext;
2226
0
  List     *evi_list;
2227
0
  ExtensionControlFile *control;
2228
0
  ListCell   *lc1;
2229
2230
  /* Check extension name validity before any filesystem access */
2231
0
  check_valid_extension_name(NameStr(*extname));
2232
2233
  /* check to see if caller supports us returning a tuplestore */
2234
0
  if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
2235
0
    ereport(ERROR,
2236
0
        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2237
0
         errmsg("set-valued function called in context that cannot accept a set")));
2238
0
  if (!(rsinfo->allowedModes & SFRM_Materialize))
2239
0
    ereport(ERROR,
2240
0
        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2241
0
         errmsg("materialize mode required, but it is not " \
2242
0
            "allowed in this context")));
2243
2244
  /* Build a tuple descriptor for our result type */
2245
0
  if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
2246
0
    elog(ERROR, "return type must be a row type");
2247
2248
  /* Build tuplestore to hold the result rows */
2249
0
  per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
2250
0
  oldcontext = MemoryContextSwitchTo(per_query_ctx);
2251
2252
0
  tupstore = tuplestore_begin_heap(true, false, work_mem);
2253
0
  rsinfo->returnMode = SFRM_Materialize;
2254
0
  rsinfo->setResult = tupstore;
2255
0
  rsinfo->setDesc = tupdesc;
2256
2257
0
  MemoryContextSwitchTo(oldcontext);
2258
2259
  /* Read the extension's control file */
2260
0
  control = read_extension_control_file(NameStr(*extname));
2261
2262
  /* Extract the version update graph from the script directory */
2263
0
  evi_list = get_ext_ver_list(control);
2264
2265
  /* Iterate over all pairs of versions */
2266
0
  foreach(lc1, evi_list)
2267
0
  {
2268
0
    ExtensionVersionInfo *evi1 = (ExtensionVersionInfo *) lfirst(lc1);
2269
0
    ListCell   *lc2;
2270
2271
0
    foreach(lc2, evi_list)
2272
0
    {
2273
0
      ExtensionVersionInfo *evi2 = (ExtensionVersionInfo *) lfirst(lc2);
2274
0
      List     *path;
2275
0
      Datum   values[3];
2276
0
      bool    nulls[3];
2277
2278
0
      if (evi1 == evi2)
2279
0
        continue;
2280
2281
      /* Find shortest path from evi1 to evi2 */
2282
0
      path = find_update_path(evi_list, evi1, evi2, false, true);
2283
2284
      /* Emit result row */
2285
0
      memset(values, 0, sizeof(values));
2286
0
      memset(nulls, 0, sizeof(nulls));
2287
2288
      /* source */
2289
0
      values[0] = CStringGetTextDatum(evi1->name);
2290
      /* target */
2291
0
      values[1] = CStringGetTextDatum(evi2->name);
2292
      /* path */
2293
0
      if (path == NIL)
2294
0
        nulls[2] = true;
2295
0
      else
2296
0
      {
2297
0
        StringInfoData pathbuf;
2298
0
        ListCell   *lcv;
2299
2300
0
        initStringInfo(&pathbuf);
2301
        /* The path doesn't include start vertex, but show it */
2302
0
        appendStringInfoString(&pathbuf, evi1->name);
2303
0
        foreach(lcv, path)
2304
0
        {
2305
0
          char     *versionName = (char *) lfirst(lcv);
2306
2307
0
          appendStringInfoString(&pathbuf, "--");
2308
0
          appendStringInfoString(&pathbuf, versionName);
2309
0
        }
2310
0
        values[2] = CStringGetTextDatum(pathbuf.data);
2311
0
        pfree(pathbuf.data);
2312
0
      }
2313
2314
0
      tuplestore_putvalues(tupstore, tupdesc, values, nulls);
2315
0
    }
2316
0
  }
2317
2318
  /* clean up and return the tuplestore */
2319
0
  tuplestore_donestoring(tupstore);
2320
2321
0
  return (Datum) 0;
2322
0
}
2323
2324
/*
2325
 * pg_extension_config_dump
2326
 *
2327
 * Record information about a configuration table that belongs to an
2328
 * extension being created, but whose contents should be dumped in whole
2329
 * or in part during pg_dump.
2330
 */
2331
Datum
2332
pg_extension_config_dump(PG_FUNCTION_ARGS)
2333
0
{
2334
0
  Oid     tableoid = PG_GETARG_OID(0);
2335
0
  text     *wherecond = PG_GETARG_TEXT_PP(1);
2336
0
  char     *tablename;
2337
0
  Relation  extRel;
2338
0
  ScanKeyData key[1];
2339
0
  SysScanDesc extScan;
2340
0
  HeapTuple extTup;
2341
0
  Datum   arrayDatum;
2342
0
  Datum   elementDatum;
2343
0
  int     arrayLength;
2344
0
  int     arrayIndex;
2345
0
  bool    isnull;
2346
0
  Datum   repl_val[Natts_pg_extension];
2347
0
  bool    repl_null[Natts_pg_extension];
2348
0
  bool    repl_repl[Natts_pg_extension];
2349
0
  ArrayType  *a;
2350
2351
  /*
2352
   * We only allow this to be called from an extension's SQL script. We
2353
   * shouldn't need any permissions check beyond that.
2354
   */
2355
0
  if (!creating_extension)
2356
0
    ereport(ERROR,
2357
0
        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2358
0
         errmsg("pg_extension_config_dump() can only be called "
2359
0
            "from an SQL script executed by CREATE EXTENSION")));
2360
2361
  /*
2362
   * Check that the table exists and is a member of the extension being
2363
   * created.  This ensures that we don't need to register an additional
2364
   * dependency to protect the extconfig entry.
2365
   */
2366
0
  tablename = get_rel_name(tableoid);
2367
0
  if (tablename == NULL)
2368
0
    ereport(ERROR,
2369
0
        (errcode(ERRCODE_UNDEFINED_TABLE),
2370
0
         errmsg("OID %u does not refer to a table", tableoid)));
2371
0
  if (getExtensionOfObject(RelationRelationId, tableoid) !=
2372
0
    CurrentExtensionObject)
2373
0
    ereport(ERROR,
2374
0
        (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2375
0
         errmsg("table \"%s\" is not a member of the extension being created",
2376
0
            tablename)));
2377
2378
  /*
2379
   * Add the table OID and WHERE condition to the extension's extconfig and
2380
   * extcondition arrays.
2381
   *
2382
   * If the table is already in extconfig, treat this as an update of the
2383
   * WHERE condition.
2384
   */
2385
2386
  /* Find the pg_extension tuple */
2387
0
  extRel = heap_open(ExtensionRelationId, RowExclusiveLock);
2388
2389
0
  ScanKeyInit(&key[0],
2390
0
        ObjectIdAttributeNumber,
2391
0
        BTEqualStrategyNumber, F_OIDEQ,
2392
0
        ObjectIdGetDatum(CurrentExtensionObject));
2393
2394
0
  extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
2395
0
                 NULL, 1, key);
2396
2397
0
  extTup = systable_getnext(extScan);
2398
2399
0
  if (!HeapTupleIsValid(extTup)) /* should not happen */
2400
0
    elog(ERROR, "could not find tuple for extension %u",
2401
0
       CurrentExtensionObject);
2402
2403
0
  memset(repl_val, 0, sizeof(repl_val));
2404
0
  memset(repl_null, false, sizeof(repl_null));
2405
0
  memset(repl_repl, false, sizeof(repl_repl));
2406
2407
  /* Build or modify the extconfig value */
2408
0
  elementDatum = ObjectIdGetDatum(tableoid);
2409
2410
0
  arrayDatum = heap_getattr(extTup, Anum_pg_extension_extconfig,
2411
0
                RelationGetDescr(extRel), &isnull);
2412
0
  if (isnull)
2413
0
  {
2414
    /* Previously empty extconfig, so build 1-element array */
2415
0
    arrayLength = 0;
2416
0
    arrayIndex = 1;
2417
2418
0
    a = construct_array(&elementDatum, 1,
2419
0
              OIDOID,
2420
0
              sizeof(Oid), true, 'i');
2421
0
  }
2422
0
  else
2423
0
  {
2424
    /* Modify or extend existing extconfig array */
2425
0
    Oid      *arrayData;
2426
0
    int     i;
2427
2428
0
    a = DatumGetArrayTypeP(arrayDatum);
2429
2430
0
    arrayLength = ARR_DIMS(a)[0];
2431
0
    if (ARR_NDIM(a) != 1 ||
2432
0
      ARR_LBOUND(a)[0] != 1 ||
2433
0
      arrayLength < 0 ||
2434
0
      ARR_HASNULL(a) ||
2435
0
      ARR_ELEMTYPE(a) != OIDOID)
2436
0
      elog(ERROR, "extconfig is not a 1-D Oid array");
2437
0
    arrayData = (Oid *) ARR_DATA_PTR(a);
2438
2439
0
    arrayIndex = arrayLength + 1; /* set up to add after end */
2440
2441
0
    for (i = 0; i < arrayLength; i++)
2442
0
    {
2443
0
      if (arrayData[i] == tableoid)
2444
0
      {
2445
0
        arrayIndex = i + 1; /* replace this element instead */
2446
0
        break;
2447
0
      }
2448
0
    }
2449
2450
0
    a = array_set(a, 1, &arrayIndex,
2451
0
            elementDatum,
2452
0
            false,
2453
0
            -1 /* varlena array */ ,
2454
0
            sizeof(Oid) /* OID's typlen */ ,
2455
0
            true /* OID's typbyval */ ,
2456
0
            'i' /* OID's typalign */ );
2457
0
  }
2458
0
  repl_val[Anum_pg_extension_extconfig - 1] = PointerGetDatum(a);
2459
0
  repl_repl[Anum_pg_extension_extconfig - 1] = true;
2460
2461
  /* Build or modify the extcondition value */
2462
0
  elementDatum = PointerGetDatum(wherecond);
2463
2464
0
  arrayDatum = heap_getattr(extTup, Anum_pg_extension_extcondition,
2465
0
                RelationGetDescr(extRel), &isnull);
2466
0
  if (isnull)
2467
0
  {
2468
0
    if (arrayLength != 0)
2469
0
      elog(ERROR, "extconfig and extcondition arrays do not match");
2470
2471
0
    a = construct_array(&elementDatum, 1,
2472
0
              TEXTOID,
2473
0
              -1, false, 'i');
2474
0
  }
2475
0
  else
2476
0
  {
2477
0
    a = DatumGetArrayTypeP(arrayDatum);
2478
2479
0
    if (ARR_NDIM(a) != 1 ||
2480
0
      ARR_LBOUND(a)[0] != 1 ||
2481
0
      ARR_HASNULL(a) ||
2482
0
      ARR_ELEMTYPE(a) != TEXTOID)
2483
0
      elog(ERROR, "extcondition is not a 1-D text array");
2484
0
    if (ARR_DIMS(a)[0] != arrayLength)
2485
0
      elog(ERROR, "extconfig and extcondition arrays do not match");
2486
2487
    /* Add or replace at same index as in extconfig */
2488
0
    a = array_set(a, 1, &arrayIndex,
2489
0
            elementDatum,
2490
0
            false,
2491
0
            -1 /* varlena array */ ,
2492
0
            -1 /* TEXT's typlen */ ,
2493
0
            false /* TEXT's typbyval */ ,
2494
0
            'i' /* TEXT's typalign */ );
2495
0
  }
2496
0
  repl_val[Anum_pg_extension_extcondition - 1] = PointerGetDatum(a);
2497
0
  repl_repl[Anum_pg_extension_extcondition - 1] = true;
2498
2499
0
  extTup = heap_modify_tuple(extTup, RelationGetDescr(extRel),
2500
0
                 repl_val, repl_null, repl_repl);
2501
2502
0
  CatalogTupleUpdate(extRel, &extTup->t_self, extTup);
2503
2504
0
  systable_endscan(extScan);
2505
2506
0
  heap_close(extRel, RowExclusiveLock);
2507
2508
0
  PG_RETURN_VOID();
2509
0
}
2510
2511
/*
2512
 * extension_config_remove
2513
 *
2514
 * Remove the specified table OID from extension's extconfig, if present.
2515
 * This is not currently exposed as a function, but it could be;
2516
 * for now, we just invoke it from ALTER EXTENSION DROP.
2517
 */
2518
static void
2519
extension_config_remove(Oid extensionoid, Oid tableoid)
2520
0
{
2521
0
  Relation  extRel;
2522
0
  ScanKeyData key[1];
2523
0
  SysScanDesc extScan;
2524
0
  HeapTuple extTup;
2525
0
  Datum   arrayDatum;
2526
0
  int     arrayLength;
2527
0
  int     arrayIndex;
2528
0
  bool    isnull;
2529
0
  Datum   repl_val[Natts_pg_extension];
2530
0
  bool    repl_null[Natts_pg_extension];
2531
0
  bool    repl_repl[Natts_pg_extension];
2532
0
  ArrayType  *a;
2533
2534
  /* Find the pg_extension tuple */
2535
0
  extRel = heap_open(ExtensionRelationId, RowExclusiveLock);
2536
2537
0
  ScanKeyInit(&key[0],
2538
0
        ObjectIdAttributeNumber,
2539
0
        BTEqualStrategyNumber, F_OIDEQ,
2540
0
        ObjectIdGetDatum(extensionoid));
2541
2542
0
  extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
2543
0
                 NULL, 1, key);
2544
2545
0
  extTup = systable_getnext(extScan);
2546
2547
0
  if (!HeapTupleIsValid(extTup)) /* should not happen */
2548
0
    elog(ERROR, "could not find tuple for extension %u",
2549
0
       extensionoid);
2550
2551
  /* Search extconfig for the tableoid */
2552
0
  arrayDatum = heap_getattr(extTup, Anum_pg_extension_extconfig,
2553
0
                RelationGetDescr(extRel), &isnull);
2554
0
  if (isnull)
2555
0
  {
2556
    /* nothing to do */
2557
0
    a = NULL;
2558
0
    arrayLength = 0;
2559
0
    arrayIndex = -1;
2560
0
  }
2561
0
  else
2562
0
  {
2563
0
    Oid      *arrayData;
2564
0
    int     i;
2565
2566
0
    a = DatumGetArrayTypeP(arrayDatum);
2567
2568
0
    arrayLength = ARR_DIMS(a)[0];
2569
0
    if (ARR_NDIM(a) != 1 ||
2570
0
      ARR_LBOUND(a)[0] != 1 ||
2571
0
      arrayLength < 0 ||
2572
0
      ARR_HASNULL(a) ||
2573
0
      ARR_ELEMTYPE(a) != OIDOID)
2574
0
      elog(ERROR, "extconfig is not a 1-D Oid array");
2575
0
    arrayData = (Oid *) ARR_DATA_PTR(a);
2576
2577
0
    arrayIndex = -1;    /* flag for no deletion needed */
2578
2579
0
    for (i = 0; i < arrayLength; i++)
2580
0
    {
2581
0
      if (arrayData[i] == tableoid)
2582
0
      {
2583
0
        arrayIndex = i; /* index to remove */
2584
0
        break;
2585
0
      }
2586
0
    }
2587
0
  }
2588
2589
  /* If tableoid is not in extconfig, nothing to do */
2590
0
  if (arrayIndex < 0)
2591
0
  {
2592
0
    systable_endscan(extScan);
2593
0
    heap_close(extRel, RowExclusiveLock);
2594
0
    return;
2595
0
  }
2596
2597
  /* Modify or delete the extconfig value */
2598
0
  memset(repl_val, 0, sizeof(repl_val));
2599
0
  memset(repl_null, false, sizeof(repl_null));
2600
0
  memset(repl_repl, false, sizeof(repl_repl));
2601
2602
0
  if (arrayLength <= 1)
2603
0
  {
2604
    /* removing only element, just set array to null */
2605
0
    repl_null[Anum_pg_extension_extconfig - 1] = true;
2606
0
  }
2607
0
  else
2608
0
  {
2609
    /* squeeze out the target element */
2610
0
    Datum    *dvalues;
2611
0
    bool     *dnulls;
2612
0
    int     nelems;
2613
0
    int     i;
2614
2615
0
    deconstruct_array(a, OIDOID, sizeof(Oid), true, 'i',
2616
0
              &dvalues, &dnulls, &nelems);
2617
2618
    /* We already checked there are no nulls, so ignore dnulls */
2619
0
    for (i = arrayIndex; i < arrayLength - 1; i++)
2620
0
      dvalues[i] = dvalues[i + 1];
2621
2622
0
    a = construct_array(dvalues, arrayLength - 1,
2623
0
              OIDOID, sizeof(Oid), true, 'i');
2624
2625
0
    repl_val[Anum_pg_extension_extconfig - 1] = PointerGetDatum(a);
2626
0
  }
2627
0
  repl_repl[Anum_pg_extension_extconfig - 1] = true;
2628
2629
  /* Modify or delete the extcondition value */
2630
0
  arrayDatum = heap_getattr(extTup, Anum_pg_extension_extcondition,
2631
0
                RelationGetDescr(extRel), &isnull);
2632
0
  if (isnull)
2633
0
  {
2634
0
    elog(ERROR, "extconfig and extcondition arrays do not match");
2635
0
  }
2636
0
  else
2637
0
  {
2638
0
    a = DatumGetArrayTypeP(arrayDatum);
2639
2640
0
    if (ARR_NDIM(a) != 1 ||
2641
0
      ARR_LBOUND(a)[0] != 1 ||
2642
0
      ARR_HASNULL(a) ||
2643
0
      ARR_ELEMTYPE(a) != TEXTOID)
2644
0
      elog(ERROR, "extcondition is not a 1-D text array");
2645
0
    if (ARR_DIMS(a)[0] != arrayLength)
2646
0
      elog(ERROR, "extconfig and extcondition arrays do not match");
2647
0
  }
2648
2649
0
  if (arrayLength <= 1)
2650
0
  {
2651
    /* removing only element, just set array to null */
2652
0
    repl_null[Anum_pg_extension_extcondition - 1] = true;
2653
0
  }
2654
0
  else
2655
0
  {
2656
    /* squeeze out the target element */
2657
0
    Datum    *dvalues;
2658
0
    bool     *dnulls;
2659
0
    int     nelems;
2660
0
    int     i;
2661
2662
0
    deconstruct_array(a, TEXTOID, -1, false, 'i',
2663
0
              &dvalues, &dnulls, &nelems);
2664
2665
    /* We already checked there are no nulls, so ignore dnulls */
2666
0
    for (i = arrayIndex; i < arrayLength - 1; i++)
2667
0
      dvalues[i] = dvalues[i + 1];
2668
2669
0
    a = construct_array(dvalues, arrayLength - 1,
2670
0
              TEXTOID, -1, false, 'i');
2671
2672
0
    repl_val[Anum_pg_extension_extcondition - 1] = PointerGetDatum(a);
2673
0
  }
2674
0
  repl_repl[Anum_pg_extension_extcondition - 1] = true;
2675
2676
0
  extTup = heap_modify_tuple(extTup, RelationGetDescr(extRel),
2677
0
                 repl_val, repl_null, repl_repl);
2678
2679
0
  CatalogTupleUpdate(extRel, &extTup->t_self, extTup);
2680
2681
0
  systable_endscan(extScan);
2682
2683
0
  heap_close(extRel, RowExclusiveLock);
2684
0
}
2685
2686
/*
2687
 * Execute ALTER EXTENSION SET SCHEMA
2688
 */
2689
ObjectAddress
2690
AlterExtensionNamespace(const char *extensionName, const char *newschema, Oid *oldschema)
2691
0
{
2692
0
  Oid     extensionOid;
2693
0
  Oid     nspOid;
2694
0
  Oid     oldNspOid = InvalidOid;
2695
0
  AclResult aclresult;
2696
0
  Relation  extRel;
2697
0
  ScanKeyData key[2];
2698
0
  SysScanDesc extScan;
2699
0
  HeapTuple extTup;
2700
0
  Form_pg_extension extForm;
2701
0
  Relation  depRel;
2702
0
  SysScanDesc depScan;
2703
0
  HeapTuple depTup;
2704
0
  ObjectAddresses *objsMoved;
2705
0
  ObjectAddress extAddr;
2706
2707
0
  extensionOid = get_extension_oid(extensionName, false);
2708
2709
0
  nspOid = LookupCreationNamespace(newschema);
2710
2711
  /*
2712
   * Permission check: must own extension.  Note that we don't bother to
2713
   * check ownership of the individual member objects ...
2714
   */
2715
0
  if (!pg_extension_ownercheck(extensionOid, GetUserId()))
2716
0
    aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_EXTENSION,
2717
0
             extensionName);
2718
2719
  /* Permission check: must have creation rights in target namespace */
2720
0
  aclresult = pg_namespace_aclcheck(nspOid, GetUserId(), ACL_CREATE);
2721
0
  if (aclresult != ACLCHECK_OK)
2722
0
    aclcheck_error(aclresult, OBJECT_SCHEMA, newschema);
2723
2724
  /*
2725
   * If the schema is currently a member of the extension, disallow moving
2726
   * the extension into the schema.  That would create a dependency loop.
2727
   */
2728
0
  if (getExtensionOfObject(NamespaceRelationId, nspOid) == extensionOid)
2729
0
    ereport(ERROR,
2730
0
        (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2731
0
         errmsg("cannot move extension \"%s\" into schema \"%s\" "
2732
0
            "because the extension contains the schema",
2733
0
            extensionName, newschema)));
2734
2735
  /* Locate the pg_extension tuple */
2736
0
  extRel = heap_open(ExtensionRelationId, RowExclusiveLock);
2737
2738
0
  ScanKeyInit(&key[0],
2739
0
        ObjectIdAttributeNumber,
2740
0
        BTEqualStrategyNumber, F_OIDEQ,
2741
0
        ObjectIdGetDatum(extensionOid));
2742
2743
0
  extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
2744
0
                 NULL, 1, key);
2745
2746
0
  extTup = systable_getnext(extScan);
2747
2748
0
  if (!HeapTupleIsValid(extTup)) /* should not happen */
2749
0
    elog(ERROR, "could not find tuple for extension %u",
2750
0
       extensionOid);
2751
2752
  /* Copy tuple so we can modify it below */
2753
0
  extTup = heap_copytuple(extTup);
2754
0
  extForm = (Form_pg_extension) GETSTRUCT(extTup);
2755
2756
0
  systable_endscan(extScan);
2757
2758
  /*
2759
   * If the extension is already in the target schema, just silently do
2760
   * nothing.
2761
   */
2762
0
  if (extForm->extnamespace == nspOid)
2763
0
  {
2764
0
    heap_close(extRel, RowExclusiveLock);
2765
0
    return InvalidObjectAddress;
2766
0
  }
2767
2768
  /* Check extension is supposed to be relocatable */
2769
0
  if (!extForm->extrelocatable)
2770
0
    ereport(ERROR,
2771
0
        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2772
0
         errmsg("extension \"%s\" does not support SET SCHEMA",
2773
0
            NameStr(extForm->extname))));
2774
2775
0
  objsMoved = new_object_addresses();
2776
2777
  /*
2778
   * Scan pg_depend to find objects that depend directly on the extension,
2779
   * and alter each one's schema.
2780
   */
2781
0
  depRel = heap_open(DependRelationId, AccessShareLock);
2782
2783
0
  ScanKeyInit(&key[0],
2784
0
        Anum_pg_depend_refclassid,
2785
0
        BTEqualStrategyNumber, F_OIDEQ,
2786
0
        ObjectIdGetDatum(ExtensionRelationId));
2787
0
  ScanKeyInit(&key[1],
2788
0
        Anum_pg_depend_refobjid,
2789
0
        BTEqualStrategyNumber, F_OIDEQ,
2790
0
        ObjectIdGetDatum(extensionOid));
2791
2792
0
  depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
2793
0
                 NULL, 2, key);
2794
2795
0
  while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
2796
0
  {
2797
0
    Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
2798
0
    ObjectAddress dep;
2799
0
    Oid     dep_oldNspOid;
2800
2801
    /*
2802
     * Ignore non-membership dependencies.  (Currently, the only other
2803
     * case we could see here is a normal dependency from another
2804
     * extension.)
2805
     */
2806
0
    if (pg_depend->deptype != DEPENDENCY_EXTENSION)
2807
0
      continue;
2808
2809
0
    dep.classId = pg_depend->classid;
2810
0
    dep.objectId = pg_depend->objid;
2811
0
    dep.objectSubId = pg_depend->objsubid;
2812
2813
0
    if (dep.objectSubId != 0) /* should not happen */
2814
0
      elog(ERROR, "extension should not have a sub-object dependency");
2815
2816
    /* Relocate the object */
2817
0
    dep_oldNspOid = AlterObjectNamespace_oid(dep.classId,
2818
0
                         dep.objectId,
2819
0
                         nspOid,
2820
0
                         objsMoved);
2821
2822
    /*
2823
     * Remember previous namespace of first object that has one
2824
     */
2825
0
    if (oldNspOid == InvalidOid && dep_oldNspOid != InvalidOid)
2826
0
      oldNspOid = dep_oldNspOid;
2827
2828
    /*
2829
     * If not all the objects had the same old namespace (ignoring any
2830
     * that are not in namespaces), complain.
2831
     */
2832
0
    if (dep_oldNspOid != InvalidOid && dep_oldNspOid != oldNspOid)
2833
0
      ereport(ERROR,
2834
0
          (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2835
0
           errmsg("extension \"%s\" does not support SET SCHEMA",
2836
0
              NameStr(extForm->extname)),
2837
0
           errdetail("%s is not in the extension's schema \"%s\"",
2838
0
                 getObjectDescription(&dep),
2839
0
                 get_namespace_name(oldNspOid))));
2840
0
  }
2841
2842
  /* report old schema, if caller wants it */
2843
0
  if (oldschema)
2844
0
    *oldschema = oldNspOid;
2845
2846
0
  systable_endscan(depScan);
2847
2848
0
  relation_close(depRel, AccessShareLock);
2849
2850
  /* Now adjust pg_extension.extnamespace */
2851
0
  extForm->extnamespace = nspOid;
2852
2853
0
  CatalogTupleUpdate(extRel, &extTup->t_self, extTup);
2854
2855
0
  heap_close(extRel, RowExclusiveLock);
2856
2857
  /* update dependencies to point to the new schema */
2858
0
  changeDependencyFor(ExtensionRelationId, extensionOid,
2859
0
            NamespaceRelationId, oldNspOid, nspOid);
2860
2861
0
  InvokeObjectPostAlterHook(ExtensionRelationId, extensionOid, 0);
2862
2863
0
  ObjectAddressSet(extAddr, ExtensionRelationId, extensionOid);
2864
2865
0
  return extAddr;
2866
0
}
2867
2868
/*
2869
 * Execute ALTER EXTENSION UPDATE
2870
 */
2871
ObjectAddress
2872
ExecAlterExtensionStmt(ParseState *pstate, AlterExtensionStmt *stmt)
2873
1
{
2874
1
  DefElem    *d_new_version = NULL;
2875
1
  char     *versionName;
2876
1
  char     *oldVersionName;
2877
1
  ExtensionControlFile *control;
2878
1
  Oid     extensionOid;
2879
1
  Relation  extRel;
2880
1
  ScanKeyData key[1];
2881
1
  SysScanDesc extScan;
2882
1
  HeapTuple extTup;
2883
1
  List     *updateVersions;
2884
1
  Datum   datum;
2885
1
  bool    isnull;
2886
1
  ListCell   *lc;
2887
1
  ObjectAddress address;
2888
2889
  /*
2890
   * We use global variables to track the extension being created, so we can
2891
   * create/update only one extension at the same time.
2892
   */
2893
1
  if (creating_extension)
2894
1
    ereport(ERROR,
2895
1
        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2896
1
         errmsg("nested ALTER EXTENSION is not supported")));
2897
2898
  /*
2899
   * Look up the extension --- it must already exist in pg_extension
2900
   */
2901
1
  extRel = heap_open(ExtensionRelationId, AccessShareLock);
2902
2903
1
  ScanKeyInit(&key[0],
2904
1
        Anum_pg_extension_extname,
2905
1
        BTEqualStrategyNumber, F_NAMEEQ,
2906
1
        CStringGetDatum(stmt->extname));
2907
2908
1
  extScan = systable_beginscan(extRel, ExtensionNameIndexId, true,
2909
1
                 NULL, 1, key);
2910
2911
1
  extTup = systable_getnext(extScan);
2912
2913
1
  if (!HeapTupleIsValid(extTup))
2914
1
    ereport(ERROR,
2915
1
        (errcode(ERRCODE_UNDEFINED_OBJECT),
2916
1
         errmsg("extension \"%s\" does not exist",
2917
1
            stmt->extname)));
2918
2919
1
  extensionOid = HeapTupleGetOid(extTup);
2920
2921
  /*
2922
   * Determine the existing version we are updating from
2923
   */
2924
1
  datum = heap_getattr(extTup, Anum_pg_extension_extversion,
2925
1
             RelationGetDescr(extRel), &isnull);
2926
1
  if (isnull)
2927
0
    elog(ERROR, "extversion is null");
2928
1
  oldVersionName = text_to_cstring(DatumGetTextPP(datum));
2929
2930
1
  systable_endscan(extScan);
2931
2932
1
  heap_close(extRel, AccessShareLock);
2933
2934
  /* Permission check: must own extension */
2935
1
  if (!pg_extension_ownercheck(extensionOid, GetUserId()))
2936
0
    aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_EXTENSION,
2937
0
             stmt->extname);
2938
2939
  /*
2940
   * Read the primary control file.  Note we assume that it does not contain
2941
   * any non-ASCII data, so there is no need to worry about encoding at this
2942
   * point.
2943
   */
2944
1
  control = read_extension_control_file(stmt->extname);
2945
2946
  /*
2947
   * Read the statement option list
2948
   */
2949
1
  foreach(lc, stmt->options)
2950
1
  {
2951
1
    DefElem    *defel = (DefElem *) lfirst(lc);
2952
2953
1
    if (strcmp(defel->defname, "new_version") == 0)
2954
1
    {
2955
1
      if (d_new_version)
2956
1
        ereport(ERROR,
2957
1
            (errcode(ERRCODE_SYNTAX_ERROR),
2958
1
             errmsg("conflicting or redundant options"),
2959
1
             parser_errposition(pstate, defel->location)));
2960
1
      d_new_version = defel;
2961
1
    }
2962
1
    else
2963
0
      elog(ERROR, "unrecognized option: %s", defel->defname);
2964
1
  }
2965
2966
  /*
2967
   * Determine the version to update to
2968
   */
2969
1
  if (d_new_version && d_new_version->arg)
2970
1
    versionName = strVal(d_new_version->arg);
2971
0
  else if (control->default_version)
2972
0
    versionName = control->default_version;
2973
0
  else
2974
0
  {
2975
0
    ereport(ERROR,
2976
0
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2977
0
         errmsg("version to install must be specified")));
2978
0
    versionName = NULL;   /* keep compiler quiet */
2979
0
  }
2980
1
  check_valid_version_name(versionName);
2981
2982
  /*
2983
   * If we're already at that version, just say so
2984
   */
2985
1
  if (strcmp(oldVersionName, versionName) == 0)
2986
1
  {
2987
1
    ereport(NOTICE,
2988
1
        (errmsg("version \"%s\" of extension \"%s\" is already installed",
2989
1
            versionName, stmt->extname)));
2990
1
    return InvalidObjectAddress;
2991
0
  }
2992
2993
  /*
2994
   * Identify the series of update script files we need to execute
2995
   */
2996
0
  updateVersions = identify_update_path(control,
2997
0
                      oldVersionName,
2998
0
                      versionName);
2999
3000
  /*
3001
   * Update the pg_extension row and execute the update scripts, one at a
3002
   * time
3003
   */
3004
0
  ApplyExtensionUpdates(extensionOid, control,
3005
0
              oldVersionName, updateVersions,
3006
0
              NULL, false, false);
3007
3008
0
  ObjectAddressSet(address, ExtensionRelationId, extensionOid);
3009
3010
0
  return address;
3011
0
}
3012
3013
/*
3014
 * Apply a series of update scripts as though individual ALTER EXTENSION
3015
 * UPDATE commands had been given, including altering the pg_extension row
3016
 * and dependencies each time.
3017
 *
3018
 * This might be more work than necessary, but it ensures that old update
3019
 * scripts don't break if newer versions have different control parameters.
3020
 */
3021
static void
3022
ApplyExtensionUpdates(Oid extensionOid,
3023
            ExtensionControlFile *pcontrol,
3024
            const char *initialVersion,
3025
            List *updateVersions,
3026
            char *origSchemaName,
3027
            bool cascade,
3028
            bool is_create)
3029
15
{
3030
15
  const char *oldVersionName = initialVersion;
3031
15
  ListCell   *lcv;
3032
3033
15
  foreach(lcv, updateVersions)
3034
13
  {
3035
13
    char     *versionName = (char *) lfirst(lcv);
3036
13
    ExtensionControlFile *control;
3037
13
    char     *schemaName;
3038
13
    Oid     schemaOid;
3039
13
    List     *requiredExtensions;
3040
13
    List     *requiredSchemas;
3041
13
    Relation  extRel;
3042
13
    ScanKeyData key[1];
3043
13
    SysScanDesc extScan;
3044
13
    HeapTuple extTup;
3045
13
    Form_pg_extension extForm;
3046
13
    Datum   values[Natts_pg_extension];
3047
13
    bool    nulls[Natts_pg_extension];
3048
13
    bool    repl[Natts_pg_extension];
3049
13
    ObjectAddress myself;
3050
13
    ListCell   *lc;
3051
3052
    /*
3053
     * Fetch parameters for specific version (pcontrol is not changed)
3054
     */
3055
13
    control = read_extension_aux_control_file(pcontrol, versionName);
3056
3057
    /* Find the pg_extension tuple */
3058
13
    extRel = heap_open(ExtensionRelationId, RowExclusiveLock);
3059
3060
13
    ScanKeyInit(&key[0],
3061
13
          ObjectIdAttributeNumber,
3062
13
          BTEqualStrategyNumber, F_OIDEQ,
3063
13
          ObjectIdGetDatum(extensionOid));
3064
3065
13
    extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
3066
13
                   NULL, 1, key);
3067
3068
13
    extTup = systable_getnext(extScan);
3069
3070
13
    if (!HeapTupleIsValid(extTup)) /* should not happen */
3071
0
      elog(ERROR, "could not find tuple for extension %u",
3072
13
         extensionOid);
3073
3074
13
    extForm = (Form_pg_extension) GETSTRUCT(extTup);
3075
3076
    /*
3077
     * Determine the target schema (set by original install)
3078
     */
3079
13
    schemaOid = extForm->extnamespace;
3080
13
    schemaName = get_namespace_name(schemaOid);
3081
3082
    /*
3083
     * Modify extrelocatable and extversion in the pg_extension tuple
3084
     */
3085
13
    memset(values, 0, sizeof(values));
3086
13
    memset(nulls, 0, sizeof(nulls));
3087
13
    memset(repl, 0, sizeof(repl));
3088
3089
13
    values[Anum_pg_extension_extrelocatable - 1] =
3090
13
      BoolGetDatum(control->relocatable);
3091
13
    repl[Anum_pg_extension_extrelocatable - 1] = true;
3092
13
    values[Anum_pg_extension_extversion - 1] =
3093
13
      CStringGetTextDatum(versionName);
3094
13
    repl[Anum_pg_extension_extversion - 1] = true;
3095
3096
13
    extTup = heap_modify_tuple(extTup, RelationGetDescr(extRel),
3097
13
                   values, nulls, repl);
3098
3099
13
    CatalogTupleUpdate(extRel, &extTup->t_self, extTup);
3100
3101
13
    systable_endscan(extScan);
3102
3103
13
    heap_close(extRel, RowExclusiveLock);
3104
3105
    /*
3106
     * Look up the prerequisite extensions for this version, install them
3107
     * if necessary, and build lists of their OIDs and the OIDs of their
3108
     * target schemas.
3109
     */
3110
13
    requiredExtensions = NIL;
3111
13
    requiredSchemas = NIL;
3112
13
    foreach(lc, control->requires)
3113
0
    {
3114
0
      char     *curreq = (char *) lfirst(lc);
3115
0
      Oid     reqext;
3116
0
      Oid     reqschema;
3117
3118
0
      reqext = get_required_extension(curreq,
3119
0
                      control->name,
3120
0
                      origSchemaName,
3121
0
                      cascade,
3122
0
                      NIL,
3123
0
                      is_create);
3124
0
      reqschema = get_extension_schema(reqext);
3125
0
      requiredExtensions = lappend_oid(requiredExtensions, reqext);
3126
0
      requiredSchemas = lappend_oid(requiredSchemas, reqschema);
3127
0
    }
3128
3129
    /*
3130
     * Remove and recreate dependencies on prerequisite extensions
3131
     */
3132
13
    deleteDependencyRecordsForClass(ExtensionRelationId, extensionOid,
3133
13
                    ExtensionRelationId,
3134
13
                    DEPENDENCY_NORMAL);
3135
3136
13
    myself.classId = ExtensionRelationId;
3137
13
    myself.objectId = extensionOid;
3138
13
    myself.objectSubId = 0;
3139
3140
13
    foreach(lc, requiredExtensions)
3141
0
    {
3142
0
      Oid     reqext = lfirst_oid(lc);
3143
0
      ObjectAddress otherext;
3144
3145
0
      otherext.classId = ExtensionRelationId;
3146
0
      otherext.objectId = reqext;
3147
0
      otherext.objectSubId = 0;
3148
3149
0
      recordDependencyOn(&myself, &otherext, DEPENDENCY_NORMAL);
3150
0
    }
3151
3152
13
    InvokeObjectPostAlterHook(ExtensionRelationId, extensionOid, 0);
3153
3154
    /*
3155
     * Finally, execute the update script file
3156
     */
3157
13
    execute_extension_script(extensionOid, control,
3158
13
                 oldVersionName, versionName,
3159
13
                 requiredSchemas,
3160
13
                 schemaName, schemaOid);
3161
3162
    /*
3163
     * Update prior-version name and loop around.  Since
3164
     * execute_sql_string did a final CommandCounterIncrement, we can
3165
     * update the pg_extension row again.
3166
     */
3167
13
    oldVersionName = versionName;
3168
13
  }
3169
15
}
3170
3171
/*
3172
 * Execute ALTER EXTENSION ADD/DROP
3173
 *
3174
 * Return value is the address of the altered extension.
3175
 *
3176
 * objAddr is an output argument which, if not NULL, is set to the address of
3177
 * the added/dropped object.
3178
 */
3179
ObjectAddress
3180
ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt,
3181
                 ObjectAddress *objAddr)
3182
24
{
3183
24
  ObjectAddress extension;
3184
24
  ObjectAddress object;
3185
24
  Relation  relation;
3186
24
  Oid     oldExtension;
3187
3188
  /*
3189
   * Find the extension and acquire a lock on it, to ensure it doesn't get
3190
   * dropped concurrently.  A sharable lock seems sufficient: there's no
3191
   * reason not to allow other sorts of manipulations, such as add/drop of
3192
   * other objects, to occur concurrently.  Concurrently adding/dropping the
3193
   * *same* object would be bad, but we prevent that by using a non-sharable
3194
   * lock on the individual object, below.
3195
   */
3196
24
  extension = get_object_address(OBJECT_EXTENSION,
3197
24
                   (Node *) makeString(stmt->extname),
3198
24
                   &relation, AccessShareLock, false);
3199
3200
  /* Permission check: must own extension */
3201
24
  if (!pg_extension_ownercheck(extension.objectId, GetUserId()))
3202
0
    aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_EXTENSION,
3203
0
             stmt->extname);
3204
3205
  /*
3206
   * Translate the parser representation that identifies the object into an
3207
   * ObjectAddress.  get_object_address() will throw an error if the object
3208
   * does not exist, and will also acquire a lock on the object to guard
3209
   * against concurrent DROP and ALTER EXTENSION ADD/DROP operations.
3210
   */
3211
24
  object = get_object_address(stmt->objtype, stmt->object,
3212
24
                &relation, ShareUpdateExclusiveLock, false);
3213
3214
24
  Assert(object.objectSubId == 0);
3215
24
  if (objAddr)
3216
24
    *objAddr = object;
3217
3218
  /* Permission check: must own target object, too */
3219
24
  check_object_ownership(GetUserId(), stmt->objtype, object,
3220
24
               stmt->object, relation);
3221
3222
  /*
3223
   * Check existing extension membership.
3224
   */
3225
24
  oldExtension = getExtensionOfObject(object.classId, object.objectId);
3226
3227
24
  if (stmt->action > 0)
3228
10
  {
3229
    /*
3230
     * ADD, so complain if object is already attached to some extension.
3231
     */
3232
10
    if (OidIsValid(oldExtension))
3233
10
      ereport(ERROR,
3234
10
          (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
3235
10
           errmsg("%s is already a member of extension \"%s\"",
3236
10
              getObjectDescription(&object),
3237
10
              get_extension_name(oldExtension))));
3238
3239
    /*
3240
     * Prevent a schema from being added to an extension if the schema
3241
     * contains the extension.  That would create a dependency loop.
3242
     */
3243
10
    if (object.classId == NamespaceRelationId &&
3244
0
      object.objectId == get_extension_schema(extension.objectId))
3245
10
      ereport(ERROR,
3246
10
          (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
3247
10
           errmsg("cannot add schema \"%s\" to extension \"%s\" "
3248
10
              "because the schema contains the extension",
3249
10
              get_namespace_name(object.objectId),
3250
10
              stmt->extname)));
3251
3252
    /*
3253
     * OK, add the dependency.
3254
     */
3255
10
    recordDependencyOn(&object, &extension, DEPENDENCY_EXTENSION);
3256
3257
    /*
3258
     * Also record the initial ACL on the object, if any.
3259
     *
3260
     * Note that this will handle the object's ACLs, as well as any ACLs
3261
     * on object subIds.  (In other words, when the object is a table,
3262
     * this will record the table's ACL and the ACLs for the columns on
3263
     * the table, if any).
3264
     */
3265
10
    recordExtObjInitPriv(object.objectId, object.classId);
3266
10
  }
3267
14
  else
3268
14
  {
3269
    /*
3270
     * DROP, so complain if it's not a member.
3271
     */
3272
14
    if (oldExtension != extension.objectId)
3273
14
      ereport(ERROR,
3274
14
          (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
3275
14
           errmsg("%s is not a member of extension \"%s\"",
3276
14
              getObjectDescription(&object),
3277
14
              stmt->extname)));
3278
3279
    /*
3280
     * OK, drop the dependency.
3281
     */
3282
14
    if (deleteDependencyRecordsForClass(object.classId, object.objectId,
3283
14
                      ExtensionRelationId,
3284
14
                      DEPENDENCY_EXTENSION) != 1)
3285
0
      elog(ERROR, "unexpected number of extension dependency records");
3286
3287
    /*
3288
     * If it's a relation, it might have an entry in the extension's
3289
     * extconfig array, which we must remove.
3290
     */
3291
14
    if (object.classId == RelationRelationId)
3292
0
      extension_config_remove(extension.objectId, object.objectId);
3293
3294
    /*
3295
     * Remove all the initial ACLs, if any.
3296
     *
3297
     * Note that this will remove the object's ACLs, as well as any ACLs
3298
     * on object subIds.  (In other words, when the object is a table,
3299
     * this will remove the table's ACL and the ACLs for the columns on
3300
     * the table, if any).
3301
     */
3302
14
    removeExtObjInitPriv(object.objectId, object.classId);
3303
14
  }
3304
3305
24
  InvokeObjectPostAlterHook(ExtensionRelationId, extension.objectId, 0);
3306
3307
  /*
3308
   * If get_object_address() opened the relation for us, we close it to keep
3309
   * the reference count correct - but we retain any locks acquired by
3310
   * get_object_address() until commit time, to guard against concurrent
3311
   * activity.
3312
   */
3313
24
  if (relation != NULL)
3314
0
    relation_close(relation, NoLock);
3315
3316
24
  return extension;
3317
24
}
3318
3319
/*
3320
 * Read the whole of file into memory.
3321
 *
3322
 * The file contents are returned as a single palloc'd chunk. For convenience
3323
 * of the callers, an extra \0 byte is added to the end.
3324
 */
3325
static char *
3326
read_whole_file(const char *filename, int *length)
3327
28
{
3328
28
  char     *buf;
3329
28
  FILE     *file;
3330
28
  size_t    bytes_to_read;
3331
28
  struct stat fst;
3332
3333
28
  if (stat(filename, &fst) < 0)
3334
28
    ereport(ERROR,
3335
28
        (errcode_for_file_access(),
3336
28
         errmsg("could not stat file \"%s\": %m", filename)));
3337
3338
28
  if (fst.st_size > (MaxAllocSize - 1))
3339
28
    ereport(ERROR,
3340
28
        (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3341
28
         errmsg("file \"%s\" is too large", filename)));
3342
28
  bytes_to_read = (size_t) fst.st_size;
3343
3344
28
  if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
3345
28
    ereport(ERROR,
3346
28
        (errcode_for_file_access(),
3347
28
         errmsg("could not open file \"%s\" for reading: %m",
3348
28
            filename)));
3349
3350
28
  buf = (char *) palloc(bytes_to_read + 1);
3351
3352
28
  *length = fread(buf, 1, bytes_to_read, file);
3353
3354
28
  if (ferror(file))
3355
28
    ereport(ERROR,
3356
28
        (errcode_for_file_access(),
3357
28
         errmsg("could not read file \"%s\": %m", filename)));
3358
3359
28
  FreeFile(file);
3360
3361
28
  buf[*length] = '\0';
3362
28
  return buf;
3363
28
}