Coverage Report

Created: 2025-10-09 06:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/commands/foreigncmds.c
Line
Count
Source
1
/*-------------------------------------------------------------------------
2
 *
3
 * foreigncmds.c
4
 *    foreign-data wrapper/server creation/manipulation commands
5
 *
6
 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7
 *
8
 *
9
 * IDENTIFICATION
10
 *    src/backend/commands/foreigncmds.c
11
 *
12
 *-------------------------------------------------------------------------
13
 */
14
#include "postgres.h"
15
16
#include "access/htup_details.h"
17
#include "access/reloptions.h"
18
#include "access/table.h"
19
#include "access/xact.h"
20
#include "catalog/catalog.h"
21
#include "catalog/dependency.h"
22
#include "catalog/indexing.h"
23
#include "catalog/objectaccess.h"
24
#include "catalog/pg_foreign_data_wrapper.h"
25
#include "catalog/pg_foreign_server.h"
26
#include "catalog/pg_foreign_table.h"
27
#include "catalog/pg_proc.h"
28
#include "catalog/pg_type.h"
29
#include "catalog/pg_user_mapping.h"
30
#include "commands/defrem.h"
31
#include "foreign/fdwapi.h"
32
#include "foreign/foreign.h"
33
#include "miscadmin.h"
34
#include "parser/parse_func.h"
35
#include "tcop/utility.h"
36
#include "utils/acl.h"
37
#include "utils/builtins.h"
38
#include "utils/lsyscache.h"
39
#include "utils/rel.h"
40
#include "utils/syscache.h"
41
42
43
typedef struct
44
{
45
  char     *tablename;
46
  char     *cmd;
47
} import_error_callback_arg;
48
49
/* Internal functions */
50
static void import_error_callback(void *arg);
51
52
53
/*
54
 * Convert a DefElem list to the text array format that is used in
55
 * pg_foreign_data_wrapper, pg_foreign_server, pg_user_mapping, and
56
 * pg_foreign_table.
57
 *
58
 * Returns the array in the form of a Datum, or PointerGetDatum(NULL)
59
 * if the list is empty.
60
 *
61
 * Note: The array is usually stored to database without further
62
 * processing, hence any validation should be done before this
63
 * conversion.
64
 */
65
static Datum
66
optionListToArray(List *options)
67
0
{
68
0
  ArrayBuildState *astate = NULL;
69
0
  ListCell   *cell;
70
71
0
  foreach(cell, options)
72
0
  {
73
0
    DefElem    *def = lfirst(cell);
74
0
    const char *name;
75
0
    const char *value;
76
0
    Size    len;
77
0
    text     *t;
78
79
0
    name = def->defname;
80
0
    value = defGetString(def);
81
82
    /* Insist that name not contain "=", else "a=b=c" is ambiguous */
83
0
    if (strchr(name, '=') != NULL)
84
0
      ereport(ERROR,
85
0
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
86
0
           errmsg("invalid option name \"%s\": must not contain \"=\"",
87
0
              name)));
88
89
0
    len = VARHDRSZ + strlen(name) + 1 + strlen(value);
90
    /* +1 leaves room for sprintf's trailing null */
91
0
    t = palloc(len + 1);
92
0
    SET_VARSIZE(t, len);
93
0
    sprintf(VARDATA(t), "%s=%s", name, value);
94
95
0
    astate = accumArrayResult(astate, PointerGetDatum(t),
96
0
                  false, TEXTOID,
97
0
                  CurrentMemoryContext);
98
0
  }
99
100
0
  if (astate)
101
0
    return makeArrayResult(astate, CurrentMemoryContext);
102
103
0
  return PointerGetDatum(NULL);
104
0
}
105
106
107
/*
108
 * Transform a list of DefElem into text array format.  This is substantially
109
 * the same thing as optionListToArray(), except we recognize SET/ADD/DROP
110
 * actions for modifying an existing list of options, which is passed in
111
 * Datum form as oldOptions.  Also, if fdwvalidator isn't InvalidOid
112
 * it specifies a validator function to call on the result.
113
 *
114
 * Returns the array in the form of a Datum, or PointerGetDatum(NULL)
115
 * if the list is empty.
116
 *
117
 * This is used by CREATE/ALTER of FOREIGN DATA WRAPPER/SERVER/USER MAPPING/
118
 * FOREIGN TABLE.
119
 */
120
Datum
121
transformGenericOptions(Oid catalogId,
122
            Datum oldOptions,
123
            List *options,
124
            Oid fdwvalidator)
125
0
{
126
0
  List     *resultOptions = untransformRelOptions(oldOptions);
127
0
  ListCell   *optcell;
128
0
  Datum   result;
129
130
0
  foreach(optcell, options)
131
0
  {
132
0
    DefElem    *od = lfirst(optcell);
133
0
    ListCell   *cell;
134
135
    /*
136
     * Find the element in resultOptions.  We need this for validation in
137
     * all cases.
138
     */
139
0
    foreach(cell, resultOptions)
140
0
    {
141
0
      DefElem    *def = lfirst(cell);
142
143
0
      if (strcmp(def->defname, od->defname) == 0)
144
0
        break;
145
0
    }
146
147
    /*
148
     * It is possible to perform multiple SET/DROP actions on the same
149
     * option.  The standard permits this, as long as the options to be
150
     * added are unique.  Note that an unspecified action is taken to be
151
     * ADD.
152
     */
153
0
    switch (od->defaction)
154
0
    {
155
0
      case DEFELEM_DROP:
156
0
        if (!cell)
157
0
          ereport(ERROR,
158
0
              (errcode(ERRCODE_UNDEFINED_OBJECT),
159
0
               errmsg("option \"%s\" not found",
160
0
                  od->defname)));
161
0
        resultOptions = list_delete_cell(resultOptions, cell);
162
0
        break;
163
164
0
      case DEFELEM_SET:
165
0
        if (!cell)
166
0
          ereport(ERROR,
167
0
              (errcode(ERRCODE_UNDEFINED_OBJECT),
168
0
               errmsg("option \"%s\" not found",
169
0
                  od->defname)));
170
0
        lfirst(cell) = od;
171
0
        break;
172
173
0
      case DEFELEM_ADD:
174
0
      case DEFELEM_UNSPEC:
175
0
        if (cell)
176
0
          ereport(ERROR,
177
0
              (errcode(ERRCODE_DUPLICATE_OBJECT),
178
0
               errmsg("option \"%s\" provided more than once",
179
0
                  od->defname)));
180
0
        resultOptions = lappend(resultOptions, od);
181
0
        break;
182
183
0
      default:
184
0
        elog(ERROR, "unrecognized action %d on option \"%s\"",
185
0
           (int) od->defaction, od->defname);
186
0
        break;
187
0
    }
188
0
  }
189
190
0
  result = optionListToArray(resultOptions);
191
192
0
  if (OidIsValid(fdwvalidator))
193
0
  {
194
0
    Datum   valarg = result;
195
196
    /*
197
     * Pass a null options list as an empty array, so that validators
198
     * don't have to be declared non-strict to handle the case.
199
     */
200
0
    if (DatumGetPointer(valarg) == NULL)
201
0
      valarg = PointerGetDatum(construct_empty_array(TEXTOID));
202
0
    OidFunctionCall2(fdwvalidator, valarg, ObjectIdGetDatum(catalogId));
203
0
  }
204
205
0
  return result;
206
0
}
207
208
209
/*
210
 * Internal workhorse for changing a data wrapper's owner.
211
 *
212
 * Allow this only for superusers; also the new owner must be a
213
 * superuser.
214
 */
215
static void
216
AlterForeignDataWrapperOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
217
0
{
218
0
  Form_pg_foreign_data_wrapper form;
219
0
  Datum   repl_val[Natts_pg_foreign_data_wrapper];
220
0
  bool    repl_null[Natts_pg_foreign_data_wrapper];
221
0
  bool    repl_repl[Natts_pg_foreign_data_wrapper];
222
0
  Acl      *newAcl;
223
0
  Datum   aclDatum;
224
0
  bool    isNull;
225
226
0
  form = (Form_pg_foreign_data_wrapper) GETSTRUCT(tup);
227
228
  /* Must be a superuser to change a FDW owner */
229
0
  if (!superuser())
230
0
    ereport(ERROR,
231
0
        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
232
0
         errmsg("permission denied to change owner of foreign-data wrapper \"%s\"",
233
0
            NameStr(form->fdwname)),
234
0
         errhint("Must be superuser to change owner of a foreign-data wrapper.")));
