Logo Search packages:      
Sourcecode: postgresql-8.4 version File versions

foreigncmds.c

/*-------------------------------------------------------------------------
 *
 * foreigncmds.c
 *      foreign-data wrapper/server creation/manipulation commands
 *
 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
 *
 *
 * IDENTIFICATION
 *      $PostgreSQL: pgsql/src/backend/commands/foreigncmds.c,v 1.7 2009/04/04 21:12:31 tgl Exp $
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/heapam.h"
#include "access/reloptions.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
#include "commands/defrem.h"
#include "foreign/foreign.h"
#include "miscadmin.h"
#include "parser/parse_func.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"


/*
 * Convert a DefElem list to the text array format that is used in
 * pg_foreign_data_wrapper, pg_foreign_server, and pg_user_mapping.
 * Returns the array in the form of a Datum, or PointerGetDatum(NULL)
 * if the list is empty.
 *
 * 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)
{
      ArrayBuildState    *astate = NULL;
      ListCell             *cell;

      foreach(cell, options)
      {
            DefElem    *def = lfirst(cell);
            const char *value;
            Size        len;
            text     *t;

            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);
}


/*
 * Transform a list of DefElem into text array format.  This is substantially
 * 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.
 *
 * Returns the array in the form of a Datum, or PointerGetDatum(NULL)
 * if the list is empty.
 *
 * This is used by CREATE/ALTER of FOREIGN DATA WRAPPER/SERVER/USER MAPPING.
 */
static Datum
transformGenericOptions(Datum oldOptions,
                                    List *options,
                                    Oid fdwvalidator)
{
      List   *resultOptions = untransformRelOptions(oldOptions);
      ListCell *optcell;
      Datum   result;

      foreach(optcell, options)
      {
            DefElem        *od = lfirst(optcell);
            ListCell   *cell;
            ListCell   *prev = NULL;

            /*
             * Find the element in resultOptions.  We need this for
             * validation in all cases.  Also identify the previous element.
             */
            foreach (cell, resultOptions)
            {
                  DefElem     *def = lfirst(cell);

                  if (strcmp(def->defname, od->defname) == 0)
                        break;
                  else
                        prev = cell;
            }

            /*
             * It is possible to perform multiple SET/DROP actions on the
             * same option.  The standard permits this, as long as the
             * options to be added are unique.  Note that an unspecified
             * action is taken to be ADD.
             */
            switch (od->defaction)
            {
                  case DEFELEM_DROP:
                        if (!cell)
                              ereport(ERROR,
                                          (errcode(ERRCODE_UNDEFINED_OBJECT),
                                           errmsg("option \"%s\" not found",
                                                      od->defname)));
                        resultOptions = list_delete_cell(resultOptions, cell, prev);
                        break;

                  case DEFELEM_SET:
                        if (!cell)
                              ereport(ERROR,
                                          (errcode(ERRCODE_UNDEFINED_OBJECT),
                                           errmsg("option \"%s\" not found",
                                                      od->defname)));
                        lfirst(cell) = od;
                        break;

                  case DEFELEM_ADD:
                  case DEFELEM_UNSPEC:
                        if (cell)
                              ereport(ERROR,
                                          (errcode(ERRCODE_DUPLICATE_OBJECT),
                                           errmsg("option \"%s\" provided more than once",
                                                      od->defname)));
                        resultOptions = lappend(resultOptions, od);
                        break;

                  default:
                        elog(ERROR, "unrecognized action %d on option \"%s\"",
                               (int) od->defaction, od->defname);
                        break;
            }
      }

      result = optionListToArray(resultOptions);

      if (fdwvalidator)
            OidFunctionCall2(fdwvalidator, result, (Datum) 0);

      return result;
}


/*
 * Convert the user mapping user name to OID
 */
static Oid
GetUserOidFromMapping(const char *username, bool missing_ok)
{
    if (!username)
        /* PUBLIC user mapping */
        return InvalidOid;

    if (strcmp(username, "current_user") == 0)
        /* map to the owner */
        return GetUserId();

    /* map to provided user */
    return missing_ok ? get_roleid(username) : get_roleid_checked(username);
}


