2008-12-19 17:25:19 +01:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* foreigncmds.c
|
|
|
|
* foreign-data wrapper/server creation/manipulation commands
|
|
|
|
*
|
2015-01-06 17:43:47 +01:00
|
|
|
* Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
|
2008-12-19 17:25:19 +01:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2010-09-20 22:08:53 +02:00
|
|
|
* src/backend/commands/foreigncmds.c
|
2008-12-19 17:25:19 +01:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
|
|
|
|
#include "access/heapam.h"
|
2012-08-30 22:15:44 +02:00
|
|
|
#include "access/htup_details.h"
|
2008-12-19 17:25:19 +01:00
|
|
|
#include "access/reloptions.h"
|
2014-07-10 21:01:31 +02:00
|
|
|
#include "access/xact.h"
|
2008-12-19 17:25:19 +01:00
|
|
|
#include "catalog/dependency.h"
|
|
|
|
#include "catalog/indexing.h"
|
2010-11-25 17:48:49 +01:00
|
|
|
#include "catalog/objectaccess.h"
|
2008-12-19 17:25:19 +01:00
|
|
|
#include "catalog/pg_foreign_data_wrapper.h"
|
|
|
|
#include "catalog/pg_foreign_server.h"
|
2011-01-02 05:48:11 +01:00
|
|
|
#include "catalog/pg_foreign_table.h"
|
2009-02-24 11:06:36 +01:00
|
|
|
#include "catalog/pg_proc.h"
|
2008-12-19 17:25:19 +01:00
|
|
|
#include "catalog/pg_type.h"
|
|
|
|
#include "catalog/pg_user_mapping.h"
|
|
|
|
#include "commands/defrem.h"
|
2014-07-10 21:01:31 +02:00
|
|
|
#include "foreign/fdwapi.h"
|
2008-12-19 17:25:19 +01:00
|
|
|
#include "foreign/foreign.h"
|
|
|
|
#include "miscadmin.h"
|
2009-02-24 11:06:36 +01:00
|
|
|
#include "parser/parse_func.h"
|
2014-07-10 21:01:31 +02:00
|
|
|
#include "tcop/utility.h"
|
2008-12-19 17:25:19 +01:00
|
|
|
#include "utils/acl.h"
|
|
|
|
#include "utils/builtins.h"
|
|
|
|
#include "utils/lsyscache.h"
|
|
|
|
#include "utils/rel.h"
|
|
|
|
#include "utils/syscache.h"
|
|
|
|
|
|
|
|
|
2014-07-10 21:01:31 +02:00
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
char *tablename;
|
|
|
|
char *cmd;
|
|
|
|
} import_error_callback_arg;
|
|
|
|
|
|
|
|
/* Internal functions */
|
|
|
|
static void import_error_callback(void *arg);
|
|
|
|
|
|
|
|
|
2008-12-19 17:25:19 +01:00
|
|
|
/*
|
|
|
|
* Convert a DefElem list to the text array format that is used in
|
2013-10-28 15:28:35 +01:00
|
|
|
* pg_foreign_data_wrapper, pg_foreign_server, pg_user_mapping, and
|
|
|
|
* pg_foreign_table.
|
|
|
|
*
|
2008-12-20 16:51:28 +01:00
|
|
|
* Returns the array in the form of a Datum, or PointerGetDatum(NULL)
|
|
|
|
* if the list is empty.
|
2008-12-19 17:25:19 +01:00
|
|
|
*
|
|
|
|
* Note: The array is usually stored to database without further
|
|
|
|
* processing, hence any validation should be done before this
|
|
|
|
* conversion.
|
|
|
|
*/
|
|
|
|
static Datum
|
|
|
|
optionListToArray(List *options)
|
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
ArrayBuildState *astate = NULL;
|
|
|
|
ListCell *cell;
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2008-12-20 16:51:28 +01:00
|
|
|
foreach(cell, options)
|
2008-12-19 17:25:19 +01:00
|
|
|
{
|
|
|
|
DefElem *def = lfirst(cell);
|
2008-12-20 16:51:28 +01:00
|
|
|
const char *value;
|
2008-12-19 17:25:19 +01:00
|
|
|
Size len;
|
2008-12-20 16:51:28 +01:00
|
|
|
text *t;
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
value = defGetString(def);
|
|
|
|
len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
|
|
|
|
t = palloc(len + 1);
|
|
|
|
SET_VARSIZE(t, len);
|
|
|
|
sprintf(VARDATA(t), "%s=%s", def->defname, value);
|
|
|
|
|
|
|
|
astate = accumArrayResult(astate, PointerGetDatum(t),
|
|
|
|
false, TEXTOID,
|
|
|
|
CurrentMemoryContext);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (astate)
|
|
|
|
return makeArrayResult(astate, CurrentMemoryContext);
|
|
|
|
|
|
|
|
return PointerGetDatum(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2014-05-06 18:12:18 +02:00
|
|
|
* Transform a list of DefElem into text array format. This is substantially
|
2009-04-04 23:12:31 +02:00
|
|
|
* the same thing as optionListToArray(), except we recognize SET/ADD/DROP
|
|
|
|
* actions for modifying an existing list of options, which is passed in
|
|
|
|
* Datum form as oldOptions. Also, if fdwvalidator isn't InvalidOid
|
|
|
|
* it specifies a validator function to call on the result.
|
2008-12-19 17:25:19 +01:00
|
|
|
*
|
2008-12-20 16:51:28 +01:00
|
|
|
* Returns the array in the form of a Datum, or PointerGetDatum(NULL)
|
|
|
|
* if the list is empty.
|
|
|
|
*
|
2013-10-28 15:28:35 +01:00
|
|
|
* This is used by CREATE/ALTER of FOREIGN DATA WRAPPER/SERVER/USER MAPPING/
|
|
|
|
* FOREIGN TABLE.
|
2008-12-19 17:25:19 +01:00
|
|
|
*/
|
2011-01-02 05:48:11 +01:00
|
|
|
Datum
|
2009-12-23 13:23:59 +01:00
|
|
|
transformGenericOptions(Oid catalogId,
|
|
|
|
Datum oldOptions,
|
2009-04-04 23:12:31 +02:00
|
|
|
List *options,
|
2009-02-24 11:06:36 +01:00
|
|
|
Oid fdwvalidator)
|
2008-12-19 17:25:19 +01:00
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
List *resultOptions = untransformRelOptions(oldOptions);
|
|
|
|
ListCell *optcell;
|
|
|
|
Datum result;
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2009-04-04 23:12:31 +02:00
|
|
|
foreach(optcell, options)
|
2008-12-19 17:25:19 +01:00
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
DefElem *od = lfirst(optcell);
|
2009-04-04 23:12:31 +02:00
|
|
|
ListCell *cell;
|
|
|
|
ListCell *prev = NULL;
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
/*
|
2009-06-11 16:49:15 +02:00
|
|
|
* Find the element in resultOptions. We need this for validation in
|
|
|
|
* all cases. Also identify the previous element.
|
2008-12-19 17:25:19 +01:00
|
|
|
*/
|
2009-06-11 16:49:15 +02:00
|
|
|
foreach(cell, resultOptions)
|
2008-12-19 17:25:19 +01:00
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
DefElem *def = lfirst(cell);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2009-04-04 23:12:31 +02:00
|
|
|
if (strcmp(def->defname, od->defname) == 0)
|
2008-12-19 17:25:19 +01:00
|
|
|
break;
|
|
|
|
else
|
|
|
|
prev = cell;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2009-06-11 16:49:15 +02:00
|
|
|
* It is possible to perform multiple SET/DROP actions on the same
|
2014-05-06 18:12:18 +02:00
|
|
|
* option. The standard permits this, as long as the options to be
|
2009-06-11 16:49:15 +02:00
|
|
|
* added are unique. Note that an unspecified action is taken to be
|
|
|
|
* ADD.
|
2008-12-19 17:25:19 +01:00
|
|
|
*/
|
2009-04-04 23:12:31 +02:00
|
|
|
switch (od->defaction)
|
2008-12-19 17:25:19 +01:00
|
|
|
{
|
2009-04-04 23:12:31 +02:00
|
|
|
case DEFELEM_DROP:
|
2008-12-19 17:25:19 +01:00
|
|
|
if (!cell)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
|
|
errmsg("option \"%s\" not found",
|
2009-04-04 23:12:31 +02:00
|
|
|
od->defname)));
|
2008-12-19 17:25:19 +01:00
|
|
|
resultOptions = list_delete_cell(resultOptions, cell, prev);
|
|
|
|
break;
|
|
|
|
|
2009-04-04 23:12:31 +02:00
|
|
|
case DEFELEM_SET:
|
2008-12-19 17:25:19 +01:00
|
|
|
if (!cell)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
|
|
errmsg("option \"%s\" not found",
|
2009-04-04 23:12:31 +02:00
|
|
|
od->defname)));
|
|
|
|
lfirst(cell) = od;
|
2008-12-19 17:25:19 +01:00
|
|
|
break;
|
|
|
|
|
2009-04-04 23:12:31 +02:00
|
|
|
case DEFELEM_ADD:
|
|
|
|
case DEFELEM_UNSPEC:
|
2008-12-19 17:25:19 +01:00
|
|
|
if (cell)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
|
|
|
errmsg("option \"%s\" provided more than once",
|
2009-04-04 23:12:31 +02:00
|
|
|
od->defname)));
|
|
|
|
resultOptions = lappend(resultOptions, od);
|
2008-12-19 17:25:19 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
elog(ERROR, "unrecognized action %d on option \"%s\"",
|
2009-04-04 23:12:31 +02:00
|
|
|
(int) od->defaction, od->defname);
|
2008-12-19 17:25:19 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-02-24 11:06:36 +01:00
|
|
|
result = optionListToArray(resultOptions);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2011-07-06 00:21:12 +02:00
|
|
|
if (OidIsValid(fdwvalidator))
|
|
|
|
{
|
2012-06-10 21:20:04 +02:00
|
|
|
Datum valarg = result;
|
2011-07-06 00:21:12 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Pass a null options list as an empty array, so that validators
|
|
|
|
* don't have to be declared non-strict to handle the case.
|
|
|
|
*/
|
|
|
|
if (DatumGetPointer(valarg) == NULL)
|
|
|
|
valarg = PointerGetDatum(construct_empty_array(TEXTOID));
|
|
|
|
OidFunctionCall2(fdwvalidator, valarg, ObjectIdGetDatum(catalogId));
|
|
|
|
}
|
2009-02-24 11:06:36 +01:00
|
|
|
|
|
|
|
return result;
|
2008-12-19 17:25:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2012-02-21 21:58:02 +01:00
|
|
|
* Internal workhorse for changing a data wrapper's owner.
|
2008-12-19 17:25:19 +01:00
|
|
|
*
|
|
|
|
* Allow this only for superusers; also the new owner must be a
|
|
|
|
* superuser.
|
|
|
|
*/
|
2012-02-21 21:58:02 +01:00
|
|
|
static void
|
|
|
|
AlterForeignDataWrapperOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
|
2008-12-19 17:25:19 +01:00
|
|
|
{
|
|
|
|
Form_pg_foreign_data_wrapper form;
|
2015-01-22 18:36:34 +01:00
|
|
|
Datum repl_val[Natts_pg_foreign_data_wrapper];
|
|
|
|
bool repl_null[Natts_pg_foreign_data_wrapper];
|
|
|
|
bool repl_repl[Natts_pg_foreign_data_wrapper];
|
|
|
|
Acl *newAcl;
|
|
|
|
Datum aclDatum;
|
|
|
|
bool isNull;
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2012-02-21 21:58:02 +01:00
|
|
|
form = (Form_pg_foreign_data_wrapper) GETSTRUCT(tup);
|
2011-08-14 21:40:21 +02:00
|
|
|
|
2009-06-11 16:49:15 +02:00
|
|
|
/* Must be a superuser to change a FDW owner */
|
2008-12-19 17:25:19 +01:00
|
|
|
if (!superuser())
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
|
|
errmsg("permission denied to change owner of foreign-data wrapper \"%s\"",
|
2012-02-21 21:58:02 +01:00
|
|
|
NameStr(form->fdwname)),
|
2008-12-19 17:25:19 +01:00
|
|
|
errhint("Must be superuser to change owner of a foreign-data wrapper.")));
|
|
|
|
|
2009-06-11 16:49:15 +02:00
|
|
|
/* New owner must also be a superuser */
|
2008-12-19 17:25:19 +01:00
|
|
|
if (!superuser_arg(newOwnerId))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
|
|
errmsg("permission denied to change owner of foreign-data wrapper \"%s\"",
|
2012-02-21 21:58:02 +01:00
|
|
|
NameStr(form->fdwname)),
|
2009-06-11 16:49:15 +02:00
|
|
|
errhint("The owner of a foreign-data wrapper must be a superuser.")));
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
if (form->fdwowner != newOwnerId)
|
|
|
|
{
|
2015-01-22 18:36:34 +01:00
|
|
|
memset(repl_null, false, sizeof(repl_null));
|
|
|
|
memset(repl_repl, false, sizeof(repl_repl));
|
|
|
|
|
|
|
|
repl_repl[Anum_pg_foreign_data_wrapper_fdwowner - 1] = true;
|
|
|
|
repl_val[Anum_pg_foreign_data_wrapper_fdwowner - 1] = ObjectIdGetDatum(newOwnerId);
|
|
|
|
|
|
|
|
aclDatum = heap_getattr(tup,
|
|
|
|
Anum_pg_foreign_data_wrapper_fdwacl,
|
|
|
|
RelationGetDescr(rel),
|
|
|
|
&isNull);
|
|
|
|
/* Null ACLs do not require changes */
|
|
|
|
if (!isNull)
|
|
|
|
{
|
|
|
|
newAcl = aclnewowner(DatumGetAclP(aclDatum),
|
|
|
|
form->fdwowner, newOwnerId);
|
|
|
|
repl_repl[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true;
|
|
|
|
repl_val[Anum_pg_foreign_data_wrapper_fdwacl - 1] = PointerGetDatum(newAcl);
|
|
|
|
}
|
|
|
|
|
|
|
|
tup = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null,
|
|
|
|
repl_repl);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
simple_heap_update(rel, &tup->t_self, tup);
|
|
|
|
CatalogUpdateIndexes(rel, tup);
|
|
|
|
|
|
|
|
/* Update owner dependency reference */
|
|
|
|
changeDependencyOnOwner(ForeignDataWrapperRelationId,
|
2012-02-21 21:58:02 +01:00
|
|
|
HeapTupleGetOid(tup),
|
2008-12-19 17:25:19 +01:00
|
|
|
newOwnerId);
|
|
|
|
}
|
2013-03-18 03:55:14 +01:00
|
|
|
|
|
|
|
InvokeObjectPostAlterHook(ForeignDataWrapperRelationId,
|
|
|
|
HeapTupleGetOid(tup), 0);
|
2012-02-21 21:58:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Change foreign-data wrapper owner -- by name
|
|
|
|
*
|
|
|
|
* Note restrictions in the "_internal" function, above.
|
|
|
|
*/
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
ObjectAddress
|
2012-02-21 21:58:02 +01:00
|
|
|
AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId)
|
|
|
|
{
|
2012-12-24 00:25:03 +01:00
|
|
|
Oid fdwId;
|
2012-02-21 21:58:02 +01:00
|
|
|
HeapTuple tup;
|
|
|
|
Relation rel;
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
ObjectAddress address;
|
2012-02-21 21:58:02 +01:00
|
|
|
|
|
|
|
rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
|
|
|
|
|
|
|
|
tup = SearchSysCacheCopy1(FOREIGNDATAWRAPPERNAME, CStringGetDatum(name));
|
|
|
|
|
|
|
|
if (!HeapTupleIsValid(tup))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
|
|
errmsg("foreign-data wrapper \"%s\" does not exist", name)));
|
|
|
|
|
2012-12-24 00:25:03 +01:00
|
|
|
fdwId = HeapTupleGetOid(tup);
|
|
|
|
|
2012-02-21 21:58:02 +01:00
|
|
|
AlterForeignDataWrapperOwner_internal(rel, tup, newOwnerId);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
ObjectAddressSet(address, ForeignDataWrapperRelationId, fdwId);
|
|
|
|
|
2008-12-19 17:25:19 +01:00
|
|
|
heap_freetuple(tup);
|
2011-02-19 06:06:18 +01:00
|
|
|
|
|
|
|
heap_close(rel, RowExclusiveLock);
|
2012-12-24 00:25:03 +01:00
|
|
|
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
return address;
|
2008-12-19 17:25:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2012-02-21 21:58:02 +01:00
|
|
|
* Change foreign-data wrapper owner -- by OID
|
|
|
|
*
|
|
|
|
* Note restrictions in the "_internal" function, above.
|
2008-12-19 17:25:19 +01:00
|
|
|
*/
|
|
|
|
void
|
2012-02-21 21:58:02 +01:00
|
|
|
AlterForeignDataWrapperOwner_oid(Oid fwdId, Oid newOwnerId)
|
2008-12-19 17:25:19 +01:00
|
|
|
{
|
|
|
|
HeapTuple tup;
|
|
|
|
Relation rel;
|
|
|
|
|
2012-02-21 21:58:02 +01:00
|
|
|
rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2012-02-21 21:58:02 +01:00
|
|
|
tup = SearchSysCacheCopy1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fwdId));
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
if (!HeapTupleIsValid(tup))
|
2009-06-11 16:49:15 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
2012-06-10 21:20:04 +02:00
|
|
|
errmsg("foreign-data wrapper with OID %u does not exist", fwdId)));
|
2012-02-21 21:58:02 +01:00
|
|
|
|
|
|
|
AlterForeignDataWrapperOwner_internal(rel, tup, newOwnerId);
|
|
|
|
|
|
|
|
heap_freetuple(tup);
|
|
|
|
|
|
|
|
heap_close(rel, RowExclusiveLock);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Internal workhorse for changing a foreign server's owner
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
AlterForeignServerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
|
|
|
|
{
|
|
|
|
Form_pg_foreign_server form;
|
2015-01-22 18:36:34 +01:00
|
|
|
Datum repl_val[Natts_pg_foreign_server];
|
|
|
|
bool repl_null[Natts_pg_foreign_server];
|
|
|
|
bool repl_repl[Natts_pg_foreign_server];
|
|
|
|
Acl *newAcl;
|
|
|
|
Datum aclDatum;
|
|
|
|
bool isNull;
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
form = (Form_pg_foreign_server) GETSTRUCT(tup);
|
|
|
|
|
|
|
|
if (form->srvowner != newOwnerId)
|
|
|
|
{
|
|
|
|
/* Superusers can always do it */
|
|
|
|
if (!superuser())
|
|
|
|
{
|
2012-02-21 21:58:02 +01:00
|
|
|
Oid srvId;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
|
|
|
srvId = HeapTupleGetOid(tup);
|
|
|
|
|
2008-12-19 17:25:19 +01:00
|
|
|
/* Must be owner */
|
|
|
|
if (!pg_foreign_server_ownercheck(srvId, GetUserId()))
|
|
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
|
2012-02-21 21:58:02 +01:00
|
|
|
NameStr(form->srvname));
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
/* Must be able to become new owner */
|
|
|
|
check_is_member_of_role(GetUserId(), newOwnerId);
|
|
|
|
|
|
|
|
/* New owner must have USAGE privilege on foreign-data wrapper */
|
|
|
|
aclresult = pg_foreign_data_wrapper_aclcheck(form->srvfdw, newOwnerId, ACL_USAGE);
|
|
|
|
if (aclresult != ACLCHECK_OK)
|
|
|
|
{
|
|
|
|
ForeignDataWrapper *fdw = GetForeignDataWrapper(form->srvfdw);
|
|
|
|
|
|
|
|
aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-22 18:36:34 +01:00
|
|
|
memset(repl_null, false, sizeof(repl_null));
|
|
|
|
memset(repl_repl, false, sizeof(repl_repl));
|
2015-01-22 22:57:16 +01:00
|
|
|
|
2015-01-22 18:36:34 +01:00
|
|
|
repl_repl[Anum_pg_foreign_server_srvowner - 1] = true;
|
|
|
|
repl_val[Anum_pg_foreign_server_srvowner - 1] = ObjectIdGetDatum(newOwnerId);
|
2015-01-22 22:57:16 +01:00
|
|
|
|
2015-01-22 18:36:34 +01:00
|
|
|
aclDatum = heap_getattr(tup,
|
|
|
|
Anum_pg_foreign_server_srvacl,
|
|
|
|
RelationGetDescr(rel),
|
|
|
|
&isNull);
|
|
|
|
/* Null ACLs do not require changes */
|
|
|
|
if (!isNull)
|
|
|
|
{
|
|
|
|
newAcl = aclnewowner(DatumGetAclP(aclDatum),
|
|
|
|
form->srvowner, newOwnerId);
|
|
|
|
repl_repl[Anum_pg_foreign_server_srvacl - 1] = true;
|
|
|
|
repl_val[Anum_pg_foreign_server_srvacl - 1] = PointerGetDatum(newAcl);
|
|
|
|
}
|
2015-01-22 22:57:16 +01:00
|
|
|
|
2015-01-22 18:36:34 +01:00
|
|
|
tup = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null,
|
|
|
|
repl_repl);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
simple_heap_update(rel, &tup->t_self, tup);
|
|
|
|
CatalogUpdateIndexes(rel, tup);
|
|
|
|
|
|
|
|
/* Update owner dependency reference */
|
|
|
|
changeDependencyOnOwner(ForeignServerRelationId, HeapTupleGetOid(tup),
|
|
|
|
newOwnerId);
|
|
|
|
}
|
2013-03-18 03:55:14 +01:00
|
|
|
|
|
|
|
InvokeObjectPostAlterHook(ForeignServerRelationId,
|
|
|
|
HeapTupleGetOid(tup), 0);
|
2012-02-21 21:58:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Change foreign server owner -- by name
|
|
|
|
*/
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
ObjectAddress
|
2012-02-21 21:58:02 +01:00
|
|
|
AlterForeignServerOwner(const char *name, Oid newOwnerId)
|
|
|
|
{
|
2012-12-24 00:25:03 +01:00
|
|
|
Oid servOid;
|
2012-02-21 21:58:02 +01:00
|
|
|
HeapTuple tup;
|
|
|
|
Relation rel;
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
ObjectAddress address;
|
2012-02-21 21:58:02 +01:00
|
|
|
|
|
|
|
rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
|
|
|
|
|
|
|
|
tup = SearchSysCacheCopy1(FOREIGNSERVERNAME, CStringGetDatum(name));
|
|
|
|
|
|
|
|
if (!HeapTupleIsValid(tup))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
|
|
errmsg("server \"%s\" does not exist", name)));
|
|
|
|
|
2012-12-24 00:25:03 +01:00
|
|
|
servOid = HeapTupleGetOid(tup);
|
|
|
|
|
2012-02-21 21:58:02 +01:00
|
|
|
AlterForeignServerOwner_internal(rel, tup, newOwnerId);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
ObjectAddressSet(address, ForeignServerRelationId, servOid);
|
|
|
|
|
2008-12-19 17:25:19 +01:00
|
|
|
heap_freetuple(tup);
|
2011-02-19 06:06:18 +01:00
|
|
|
|
|
|
|
heap_close(rel, RowExclusiveLock);
|
2012-12-24 00:25:03 +01:00
|
|
|
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
return address;
|
2008-12-19 17:25:19 +01:00
|
|
|
}
|
|
|
|
|
2012-02-21 21:58:02 +01:00
|
|
|
/*
|
|
|
|
* Change foreign server owner -- by OID
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
AlterForeignServerOwner_oid(Oid srvId, Oid newOwnerId)
|
|
|
|
{
|
|
|
|
HeapTuple tup;
|
|
|
|
Relation rel;
|
|
|
|
|
|
|
|
rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
|
|
|
|
|
|
|
|
tup = SearchSysCacheCopy1(FOREIGNSERVEROID, ObjectIdGetDatum(srvId));
|
|
|
|
|
|
|
|
if (!HeapTupleIsValid(tup))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
2012-02-23 11:51:33 +01:00
|
|
|
errmsg("foreign server with OID %u does not exist", srvId)));
|
2012-02-21 21:58:02 +01:00
|
|
|
|
|
|
|
AlterForeignServerOwner_internal(rel, tup, newOwnerId);
|
|
|
|
|
|
|
|
heap_freetuple(tup);
|
|
|
|
|
|
|
|
heap_close(rel, RowExclusiveLock);
|
|
|
|
}
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2011-02-19 06:06:18 +01:00
|
|
|
/*
|
|
|
|
* Convert a handler function name passed from the parser to an Oid.
|
|
|
|
*/
|
|
|
|
static Oid
|
|
|
|
lookup_fdw_handler_func(DefElem *handler)
|
|
|
|
{
|
|
|
|
Oid handlerOid;
|
2015-06-27 23:47:39 +02:00
|
|
|
Oid funcargtypes[1]; /* dummy */
|
2011-02-19 06:06:18 +01:00
|
|
|
|
|
|
|
if (handler == NULL || handler->arg == NULL)
|
|
|
|
return InvalidOid;
|
|
|
|
|
|
|
|
/* handlers have no arguments */
|
2015-06-27 23:47:39 +02:00
|
|
|
handlerOid = LookupFuncName((List *) handler->arg, 0, funcargtypes, false);
|
2011-02-19 06:06:18 +01:00
|
|
|
|
|
|
|
/* check that handler has correct return type */
|
|
|
|
if (get_func_rettype(handlerOid) != FDW_HANDLEROID)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
|
|
|
errmsg("function %s must return type \"fdw_handler\"",
|
|
|
|
NameListToString((List *) handler->arg))));
|
|
|
|
|
|
|
|
return handlerOid;
|
|
|
|
}
|
|
|
|
|
2009-02-24 11:06:36 +01:00
|
|
|
/*
|
|
|
|
* Convert a validator function name passed from the parser to an Oid.
|
|
|
|
*/
|
|
|
|
static Oid
|
2011-02-19 06:06:18 +01:00
|
|
|
lookup_fdw_validator_func(DefElem *validator)
|
2009-02-24 11:06:36 +01:00
|
|
|
{
|
|
|
|
Oid funcargtypes[2];
|
|
|
|
|
2011-02-19 06:06:18 +01:00
|
|
|
if (validator == NULL || validator->arg == NULL)
|
|
|
|
return InvalidOid;
|
|
|
|
|
|
|
|
/* validators take text[], oid */
|
2009-02-24 11:06:36 +01:00
|
|
|
funcargtypes[0] = TEXTARRAYOID;
|
|
|
|
funcargtypes[1] = OIDOID;
|
2011-02-19 06:06:18 +01:00
|
|
|
|
|
|
|
return LookupFuncName((List *) validator->arg, 2, funcargtypes, false);
|
|
|
|
/* validator's return value is ignored, so we don't check the type */
|
2009-02-24 11:06:36 +01:00
|
|
|
}
|
|
|
|
|
2011-02-19 06:06:18 +01:00
|
|
|
/*
|
|
|
|
* Process function options of CREATE/ALTER FDW
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
parse_func_options(List *func_options,
|
|
|
|
bool *handler_given, Oid *fdwhandler,
|
|
|
|
bool *validator_given, Oid *fdwvalidator)
|
|
|
|
{
|
|
|
|
ListCell *cell;
|
|
|
|
|
|
|
|
*handler_given = false;
|
|
|
|
*validator_given = false;
|
|
|
|
/* return InvalidOid if not given */
|
|
|
|
*fdwhandler = InvalidOid;
|
|
|
|
*fdwvalidator = InvalidOid;
|
|
|
|
|
|
|
|
foreach(cell, func_options)
|
|
|
|
{
|
|
|
|
DefElem *def = (DefElem *) lfirst(cell);
|
|
|
|
|
|
|
|
if (strcmp(def->defname, "handler") == 0)
|
|
|
|
{
|
|
|
|
if (*handler_given)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
|
|
errmsg("conflicting or redundant options")));
|
|
|
|
*handler_given = true;
|
|
|
|
*fdwhandler = lookup_fdw_handler_func(def);
|
|
|
|
}
|
|
|
|
else if (strcmp(def->defname, "validator") == 0)
|
|
|
|
{
|
|
|
|
if (*validator_given)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
|
|
errmsg("conflicting or redundant options")));
|
|
|
|
*validator_given = true;
|
|
|
|
*fdwvalidator = lookup_fdw_validator_func(def);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
elog(ERROR, "option \"%s\" not recognized",
|
|
|
|
def->defname);
|
|
|
|
}
|
|
|
|
}
|
2009-02-24 11:06:36 +01:00
|
|
|
|
2008-12-19 17:25:19 +01:00
|
|
|
/*
|
|
|
|
* Create a foreign-data wrapper
|
|
|
|
*/
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
ObjectAddress
|
2008-12-19 17:25:19 +01:00
|
|
|
CreateForeignDataWrapper(CreateFdwStmt *stmt)
|
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
Relation rel;
|
|
|
|
Datum values[Natts_pg_foreign_data_wrapper];
|
|
|
|
bool nulls[Natts_pg_foreign_data_wrapper];
|
|
|
|
HeapTuple tuple;
|
|
|
|
Oid fdwId;
|
2011-02-19 06:06:18 +01:00
|
|
|
bool handler_given;
|
|
|
|
bool validator_given;
|
|
|
|
Oid fdwhandler;
|
2009-06-11 16:49:15 +02:00
|
|
|
Oid fdwvalidator;
|
|
|
|
Datum fdwoptions;
|
|
|
|
Oid ownerId;
|
2011-02-08 22:08:41 +01:00
|
|
|
ObjectAddress myself;
|
|
|
|
ObjectAddress referenced;
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2011-08-14 21:40:21 +02:00
|
|
|
rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
|
|
|
|
|
2008-12-19 17:25:19 +01:00
|
|
|
/* Must be super user */
|
|
|
|
if (!superuser())
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
2009-06-11 16:49:15 +02:00
|
|
|
errmsg("permission denied to create foreign-data wrapper \"%s\"",
|
|
|
|
stmt->fdwname),
|
|
|
|
errhint("Must be superuser to create a foreign-data wrapper.")));
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
/* For now the owner cannot be specified on create. Use effective user ID. */
|
|
|
|
ownerId = GetUserId();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check that there is no other foreign-data wrapper by this name.
|
|
|
|
*/
|
|
|
|
if (GetForeignDataWrapperByName(stmt->fdwname, true) != NULL)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
|
|
|
errmsg("foreign-data wrapper \"%s\" already exists",
|
|
|
|
stmt->fdwname)));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Insert tuple into pg_foreign_data_wrapper.
|
|
|
|
*/
|
2008-12-20 16:51:28 +01:00
|
|
|
memset(values, 0, sizeof(values));
|
|
|
|
memset(nulls, false, sizeof(nulls));
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
values[Anum_pg_foreign_data_wrapper_fdwname - 1] =
|
|
|
|
DirectFunctionCall1(namein, CStringGetDatum(stmt->fdwname));
|
|
|
|
values[Anum_pg_foreign_data_wrapper_fdwowner - 1] = ObjectIdGetDatum(ownerId);
|
|
|
|
|
2011-02-19 06:06:18 +01:00
|
|
|
/* Lookup handler and validator functions, if given */
|
|
|
|
parse_func_options(stmt->func_options,
|
|
|
|
&handler_given, &fdwhandler,
|
|
|
|
&validator_given, &fdwvalidator);
|
2009-02-24 11:06:36 +01:00
|
|
|
|
2011-02-19 06:06:18 +01:00
|
|
|
values[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = ObjectIdGetDatum(fdwhandler);
|
|
|
|
values[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = ObjectIdGetDatum(fdwvalidator);
|
2009-02-24 11:06:36 +01:00
|
|
|
|
|
|
|
nulls[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true;
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2009-12-23 13:23:59 +01:00
|
|
|
fdwoptions = transformGenericOptions(ForeignDataWrapperRelationId,
|
|
|
|
PointerGetDatum(NULL),
|
|
|
|
stmt->options,
|
2009-02-24 11:06:36 +01:00
|
|
|
fdwvalidator);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2008-12-20 10:40:56 +01:00
|
|
|
if (PointerIsValid(DatumGetPointer(fdwoptions)))
|
2008-12-19 17:25:19 +01:00
|
|
|
values[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = fdwoptions;
|
|
|
|
else
|
|
|
|
nulls[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
|
|
|
|
|
|
|
|
tuple = heap_form_tuple(rel->rd_att, values, nulls);
|
|
|
|
|
|
|
|
fdwId = simple_heap_insert(rel, tuple);
|
|
|
|
CatalogUpdateIndexes(rel, tuple);
|
|
|
|
|
|
|
|
heap_freetuple(tuple);
|
|
|
|
|
2011-02-08 22:08:41 +01:00
|
|
|
/* record dependencies */
|
|
|
|
myself.classId = ForeignDataWrapperRelationId;
|
|
|
|
myself.objectId = fdwId;
|
|
|
|
myself.objectSubId = 0;
|
|
|
|
|
2011-02-19 06:06:18 +01:00
|
|
|
if (OidIsValid(fdwhandler))
|
|
|
|
{
|
|
|
|
referenced.classId = ProcedureRelationId;
|
|
|
|
referenced.objectId = fdwhandler;
|
|
|
|
referenced.objectSubId = 0;
|
|
|
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (OidIsValid(fdwvalidator))
|
2009-02-24 11:06:36 +01:00
|
|
|
{
|
|
|
|
referenced.classId = ProcedureRelationId;
|
|
|
|
referenced.objectId = fdwvalidator;
|
|
|
|
referenced.objectSubId = 0;
|
|
|
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
|
|
|
|
}
|
|
|
|
|
2008-12-19 17:25:19 +01:00
|
|
|
recordDependencyOnOwner(ForeignDataWrapperRelationId, fdwId, ownerId);
|
|
|
|
|
2011-02-08 22:08:41 +01:00
|
|
|
/* dependency on extension */
|
2011-07-23 22:59:39 +02:00
|
|
|
recordDependencyOnCurrentExtension(&myself, false);
|
2011-02-08 22:08:41 +01:00
|
|
|
|
2010-11-25 17:48:49 +01:00
|
|
|
/* Post creation hook for new foreign data wrapper */
|
2013-03-07 02:52:06 +01:00
|
|
|
InvokeObjectPostCreateHook(ForeignDataWrapperRelationId, fdwId, 0);
|
2010-11-25 17:48:49 +01:00
|
|
|
|
2011-02-19 06:06:18 +01:00
|
|
|
heap_close(rel, RowExclusiveLock);
|
2012-12-29 13:55:37 +01:00
|
|
|
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
return myself;
|
2008-12-19 17:25:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Alter foreign-data wrapper
|
|
|
|
*/
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
ObjectAddress
|
2008-12-19 17:25:19 +01:00
|
|
|
AlterForeignDataWrapper(AlterFdwStmt *stmt)
|
|
|
|
{
|
|
|
|
Relation rel;
|
|
|
|
HeapTuple tp;
|
2011-02-19 06:06:18 +01:00
|
|
|
Form_pg_foreign_data_wrapper fdwForm;
|
2008-12-19 17:25:19 +01:00
|
|
|
Datum repl_val[Natts_pg_foreign_data_wrapper];
|
|
|
|
bool repl_null[Natts_pg_foreign_data_wrapper];
|
|
|
|
bool repl_repl[Natts_pg_foreign_data_wrapper];
|
|
|
|
Oid fdwId;
|
|
|
|
bool isnull;
|
|
|
|
Datum datum;
|
2011-02-19 06:06:18 +01:00
|
|
|
bool handler_given;
|
|
|
|
bool validator_given;
|
|
|
|
Oid fdwhandler;
|
2009-02-24 11:06:36 +01:00
|
|
|
Oid fdwvalidator;
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
ObjectAddress myself;
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2011-08-14 21:40:21 +02:00
|
|
|
rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
|
|
|
|
|
2008-12-19 17:25:19 +01:00
|
|
|
/* Must be super user */
|
|
|
|
if (!superuser())
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
2009-06-11 16:49:15 +02:00
|
|
|
errmsg("permission denied to alter foreign-data wrapper \"%s\"",
|
|
|
|
stmt->fdwname),
|
|
|
|
errhint("Must be superuser to alter a foreign-data wrapper.")));
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
tp = SearchSysCacheCopy1(FOREIGNDATAWRAPPERNAME,
|
|
|
|
CStringGetDatum(stmt->fdwname));
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
if (!HeapTupleIsValid(tp))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
2009-06-11 16:49:15 +02:00
|
|
|
errmsg("foreign-data wrapper \"%s\" does not exist", stmt->fdwname)));
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2011-02-19 06:06:18 +01:00
|
|
|
fdwForm = (Form_pg_foreign_data_wrapper) GETSTRUCT(tp);
|
2008-12-19 17:25:19 +01:00
|
|
|
fdwId = HeapTupleGetOid(tp);
|
|
|
|
|
|
|
|
memset(repl_val, 0, sizeof(repl_val));
|
|
|
|
memset(repl_null, false, sizeof(repl_null));
|
|
|
|
memset(repl_repl, false, sizeof(repl_repl));
|
|
|
|
|
2011-02-19 06:06:18 +01:00
|
|
|
parse_func_options(stmt->func_options,
|
|
|
|
&handler_given, &fdwhandler,
|
|
|
|
&validator_given, &fdwvalidator);
|
|
|
|
|
|
|
|
if (handler_given)
|
|
|
|
{
|
|
|
|
repl_val[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = ObjectIdGetDatum(fdwhandler);
|
|
|
|
repl_repl[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = true;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* It could be that the behavior of accessing foreign table changes
|
|
|
|
* with the new handler. Warn about this.
|
|
|
|
*/
|
|
|
|
ereport(WARNING,
|
|
|
|
(errmsg("changing the foreign-data wrapper handler can change behavior of existing foreign tables")));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (validator_given)
|
2008-12-19 17:25:19 +01:00
|
|
|
{
|
2009-02-24 11:06:36 +01:00
|
|
|
repl_val[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = ObjectIdGetDatum(fdwvalidator);
|
|
|
|
repl_repl[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = true;
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
/*
|
2013-10-28 15:28:35 +01:00
|
|
|
* It could be that existing options for the FDW or dependent SERVER,
|
|
|
|
* USER MAPPING or FOREIGN TABLE objects are no longer valid according
|
|
|
|
* to the new validator. Warn about this.
|
2008-12-19 17:25:19 +01:00
|
|
|
*/
|
2011-02-19 06:06:18 +01:00
|
|
|
if (OidIsValid(fdwvalidator))
|
2009-02-24 11:06:36 +01:00
|
|
|
ereport(WARNING,
|
2011-04-10 17:42:00 +02:00
|
|
|
(errmsg("changing the foreign-data wrapper validator can cause "
|
|
|
|
"the options for dependent objects to become invalid")));
|
2008-12-19 17:25:19 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
2009-06-11 16:49:15 +02:00
|
|
|
* Validator is not changed, but we need it for validating options.
|
2008-12-19 17:25:19 +01:00
|
|
|
*/
|
2011-02-19 06:06:18 +01:00
|
|
|
fdwvalidator = fdwForm->fdwvalidator;
|
2008-12-19 17:25:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2011-02-19 06:06:18 +01:00
|
|
|
* If options specified, validate and update.
|
2008-12-19 17:25:19 +01:00
|
|
|
*/
|
|
|
|
if (stmt->options)
|
|
|
|
{
|
|
|
|
/* Extract the current options */
|
|
|
|
datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
|
|
|
|
tp,
|
|
|
|
Anum_pg_foreign_data_wrapper_fdwoptions,
|
|
|
|
&isnull);
|
2008-12-20 16:51:28 +01:00
|
|
|
if (isnull)
|
|
|
|
datum = PointerGetDatum(NULL);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
/* Transform the options */
|
2009-12-23 13:23:59 +01:00
|
|
|
datum = transformGenericOptions(ForeignDataWrapperRelationId,
|
|
|
|
datum,
|
|
|
|
stmt->options,
|
|
|
|
fdwvalidator);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2008-12-20 10:40:56 +01:00
|
|
|
if (PointerIsValid(DatumGetPointer(datum)))
|
2008-12-20 16:51:28 +01:00
|
|
|
repl_val[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = datum;
|
2008-12-19 17:25:19 +01:00
|
|
|
else
|
|
|
|
repl_null[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
|
|
|
|
|
|
|
|
repl_repl[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Everything looks good - update the tuple */
|
|
|
|
tp = heap_modify_tuple(tp, RelationGetDescr(rel),
|
2009-06-11 16:49:15 +02:00
|
|
|
repl_val, repl_null, repl_repl);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
simple_heap_update(rel, &tp->t_self, tp);
|
|
|
|
CatalogUpdateIndexes(rel, tp);
|
|
|
|
|
|
|
|
heap_freetuple(tp);
|
2011-02-19 06:06:18 +01:00
|
|
|
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
ObjectAddressSet(myself, ForeignDataWrapperRelationId, fdwId);
|
|
|
|
|
2011-02-19 06:06:18 +01:00
|
|
|
/* Update function dependencies if we changed them */
|
|
|
|
if (handler_given || validator_given)
|
|
|
|
{
|
|
|
|
ObjectAddress referenced;
|
|
|
|
|
|
|
|
/*
|
2011-04-10 17:42:00 +02:00
|
|
|
* Flush all existing dependency records of this FDW on functions; we
|
|
|
|
* assume there can be none other than the ones we are fixing.
|
2011-02-19 06:06:18 +01:00
|
|
|
*/
|
|
|
|
deleteDependencyRecordsForClass(ForeignDataWrapperRelationId,
|
|
|
|
fdwId,
|
|
|
|
ProcedureRelationId,
|
|
|
|
DEPENDENCY_NORMAL);
|
|
|
|
|
|
|
|
/* And build new ones. */
|
|
|
|
|
|
|
|
if (OidIsValid(fdwhandler))
|
|
|
|
{
|
|
|
|
referenced.classId = ProcedureRelationId;
|
|
|
|
referenced.objectId = fdwhandler;
|
|
|
|
referenced.objectSubId = 0;
|
|
|
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (OidIsValid(fdwvalidator))
|
|
|
|
{
|
|
|
|
referenced.classId = ProcedureRelationId;
|
|
|
|
referenced.objectId = fdwvalidator;
|
|
|
|
referenced.objectSubId = 0;
|
|
|
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-18 03:55:14 +01:00
|
|
|
InvokeObjectPostAlterHook(ForeignDataWrapperRelationId, fdwId, 0);
|
|
|
|
|
2011-02-19 06:06:18 +01:00
|
|
|
heap_close(rel, RowExclusiveLock);
|
2012-12-29 13:55:37 +01:00
|
|
|
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
return myself;
|
2008-12-19 17:25:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Drop foreign-data wrapper by OID
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
RemoveForeignDataWrapperById(Oid fdwId)
|
|
|
|
{
|
|
|
|
HeapTuple tp;
|
|
|
|
Relation rel;
|
|
|
|
|
|
|
|
rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
tp = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwId));
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
if (!HeapTupleIsValid(tp))
|
|
|
|
elog(ERROR, "cache lookup failed for foreign-data wrapper %u", fdwId);
|
|
|
|
|
|
|
|
simple_heap_delete(rel, &tp->t_self);
|
|
|
|
|
|
|
|
ReleaseSysCache(tp);
|
|
|
|
|
|
|
|
heap_close(rel, RowExclusiveLock);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create a foreign server
|
|
|
|
*/
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
ObjectAddress
|
2008-12-19 17:25:19 +01:00
|
|
|
CreateForeignServer(CreateForeignServerStmt *stmt)
|
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
Relation rel;
|
|
|
|
Datum srvoptions;
|
|
|
|
Datum values[Natts_pg_foreign_server];
|
|
|
|
bool nulls[Natts_pg_foreign_server];
|
|
|
|
HeapTuple tuple;
|
|
|
|
Oid srvId;
|
|
|
|
Oid ownerId;
|
|
|
|
AclResult aclresult;
|
|
|
|
ObjectAddress myself;
|
|
|
|
ObjectAddress referenced;
|
2008-12-19 17:25:19 +01:00
|
|
|
ForeignDataWrapper *fdw;
|
|
|
|
|
2011-08-14 21:40:21 +02:00
|
|
|
rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
|
|
|
|
|
2008-12-19 17:25:19 +01:00
|
|
|
/* For now the owner cannot be specified on create. Use effective user ID. */
|
|
|
|
ownerId = GetUserId();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check that there is no other foreign server by this name.
|
|
|
|
*/
|
|
|
|
if (GetForeignServerByName(stmt->servername, true) != NULL)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
|
|
|
errmsg("server \"%s\" already exists",
|
|
|
|
stmt->servername)));
|
|
|
|
|
|
|
|
/*
|
2009-06-11 16:49:15 +02:00
|
|
|
* Check that the FDW exists and that we have USAGE on it. Also get the
|
|
|
|
* actual FDW for option validation etc.
|
2008-12-19 17:25:19 +01:00
|
|
|
*/
|
|
|
|
fdw = GetForeignDataWrapperByName(stmt->fdwname, false);
|
|
|
|
|
|
|
|
aclresult = pg_foreign_data_wrapper_aclcheck(fdw->fdwid, ownerId, ACL_USAGE);
|
|
|
|
if (aclresult != ACLCHECK_OK)
|
|
|
|
aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Insert tuple into pg_foreign_server.
|
|
|
|
*/
|
2008-12-20 16:51:28 +01:00
|
|
|
memset(values, 0, sizeof(values));
|
|
|
|
memset(nulls, false, sizeof(nulls));
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
values[Anum_pg_foreign_server_srvname - 1] =
|
|
|
|
DirectFunctionCall1(namein, CStringGetDatum(stmt->servername));
|
|
|
|
values[Anum_pg_foreign_server_srvowner - 1] = ObjectIdGetDatum(ownerId);
|
|
|
|
values[Anum_pg_foreign_server_srvfdw - 1] = ObjectIdGetDatum(fdw->fdwid);
|
|
|
|
|
|
|
|
/* Add server type if supplied */
|
|
|
|
if (stmt->servertype)
|
|
|
|
values[Anum_pg_foreign_server_srvtype - 1] =
|
|
|
|
CStringGetTextDatum(stmt->servertype);
|
|
|
|
else
|
|
|
|
nulls[Anum_pg_foreign_server_srvtype - 1] = true;
|
|
|
|
|
|
|
|
/* Add server version if supplied */
|
|
|
|
if (stmt->version)
|
|
|
|
values[Anum_pg_foreign_server_srvversion - 1] =
|
|
|
|
CStringGetTextDatum(stmt->version);
|
|
|
|
else
|
|
|
|
nulls[Anum_pg_foreign_server_srvversion - 1] = true;
|
|
|
|
|
|
|
|
/* Start with a blank acl */
|
|
|
|
nulls[Anum_pg_foreign_server_srvacl - 1] = true;
|
|
|
|
|
|
|
|
/* Add server options */
|
2009-12-23 13:23:59 +01:00
|
|
|
srvoptions = transformGenericOptions(ForeignServerRelationId,
|
|
|
|
PointerGetDatum(NULL),
|
|
|
|
stmt->options,
|
2009-02-24 11:06:36 +01:00
|
|
|
fdw->fdwvalidator);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2008-12-20 10:40:56 +01:00
|
|
|
if (PointerIsValid(DatumGetPointer(srvoptions)))
|
2008-12-19 17:25:19 +01:00
|
|
|
values[Anum_pg_foreign_server_srvoptions - 1] = srvoptions;
|
|
|
|
else
|
|
|
|
nulls[Anum_pg_foreign_server_srvoptions - 1] = true;
|
|
|
|
|
|
|
|
tuple = heap_form_tuple(rel->rd_att, values, nulls);
|
|
|
|
|
|
|
|
srvId = simple_heap_insert(rel, tuple);
|
|
|
|
|
|
|
|
CatalogUpdateIndexes(rel, tuple);
|
|
|
|
|
|
|
|
heap_freetuple(tuple);
|
|
|
|
|
2011-02-08 22:08:41 +01:00
|
|
|
/* record dependencies */
|
2008-12-19 17:25:19 +01:00
|
|
|
myself.classId = ForeignServerRelationId;
|
|
|
|
myself.objectId = srvId;
|
|
|
|
myself.objectSubId = 0;
|
|
|
|
|
|
|
|
referenced.classId = ForeignDataWrapperRelationId;
|
|
|
|
referenced.objectId = fdw->fdwid;
|
|
|
|
referenced.objectSubId = 0;
|
|
|
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
|
|
|
|
|
|
|
|
recordDependencyOnOwner(ForeignServerRelationId, srvId, ownerId);
|
|
|
|
|
2011-02-08 22:08:41 +01:00
|
|
|
/* dependency on extension */
|
2011-07-23 22:59:39 +02:00
|
|
|
recordDependencyOnCurrentExtension(&myself, false);
|
2011-02-08 22:08:41 +01:00
|
|
|
|
2010-11-25 17:48:49 +01:00
|
|
|
/* Post creation hook for new foreign server */
|
2013-03-07 02:52:06 +01:00
|
|
|
InvokeObjectPostCreateHook(ForeignServerRelationId, srvId, 0);
|
2010-11-25 17:48:49 +01:00
|
|
|
|
2011-02-19 06:06:18 +01:00
|
|
|
heap_close(rel, RowExclusiveLock);
|
2012-12-29 13:55:37 +01:00
|
|
|
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
return myself;
|
2008-12-19 17:25:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Alter foreign server
|
|
|
|
*/
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
ObjectAddress
|
2008-12-19 17:25:19 +01:00
|
|
|
AlterForeignServer(AlterForeignServerStmt *stmt)
|
|
|
|
{
|
|
|
|
Relation rel;
|
|
|
|
HeapTuple tp;
|
|
|
|
Datum repl_val[Natts_pg_foreign_server];
|
|
|
|
bool repl_null[Natts_pg_foreign_server];
|
|
|
|
bool repl_repl[Natts_pg_foreign_server];
|
|
|
|
Oid srvId;
|
2009-06-11 16:49:15 +02:00
|
|
|
Form_pg_foreign_server srvForm;
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
ObjectAddress address;
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2011-08-14 21:40:21 +02:00
|
|
|
rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
tp = SearchSysCacheCopy1(FOREIGNSERVERNAME,
|
|
|
|
CStringGetDatum(stmt->servername));
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
if (!HeapTupleIsValid(tp))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
2009-06-11 16:49:15 +02:00
|
|
|
errmsg("server \"%s\" does not exist", stmt->servername)));
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
srvId = HeapTupleGetOid(tp);
|
|
|
|
srvForm = (Form_pg_foreign_server) GETSTRUCT(tp);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Only owner or a superuser can ALTER a SERVER.
|
|
|
|
*/
|
|
|
|
if (!pg_foreign_server_ownercheck(srvId, GetUserId()))
|
|
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
|
|
|
|
stmt->servername);
|
|
|
|
|
|
|
|
memset(repl_val, 0, sizeof(repl_val));
|
|
|
|
memset(repl_null, false, sizeof(repl_null));
|
|
|
|
memset(repl_repl, false, sizeof(repl_repl));
|
|
|
|
|
|
|
|
if (stmt->has_version)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Change the server VERSION string.
|
|
|
|
*/
|
|
|
|
if (stmt->version)
|
|
|
|
repl_val[Anum_pg_foreign_server_srvversion - 1] =
|
|
|
|
CStringGetTextDatum(stmt->version);
|
|
|
|
else
|
|
|
|
repl_null[Anum_pg_foreign_server_srvversion - 1] = true;
|
|
|
|
|
|
|
|
repl_repl[Anum_pg_foreign_server_srvversion - 1] = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stmt->options)
|
|
|
|
{
|
|
|
|
ForeignDataWrapper *fdw = GetForeignDataWrapper(srvForm->srvfdw);
|
2009-06-11 16:49:15 +02:00
|
|
|
Datum datum;
|
|
|
|
bool isnull;
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
/* Extract the current srvoptions */
|
|
|
|
datum = SysCacheGetAttr(FOREIGNSERVEROID,
|
|
|
|
tp,
|
|
|
|
Anum_pg_foreign_server_srvoptions,
|
|
|
|
&isnull);
|
2008-12-20 16:51:28 +01:00
|
|
|
if (isnull)
|
|
|
|
datum = PointerGetDatum(NULL);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
/* Prepare the options array */
|
2009-12-23 13:23:59 +01:00
|
|
|
datum = transformGenericOptions(ForeignServerRelationId,
|
|
|
|
datum,
|
|
|
|
stmt->options,
|
2009-04-04 23:12:31 +02:00
|
|
|
fdw->fdwvalidator);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2008-12-20 10:40:56 +01:00
|
|
|
if (PointerIsValid(DatumGetPointer(datum)))
|
2008-12-19 17:25:19 +01:00
|
|
|
repl_val[Anum_pg_foreign_server_srvoptions - 1] = datum;
|
|
|
|
else
|
|
|
|
repl_null[Anum_pg_foreign_server_srvoptions - 1] = true;
|
|
|
|
|
|
|
|
repl_repl[Anum_pg_foreign_server_srvoptions - 1] = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Everything looks good - update the tuple */
|
|
|
|
tp = heap_modify_tuple(tp, RelationGetDescr(rel),
|
2009-06-11 16:49:15 +02:00
|
|
|
repl_val, repl_null, repl_repl);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
simple_heap_update(rel, &tp->t_self, tp);
|
|
|
|
CatalogUpdateIndexes(rel, tp);
|
|
|
|
|
2013-03-18 03:55:14 +01:00
|
|
|
InvokeObjectPostAlterHook(ForeignServerRelationId, srvId, 0);
|
|
|
|
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
ObjectAddressSet(address, ForeignServerRelationId, srvId);
|
|
|
|
|
2008-12-19 17:25:19 +01:00
|
|
|
heap_freetuple(tp);
|
2011-02-19 06:06:18 +01:00
|
|
|
|
|
|
|
heap_close(rel, RowExclusiveLock);
|
2012-12-29 13:55:37 +01:00
|
|
|
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
return address;
|
2008-12-19 17:25:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Drop foreign server by OID
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
RemoveForeignServerById(Oid srvId)
|
|
|
|
{
|
|
|
|
HeapTuple tp;
|
|
|
|
Relation rel;
|
|
|
|
|
|
|
|
rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
tp = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(srvId));
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
if (!HeapTupleIsValid(tp))
|
|
|
|
elog(ERROR, "cache lookup failed for foreign server %u", srvId);
|
|
|
|
|
|
|
|
simple_heap_delete(rel, &tp->t_self);
|
|
|
|
|
|
|
|
ReleaseSysCache(tp);
|
|
|
|
|
|
|
|
heap_close(rel, RowExclusiveLock);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-01-20 10:10:20 +01:00
|
|
|
/*
|
|
|
|
* Common routine to check permission for user-mapping-related DDL
|
|
|
|
* commands. We allow server owners to operate on any mapping, and
|
|
|
|
* users to operate on their own mapping.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
user_mapping_ddl_aclcheck(Oid umuserid, Oid serverid, const char *servername)
|
|
|
|
{
|
|
|
|
Oid curuserid = GetUserId();
|
|
|
|
|
|
|
|
if (!pg_foreign_server_ownercheck(serverid, curuserid))
|
|
|
|
{
|
|
|
|
if (umuserid == curuserid)
|
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
AclResult aclresult;
|
2009-01-20 10:10:20 +01:00
|
|
|
|
|
|
|
aclresult = pg_foreign_server_aclcheck(serverid, curuserid, ACL_USAGE);
|
|
|
|
if (aclresult != ACLCHECK_OK)
|
|
|
|
aclcheck_error(aclresult, ACL_KIND_FOREIGN_SERVER, servername);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
|
|
|
|
servername);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-12-19 17:25:19 +01:00
|
|
|
/*
|
|
|
|
* Create user mapping
|
|
|
|
*/
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
ObjectAddress
|
2008-12-19 17:25:19 +01:00
|
|
|
CreateUserMapping(CreateUserMappingStmt *stmt)
|
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
Relation rel;
|
|
|
|
Datum useoptions;
|
|
|
|
Datum values[Natts_pg_user_mapping];
|
|
|
|
bool nulls[Natts_pg_user_mapping];
|
|
|
|
HeapTuple tuple;
|
|
|
|
Oid useId;
|
|
|
|
Oid umId;
|
|
|
|
ObjectAddress myself;
|
|
|
|
ObjectAddress referenced;
|
|
|
|
ForeignServer *srv;
|
2008-12-19 17:25:19 +01:00
|
|
|
ForeignDataWrapper *fdw;
|
Allow CURRENT/SESSION_USER to be used in certain commands
Commands such as ALTER USER, ALTER GROUP, ALTER ROLE, GRANT, and the
various ALTER OBJECT / OWNER TO, as well as ad-hoc clauses related to
roles such as the AUTHORIZATION clause of CREATE SCHEMA, the FOR clause
of CREATE USER MAPPING, and the FOR ROLE clause of ALTER DEFAULT
PRIVILEGES can now take the keywords CURRENT_USER and SESSION_USER as
user specifiers in place of an explicit user name.
This commit also fixes some quite ugly handling of special standards-
mandated syntax in CREATE USER MAPPING, which in particular would fail
to work in presence of a role named "current_user".
The special role specifiers PUBLIC and NONE also have more consistent
handling now.
Also take the opportunity to add location tracking to user specifiers.
Authors: Kyotaro Horiguchi. Heavily reworked by Álvaro Herrera.
Reviewed by: Rushabh Lathia, Adam Brightwell, Marti Raudsepp.
2015-03-09 19:41:54 +01:00
|
|
|
RoleSpec *role = (RoleSpec *) stmt->user;
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2011-08-14 21:40:21 +02:00
|
|
|
rel = heap_open(UserMappingRelationId, RowExclusiveLock);
|
|
|
|
|
Allow CURRENT/SESSION_USER to be used in certain commands
Commands such as ALTER USER, ALTER GROUP, ALTER ROLE, GRANT, and the
various ALTER OBJECT / OWNER TO, as well as ad-hoc clauses related to
roles such as the AUTHORIZATION clause of CREATE SCHEMA, the FOR clause
of CREATE USER MAPPING, and the FOR ROLE clause of ALTER DEFAULT
PRIVILEGES can now take the keywords CURRENT_USER and SESSION_USER as
user specifiers in place of an explicit user name.
This commit also fixes some quite ugly handling of special standards-
mandated syntax in CREATE USER MAPPING, which in particular would fail
to work in presence of a role named "current_user".
The special role specifiers PUBLIC and NONE also have more consistent
handling now.
Also take the opportunity to add location tracking to user specifiers.
Authors: Kyotaro Horiguchi. Heavily reworked by Álvaro Herrera.
Reviewed by: Rushabh Lathia, Adam Brightwell, Marti Raudsepp.
2015-03-09 19:41:54 +01:00
|
|
|
if (role->roletype == ROLESPEC_PUBLIC)
|
|
|
|
useId = ACL_ID_PUBLIC;
|
|
|
|
else
|
|
|
|
useId = get_rolespec_oid(stmt->user, false);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2009-01-20 10:10:20 +01:00
|
|
|
/* Check that the server exists. */
|
2008-12-19 17:25:19 +01:00
|
|
|
srv = GetForeignServerByName(stmt->servername, false);
|
|
|
|
|
2009-01-20 10:10:20 +01:00
|
|
|
user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Check that the user mapping is unique within server.
|
|
|
|
*/
|
2010-02-14 19:42:19 +01:00
|
|
|
umId = GetSysCacheOid2(USERMAPPINGUSERSERVER,
|
|
|
|
ObjectIdGetDatum(useId),
|
|
|
|
ObjectIdGetDatum(srv->serverid));
|
2008-12-19 17:25:19 +01:00
|
|
|
if (OidIsValid(umId))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
|
|
|
errmsg("user mapping \"%s\" already exists for server %s",
|
|
|
|
MappingUserName(useId),
|
|
|
|
stmt->servername)));
|
|
|
|
|
|
|
|
fdw = GetForeignDataWrapper(srv->fdwid);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Insert tuple into pg_user_mapping.
|
|
|
|
*/
|
2008-12-20 16:51:28 +01:00
|
|
|
memset(values, 0, sizeof(values));
|
|
|
|
memset(nulls, false, sizeof(nulls));
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
values[Anum_pg_user_mapping_umuser - 1] = ObjectIdGetDatum(useId);
|
|
|
|
values[Anum_pg_user_mapping_umserver - 1] = ObjectIdGetDatum(srv->serverid);
|
|
|
|
|
|
|
|
/* Add user options */
|
2009-12-23 13:23:59 +01:00
|
|
|
useoptions = transformGenericOptions(UserMappingRelationId,
|
|
|
|
PointerGetDatum(NULL),
|
|
|
|
stmt->options,
|
2009-04-04 23:12:31 +02:00
|
|
|
fdw->fdwvalidator);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2008-12-20 10:40:56 +01:00
|
|
|
if (PointerIsValid(DatumGetPointer(useoptions)))
|
2008-12-19 17:25:19 +01:00
|
|
|
values[Anum_pg_user_mapping_umoptions - 1] = useoptions;
|
|
|
|
else
|
|
|
|
nulls[Anum_pg_user_mapping_umoptions - 1] = true;
|
|
|
|
|
|
|
|
tuple = heap_form_tuple(rel->rd_att, values, nulls);
|
|
|
|
|
|
|
|
umId = simple_heap_insert(rel, tuple);
|
|
|
|
|
|
|
|
CatalogUpdateIndexes(rel, tuple);
|
|
|
|
|
|
|
|
heap_freetuple(tuple);
|
|
|
|
|
|
|
|
/* Add dependency on the server */
|
|
|
|
myself.classId = UserMappingRelationId;
|
|
|
|
myself.objectId = umId;
|
|
|
|
myself.objectSubId = 0;
|
|
|
|
|
|
|
|
referenced.classId = ForeignServerRelationId;
|
|
|
|
referenced.objectId = srv->serverid;
|
|
|
|
referenced.objectSubId = 0;
|
|
|
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
|
|
|
|
|
|
|
|
if (OidIsValid(useId))
|
2011-02-08 22:08:41 +01:00
|
|
|
{
|
2008-12-19 17:25:19 +01:00
|
|
|
/* Record the mapped user dependency */
|
|
|
|
recordDependencyOnOwner(UserMappingRelationId, umId, useId);
|
2011-02-08 22:08:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* dependency on extension */
|
2011-07-23 22:59:39 +02:00
|
|
|
recordDependencyOnCurrentExtension(&myself, false);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2010-11-25 17:48:49 +01:00
|
|
|
/* Post creation hook for new user mapping */
|
2013-03-07 02:52:06 +01:00
|
|
|
InvokeObjectPostCreateHook(UserMappingRelationId, umId, 0);
|
2010-11-25 17:48:49 +01:00
|
|
|
|
2011-02-19 06:06:18 +01:00
|
|
|
heap_close(rel, RowExclusiveLock);
|
2012-12-29 13:55:37 +01:00
|
|
|
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
return myself;
|
2008-12-19 17:25:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Alter user mapping
|
|
|
|
*/
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
ObjectAddress
|
2008-12-19 17:25:19 +01:00
|
|
|
AlterUserMapping(AlterUserMappingStmt *stmt)
|
|
|
|
{
|
|
|
|
Relation rel;
|
|
|
|
HeapTuple tp;
|
|
|
|
Datum repl_val[Natts_pg_user_mapping];
|
|
|
|
bool repl_null[Natts_pg_user_mapping];
|
|
|
|
bool repl_repl[Natts_pg_user_mapping];
|
|
|
|
Oid useId;
|
|
|
|
Oid umId;
|
2009-06-11 16:49:15 +02:00
|
|
|
ForeignServer *srv;
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
ObjectAddress address;
|
Allow CURRENT/SESSION_USER to be used in certain commands
Commands such as ALTER USER, ALTER GROUP, ALTER ROLE, GRANT, and the
various ALTER OBJECT / OWNER TO, as well as ad-hoc clauses related to
roles such as the AUTHORIZATION clause of CREATE SCHEMA, the FOR clause
of CREATE USER MAPPING, and the FOR ROLE clause of ALTER DEFAULT
PRIVILEGES can now take the keywords CURRENT_USER and SESSION_USER as
user specifiers in place of an explicit user name.
This commit also fixes some quite ugly handling of special standards-
mandated syntax in CREATE USER MAPPING, which in particular would fail
to work in presence of a role named "current_user".
The special role specifiers PUBLIC and NONE also have more consistent
handling now.
Also take the opportunity to add location tracking to user specifiers.
Authors: Kyotaro Horiguchi. Heavily reworked by Álvaro Herrera.
Reviewed by: Rushabh Lathia, Adam Brightwell, Marti Raudsepp.
2015-03-09 19:41:54 +01:00
|
|
|
RoleSpec *role = (RoleSpec *) stmt->user;
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2011-08-14 21:40:21 +02:00
|
|
|
rel = heap_open(UserMappingRelationId, RowExclusiveLock);
|
|
|
|
|
Allow CURRENT/SESSION_USER to be used in certain commands
Commands such as ALTER USER, ALTER GROUP, ALTER ROLE, GRANT, and the
various ALTER OBJECT / OWNER TO, as well as ad-hoc clauses related to
roles such as the AUTHORIZATION clause of CREATE SCHEMA, the FOR clause
of CREATE USER MAPPING, and the FOR ROLE clause of ALTER DEFAULT
PRIVILEGES can now take the keywords CURRENT_USER and SESSION_USER as
user specifiers in place of an explicit user name.
This commit also fixes some quite ugly handling of special standards-
mandated syntax in CREATE USER MAPPING, which in particular would fail
to work in presence of a role named "current_user".
The special role specifiers PUBLIC and NONE also have more consistent
handling now.
Also take the opportunity to add location tracking to user specifiers.
Authors: Kyotaro Horiguchi. Heavily reworked by Álvaro Herrera.
Reviewed by: Rushabh Lathia, Adam Brightwell, Marti Raudsepp.
2015-03-09 19:41:54 +01:00
|
|
|
if (role->roletype == ROLESPEC_PUBLIC)
|
|
|
|
useId = ACL_ID_PUBLIC;
|
|
|
|
else
|
|
|
|
useId = get_rolespec_oid(stmt->user, false);
|
|
|
|
|
2008-12-19 17:25:19 +01:00
|
|
|
srv = GetForeignServerByName(stmt->servername, false);
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
umId = GetSysCacheOid2(USERMAPPINGUSERSERVER,
|
|
|
|
ObjectIdGetDatum(useId),
|
|
|
|
ObjectIdGetDatum(srv->serverid));
|
2008-12-19 17:25:19 +01:00
|
|
|
if (!OidIsValid(umId))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
2009-06-11 16:49:15 +02:00
|
|
|
errmsg("user mapping \"%s\" does not exist for the server",
|
|
|
|
MappingUserName(useId))));
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2009-01-20 10:10:20 +01:00
|
|
|
user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
tp = SearchSysCacheCopy1(USERMAPPINGOID, ObjectIdGetDatum(umId));
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
if (!HeapTupleIsValid(tp))
|
|
|
|
elog(ERROR, "cache lookup failed for user mapping %u", umId);
|
|
|
|
|
|
|
|
memset(repl_val, 0, sizeof(repl_val));
|
|
|
|
memset(repl_null, false, sizeof(repl_null));
|
|
|
|
memset(repl_repl, false, sizeof(repl_repl));
|
|
|
|
|
|
|
|
if (stmt->options)
|
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
ForeignDataWrapper *fdw;
|
|
|
|
Datum datum;
|
|
|
|
bool isnull;
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Process the options.
|
|
|
|
*/
|
|
|
|
|
|
|
|
fdw = GetForeignDataWrapper(srv->fdwid);
|
|
|
|
|
|
|
|
datum = SysCacheGetAttr(USERMAPPINGUSERSERVER,
|
|
|
|
tp,
|
|
|
|
Anum_pg_user_mapping_umoptions,
|
|
|
|
&isnull);
|
2008-12-20 16:51:28 +01:00
|
|
|
if (isnull)
|
|
|
|
datum = PointerGetDatum(NULL);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
/* Prepare the options array */
|
2009-12-23 13:23:59 +01:00
|
|
|
datum = transformGenericOptions(UserMappingRelationId,
|
|
|
|
datum,
|
|
|
|
stmt->options,
|
2009-04-04 23:12:31 +02:00
|
|
|
fdw->fdwvalidator);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2008-12-20 10:40:56 +01:00
|
|
|
if (PointerIsValid(DatumGetPointer(datum)))
|
2008-12-19 17:25:19 +01:00
|
|
|
repl_val[Anum_pg_user_mapping_umoptions - 1] = datum;
|
|
|
|
else
|
|
|
|
repl_null[Anum_pg_user_mapping_umoptions - 1] = true;
|
|
|
|
|
|
|
|
repl_repl[Anum_pg_user_mapping_umoptions - 1] = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Everything looks good - update the tuple */
|
|
|
|
tp = heap_modify_tuple(tp, RelationGetDescr(rel),
|
2009-06-11 16:49:15 +02:00
|
|
|
repl_val, repl_null, repl_repl);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
simple_heap_update(rel, &tp->t_self, tp);
|
|
|
|
CatalogUpdateIndexes(rel, tp);
|
|
|
|
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
ObjectAddressSet(address, UserMappingRelationId, umId);
|
|
|
|
|
2008-12-19 17:25:19 +01:00
|
|
|
heap_freetuple(tp);
|
2011-02-19 06:06:18 +01:00
|
|
|
|
|
|
|
heap_close(rel, RowExclusiveLock);
|
2012-12-29 13:55:37 +01:00
|
|
|
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
return address;
|
2008-12-19 17:25:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Drop user mapping
|
|
|
|
*/
|
2012-12-29 13:55:37 +01:00
|
|
|
Oid
|
2008-12-19 17:25:19 +01:00
|
|
|
RemoveUserMapping(DropUserMappingStmt *stmt)
|
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
ObjectAddress object;
|
|
|
|
Oid useId;
|
|
|
|
Oid umId;
|
|
|
|
ForeignServer *srv;
|
Allow CURRENT/SESSION_USER to be used in certain commands
Commands such as ALTER USER, ALTER GROUP, ALTER ROLE, GRANT, and the
various ALTER OBJECT / OWNER TO, as well as ad-hoc clauses related to
roles such as the AUTHORIZATION clause of CREATE SCHEMA, the FOR clause
of CREATE USER MAPPING, and the FOR ROLE clause of ALTER DEFAULT
PRIVILEGES can now take the keywords CURRENT_USER and SESSION_USER as
user specifiers in place of an explicit user name.
This commit also fixes some quite ugly handling of special standards-
mandated syntax in CREATE USER MAPPING, which in particular would fail
to work in presence of a role named "current_user".
The special role specifiers PUBLIC and NONE also have more consistent
handling now.
Also take the opportunity to add location tracking to user specifiers.
Authors: Kyotaro Horiguchi. Heavily reworked by Álvaro Herrera.
Reviewed by: Rushabh Lathia, Adam Brightwell, Marti Raudsepp.
2015-03-09 19:41:54 +01:00
|
|
|
RoleSpec *role = (RoleSpec *) stmt->user;
|
2008-12-19 17:25:19 +01:00
|
|
|
|
Allow CURRENT/SESSION_USER to be used in certain commands
Commands such as ALTER USER, ALTER GROUP, ALTER ROLE, GRANT, and the
various ALTER OBJECT / OWNER TO, as well as ad-hoc clauses related to
roles such as the AUTHORIZATION clause of CREATE SCHEMA, the FOR clause
of CREATE USER MAPPING, and the FOR ROLE clause of ALTER DEFAULT
PRIVILEGES can now take the keywords CURRENT_USER and SESSION_USER as
user specifiers in place of an explicit user name.
This commit also fixes some quite ugly handling of special standards-
mandated syntax in CREATE USER MAPPING, which in particular would fail
to work in presence of a role named "current_user".
The special role specifiers PUBLIC and NONE also have more consistent
handling now.
Also take the opportunity to add location tracking to user specifiers.
Authors: Kyotaro Horiguchi. Heavily reworked by Álvaro Herrera.
Reviewed by: Rushabh Lathia, Adam Brightwell, Marti Raudsepp.
2015-03-09 19:41:54 +01:00
|
|
|
if (role->roletype == ROLESPEC_PUBLIC)
|
|
|
|
useId = ACL_ID_PUBLIC;
|
|
|
|
else
|
2008-12-19 17:25:19 +01:00
|
|
|
{
|
Allow CURRENT/SESSION_USER to be used in certain commands
Commands such as ALTER USER, ALTER GROUP, ALTER ROLE, GRANT, and the
various ALTER OBJECT / OWNER TO, as well as ad-hoc clauses related to
roles such as the AUTHORIZATION clause of CREATE SCHEMA, the FOR clause
of CREATE USER MAPPING, and the FOR ROLE clause of ALTER DEFAULT
PRIVILEGES can now take the keywords CURRENT_USER and SESSION_USER as
user specifiers in place of an explicit user name.
This commit also fixes some quite ugly handling of special standards-
mandated syntax in CREATE USER MAPPING, which in particular would fail
to work in presence of a role named "current_user".
The special role specifiers PUBLIC and NONE also have more consistent
handling now.
Also take the opportunity to add location tracking to user specifiers.
Authors: Kyotaro Horiguchi. Heavily reworked by Álvaro Herrera.
Reviewed by: Rushabh Lathia, Adam Brightwell, Marti Raudsepp.
2015-03-09 19:41:54 +01:00
|
|
|
useId = get_rolespec_oid(stmt->user, stmt->missing_ok);
|
|
|
|
if (!OidIsValid(useId))
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* IF EXISTS specified, role not found and not public. Notice this
|
|
|
|
* and leave.
|
|
|
|
*/
|
|
|
|
elog(NOTICE, "role \"%s\" does not exist, skipping",
|
|
|
|
role->rolename);
|
|
|
|
return InvalidOid;
|
|
|
|
}
|
2008-12-19 17:25:19 +01:00
|
|
|
}
|
|
|
|
|
Allow CURRENT/SESSION_USER to be used in certain commands
Commands such as ALTER USER, ALTER GROUP, ALTER ROLE, GRANT, and the
various ALTER OBJECT / OWNER TO, as well as ad-hoc clauses related to
roles such as the AUTHORIZATION clause of CREATE SCHEMA, the FOR clause
of CREATE USER MAPPING, and the FOR ROLE clause of ALTER DEFAULT
PRIVILEGES can now take the keywords CURRENT_USER and SESSION_USER as
user specifiers in place of an explicit user name.
This commit also fixes some quite ugly handling of special standards-
mandated syntax in CREATE USER MAPPING, which in particular would fail
to work in presence of a role named "current_user".
The special role specifiers PUBLIC and NONE also have more consistent
handling now.
Also take the opportunity to add location tracking to user specifiers.
Authors: Kyotaro Horiguchi. Heavily reworked by Álvaro Herrera.
Reviewed by: Rushabh Lathia, Adam Brightwell, Marti Raudsepp.
2015-03-09 19:41:54 +01:00
|
|
|
srv = GetForeignServerByName(stmt->servername, true);
|
|
|
|
|
2008-12-19 17:25:19 +01:00
|
|
|
if (!srv)
|
|
|
|
{
|
|
|
|
if (!stmt->missing_ok)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
2009-06-11 16:49:15 +02:00
|
|
|
errmsg("server \"%s\" does not exist",
|
|
|
|
stmt->servername)));
|
2008-12-19 17:25:19 +01:00
|
|
|
/* IF EXISTS, just note it */
|
|
|
|
ereport(NOTICE, (errmsg("server does not exist, skipping")));
|
2012-12-29 13:55:37 +01:00
|
|
|
return InvalidOid;
|
2008-12-19 17:25:19 +01:00
|
|
|
}
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
umId = GetSysCacheOid2(USERMAPPINGUSERSERVER,
|
|
|
|
ObjectIdGetDatum(useId),
|
|
|
|
ObjectIdGetDatum(srv->serverid));
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
if (!OidIsValid(umId))
|
|
|
|
{
|
|
|
|
if (!stmt->missing_ok)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
2009-06-11 16:49:15 +02:00
|
|
|
errmsg("user mapping \"%s\" does not exist for the server",
|
|
|
|
MappingUserName(useId))));
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
/* IF EXISTS specified, just note it */
|
|
|
|
ereport(NOTICE,
|
2009-06-11 16:49:15 +02:00
|
|
|
(errmsg("user mapping \"%s\" does not exist for the server, skipping",
|
|
|
|
MappingUserName(useId))));
|
2012-12-29 13:55:37 +01:00
|
|
|
return InvalidOid;
|
2008-12-19 17:25:19 +01:00
|
|
|
}
|
|
|
|
|
2009-01-20 10:10:20 +01:00
|
|
|
user_mapping_ddl_aclcheck(useId, srv->serverid, srv->servername);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Do the deletion
|
|
|
|
*/
|
|
|
|
object.classId = UserMappingRelationId;
|
|
|
|
object.objectId = umId;
|
|
|
|
object.objectSubId = 0;
|
|
|
|
|
2012-01-26 15:24:54 +01:00
|
|
|
performDeletion(&object, DROP_CASCADE, 0);
|
2012-12-29 13:55:37 +01:00
|
|
|
|
|
|
|
return umId;
|
2008-12-19 17:25:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Drop user mapping by OID. This is called to clean up dependencies.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
RemoveUserMappingById(Oid umId)
|
|
|
|
{
|
|
|
|
HeapTuple tp;
|
|
|
|
Relation rel;
|
|
|
|
|
|
|
|
rel = heap_open(UserMappingRelationId, RowExclusiveLock);
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
tp = SearchSysCache1(USERMAPPINGOID, ObjectIdGetDatum(umId));
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
if (!HeapTupleIsValid(tp))
|
|
|
|
elog(ERROR, "cache lookup failed for user mapping %u", umId);
|
|
|
|
|
|
|
|
simple_heap_delete(rel, &tp->t_self);
|
|
|
|
|
|
|
|
ReleaseSysCache(tp);
|
|
|
|
|
|
|
|
heap_close(rel, RowExclusiveLock);
|
|
|
|
}
|
2011-01-02 05:48:11 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Create a foreign table
|
|
|
|
* call after DefineRelation().
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid)
|
|
|
|
{
|
|
|
|
Relation ftrel;
|
|
|
|
Datum ftoptions;
|
|
|
|
Datum values[Natts_pg_foreign_table];
|
|
|
|
bool nulls[Natts_pg_foreign_table];
|
|
|
|
HeapTuple tuple;
|
|
|
|
AclResult aclresult;
|
|
|
|
ObjectAddress myself;
|
|
|
|
ObjectAddress referenced;
|
|
|
|
Oid ownerId;
|
|
|
|
ForeignDataWrapper *fdw;
|
|
|
|
ForeignServer *server;
|
|
|
|
|
|
|
|
/*
|
2011-08-14 21:40:21 +02:00
|
|
|
* Advance command counter to ensure the pg_attribute tuple is visible;
|
|
|
|
* the tuple might be updated to add constraints in previous step.
|
2011-01-02 05:48:11 +01:00
|
|
|
*/
|
|
|
|
CommandCounterIncrement();
|
|
|
|
|
2011-08-14 21:40:21 +02:00
|
|
|
ftrel = heap_open(ForeignTableRelationId, RowExclusiveLock);
|
|
|
|
|
2011-01-02 05:48:11 +01:00
|
|
|
/*
|
|
|
|
* For now the owner cannot be specified on create. Use effective user ID.
|
|
|
|
*/
|
|
|
|
ownerId = GetUserId();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check that the foreign server exists and that we have USAGE on it. Also
|
|
|
|
* get the actual FDW for option validation etc.
|
|
|
|
*/
|
|
|
|
server = GetForeignServerByName(stmt->servername, false);
|
|
|
|
aclresult = pg_foreign_server_aclcheck(server->serverid, ownerId, ACL_USAGE);
|
|
|
|
if (aclresult != ACLCHECK_OK)
|
|
|
|
aclcheck_error(aclresult, ACL_KIND_FOREIGN_SERVER, server->servername);
|
|
|
|
|
|
|
|
fdw = GetForeignDataWrapper(server->fdwid);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Insert tuple into pg_foreign_table.
|
|
|
|
*/
|
|
|
|
memset(values, 0, sizeof(values));
|
|
|
|
memset(nulls, false, sizeof(nulls));
|
|
|
|
|
|
|
|
values[Anum_pg_foreign_table_ftrelid - 1] = ObjectIdGetDatum(relid);
|
|
|
|
values[Anum_pg_foreign_table_ftserver - 1] = ObjectIdGetDatum(server->serverid);
|
|
|
|
/* Add table generic options */
|
|
|
|
ftoptions = transformGenericOptions(ForeignTableRelationId,
|
|
|
|
PointerGetDatum(NULL),
|
|
|
|
stmt->options,
|
|
|
|
fdw->fdwvalidator);
|
|
|
|
|
|
|
|
if (PointerIsValid(DatumGetPointer(ftoptions)))
|
|
|
|
values[Anum_pg_foreign_table_ftoptions - 1] = ftoptions;
|
|
|
|
else
|
|
|
|
nulls[Anum_pg_foreign_table_ftoptions - 1] = true;
|
|
|
|
|
|
|
|
tuple = heap_form_tuple(ftrel->rd_att, values, nulls);
|
|
|
|
|
2011-02-19 06:06:18 +01:00
|
|
|
simple_heap_insert(ftrel, tuple);
|
2011-01-02 05:48:11 +01:00
|
|
|
CatalogUpdateIndexes(ftrel, tuple);
|
|
|
|
|
|
|
|
heap_freetuple(tuple);
|
|
|
|
|
|
|
|
/* Add pg_class dependency on the server */
|
|
|
|
myself.classId = RelationRelationId;
|
|
|
|
myself.objectId = relid;
|
|
|
|
myself.objectSubId = 0;
|
|
|
|
|
|
|
|
referenced.classId = ForeignServerRelationId;
|
|
|
|
referenced.objectId = server->serverid;
|
|
|
|
referenced.objectSubId = 0;
|
|
|
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
|
|
|
|
|
2011-02-19 06:06:18 +01:00
|
|
|
heap_close(ftrel, RowExclusiveLock);
|
2011-01-02 05:48:11 +01:00
|
|
|
}
|
2014-07-10 21:01:31 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Import a foreign schema
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
ImportForeignSchema(ImportForeignSchemaStmt *stmt)
|
|
|
|
{
|
|
|
|
ForeignServer *server;
|
|
|
|
ForeignDataWrapper *fdw;
|
|
|
|
FdwRoutine *fdw_routine;
|
|
|
|
AclResult aclresult;
|
|
|
|
List *cmd_list;
|
|
|
|
ListCell *lc;
|
|
|
|
|
|
|
|
/* Check that the foreign server exists and that we have USAGE on it */
|
|
|
|
server = GetForeignServerByName(stmt->server_name, false);
|
|
|
|
aclresult = pg_foreign_server_aclcheck(server->serverid, GetUserId(), ACL_USAGE);
|
|
|
|
if (aclresult != ACLCHECK_OK)
|
|
|
|
aclcheck_error(aclresult, ACL_KIND_FOREIGN_SERVER, server->servername);
|
|
|
|
|
|
|
|
/* Check that the schema exists and we have CREATE permissions on it */
|
|
|
|
(void) LookupCreationNamespace(stmt->local_schema);
|
|
|
|
|
|
|
|
/* Get the FDW and check it supports IMPORT */
|
|
|
|
fdw = GetForeignDataWrapper(server->fdwid);
|
|
|
|
if (!OidIsValid(fdw->fdwhandler))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
|
|
|
errmsg("foreign-data wrapper \"%s\" has no handler",
|
|
|
|
fdw->fdwname)));
|
|
|
|
fdw_routine = GetFdwRoutine(fdw->fdwhandler);
|
|
|
|
if (fdw_routine->ImportForeignSchema == NULL)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_FDW_NO_SCHEMAS),
|
|
|
|
errmsg("foreign-data wrapper \"%s\" does not support IMPORT FOREIGN SCHEMA",
|
|
|
|
fdw->fdwname)));
|
|
|
|
|
|
|
|
/* Call FDW to get a list of commands */
|
|
|
|
cmd_list = fdw_routine->ImportForeignSchema(stmt, server->serverid);
|
|
|
|
|
|
|
|
/* Parse and execute each command */
|
|
|
|
foreach(lc, cmd_list)
|
|
|
|
{
|
|
|
|
char *cmd = (char *) lfirst(lc);
|
|
|
|
import_error_callback_arg callback_arg;
|
|
|
|
ErrorContextCallback sqlerrcontext;
|
|
|
|
List *raw_parsetree_list;
|
|
|
|
ListCell *lc2;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Setup error traceback support for ereport(). This is so that any
|
|
|
|
* error in the generated SQL will be displayed nicely.
|
|
|
|
*/
|
|
|
|
callback_arg.tablename = NULL; /* not known yet */
|
|
|
|
callback_arg.cmd = cmd;
|
|
|
|
sqlerrcontext.callback = import_error_callback;
|
|
|
|
sqlerrcontext.arg = (void *) &callback_arg;
|
|
|
|
sqlerrcontext.previous = error_context_stack;
|
|
|
|
error_context_stack = &sqlerrcontext;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Parse the SQL string into a list of raw parse trees.
|
|
|
|
*/
|
|
|
|
raw_parsetree_list = pg_parse_query(cmd);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Process each parse tree (we allow the FDW to put more than one
|
|
|
|
* command per string, though this isn't really advised).
|
|
|
|
*/
|
|
|
|
foreach(lc2, raw_parsetree_list)
|
|
|
|
{
|
|
|
|
CreateForeignTableStmt *cstmt = lfirst(lc2);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Because we only allow CreateForeignTableStmt, we can skip parse
|
|
|
|
* analysis, rewrite, and planning steps here.
|
|
|
|
*/
|
|
|
|
if (!IsA(cstmt, CreateForeignTableStmt))
|
|
|
|
elog(ERROR,
|
|
|
|
"foreign-data wrapper \"%s\" returned incorrect statement type %d",
|
|
|
|
fdw->fdwname, (int) nodeTag(cstmt));
|
|
|
|
|
|
|
|
/* Ignore commands for tables excluded by filter options */
|
|
|
|
if (!IsImportableForeignTable(cstmt->base.relation->relname, stmt))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Enable reporting of current table's name on error */
|
|
|
|
callback_arg.tablename = cstmt->base.relation->relname;
|
|
|
|
|
|
|
|
/* Ensure creation schema is the one given in IMPORT statement */
|
|
|
|
cstmt->base.relation->schemaname = pstrdup(stmt->local_schema);
|
|
|
|
|
|
|
|
/* Execute statement */
|
|
|
|
ProcessUtility((Node *) cstmt,
|
|
|
|
cmd,
|
|
|
|
PROCESS_UTILITY_SUBCOMMAND, NULL,
|
|
|
|
None_Receiver, NULL);
|
|
|
|
|
|
|
|
/* Be sure to advance the command counter between subcommands */
|
|
|
|
CommandCounterIncrement();
|
|
|
|
|
|
|
|
callback_arg.tablename = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
error_context_stack = sqlerrcontext.previous;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* error context callback to let us supply the failing SQL statement's text
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
import_error_callback(void *arg)
|
|
|
|
{
|
|
|
|
import_error_callback_arg *callback_arg = (import_error_callback_arg *) arg;
|
|
|
|
int syntaxerrposition;
|
|
|
|
|
|
|
|
/* If it's a syntax error, convert to internal syntax error report */
|
|
|
|
syntaxerrposition = geterrposition();
|
|
|
|
if (syntaxerrposition > 0)
|
|
|
|
{
|
|
|
|
errposition(0);
|
|
|
|
internalerrposition(syntaxerrposition);
|
|
|
|
internalerrquery(callback_arg->cmd);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (callback_arg->tablename)
|
|
|
|
errcontext("importing foreign table \"%s\"",
|
|
|
|
callback_arg->tablename);
|
|
|
|
}
|