235
236
  /* New owner must also be a superuser */
237
0
  if (!superuser_arg(newOwnerId))
238
0
    ereport(ERROR,
239
0
        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
240
0
         errmsg("permission denied to change owner of foreign-data wrapper \"%s\"",
241
0
            NameStr(form->fdwname)),
242
0
         errhint("The owner of a foreign-data wrapper must be a superuser.")));
243
244
0
  if (form->fdwowner != newOwnerId)
245
0
  {
246
0
    memset(repl_null, false, sizeof(repl_null));
247
0
    memset(repl_repl, false, sizeof(repl_repl));
248
249
0
    repl_repl[Anum_pg_foreign_data_wrapper_fdwowner - 1] = true;
250
0
    repl_val[Anum_pg_foreign_data_wrapper_fdwowner - 1] = ObjectIdGetDatum(newOwnerId);
251
252
0
    aclDatum = heap_getattr(tup,
253
0
                Anum_pg_foreign_data_wrapper_fdwacl,
254
0
                RelationGetDescr(rel),
255
0
                &isNull);
256
    /* Null ACLs do not require changes */
257
0
    if (!isNull)
258
0
    {
259
0
      newAcl = aclnewowner(DatumGetAclP(aclDatum),
260
0
                 form->fdwowner, newOwnerId);
261
0
      repl_repl[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true;
262
0
      repl_val[Anum_pg_foreign_data_wrapper_fdwacl - 1] = PointerGetDatum(newAcl);
263
0
    }
264
265
0
    tup = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null,
266
0
                repl_repl);
267
268
0
    CatalogTupleUpdate(rel, &tup->t_self, tup);
269
270
    /* Update owner dependency reference */
271
0
    changeDependencyOnOwner(ForeignDataWrapperRelationId,
272
0
                form->oid,
273
0
                newOwnerId);
274
0
  }
275
276
0
  InvokeObjectPostAlterHook(ForeignDataWrapperRelationId,
277
0
                form->oid, 0);
278
0
}
279
280
/*
281
 * Change foreign-data wrapper owner -- by name
282
 *
283
 * Note restrictions in the "_internal" function, above.
284
 */
285
ObjectAddress
286
AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId)
287
0
{
288
0
  Oid     fdwId;
289
0
  HeapTuple tup;
290
0
  Relation  rel;
291
0
  ObjectAddress address;
292
0
  Form_pg_foreign_data_wrapper form;
293
294
295
0
  rel = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);
296
297
0
  tup = SearchSysCacheCopy1(FOREIGNDATAWRAPPERNAME, CStringGetDatum(name));
298
299
0
  if (!HeapTupleIsValid(tup))
300
0
    ereport(ERROR,
301
0
        (errcode(ERRCODE_UNDEFINED_OBJECT),
302
0
         errmsg("foreign-data wrapper \"%s\" does not exist", name)));
303
304
0
  form = (Form_pg_foreign_data_wrapper) GETSTRUCT(tup);
305
0
  fdwId = form->oid;
306
307
0
  AlterForeignDataWrapperOwner_internal(rel, tup, newOwnerId);
308
309
0
  ObjectAddressSet(address, ForeignDataWrapperRelationId, fdwId);
310
311
0
  heap_freetuple(tup);
312
313
0
  table_close(rel, RowExclusiveLock);
314
315
0
  return address;
316
0
}
317
318
/*
319
 * Change foreign-data wrapper owner -- by OID
320
 *
321
 * Note restrictions in the "_internal" function, above.
322
 */
323
void
324
AlterForeignDataWrapperOwner_oid(Oid fwdId, Oid newOwnerId)
325
0
{
326
0
  HeapTuple tup;
327
0
  Relation  rel;
328
329
0
  rel = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);
330
331
0
  tup = SearchSysCacheCopy1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fwdId));
332
333
0
  if (!HeapTupleIsValid(tup))
334
0
    ereport(ERROR,
335
0
        (errcode(ERRCODE_UNDEFINED_OBJECT),
336
0
         errmsg("foreign-data wrapper with OID %u does not exist", fwdId)));
337
338
0
  AlterForeignDataWrapperOwner_internal(rel, tup, newOwnerId);
339
340
0
  heap_freetuple(tup);
341
342
0
  table_close(rel, RowExclusiveLock);
343
0
}
344
345
/*
346
 * Internal workhorse for changing a foreign server's owner
347
 */
348
static void
349
AlterForeignServerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
350
0
{
351
0
  Form_pg_foreign_server form;
352
0
  Datum   repl_val[Natts_pg_foreign_server];
353
0
  bool    repl_null[Natts_pg_foreign_server];
354
0
  bool    repl_repl[Natts_pg_foreign_server];
355
0
  Acl      *newAcl;
356
0
  Datum   aclDatum;
357
0
  bool    isNull;
358
359
0
  form = (Form_pg_foreign_server) GETSTRUCT(tup);
360
361
0
  if (form->srvowner != newOwnerId)
362
0
  {
363
    /* Superusers can always do it */
364
0
    if (!superuser())
365
0
    {
366
0
      Oid     srvId;
367
0
      AclResult aclresult;
368
369
0
      srvId = form->oid;
370
371
      /* Must be owner */
372
0
      if (!object_ownercheck(ForeignServerRelationId, srvId, GetUserId()))
373
0
        aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FOREIGN_SERVER,
374
0
                 NameStr(form->srvname));
375
376
      /* Must be able to become new owner */
377
0
      check_can_set_role(GetUserId(), newOwnerId);
378
379
      /* New owner must have USAGE privilege on foreign-data wrapper */
380
0
      aclresult = object_aclcheck(ForeignDataWrapperRelationId, form->srvfdw, newOwnerId, ACL_USAGE);
381
0
      if (aclresult != ACLCHECK_OK)
382
0
      {
383
0
        ForeignDataWrapper *fdw = GetForeignDataWrapper(form->srvfdw);
384
385
0
        aclcheck_error(aclresult, OBJECT_FDW, fdw->fdwname);
386
0
      }
387
0
    }
388
389
0
    memset(repl_null, false, sizeof(repl_null));
390
0
    memset(repl_repl, false, sizeof(repl_repl));
391
392
0
    repl_repl[Anum_pg_foreign_server_srvowner - 1] = true;
393
0
    repl_val[Anum_pg_foreign_server_srvowner - 1] = ObjectIdGetDatum(newOwnerId);
394
395
0
    aclDatum = heap_getattr(tup,
396
0
                Anum_pg_foreign_server_srvacl,
397
0
                RelationGetDescr(rel),
398
0
                &isNull);
399
    /* Null ACLs do not require changes */
400
0
    if (!isNull)
401
0
    {
402
0
      newAcl = aclnewowner(DatumGetAclP(aclDatum),
403
0
                 form->srvowner, newOwnerId);
404
0
      repl_repl[Anum_pg_foreign_server_srvacl - 1] = true;
405
0
      repl_val[Anum_pg_foreign_server_srvacl - 1] = PointerGetDatum(newAcl);
406
0
    }
407
408
0
    tup = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null,
409
0
                repl_repl);
410
411
0
    CatalogTupleUpdate(rel, &tup->t_self, tup);
412
413
    /* Update owner dependency reference */
414
0
    changeDependencyOnOwner(ForeignServerRelationId, form->oid,
415
0
                newOwnerId);
416
0
  }
417
418
0
  InvokeObjectPostAlterHook(ForeignServerRelationId,
419
0
                form->oid, 0);
420
0
}
421
422
/*
423
 * Change foreign server owner -- by name
424
 */