/*
 * Change foreign-data wrapper owner.
 *
 * Allow this only for superusers; also the new owner must be a
 * superuser.
 */
void
AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId)
{
      HeapTuple   tup;
      Relation    rel;
      Oid               fdwId;
      Form_pg_foreign_data_wrapper form;

    /* Must be a superuser to change a FDW owner */
      if (!superuser())
            ereport(ERROR,
                        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                         errmsg("permission denied to change owner of foreign-data wrapper \"%s\"",
                                    name),
                         errhint("Must be superuser to change owner of a foreign-data wrapper.")));

    /* New owner must also be a superuser */
      if (!superuser_arg(newOwnerId))
            ereport(ERROR,
                        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                         errmsg("permission denied to change owner of foreign-data wrapper \"%s\"",
                                    name),
                         errhint("The owner of a foreign-data wrapper must be a superuser.")));

      rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);

      tup = SearchSysCacheCopy(FOREIGNDATAWRAPPERNAME,
                                           CStringGetDatum(name),
                                           0, 0, 0);

      if (!HeapTupleIsValid(tup))
            ereport(ERROR,
                        (errcode(ERRCODE_UNDEFINED_OBJECT),
                        errmsg("foreign-data wrapper \"%s\" does not exist", name)));

      fdwId = HeapTupleGetOid(tup);
      form = (Form_pg_foreign_data_wrapper) GETSTRUCT(tup);

      if (form->fdwowner != newOwnerId)
      {
            form->fdwowner = newOwnerId;

            simple_heap_update(rel, &tup->t_self, tup);
            CatalogUpdateIndexes(rel, tup);

            /* Update owner dependency reference */
            changeDependencyOnOwner(ForeignDataWrapperRelationId,
                                                fdwId,
                                                newOwnerId);
      }

      heap_close(rel, NoLock);
      heap_freetuple(tup);
}


/*
 * Change foreign server owner
 */
void
AlterForeignServerOwner(const char *name, Oid newOwnerId)
{
      HeapTuple   tup;
      Relation    rel;
      Oid               srvId;
      AclResult   aclresult;
      Form_pg_foreign_server form;

      rel = heap_open(ForeignServerRelationId, RowExclusiveLock);

      tup = SearchSysCacheCopy(FOREIGNSERVERNAME,
                                           CStringGetDatum(name),
                                           0, 0, 0);

      if (!HeapTupleIsValid(tup))
        ereport(ERROR,
                (errcode(ERRCODE_UNDEFINED_OBJECT),
                errmsg("server \"%s\" does not exist", name)));

      srvId = HeapTupleGetOid(tup);
      form = (Form_pg_foreign_server) GETSTRUCT(tup);

      if (form->srvowner != newOwnerId)
      {
            /* Superusers can always do it */
            if (!superuser())
            {
                  /* Must be owner */
                  if (!pg_foreign_server_ownercheck(srvId, GetUserId()))
                        aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
                                             name);

                  /* 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);
                  }
            }

            form->srvowner = newOwnerId;

            simple_heap_update(rel, &tup->t_self, tup);
            CatalogUpdateIndexes(rel, tup);

            /* Update owner dependency reference */
            changeDependencyOnOwner(ForeignServerRelationId, HeapTupleGetOid(tup),
                                                newOwnerId);
      }

      heap_close(rel, NoLock);
      heap_freetuple(tup);
}


/*
 * Convert a validator function name passed from the parser to an Oid.
 */
static Oid
lookup_fdw_validator_func(List *validator)
{
      Oid               funcargtypes[2];

      funcargtypes[0] = TEXTARRAYOID;
      funcargtypes[1] = OIDOID;
      return LookupFuncName(validator, 2, funcargtypes, false);
      /* return value is ignored, so we don't check the type */
}


