Coverage Report

Created: 2025-06-15 06:31

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