425
ObjectAddress
426
AlterForeignServerOwner(const char *name, Oid newOwnerId)
427
0
{
428
0
  Oid     servOid;
429
0
  HeapTuple tup;
430
0
  Relation  rel;
431
0
  ObjectAddress address;
432
0
  Form_pg_foreign_server form;
433
434
0
  rel = table_open(ForeignServerRelationId, RowExclusiveLock);
435
436
0
  tup = SearchSysCacheCopy1(FOREIGNSERVERNAME, CStringGetDatum(name));
437
438
0
  if (!HeapTupleIsValid(tup))
439
0
    ereport(ERROR,
440
0
        (errcode(ERRCODE_UNDEFINED_OBJECT),
441
0
         errmsg("server \"%s\" does not exist", name)));
442
443
0
  form = (Form_pg_foreign_server) GETSTRUCT(tup);
444
0
  servOid = form->oid;
445
446
0
  AlterForeignServerOwner_internal(rel, tup, newOwnerId);
447
448
0
  ObjectAddressSet(address, ForeignServerRelationId, servOid);
449
450
0
  heap_freetuple(tup);
451
452
0
  table_close(rel, RowExclusiveLock);
453
454
0
  return address;
455
0
}
456
457
/*
458
 * Change foreign server owner -- by OID
459
 */
460
void
461
AlterForeignServerOwner_oid(Oid srvId, Oid newOwnerId)
462
0
{
463
0
  HeapTuple tup;
464
0
  Relation  rel;
465
466
0
  rel = table_open(ForeignServerRelationId, RowExclusiveLock);
467
468
0
  tup = SearchSysCacheCopy1(FOREIGNSERVEROID, ObjectIdGetDatum(srvId));
469
470
0
  if (!HeapTupleIsValid(tup))
471
0
    ereport(ERROR,
472
0
        (errcode(ERRCODE_UNDEFINED_OBJECT),
473
0
         errmsg("foreign server with OID %u does not exist", srvId)));
474
475
0
  AlterForeignServerOwner_internal(rel, tup, newOwnerId);
476
477
0
  heap_freetuple(tup);
478
479
0
  table_close(rel, RowExclusiveLock);
480
0
}
481
482
/*
483
 * Convert a handler function name passed from the parser to an Oid.
484
 */
485
static Oid
486
lookup_fdw_handler_func(DefElem *handler)
487
0
{
488
0
  Oid     handlerOid;
489
490
0
  if (handler == NULL || handler->arg == NULL)
491
0
    return InvalidOid;
492
493
  /* handlers have no arguments */
494
0
  handlerOid = LookupFuncName((List *) handler->arg, 0, NULL, false);
495
496
  /* check that handler has correct return type */
497
0
  if (get_func_rettype(handlerOid) != FDW_HANDLEROID)
498
0
    ereport(ERROR,
499
0
        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
500
0
         errmsg("function %s must return type %s",
501
0
            NameListToString((List *) handler->arg), "fdw_handler")));
502
503
0
  return handlerOid;
504
0
}
505
506
/*
507
 * Convert a validator function name passed from the parser to an Oid.
508
 */
509
static Oid
510
lookup_fdw_validator_func(DefElem *validator)
511
0
{
512
0
  Oid     funcargtypes[2];
513
514
0
  if (validator == NULL || validator->arg == NULL)
515
0
    return InvalidOid;
516
517
  /* validators take text[], oid */
518
0
  funcargtypes[0] = TEXTARRAYOID;
519
0
  funcargtypes[1] = OIDOID;
520
521
0
  return LookupFuncName((List *) validator->arg, 2, funcargtypes, false);
522
  /* validator's return value is ignored, so we don't check the type */
523
0
}
524
525
/*
526
 * Process function options of CREATE/ALTER FDW
527
 */
528
static void
529
parse_func_options(ParseState *pstate, List *func_options,
530
           bool *handler_given, Oid *fdwhandler,
531
           bool *validator_given, Oid *fdwvalidator)
532
0
{
533
0
  ListCell   *cell;
534
535
0
  *handler_given = false;
536
0
  *validator_given = false;
537
  /* return InvalidOid if not given */
538
0
  *fdwhandler = InvalidOid;
539
0
  *fdwvalidator = InvalidOid;
540
541
0
  foreach(cell, func_options)
542
0
  {
543
0
    DefElem    *def = (DefElem *) lfirst(cell);
544
545
0
    if (strcmp(def->defname, "handler") == 0)
546
0
    {
547
0
      if (*handler_given)
548
0
        errorConflictingDefElem(def, pstate);
549
0
      *handler_given = true;
550
0
      *fdwhandler = lookup_fdw_handler_func(def);
551
0
    }
552
0
    else if (strcmp(def->defname, "validator") == 0)
553
0
    {
554
0
      if (*validator_given)
555
0
        errorConflictingDefElem(def, pstate);
556
0
      *validator_given = true;
557
0
      *fdwvalidator = lookup_fdw_validator_func(def);
558
0
    }
559
0
    else
560
0
      elog(ERROR, "option \"%s\" not recognized",
561
0
         def->defname);
562
0
  }
563
0
}
564
565
/*
566
 * Create a foreign-data wrapper
567
 */
568
ObjectAddress
569
CreateForeignDataWrapper(ParseState *pstate, CreateFdwStmt *stmt)
570
0
{
571
0
  Relation  rel;
572
0
  Datum   values[Natts_pg_foreign_data_wrapper];
573
0
  bool    nulls[Natts_pg_foreign_data_wrapper];
574
0
  HeapTuple tuple;
575
0
  Oid     fdwId;
576
0
  bool    handler_given;
577
0
  bool    validator_given;
578
0
  Oid     fdwhandler;
579
0
  Oid     fdwvalidator;
580
0
  Datum   fdwoptions;
581
0
  Oid     ownerId;
582
0
  ObjectAddress myself;
583
0
  ObjectAddress referenced;
584
585
0
  rel = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);
586
587
  /* Must be superuser */
588
0
  if (!superuser())
589
0
    ereport(ERROR,
590
0
        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
591
0
         errmsg("permission denied to create foreign-data wrapper \"%s\"",
592
0
            stmt->fdwname),
593
0
         errhint("Must be superuser to create a foreign-data wrapper.")));
594
595
  /* For now the owner cannot be specified on create. Use effective user ID. */
596
0
  ownerId = GetUserId();
597
598
  /*
599
   * Check that there is no other foreign-data wrapper by this name.
600
   */
601
0
  if (GetForeignDataWrapperByName(stmt->fdwname, true) != NULL)
602
0
    ereport(ERROR,
603
0
        (errcode(ERRCODE_DUPLICATE_OBJECT),
604
0
         errmsg("foreign-data wrapper \"%s\" already exists",
605
0
            stmt->fdwname)));
606
607
  /*
608
   * Insert tuple into pg_foreign_data_wrapper.
609
   */
610
0
  memset(values, 0, sizeof(values));
611
0
  memset(nulls, false, sizeof(nulls));
612
613
0
  fdwId = GetNewOidWithIndex(rel, ForeignDataWrapperOidIndexId,
614
0
                 Anum_pg_foreign_data_wrapper_oid);
615
0
  values[Anum_pg_foreign_data_wrapper_oid - 1] = ObjectIdGetDatum(fdwId);
616
0
  values[Anum_pg_foreign_data_wrapper_fdwname - 1] =
617
0
    DirectFunctionCall1(namein, CStringGetDatum(stmt->fdwname));
618
0
  values[Anum_pg_foreign_data_wrapper_fdwowner - 1] = ObjectIdGetDatum(ownerId);
619
620
  /* Lookup handler and validator functions, if given */
621
0
  parse_func_options(pstate, stmt->func_options,
622
0
             &handler_given, &fdwhandler,
623
0
             &validator_given, &fdwvalidator);