/*
 * Create a foreign-data wrapper
 */
void
CreateForeignDataWrapper(CreateFdwStmt *stmt)
{
      Relation          rel;
      Datum             values[Natts_pg_foreign_data_wrapper];
      bool              nulls[Natts_pg_foreign_data_wrapper];
      HeapTuple         tuple;
      Oid                     fdwId;
      Oid                     fdwvalidator;
      Datum             fdwoptions;
      Oid                     ownerId;

      /* Must be super user */
      if (!superuser())
            ereport(ERROR,
                        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                         errmsg("permission denied to create foreign-data wrapper \"%s\"",
                                    stmt->fdwname),
                         errhint("Must be superuser to create a foreign-data wrapper.")));

      /* 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.
       */
      rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);

      memset(values, 0, sizeof(values));
      memset(nulls, false, sizeof(nulls));

      values[Anum_pg_foreign_data_wrapper_fdwname - 1] =
            DirectFunctionCall1(namein, CStringGetDatum(stmt->fdwname));
      values[Anum_pg_foreign_data_wrapper_fdwowner - 1] = ObjectIdGetDatum(ownerId);

      if (stmt->validator)
            fdwvalidator = lookup_fdw_validator_func(stmt->validator);
      else
            fdwvalidator = InvalidOid;

      values[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = fdwvalidator;

      nulls[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true;

      fdwoptions = transformGenericOptions(PointerGetDatum(NULL), stmt->options,
                                                             fdwvalidator);

      if (PointerIsValid(DatumGetPointer(fdwoptions)))
            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);

      if (fdwvalidator)
      {
            ObjectAddress myself;
            ObjectAddress referenced;

            myself.classId = ForeignDataWrapperRelationId;
            myself.objectId = fdwId;
            myself.objectSubId = 0;

            referenced.classId = ProcedureRelationId;
            referenced.objectId = fdwvalidator;
            referenced.objectSubId = 0;
            recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
      }

      recordDependencyOnOwner(ForeignDataWrapperRelationId, fdwId, ownerId);

      heap_close(rel, NoLock);
}


/*
 * Alter foreign-data wrapper
 */
void
AlterForeignDataWrapper(AlterFdwStmt *stmt)
{
      Relation    rel;
      HeapTuple   tp;
      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;
      Oid               fdwvalidator;

      /* Must be super user */
      if (!superuser())
            ereport(ERROR,
                        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                         errmsg("permission denied to alter foreign-data wrapper \"%s\"",
                                    stmt->fdwname),
                         errhint("Must be superuser to alter a foreign-data wrapper.")));

      tp = SearchSysCacheCopy(FOREIGNDATAWRAPPERNAME,
                                          CStringGetDatum(stmt->fdwname),
                                          0, 0, 0);

      if (!HeapTupleIsValid(tp))
            ereport(ERROR,
                        (errcode(ERRCODE_UNDEFINED_OBJECT),
                        errmsg("foreign-data wrapper \"%s\" does not exist", stmt->fdwname)));

      fdwId = HeapTupleGetOid(tp);

      memset(repl_val, 0, sizeof(repl_val));
      memset(repl_null, false, sizeof(repl_null));
      memset(repl_repl, false, sizeof(repl_repl));

      if (stmt->change_validator)
      {
            fdwvalidator = stmt->validator ? lookup_fdw_validator_func(stmt->validator) : InvalidOid;
            repl_val[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = ObjectIdGetDatum(fdwvalidator);
            repl_repl[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = true;

            /*
             * It could be that the options for the FDW, SERVER and USER MAPPING
             * are no longer valid with the new validator.  Warn about this.
             */
            if (stmt->validator)
                  ereport(WARNING,
                              (errmsg("changing the foreign-data wrapper validator can cause "
                                          "the options for dependent objects to become invalid")));
      }
      else
      {
            /*
             * Validator is not changed, but we need it for validating
             * options.
             */
            datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
                                                tp,
                                                Anum_pg_foreign_data_wrapper_fdwvalidator,
                                                &isnull);
            Assert(!isnull);
            fdwvalidator = DatumGetObjectId(datum);
      }

      /*
       * Options specified, validate and update.
       */
      if (stmt->options)
      {
            /* Extract the current options */
            datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
                                                tp,
                                                Anum_pg_foreign_data_wrapper_fdwoptions,
                                                &isnull);
            if (isnull)
                  datum = PointerGetDatum(NULL);

            /* Transform the options */
            datum = transformGenericOptions(datum, stmt->options, fdwvalidator);

            if (PointerIsValid(DatumGetPointer(datum)))
                  repl_val[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = datum;
            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 */

      rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);

      tp = heap_modify_tuple(tp, RelationGetDescr(rel),
                                      repl_val, repl_null, repl_repl);

      simple_heap_update(rel, &tp->t_self, tp);
      CatalogUpdateIndexes(rel, tp);

      heap_close(rel, RowExclusiveLock);
      heap_freetuple(tp);
}


/*
 * Drop foreign-data wrapper
 */
void
RemoveForeignDataWrapper(DropFdwStmt *stmt)
{
      Oid                           fdwId;
      ObjectAddress           object;

      fdwId = GetForeignDataWrapperOidByName(stmt->fdwname, true);

      if (!superuser())
            ereport(ERROR,
                        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                        errmsg("permission denied to drop foreign-data wrapper \"%s\"",
                              stmt->fdwname),
                        errhint("Must be superuser to drop a foreign-data wrapper.")));

      if (!OidIsValid(fdwId))
      {
            if (!stmt->missing_ok)
                  ereport(ERROR,
                              (errcode(ERRCODE_UNDEFINED_OBJECT),
                              errmsg("foreign-data wrapper \"%s\" does not exist",
                              stmt->fdwname)));

            /* IF EXISTS specified, just note it */
            ereport(NOTICE,
                        (errmsg("foreign-data wrapper \"%s\" does not exist, skipping",
                              stmt->fdwname)));
            return;
      }

      /*
       * Do the deletion
       */
      object.classId = ForeignDataWrapperRelationId;
      object.objectId = fdwId;
      object.objectSubId = 0;

      performDeletion(&object, stmt->behavior);
}


/*
 * Drop foreign-data wrapper by OID
 */
void
RemoveForeignDataWrapperById(Oid fdwId)
{
      HeapTuple   tp;
      Relation    rel;

      rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);

      tp = SearchSysCache(FOREIGNDATAWRAPPEROID,
                                    ObjectIdGetDatum(fdwId),
                                    0, 0, 0);

      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
 */
void
CreateForeignServer(CreateForeignServerStmt *stmt)
{
      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;
      ForeignDataWrapper *fdw;

      /* 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)));

      /*
       * Check that the FDW exists and that we have USAGE on it.
       * Also get the actual FDW for option validation etc.
       */
      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.
       */
      rel = heap_open(ForeignServerRelationId, RowExclusiveLock);

      memset(values, 0, sizeof(values));
      memset(nulls, false, sizeof(nulls));

      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 */
      srvoptions = transformGenericOptions(PointerGetDatum(NULL), stmt->options,
                                                             fdw->fdwvalidator);

      if (PointerIsValid(DatumGetPointer(srvoptions)))
            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);

      /* Add dependency on FDW and owner */
      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);

      heap_close(rel, NoLock);
}


/*
 * Alter foreign server
 */