624
625
0
  values[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = ObjectIdGetDatum(fdwhandler);
626
0
  values[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = ObjectIdGetDatum(fdwvalidator);
627
628
0
  nulls[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true;
629
630
0
  fdwoptions = transformGenericOptions(ForeignDataWrapperRelationId,
631
0
                     PointerGetDatum(NULL),
632
0
                     stmt->options,
633
0
                     fdwvalidator);
634
635
0
  if (DatumGetPointer(fdwoptions) != NULL)
636
0
    values[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = fdwoptions;
637
0
  else
638
0
    nulls[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
639
640
0
  tuple = heap_form_tuple(rel->rd_att, values, nulls);
641
642
0
  CatalogTupleInsert(rel, tuple);
643
644
0
  heap_freetuple(tuple);
645
646
  /* record dependencies */
647
0
  myself.classId = ForeignDataWrapperRelationId;
648
0
  myself.objectId = fdwId;
649
0
  myself.objectSubId = 0;
650
651
0
  if (OidIsValid(fdwhandler))
652
0
  {
653
0
    referenced.classId = ProcedureRelationId;
654
0
    referenced.objectId = fdwhandler;
655
0
    referenced.objectSubId = 0;
656
0
    recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
657
0
  }
658
659
0
  if (OidIsValid(fdwvalidator))
660
0
  {
661
0
    referenced.classId = ProcedureRelationId;
662
0
    referenced.objectId = fdwvalidator;
663
0
    referenced.objectSubId = 0;
664
0
    recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
665
0
  }
666
667
0
  recordDependencyOnOwner(ForeignDataWrapperRelationId, fdwId, ownerId);
668
669
  /* dependency on extension */
670
0
  recordDependencyOnCurrentExtension(&myself, false);
671
672
  /* Post creation hook for new foreign data wrapper */
673
0
  InvokeObjectPostCreateHook(ForeignDataWrapperRelationId, fdwId, 0);
674
675
0
  table_close(rel, RowExclusiveLock);
676
677
0
  return myself;
678
0
}
679
680
681
/*
682
 * Alter foreign-data wrapper
683
 */
684
ObjectAddress
685
AlterForeignDataWrapper(ParseState *pstate, AlterFdwStmt *stmt)
686
0
{
687
0
  Relation  rel;
688
0
  HeapTuple tp;
689
0
  Form_pg_foreign_data_wrapper fdwForm;
690
0
  Datum   repl_val[Natts_pg_foreign_data_wrapper];
691
0
  bool    repl_null[Natts_pg_foreign_data_wrapper];
692
0
  bool    repl_repl[Natts_pg_foreign_data_wrapper];
693
0
  Oid     fdwId;
694
0
  bool    isnull;
695
0
  Datum   datum;
696
0
  bool    handler_given;
697
0
  bool    validator_given;
698
0
  Oid     fdwhandler;
699
0
  Oid     fdwvalidator;
700
0
  ObjectAddress myself;
701
702
0
  rel = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);
703
704
  /* Must be superuser */
705
0
  if (!superuser())
706
0
    ereport(ERROR,
707
0
        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
708
0
         errmsg("permission denied to alter foreign-data wrapper \"%s\"",
709
0
            stmt->fdwname),
710
0
         errhint("Must be superuser to alter a foreign-data wrapper.")));
711
712
0
  tp = SearchSysCacheCopy1(FOREIGNDATAWRAPPERNAME,
713
0
               CStringGetDatum(stmt->fdwname));
714
715
0
  if (!HeapTupleIsValid(tp))
716
0
    ereport(ERROR,
717
0
        (errcode(ERRCODE_UNDEFINED_OBJECT),
718
0
         errmsg("foreign-data wrapper \"%s\" does not exist", stmt->fdwname)));
719
720
0
  fdwForm = (Form_pg_foreign_data_wrapper) GETSTRUCT(tp);
721
0
  fdwId = fdwForm->oid;
722
723
0
  memset(repl_val, 0, sizeof(repl_val));
724
0
  memset(repl_null, false, sizeof(repl_null));
725
0
  memset(repl_repl, false, sizeof(repl_repl));
726
727
0
  parse_func_options(pstate, stmt->func_options,
728
0
             &handler_given, &fdwhandler,
729
0
             &validator_given, &fdwvalidator);
730
731
0
  if (handler_given)
732
0
  {
733
0
    repl_val[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = ObjectIdGetDatum(fdwhandler);
734
0
    repl_repl[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = true;
735
736
    /*
737
     * It could be that the behavior of accessing foreign table changes
738
     * with the new handler.  Warn about this.
739
     */
740
0
    ereport(WARNING,
741
0
        (errmsg("changing the foreign-data wrapper handler can change behavior of existing foreign tables")));
742
0
  }
743
744
0
  if (validator_given)
745
0
  {
746
0
    repl_val[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = ObjectIdGetDatum(fdwvalidator);
747
0
    repl_repl[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = true;
748
749
    /*
750
     * It could be that existing options for the FDW or dependent SERVER,
751
     * USER MAPPING or FOREIGN TABLE objects are no longer valid according
752
     * to the new validator.  Warn about this.
753
     */
754
0
    if (OidIsValid(fdwvalidator))
755
0
      ereport(WARNING,
756
0
          (errmsg("changing the foreign-data wrapper validator can cause "
757
0
              "the options for dependent objects to become invalid")));
758
0
  }
759
0
  else
760
0
  {
761
    /*
762
     * Validator is not changed, but we need it for validating options.
763
     */
764
0
    fdwvalidator = fdwForm->fdwvalidator;
765
0
  }
766
767
  /*
768
   * If options specified, validate and update.
769
   */
770
0
  if (stmt->options)
771
0
  {
772
    /* Extract the current options */
773
0
    datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
774
0
                tp,
775
0
                Anum_pg_foreign_data_wrapper_fdwoptions,
776
0
                &isnull);
777
0
    if (isnull)
778
0
      datum = PointerGetDatum(NULL);
779
780
    /* Transform the options */
781
0
    datum = transformGenericOptions(ForeignDataWrapperRelationId,
782
0
                    datum,
783
0
                    stmt->options,
784
0
                    fdwvalidator);
785
786
0
    if (DatumGetPointer(datum) != NULL)
787
0
      repl_val[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = datum;
788
0
    else
789
0
      repl_null[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
790
791
0
    repl_repl[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
792
0
  }
793
794
  /* Everything looks good - update the tuple */
795
0
  tp = heap_modify_tuple(tp, RelationGetDescr(rel),
796
0
               repl_val, repl_null, repl_repl);
797
798
0
  CatalogTupleUpdate(rel, &tp->t_self, tp);
799
800
0
  heap_freetuple(tp);
801
802
0
  ObjectAddressSet(myself, ForeignDataWrapperRelationId, fdwId);
803
804
  /* Update function dependencies if we changed them */
805
0
  if (handler_given || validator_given)
806
0
  {
807
0
    ObjectAddress referenced;
808
809
    /*
810
     * Flush all existing dependency records of this FDW on functions; we
811
     * assume there can be none other than the ones we are fixing.
812
     */
813
0
    deleteDependencyRecordsForClass(ForeignDataWrapperRelationId,
814
0
                    fdwId,
815
0
                    ProcedureRelationId,
816
0
                    DEPENDENCY_NORMAL);
817
818
    /* And build new ones. */
819
820
0
    if (OidIsValid(fdwhandler))
821
0
    {
822
0
      referenced.classId = ProcedureRelationId;
823
0
      referenced.objectId = fdwhandler;
824
0
      referenced.objectSubId = 0;
825
0
      recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
826
0
    }
827
828
0
    if (OidIsValid(fdwvalidator))
829
0
    {
830
0
      referenced.classId = ProcedureRelationId;
831
0
      referenced.objectId = fdwvalidator;
832
0
      referenced.objectSubId = 0;
833
0
      recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
834
0
    }
835
0
  }
836
837
0
  InvokeObjectPostAlterHook(ForeignDataWrapperRelationId, fdwId, 0);
838
839
0
  table_close(rel, RowExclusiveLock);
840
841
0
  return myself;
842
0
}
843
844
845
/*
846
 * Create a foreign server
847
 */
848
ObjectAddress
849
CreateForeignServer(CreateForeignServerStmt *stmt)
850
0
{
851
0
  Relation  rel;
852
0
  Datum   srvoptions;
853
0
  Datum   values[Natts_pg_foreign_server];
854
0
  bool    nulls[Natts_pg_foreign_server];
855
0
  HeapTuple tuple;
856
0
  Oid     srvId;
857
0
  Oid     ownerId;
858
0
  AclResult aclresult;
859
0
  ObjectAddress myself;
860
0
  ObjectAddress referenced;
861
0
  ForeignDataWrapper *fdw;
862
863
0
  rel = table_open(ForeignServerRelationId, RowExclusiveLock);
864
865
  /* For now the owner cannot be specified on create. Use effective user ID. */
866
0
  ownerId = GetUserId();
867
868
  /*
869
   * Check that there is no other foreign server by this name.  If there is
870
   * one, do nothing if IF NOT EXISTS was specified.
871
   */
872
0
  srvId = get_foreign_server_oid(stmt->servername, true);
873
0
  if (OidIsValid(srvId))
874
0
  {
875
0
    if (stmt->if_not_exists)
876
0
    {
877
      /*
878
       * If we are in an extension script, insist that the pre-existing
879
       * object be a member of the extension, to avoid security risks.
880
       */
881
0
      ObjectAddressSet(myself, ForeignServerRelationId, srvId);
882
0
      checkMembershipInCurrentExtension(&myself);
883
884
      /* OK to skip */
885
0
      ereport(NOTICE,
886
0
          (errcode(ERRCODE_DUPLICATE_OBJECT),
887
0
           errmsg("server \"%s\" already exists, skipping",
888
0
              stmt->servername)));
889
0
      table_close(rel, RowExclusiveLock);
890
0
      return InvalidObjectAddress;
891
0
    }
892
0
    else
893
0
      ereport(ERROR,
894
0
          (errcode(ERRCODE_DUPLICATE_OBJECT),
895
0
           errmsg("server \"%s\" already exists",
896
0
              stmt->servername)));
897
0
  }
898
899
  /*
900
   * Check that the FDW exists and that we have USAGE on it. Also get the
901
   * actual FDW for option validation etc.
902
   */
903
0
  fdw = GetForeignDataWrapperByName(stmt->fdwname, false);
904
905
0
  aclresult = object_aclcheck(ForeignDataWrapperRelationId, fdw->fdwid, ownerId, ACL_USAGE);
906
0
  if (aclresult != ACLCHECK_OK)
907
0
    aclcheck_error(aclresult, OBJECT_FDW, fdw->fdwname);
908
909
  /*
910
   * Insert tuple into pg_foreign_server.
911
   */
912
0
  memset(values, 0, sizeof(values));
913
0
  memset(nulls, false, sizeof(nulls));
914
915
0
  srvId = GetNewOidWithIndex(rel, ForeignServerOidIndexId,
916
0
                 Anum_pg_foreign_server_oid);
917
0
  values[Anum_pg_foreign_server_oid - 1] = ObjectIdGetDatum(srvId);
918
0
  values[Anum_pg_foreign_server_srvname - 1] =
919
0
    DirectFunctionCall1(namein, CStringGetDatum(stmt->servername));
920
0
  values[Anum_pg_foreign_server_srvowner - 1] = ObjectIdGetDatum(ownerId);
921
0
  values[Anum_pg_foreign_server_srvfdw - 1] = ObjectIdGetDatum(fdw->fdwid);
922
923
  /* Add server type if supplied */
924
0
  if (stmt->servertype)
925
0
    values[Anum_pg_foreign_server_srvtype - 1] =
926
0
      CStringGetTextDatum(stmt->servertype);
927
0
  else
928
0
    nulls[Anum_pg_foreign_server_srvtype - 1] = true;
929
930
  /* Add server version if supplied */
931
0
  if (stmt->version)
932
0
    values[Anum_pg_foreign_server_srvversion - 1] =
933
0
      CStringGetTextDatum(stmt->version);
934
0
  else
935
0
    nulls[Anum_pg_foreign_server_srvversion - 1] = true;
936
937
  /* Start with a blank acl */
938
0
  nulls[Anum_pg_foreign_server_srvacl - 1] = true;
939
940
  /* Add server options */
941
0
  srvoptions = transformGenericOptions(ForeignServerRelationId,
942
0
                     PointerGetDatum(NULL),
943
0
                     stmt->options,
944
0
                     fdw->fdwvalidator);
945
946
0
  if (DatumGetPointer(srvoptions) != NULL)
947
0
    values[Anum_pg_foreign_server_srvoptions - 1] = srvoptions;
948
0
  else
949
0
    nulls[Anum_pg_foreign_server_srvoptions - 1] = true;
950
951
0
  tuple = heap_form_tuple(rel->rd_att, values, nulls);
952
953
0
  CatalogTupleInsert(rel, tuple);
954
955
0
  heap_freetuple(tuple);
956
957
  /* record dependencies */
958
0
  myself.classId = ForeignServerRelationId;
959
0
  myself.objectId = srvId;
960
0
  myself.objectSubId = 0;
961
962
0
  referenced.classId = ForeignDataWrapperRelationId;
963
0
  referenced.objectId = fdw->fdwid;
964
0
  referenced.objectSubId = 0;
965
0
  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
966
967
0
  recordDependencyOnOwner(ForeignServerRelationId, srvId, ownerId);
968
969
  /* dependency on extension */
970
0
  recordDependencyOnCurrentExtension(&myself, false);
971
972
  /* Post creation hook for new foreign server */
973
0
  InvokeObjectPostCreateHook(ForeignServerRelationId, srvId, 0);
974
975
0
  table_close(rel, RowExclusiveLock);
976
977
0
  return myself;
978
0
}
979
980
981
/*
982
 * Alter foreign server
983
 */
984
ObjectAddress
985
AlterForeignServer(AlterForeignServerStmt *stmt)
986
0
{
987
0
  Relation  rel;
988
0
  HeapTuple tp;
989
0
  Datum   repl_val[Natts_pg_foreign_server];
990
0
  bool    repl_null[Natts_pg_foreign_server];
991
0
  bool    repl_repl[Natts_pg_foreign_server];
992
0
  Oid     srvId;
993
0
  Form_pg_foreign_server srvForm;
994
0
  ObjectAddress address;
995
996
0
  rel = table_open(ForeignServerRelationId, RowExclusiveLock);
997
998
0
  tp = SearchSysCacheCopy1(FOREIGNSERVERNAME,
999
0
               CStringGetDatum(stmt->servername));
1000
1001
0
  if (!HeapTupleIsValid(tp))
1002
0
    ereport(ERROR,
1003
0
        (errcode(ERRCODE_UNDEFINED_OBJECT),
1004
0
         errmsg("server \"%s\" does not exist", stmt->servername)));
1005
1006
0
  srvForm = (Form_pg_foreign_server) GETSTRUCT(tp);
1007
0
  srvId = srvForm->oid;
1008
1009
  /*
1010
   * Only owner or a superuser can ALTER a SERVER.
1011
   */
1012
0
  if (!object_ownercheck(ForeignServerRelationId, srvId, GetUserId()))
1013
0
    aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FOREIGN_SERVER,
1014
0
             stmt->servername);
1015
1016
0
  memset(repl_val, 0, sizeof(repl_val));
1017
0
  memset(repl_null, false, sizeof(repl_null));
1018
0
  memset(repl_repl, false, sizeof(repl_repl));
1019
1020
0
  if (stmt->has_version)
1021
0
  {
1022
    /*
1023
     * Change the server VERSION string.
1024
     */
1025
0
    if (stmt->version)
1026
0
      repl_val[Anum_pg_foreign_server_srvversion - 1] =
1027
0
        CStringGetTextDatum(stmt->version);
1028
0
    else
1029
0
      repl_null[Anum_pg_foreign_server_srvversion - 1] = true;
1030
1031
0
    repl_repl[Anum_pg_foreign_server_srvversion - 1] = true;
1032
0
  }
1033
1034
0
  if (stmt->options)
1035
0
  {
1036
0
    ForeignDataWrapper *fdw = GetForeignDataWrapper(srvForm->srvfdw);
1037
0
    Datum   datum;
1038
0
    bool    isnull;
1039
1040
    /* Extract the current srvoptions */
1041
0
    datum = SysCacheGetAttr(FOREIGNSERVEROID,
1042
0
                tp,
1043
0
                Anum_pg_foreign_server_srvoptions,
1044
0
                &isnull);
1045
0
    if (isnull)
1046
0
      datum = PointerGetDatum(NULL);
1047
1048
    /* Prepare the options array */
1049
0
    datum = transformGenericOptions(ForeignServerRelationId,
1050
0
                    datum,
1051
0
                    stmt->options,
1052
0
                    fdw->fdwvalidator);
1053
1054
0
    if (DatumGetPointer(datum) != NULL)
1055
0
      repl_val[Anum_pg_foreign_server_srvoptions - 1] = datum;
1056
0
    else
1057
0
      repl_null[Anum_pg_foreign_server_srvoptions - 1] = true;
1058
1059
0
    repl_repl[Anum_pg_foreign_server_srvoptions - 1] = true;
1060
0
  }
1061
1062
  /* Everything looks good - update the tuple */
1063
0
  tp = heap_modify_tuple(tp, RelationGetDescr(rel),
1064
0
               repl_val, repl_null, repl_repl);
1065
1066
0
  CatalogTupleUpdate(rel, &tp->t_self, tp);
1067
1068
0
  InvokeObjectPostAlterHook(ForeignServerRelationId, srvId, 0);
1069
1070
0
  ObjectAddressSet(address, ForeignServerRelationId, srvId);
1071
1072
0
  heap_freetuple(tp);
1073
1074
0
  table_close(rel, RowExclusiveLock);
1075
1076
0
  return address;
1077
0
}
1078
1079
1080
/*
1081
 * Common routine to check permission for user-mapping-related DDL
1082
 * commands.  We allow server owners to operate on any mapping, and
1083
 * users to operate on their own mapping.
1084
 */
1085
static void
1086
user_mapping_ddl_aclcheck(Oid umuserid, Oid serverid, const char *servername)
1087
0
{
1088
0
  Oid     curuserid = GetUserId();
1089
1090
0
  if (!object_ownercheck(ForeignServerRelationId, serverid, curuserid))
1091
0
  {
1092
0
    if (umuserid == curuserid)
1093
0
    {
1094
0
      AclResult aclresult;
1095
1096
0
      aclresult = object_aclcheck(ForeignServerRelationId, serverid, curuserid, ACL_USAGE);
1097
0
      if (aclresult != ACLCHECK_OK)
1098
0
        aclcheck_error(aclresult, OBJECT_FOREIGN_SERVER, servername);
1099
0
    }
1100
0
    else
1101
0
      aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FOREIGN_SERVER,
1102
0
               servername);
1103
0
  }
1104
0
}
1105
1106
1107
/*
1108
 * Create user mapping
1109
 */
1110
ObjectAddress
1111
CreateUserMapping(CreateUserMappingStmt *stmt)
1112
0
{
1113
0
  Relation  rel;
1114
0
  Datum   useoptions;
1115
0
  Datum   values[Natts_pg_user_mapping];
1116
0
  bool    nulls[Natts_pg_user_mapping];
1117
0
  HeapTuple tuple;
1118
0
  Oid     useId;
1119
0
  Oid     umId;
1120
0
  ObjectAddress myself;
1121
0
  ObjectAddress referenced;
1122
0
  ForeignServer *srv;
1123
0
  ForeignDataWrapper *fdw;
1124
0
  RoleSpec   *role = (RoleSpec *) stmt->user;
1125
1126
0
  rel = table_open(UserMappingRelationId, RowExclusiveLock);
1127
1128
0
  if (role->roletype == ROLESPEC_PUBLIC)
1129
0
    useId = ACL_ID_PUBLIC;
1130
0
  else
1131
0
    useId = get_rolespec_oid(stmt->user, false);
1132
1133
  /* Check that the server exists. */
1134
0
  srv = GetForeignServerByName(stmt->servername, false);
1135
1136
0
  user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername);
1137
1138
  /*
1139
   * Check that the user mapping is unique within server.
1140
   */
1141
0
  umId = GetSysCacheOid2(USERMAPPINGUSERSERVER, Anum_pg_user_mapping_oid,
1142
0
               ObjectIdGetDatum(useId),
1143
0
               ObjectIdGetDatum(srv->serverid));
1144
1145
0
  if (OidIsValid(umId))
1146
0
  {
1147
0
    if (stmt->if_not_exists)
1148
0
    {
1149
      /*
1150
       * Since user mappings aren't members of extensions (see comments
1151
       * below), no need for checkMembershipInCurrentExtension here.
1152
       */
1153
0
      ereport(NOTICE,
1154
0
          (errcode(ERRCODE_DUPLICATE_OBJECT),
1155
0
           errmsg("user mapping for \"%s\" already exists for server \"%s\", skipping",
1156
0
              MappingUserName(useId),
1157
0
              stmt->servername)));
1158
1159
0
      table_close(rel, RowExclusiveLock);
1160
0
      return InvalidObjectAddress;
1161
0
    }
1162
0
    else
1163
0
      ereport(ERROR,
1164
0
          (errcode(ERRCODE_DUPLICATE_OBJECT),
1165
0
           errmsg("user mapping for \"%s\" already exists for server \"%s\"",
1166
0
              MappingUserName(useId),
1167
0
              stmt->servername)));
1168
0
  }
1169
1170
0
  fdw = GetForeignDataWrapper(srv->fdwid);
1171
1172
  /*
1173
   * Insert tuple into pg_user_mapping.
1174
   */
1175
0
  memset(values, 0, sizeof(values));
1176
0
  memset(nulls, false, sizeof(nulls));
1177
1178
0
  umId = GetNewOidWithIndex(rel, UserMappingOidIndexId,
1179
0
                Anum_pg_user_mapping_oid);
1180
0
  values[Anum_pg_user_mapping_oid - 1] = ObjectIdGetDatum(umId);
1181
0
  values[Anum_pg_user_mapping_umuser - 1] = ObjectIdGetDatum(useId);
1182
0
  values[Anum_pg_user_mapping_umserver - 1] = ObjectIdGetDatum(srv->serverid);
1183
1184
  /* Add user options */
1185
0
  useoptions = transformGenericOptions(UserMappingRelationId,
1186
0
                     PointerGetDatum(NULL),
1187
0
                     stmt->options,
1188
0
                     fdw->fdwvalidator);
1189
1190
0
  if (DatumGetPointer(useoptions) != NULL)
1191
0
    values[Anum_pg_user_mapping_umoptions - 1] = useoptions;
1192
0
  else
1193
0
    nulls[Anum_pg_user_mapping_umoptions - 1] = true;
1194
1195
0
  tuple = heap_form_tuple(rel->rd_att, values, nulls);
1196
1197
0
  CatalogTupleInsert(rel, tuple);
1198
1199
0
  heap_freetuple(tuple);
1200
1201
  /* Add dependency on the server */
1202
0
  myself.classId = UserMappingRelationId;
1203
0
  myself.objectId = umId;
1204
0
  myself.objectSubId = 0;
1205
1206
0
  referenced.classId = ForeignServerRelationId;
1207
0
  referenced.objectId = srv->serverid;
1208
0
  referenced.objectSubId = 0;
1209
0
  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1210
1211
0
  if (OidIsValid(useId))
1212
0
  {
1213
    /* Record the mapped user dependency */
1214
0
    recordDependencyOnOwner(UserMappingRelationId, umId, useId);
1215
0
  }
1216
1217
  /*
1218
   * Perhaps someday there should be a recordDependencyOnCurrentExtension
1219
   * call here; but since roles aren't members of extensions, it seems like
1220
   * user mappings shouldn't be either.  Note that the grammar and pg_dump
1221
   * would need to be extended too if we change this.
1222
   */
1223
1224
  /* Post creation hook for new user mapping */
1225
0
  InvokeObjectPostCreateHook(UserMappingRelationId, umId, 0);
1226
1227
0
  table_close(rel, RowExclusiveLock);
1228
1229
0
  return myself;
1230
0
}
1231
1232
1233
/*
1234
 * Alter user mapping
1235
 */
1236
ObjectAddress
1237
AlterUserMapping(AlterUserMappingStmt *stmt)
1238
0
{
1239
0
  Relation  rel;
1240
0
  HeapTuple tp;
1241
0
  Datum   repl_val[Natts_pg_user_mapping];
1242
0
  bool    repl_null[Natts_pg_user_mapping];
1243
0
  bool    repl_repl[Natts_pg_user_mapping];
1244
0
  Oid     useId;
1245
0
  Oid     umId;
1246
0
  ForeignServer *srv;
1247
0
  ObjectAddress address;
1248
0
  RoleSpec   *role = (RoleSpec *) stmt->user;
1249
1250
0
  rel = table_open(UserMappingRelationId, RowExclusiveLock);
1251
1252
0
  if (role->roletype == ROLESPEC_PUBLIC)
1253
0
    useId = ACL_ID_PUBLIC;
1254
0
  else
1255
0
    useId = get_rolespec_oid(stmt->user, false);
1256
1257
0
  srv = GetForeignServerByName(stmt->servername, false);
1258
1259
0
  umId = GetSysCacheOid2(USERMAPPINGUSERSERVER, Anum_pg_user_mapping_oid,
1260
0
               ObjectIdGetDatum(useId),
1261
0
               ObjectIdGetDatum(srv->serverid));
1262
0
  if (!OidIsValid(umId))
1263
0
    ereport(ERROR,
1264
0
        (errcode(ERRCODE_UNDEFINED_OBJECT),
1265
0
         errmsg("user mapping for \"%s\" does not exist for server \"%s\"",
1266
0
            MappingUserName(useId), stmt->servername)));
1267
1268
0
  user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername);
1269
1270
0
  tp = SearchSysCacheCopy1(USERMAPPINGOID, ObjectIdGetDatum(umId));
1271
1272
0
  if (!HeapTupleIsValid(tp))
1273
0
    elog(ERROR, "cache lookup failed for user mapping %u", umId);
1274
1275
0
  memset(repl_val, 0, sizeof(repl_val));
1276
0
  memset(repl_null, false, sizeof(repl_null));
1277
0
  memset(repl_repl, false, sizeof(repl_repl));
1278
1279
0
  if (stmt->options)
1280
0
  {
1281
0
    ForeignDataWrapper *fdw;
1282
0
    Datum   datum;
1283
0
    bool    isnull;
1284
1285
    /*
1286
     * Process the options.
1287
     */
1288
1289
0
    fdw = GetForeignDataWrapper(srv->fdwid);
1290
1291
0
    datum = SysCacheGetAttr(USERMAPPINGUSERSERVER,
1292
0
                tp,
1293
0
                Anum_pg_user_mapping_umoptions,
1294
0
                &isnull);
1295
0
    if (isnull)
1296
0
      datum = PointerGetDatum(NULL);
1297
1298
    /* Prepare the options array */
1299
0
    datum = transformGenericOptions(UserMappingRelationId,
1300
0
                    datum,
1301
0
                    stmt->options,
1302
0
                    fdw->fdwvalidator);
1303
1304
0
    if (DatumGetPointer(datum) != NULL)
1305
0
      repl_val[Anum_pg_user_mapping_umoptions - 1] = datum;
1306
0
    else
1307
0
      repl_null[Anum_pg_user_mapping_umoptions - 1] = true;
1308
1309
0
    repl_repl[Anum_pg_user_mapping_umoptions - 1] = true;
1310
0
  }
1311
1312
  /* Everything looks good - update the tuple */
1313
0
  tp = heap_modify_tuple(tp, RelationGetDescr(rel),
1314
0
               repl_val, repl_null, repl_repl);
1315
1316
0
  CatalogTupleUpdate(rel, &tp->t_self, tp);
1317
1318
0
  InvokeObjectPostAlterHook(UserMappingRelationId,
1319
0
                umId, 0);
1320
1321
0
  ObjectAddressSet(address, UserMappingRelationId, umId);
1322
1323
0
  heap_freetuple(tp);
1324
1325
0
  table_close(rel, RowExclusiveLock);
1326
1327
0
  return address;
1328
0
}
1329
1330
1331
/*
1332
 * Drop user mapping
1333
 */
1334
Oid
1335
RemoveUserMapping(DropUserMappingStmt *stmt)
1336
{
1337
  ObjectAddress object;
1338
  Oid     useId;
1339
  Oid     umId;
1340
  ForeignServer *srv;
1341
  RoleSpec   *role = (RoleSpec *) stmt->user;
1342
1343
  if (role->roletype == ROLESPEC_PUBLIC)
1344
    useId = ACL_ID_PUBLIC;
1345
  else
1346
  {
1347
    useId = get_rolespec_oid(stmt->user, stmt->missing_ok);
1348
    if (!OidIsValid(useId))
1349
    {
1350
      /*
1351
       * IF EXISTS specified, role not found and not public. Notice this
1352
       * and leave.
1353
       */
1354
      elog(NOTICE, "role \"%s\" does not exist, skipping",
1355
         role->rolename);
1356
      return InvalidOid;
1357
    }
1358
  }
1359
1360
  srv = GetForeignServerByName(stmt->servername, true);
1361
1362
  if (!srv)
1363
  {
1364
    if (!stmt->missing_ok)
1365
      ereport(ERROR,
1366
          (errcode(ERRCODE_UNDEFINED_OBJECT),
1367
           errmsg("server \"%s\" does not exist",
1368
              stmt->servername)));
1369
    /* IF EXISTS, just note it */
1370
    ereport(NOTICE,
1371
        (errmsg("server \"%s\" does not exist, skipping",
1372
            stmt->servername)));
1373
    return InvalidOid;
1374
  }
1375
1376
  umId = GetSysCacheOid2(USERMAPPINGUSERSERVER, Anum_pg_user_mapping_oid,
1377
               ObjectIdGetDatum(useId),
1378
               ObjectIdGetDatum(srv->serverid));
1379
1380
  if (!OidIsValid(umId))
1381
  {
1382
    if (!stmt->missing_ok)
1383
      ereport(ERROR,
1384
          (errcode(ERRCODE_UNDEFINED_OBJECT),
1385
           errmsg("user mapping for \"%s\" does not exist for server \"%s\"",
1386
              MappingUserName(useId), stmt->servername)));
1387
1388
    /* IF EXISTS specified, just note it */
1389
    ereport(NOTICE,
1390
        (errmsg("user mapping for \"%s\" does not exist for server \"%s\", skipping",
1391
            MappingUserName(useId), stmt->servername)));
1392
    return InvalidOid;
1393
  }
1394
1395
  user_mapping_ddl_aclcheck(useId, srv->serverid, srv->servername);
1396
1397
  /*
1398
   * Do the deletion
1399
   */
1400
  object.classId = UserMappingRelationId;
1401
  object.objectId = umId;
1402
  object.objectSubId = 0;
1403
1404
  performDeletion(&object, DROP_CASCADE, 0);
1405
1406
  return umId;
1407
}
1408
1409
1410
/*
1411
 * Create a foreign table
1412
 * call after DefineRelation().
1413
 */
1414
void
1415
CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid)
1416
0
{
1417
0
  Relation  ftrel;
1418
0
  Datum   ftoptions;
1419
0
  Datum   values[Natts_pg_foreign_table];
1420
0
  bool    nulls[Natts_pg_foreign_table];
1421
0
  HeapTuple tuple;
1422
0
  AclResult aclresult;
1423
0
  ObjectAddress myself;
1424
0
  ObjectAddress referenced;
1425
0
  Oid     ownerId;
1426
0
  ForeignDataWrapper *fdw;
1427
0
  ForeignServer *server;
1428
1429
  /*
1430
   * Advance command counter to ensure the pg_attribute tuple is visible;
1431
   * the tuple might be updated to add constraints in previous step.
1432
   */
1433
0
  CommandCounterIncrement();
1434
1435
0
  ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
1436
1437
  /*
1438
   * For now the owner cannot be specified on create. Use effective user ID.
1439
   */
1440
0
  ownerId = GetUserId();
1441
1442
  /*
1443
   * Check that the foreign server exists and that we have USAGE on it. Also
1444
   * get the actual FDW for option validation etc.
1445
   */
1446
0
  server = GetForeignServerByName(stmt->servername, false);
1447
0
  aclresult = object_aclcheck(ForeignServerRelationId, server->serverid, ownerId, ACL_USAGE);
1448
0
  if (aclresult != ACLCHECK_OK)
1449
0
    aclcheck_error(aclresult, OBJECT_FOREIGN_SERVER, server->servername);
1450
1451
0
  fdw = GetForeignDataWrapper(server->fdwid);
1452
1453
  /*
1454
   * Insert tuple into pg_foreign_table.
1455
   */
1456
0
  memset(values, 0, sizeof(values));
1457
0
  memset(nulls, false, sizeof(nulls));
1458
1459
0
  values[Anum_pg_foreign_table_ftrelid - 1] = ObjectIdGetDatum(relid);
1460
0
  values[Anum_pg_foreign_table_ftserver - 1] = ObjectIdGetDatum(server->serverid);
1461
  /* Add table generic options */
1462
0
  ftoptions = transformGenericOptions(ForeignTableRelationId,
1463
0
                    PointerGetDatum(NULL),
1464
0
                    stmt->options,
1465
0
                    fdw->fdwvalidator);
1466
1467
0
  if (DatumGetPointer(ftoptions) != NULL)
1468
0
    values[Anum_pg_foreign_table_ftoptions - 1] = ftoptions;
1469
0
  else
1470
0
    nulls[Anum_pg_foreign_table_ftoptions - 1] = true;
1471
1472
0
  tuple = heap_form_tuple(ftrel->rd_att, values, nulls);
1473
1474
0
  CatalogTupleInsert(ftrel, tuple);
1475
1476
0
  heap_freetuple(tuple);
1477
1478
  /* Add pg_class dependency on the server */
1479
0
  myself.classId = RelationRelationId;
1480
0
  myself.objectId = relid;
1481
0
  myself.objectSubId = 0;
1482
1483
0
  referenced.classId = ForeignServerRelationId;
1484
0
  referenced.objectId = server->serverid;
1485
0
  referenced.objectSubId = 0;
1486
0
  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1487
1488
0
  table_close(ftrel, RowExclusiveLock);
1489
0
}
1490
1491
/*
1492
 * Import a foreign schema
1493
 */
1494
void
1495
ImportForeignSchema(ImportForeignSchemaStmt *stmt)
1496
0
{
1497
0
  ForeignServer *server;
1498
0
  ForeignDataWrapper *fdw;
1499
0
  FdwRoutine *fdw_routine;
1500
0
  AclResult aclresult;
1501
0
  List     *cmd_list;
1502
0
  ListCell   *lc;
1503
1504
  /* Check that the foreign server exists and that we have USAGE on it */
1505
0
  server = GetForeignServerByName(stmt->server_name, false);
1506
0
  aclresult = object_aclcheck(ForeignServerRelationId, server->serverid, GetUserId(), ACL_USAGE);
1507
0
  if (aclresult != ACLCHECK_OK)
1508
0
    aclcheck_error(aclresult, OBJECT_FOREIGN_SERVER, server->servername);
1509
1510
  /* Check that the schema exists and we have CREATE permissions on it */
1511
0
  (void) LookupCreationNamespace(stmt->local_schema);
1512
1513
  /* Get the FDW and check it supports IMPORT */
1514
0
  fdw = GetForeignDataWrapper(server->fdwid);
1515
0
  if (!OidIsValid(fdw->fdwhandler))
1516
0
    ereport(ERROR,
1517
0
        (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1518
0
         errmsg("foreign-data wrapper \"%s\" has no handler",
1519
0
            fdw->fdwname)));
1520
0
  fdw_routine = GetFdwRoutine(fdw->fdwhandler);
1521
0
  if (fdw_routine->ImportForeignSchema == NULL)
1522
0
    ereport(ERROR,
1523
0
        (errcode(ERRCODE_FDW_NO_SCHEMAS),
1524
0
         errmsg("foreign-data wrapper \"%s\" does not support IMPORT FOREIGN SCHEMA",
1525
0
            fdw->fdwname)));
1526
1527
  /* Call FDW to get a list of commands */
1528
0
  cmd_list = fdw_routine->ImportForeignSchema(stmt, server->serverid);
1529
1530
  /* Parse and execute each command */
1531
0
  foreach(lc, cmd_list)
1532
0
  {
1533
0
    char     *cmd = (char *) lfirst(lc);
1534
0
    import_error_callback_arg callback_arg;
1535
0
    ErrorContextCallback sqlerrcontext;
1536
0
    List     *raw_parsetree_list;
1537
0
    ListCell   *lc2;
1538
1539
    /*
1540
     * Setup error traceback support for ereport().  This is so that any
1541
     * error in the generated SQL will be displayed nicely.
1542
     */
1543
0
    callback_arg.tablename = NULL; /* not known yet */
1544
0
    callback_arg.cmd = cmd;
1545
0
    sqlerrcontext.callback = import_error_callback;
1546
0
    sqlerrcontext.arg = &callback_arg;
1547
0
    sqlerrcontext.previous = error_context_stack;
1548
0
    error_context_stack = &sqlerrcontext;
1549
1550
    /*
1551
     * Parse the SQL string into a list of raw parse trees.
1552
     */
1553
0
    raw_parsetree_list = pg_parse_query(cmd);
1554
1555
    /*
1556
     * Process each parse tree (we allow the FDW to put more than one
1557
     * command per string, though this isn't really advised).
1558
     */
1559
0
    foreach(lc2, raw_parsetree_list)
1560
0
    {
1561
0
      RawStmt    *rs = lfirst_node(RawStmt, lc2);
1562
0
      CreateForeignTableStmt *cstmt = (CreateForeignTableStmt *) rs->stmt;
1563
0
      PlannedStmt *pstmt;
1564
1565
      /*
1566
       * Because we only allow CreateForeignTableStmt, we can skip parse
1567
       * analysis, rewrite, and planning steps here.
1568
       */
1569
0
      if (!IsA(cstmt, CreateForeignTableStmt))
1570
0
        elog(ERROR,
1571
0
           "foreign-data wrapper \"%s\" returned incorrect statement type %d",
1572
0
           fdw->fdwname, (int) nodeTag(cstmt));
1573
1574
      /* Ignore commands for tables excluded by filter options */
1575
0
      if (!IsImportableForeignTable(cstmt->base.relation->relname, stmt))
1576
0
        continue;
1577
1578
      /* Enable reporting of current table's name on error */
1579
0
      callback_arg.tablename = cstmt->base.relation->relname;
1580
1581
      /* Ensure creation schema is the one given in IMPORT statement */
1582
0
      cstmt->base.relation->schemaname = pstrdup(stmt->local_schema);
1583
1584
      /* No planning needed, just make a wrapper PlannedStmt */
1585
0
      pstmt = makeNode(PlannedStmt);
1586
0
      pstmt->commandType = CMD_UTILITY;
1587
0
      pstmt->canSetTag = false;
1588
0
      pstmt->utilityStmt = (Node *) cstmt;
1589
0
      pstmt->stmt_location = rs->stmt_location;
1590
0
      pstmt->stmt_len = rs->stmt_len;
1591
0
      pstmt->planOrigin = PLAN_STMT_INTERNAL;
1592
1593
      /* Execute statement */
1594
0
      ProcessUtility(pstmt, cmd, false,
1595
0
               PROCESS_UTILITY_SUBCOMMAND, NULL, NULL,
1596
0
               None_Receiver, NULL);
1597
1598
      /* Be sure to advance the command counter between subcommands */
1599
0
      CommandCounterIncrement();
1600
1601
0
      callback_arg.tablename = NULL;
1602
0
    }
1603
1604
0
    error_context_stack = sqlerrcontext.previous;
1605
0
  }
1606
0
}
1607
1608
/*
1609
 * error context callback to let us supply the failing SQL statement's text
1610
 */
1611
static void
1612
import_error_callback(void *arg)
1613
0
{
1614
0
  import_error_callback_arg *callback_arg = (import_error_callback_arg *) arg;
1615
0
  int     syntaxerrposition;
1616
1617
  /* If it's a syntax error, convert to internal syntax error report */
1618
0
  syntaxerrposition = geterrposition();
1619
0
  if (syntaxerrposition > 0)
1620
0
  {
1621
0
    errposition(0);
1622
0
    internalerrposition(syntaxerrposition);
1623
0
    internalerrquery(callback_arg->cmd);
1624
0
  }
1625
1626
0
  if (callback_arg->tablename)
1627
0
    errcontext("importing foreign table \"%s\"",
1628
0
           callback_arg->tablename);
1629
0
}