void
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;
      Form_pg_foreign_server  srvForm;

      tp = SearchSysCacheCopy(FOREIGNSERVERNAME,
                                          CStringGetDatum(stmt->servername),
                                          0, 0, 0);

      if (!HeapTupleIsValid(tp))
            ereport(ERROR,
                        (errcode(ERRCODE_UNDEFINED_OBJECT),
                        errmsg("server \"%s\" does not exist", stmt->servername)));

      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);
            Datum                   datum;
            bool                    isnull;

            /* Extract the current srvoptions */
            datum = SysCacheGetAttr(FOREIGNSERVEROID,
                                                tp,
                                                Anum_pg_foreign_server_srvoptions,
                                                &isnull);
            if (isnull)
                  datum = PointerGetDatum(NULL);

            /* Prepare the options array */
            datum = transformGenericOptions(datum, stmt->options,
                                                            fdw->fdwvalidator);

            if (PointerIsValid(DatumGetPointer(datum)))
                  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 */

      rel = heap_open(ForeignServerRelationId, RowExclusiveLock);

      tp = heap_modify_tuple(tp, RelationGetDescr(rel),
                                      repl_val, repl_null, repl_repl);

      simple_heap_update(rel, &tp->t_self, tp);
      CatalogUpdateIndexes(rel, tp);

      heap_close(rel, RowExclusiveLock);
      heap_freetuple(tp);
}


/*
 * Drop foreign server
 */
void
RemoveForeignServer(DropForeignServerStmt *stmt)
{
      Oid                     srvId;
      ObjectAddress     object;

      srvId = GetForeignServerOidByName(stmt->servername, true);

      if (!OidIsValid(srvId))
      {
            /* Server not found, complain or notice */
            if (!stmt->missing_ok)
                  ereport(ERROR,
                              (errcode(ERRCODE_UNDEFINED_OBJECT),
                              errmsg("server \"%s\" does not exist", stmt->servername)));

            /* IF EXISTS specified, just note it */
            ereport(NOTICE,
                        (errmsg("server \"%s\" does not exist, skipping",
                                    stmt->servername)));
            return;
      }

      /* Only allow DROP if the server is owned by the user. */
      if (!pg_foreign_server_ownercheck(srvId, GetUserId()))
            aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
                                 stmt->servername);

      object.classId = ForeignServerRelationId;
      object.objectId = srvId;
      object.objectSubId = 0;

      performDeletion(&object, stmt->behavior);
}


/*
 * Drop foreign server by OID
 */
void
RemoveForeignServerById(Oid srvId)
{
      HeapTuple   tp;
      Relation    rel;

      rel = heap_open(ForeignServerRelationId, RowExclusiveLock);

      tp = SearchSysCache(FOREIGNSERVEROID,
                                    ObjectIdGetDatum(srvId),
                                    0, 0, 0);

      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);
}


/*
 * 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)
            {
                  AclResult               aclresult;

                  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);
      }
}


/*
 * Create user mapping
 */
void
CreateUserMapping(CreateUserMappingStmt *stmt)
{
      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;
      ForeignDataWrapper *fdw;

      useId = GetUserOidFromMapping(stmt->username, false);

      /* Check that the server exists. */
      srv = GetForeignServerByName(stmt->servername, false);

      user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername);

      /*
       * Check that the user mapping is unique within server.
       */
      umId = GetSysCacheOid(USERMAPPINGUSERSERVER,
                                       ObjectIdGetDatum(useId),
                                       ObjectIdGetDatum(srv->serverid),
                                       0, 0);
      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.
       */
      rel = heap_open(UserMappingRelationId, RowExclusiveLock);

      memset(values, 0, sizeof(values));
      memset(nulls, false, sizeof(nulls));

      values[Anum_pg_user_mapping_umuser - 1] = ObjectIdGetDatum(useId);
      values[Anum_pg_user_mapping_umserver - 1] = ObjectIdGetDatum(srv->serverid);

      /* Add user options */
      useoptions = transformGenericOptions(PointerGetDatum(NULL), stmt->options,
                                                             fdw->fdwvalidator);

      if (PointerIsValid(DatumGetPointer(useoptions)))
            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))
            /* Record the mapped user dependency */
            recordDependencyOnOwner(UserMappingRelationId, umId, useId);

      heap_close(rel, NoLock);
}


/*
 * Alter user mapping
 */
void
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;
      ForeignServer     *srv;

      useId = GetUserOidFromMapping(stmt->username, false);
      srv = GetForeignServerByName(stmt->servername, false);

      umId = GetSysCacheOid(USERMAPPINGUSERSERVER,
                                      ObjectIdGetDatum(useId),
                                      ObjectIdGetDatum(srv->serverid),
                                      0, 0);
      if (!OidIsValid(umId))
            ereport(ERROR,
                        (errcode(ERRCODE_UNDEFINED_OBJECT),
                        errmsg("user mapping \"%s\" does not exist for the server",
                              MappingUserName(useId))));

      user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername);

      tp = SearchSysCacheCopy(USERMAPPINGOID,
                                          ObjectIdGetDatum(umId),
                                          0, 0, 0);

      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)
      {
            ForeignDataWrapper               *fdw;
            Datum                               datum;
            bool                                isnull;

            /*
             * Process the options.
             */

            fdw = GetForeignDataWrapper(srv->fdwid);

            datum = SysCacheGetAttr(USERMAPPINGUSERSERVER,
                                                tp,
                                                Anum_pg_user_mapping_umoptions,
                                                &isnull);
            if (isnull)
                  datum = PointerGetDatum(NULL);

            /* Prepare the options array */
            datum = transformGenericOptions(datum, stmt->options,
                                                            fdw->fdwvalidator);

            if (PointerIsValid(DatumGetPointer(datum)))
                  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 */

      rel = heap_open(UserMappingRelationId, RowExclusiveLock);

      tp = heap_modify_tuple(tp, RelationGetDescr(rel),
                                      repl_val, repl_null, repl_repl);

      simple_heap_update(rel, &tp->t_self, tp);
      CatalogUpdateIndexes(rel, tp);

      heap_close(rel, RowExclusiveLock);
      heap_freetuple(tp);
}


/*
 * Drop user mapping
 */
void
RemoveUserMapping(DropUserMappingStmt *stmt)
{
      ObjectAddress     object;
      Oid                     useId;
      Oid                     umId;
      ForeignServer  *srv;

      useId = GetUserOidFromMapping(stmt->username, stmt->missing_ok);
      srv = GetForeignServerByName(stmt->servername, true);

      if (stmt->username && !OidIsValid(useId))
      {
            /*
             * IF EXISTS specified, role not found and not public.
             * Notice this and leave.
             */
            elog(NOTICE, "role \"%s\" does not exist, skipping", stmt->username);
            return;
      }

      if (!srv)
      {
            if (!stmt->missing_ok)
                  ereport(ERROR,
                              (errcode(ERRCODE_UNDEFINED_OBJECT),
                              errmsg("server \"%s\" does not exist",
                              stmt->servername)));
            /* IF EXISTS, just note it */
            ereport(NOTICE, (errmsg("server does not exist, skipping")));
            return;
      }

      umId = GetSysCacheOid(USERMAPPINGUSERSERVER,
                                      ObjectIdGetDatum(useId),
                                      ObjectIdGetDatum(srv->serverid),
                                      0, 0);

      if (!OidIsValid(umId))
      {
            if (!stmt->missing_ok)
                  ereport(ERROR,
                              (errcode(ERRCODE_UNDEFINED_OBJECT),
                              errmsg("user mapping \"%s\" does not exist for the server",
                                    MappingUserName(useId))));

            /* IF EXISTS specified, just note it */
            ereport(NOTICE,
                        (errmsg("user mapping \"%s\" does not exist for the server, skipping",
                              MappingUserName(useId))));
            return;
      }

      user_mapping_ddl_aclcheck(useId, srv->serverid, srv->servername);

      /*
       * Do the deletion
       */
      object.classId = UserMappingRelationId;
      object.objectId = umId;
      object.objectSubId = 0;

      performDeletion(&object, DROP_CASCADE);
}


/*
 * 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);

      tp = SearchSysCache(USERMAPPINGOID,
                                    ObjectIdGetDatum(umId),
                                    0, 0, 0);

      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);
}

Generated by  Doxygen 1.6.0   Back to index