summaryrefslogtreecommitdiff
path: root/contrib/postgres_fdw/postgres_fdw.c
diff options
context:
space:
mode:
authorPavan Deolasee2015-06-04 08:02:02 +0000
committerPavan Deolasee2015-06-04 08:02:02 +0000
commit1e86f652fa5e214a284f9862c67bb976317ac730 (patch)
tree2478baa40ebd47196528ee0a5aa6f6921d8e5bd9 /contrib/postgres_fdw/postgres_fdw.c
parent3165b5fde927ff766921270bd56d3236b6c09c21 (diff)
parent4cb7d671fddc8855c8def2de51fb23df1c8ac0af (diff)
Merge remote-tracking branch 'remotes/PGSQL/master' into XL_NEW_MASTER
Conflicts: COPYRIGHT configure configure.in contrib/Makefile doc/bug.template src/backend/access/common/heaptuple.c src/backend/access/common/printtup.c src/backend/access/transam/Makefile src/backend/access/transam/clog.c src/backend/access/transam/twophase.c src/backend/access/transam/varsup.c src/backend/access/transam/xact.c src/backend/access/transam/xlog.c src/backend/bootstrap/bootstrap.c src/backend/catalog/Makefile src/backend/catalog/catalog.c src/backend/catalog/dependency.c src/backend/catalog/genbki.pl src/backend/catalog/namespace.c src/backend/catalog/pg_aggregate.c src/backend/catalog/pg_proc.c src/backend/catalog/storage.c src/backend/commands/aggregatecmds.c src/backend/commands/analyze.c src/backend/commands/comment.c src/backend/commands/copy.c src/backend/commands/dbcommands.c src/backend/commands/event_trigger.c src/backend/commands/explain.c src/backend/commands/indexcmds.c src/backend/commands/portalcmds.c src/backend/commands/schemacmds.c src/backend/commands/sequence.c src/backend/commands/tablecmds.c src/backend/commands/trigger.c src/backend/commands/vacuum.c src/backend/commands/variable.c src/backend/commands/view.c src/backend/executor/execAmi.c src/backend/executor/execCurrent.c src/backend/executor/execMain.c src/backend/executor/execProcnode.c src/backend/executor/execTuples.c src/backend/executor/execUtils.c src/backend/executor/nodeAgg.c src/backend/executor/nodeModifyTable.c src/backend/executor/nodeSubplan.c src/backend/executor/nodeWindowAgg.c src/backend/libpq/hba.c src/backend/nodes/copyfuncs.c src/backend/nodes/equalfuncs.c src/backend/nodes/outfuncs.c src/backend/nodes/readfuncs.c src/backend/optimizer/path/allpaths.c src/backend/optimizer/path/costsize.c src/backend/optimizer/plan/createplan.c src/backend/optimizer/plan/planagg.c src/backend/optimizer/plan/planner.c src/backend/optimizer/plan/setrefs.c src/backend/optimizer/plan/subselect.c src/backend/optimizer/prep/preptlist.c src/backend/optimizer/prep/prepunion.c src/backend/optimizer/util/pathnode.c src/backend/optimizer/util/plancat.c src/backend/parser/analyze.c src/backend/parser/gram.y src/backend/parser/parse_agg.c src/backend/parser/parse_relation.c src/backend/parser/parse_utilcmd.c src/backend/postmaster/autovacuum.c src/backend/postmaster/pgstat.c src/backend/postmaster/postmaster.c src/backend/replication/logical/decode.c src/backend/storage/buffer/bufmgr.c src/backend/storage/ipc/ipci.c src/backend/storage/ipc/procarray.c src/backend/storage/ipc/procsignal.c src/backend/storage/lmgr/lock.c src/backend/storage/lmgr/lwlock.c src/backend/storage/lmgr/proc.c src/backend/tcop/dest.c src/backend/tcop/postgres.c src/backend/tcop/pquery.c src/backend/tcop/utility.c src/backend/utils/adt/arrayfuncs.c src/backend/utils/adt/date.c src/backend/utils/adt/dbsize.c src/backend/utils/adt/pseudotypes.c src/backend/utils/adt/ri_triggers.c src/backend/utils/adt/ruleutils.c src/backend/utils/adt/version.c src/backend/utils/cache/inval.c src/backend/utils/cache/lsyscache.c src/backend/utils/cache/plancache.c src/backend/utils/cache/relcache.c src/backend/utils/init/globals.c src/backend/utils/init/miscinit.c src/backend/utils/init/postinit.c src/backend/utils/misc/guc.c src/backend/utils/mmgr/portalmem.c src/backend/utils/sort/tuplesort.c src/backend/utils/sort/tuplestore.c src/backend/utils/time/combocid.c src/backend/utils/time/snapmgr.c src/bin/Makefile src/bin/initdb/initdb.c src/bin/pg_ctl/pg_ctl.c src/bin/pg_dump/pg_dump.c src/bin/pgbench/pgbench.c src/bin/psql/tab-complete.c src/include/access/htup.h src/include/access/rmgrlist.h src/include/access/transam.h src/include/access/xact.h src/include/catalog/catalog.h src/include/catalog/namespace.h src/include/catalog/pg_aggregate.h src/include/catalog/pg_namespace.h src/include/catalog/pg_proc.h src/include/catalog/pg_type.h src/include/commands/explain.h src/include/commands/sequence.h src/include/commands/vacuum.h src/include/commands/variable.h src/include/executor/execdesc.h src/include/executor/executor.h src/include/executor/tuptable.h src/include/miscadmin.h src/include/nodes/execnodes.h src/include/nodes/nodes.h src/include/nodes/params.h src/include/nodes/parsenodes.h src/include/nodes/plannodes.h src/include/nodes/primnodes.h src/include/nodes/relation.h src/include/optimizer/cost.h src/include/optimizer/pathnode.h src/include/optimizer/planmain.h src/include/parser/analyze.h src/include/parser/parse_agg.h src/include/parser/parse_utilcmd.h src/include/pg_config.h.win32 src/include/pgstat.h src/include/storage/backendid.h src/include/storage/barrier.h src/include/storage/lwlock.h src/include/storage/proc.h src/include/storage/procarray.h src/include/storage/procsignal.h src/include/storage/smgr.h src/include/tcop/dest.h src/include/tcop/pquery.h src/include/utils/builtins.h src/include/utils/guc.h src/include/utils/lsyscache.h src/include/utils/plancache.h src/include/utils/portal.h src/include/utils/rel.h src/include/utils/tuplesort.h src/include/utils/tuplestore.h src/test/regress/expected/aggregates.out src/test/regress/expected/create_index.out src/test/regress/expected/foreign_data.out src/test/regress/expected/join.out src/test/regress/expected/macaddr.out src/test/regress/expected/polygon.out src/test/regress/expected/rangetypes.out src/test/regress/expected/update.out src/test/regress/input/constraints.source src/test/regress/pg_regress.c src/test/regress/serial_schedule src/test/regress/sql/rangetypes.sql
Diffstat (limited to 'contrib/postgres_fdw/postgres_fdw.c')
-rw-r--r--contrib/postgres_fdw/postgres_fdw.c304
1 files changed, 289 insertions, 15 deletions
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 7dd43a9937..478e12484b 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -3,7 +3,7 @@
* postgres_fdw.c
* Foreign-data wrapper for remote PostgreSQL servers
*
- * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 2012-2015, PostgreSQL Global Development Group
*
* IDENTIFICATION
* contrib/postgres_fdw/postgres_fdw.c
@@ -36,7 +36,7 @@
#include "utils/guc.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
-
+#include "utils/rel.h"
PG_MODULE_MAGIC;
@@ -286,6 +286,8 @@ static void postgresExplainForeignModify(ModifyTableState *mtstate,
static bool postgresAnalyzeForeignTable(Relation relation,
AcquireSampleRowsFunc *func,
BlockNumber *totalpages);
+static List *postgresImportForeignSchema(ImportForeignSchemaStmt *stmt,
+ Oid serverOid);
/*
* Helper functions
@@ -363,6 +365,9 @@ postgres_fdw_handler(PG_FUNCTION_ARGS)
/* Support functions for ANALYZE */
routine->AnalyzeForeignTable = postgresAnalyzeForeignTable;
+ /* Support functions for IMPORT FOREIGN SCHEMA */
+ routine->ImportForeignSchema = postgresImportForeignSchema;
+
PG_RETURN_POINTER(routine);
}
@@ -514,7 +519,7 @@ postgresGetForeignRelSize(PlannerInfo *root,
{
baserel->pages = 10;
baserel->tuples =
- (10 * BLCKSZ) / (baserel->width + sizeof(HeapTupleHeaderData));
+ (10 * BLCKSZ) / (baserel->width + MAXALIGN(SizeofHeapTupleHeader));
}
/* Estimate baserel size as best we can with local statistics. */
@@ -817,13 +822,14 @@ postgresGetForeignPlan(PlannerInfo *root,
}
else
{
- RowMarkClause *rc = get_parse_rowmark(root->parse, baserel->relid);
+ PlanRowMark *rc = get_plan_rowmark(root->rowMarks, baserel->relid);
if (rc)
{
/*
* Relation is specified as a FOR UPDATE/SHARE target, so handle
- * that.
+ * that. (But we could also see LCS_NONE, meaning this isn't a
+ * target relation after all.)
*
* For now, just ignore any [NO] KEY specification, since (a) it's
* not clear what that means for a remote table that we don't have
@@ -832,6 +838,9 @@ postgresGetForeignPlan(PlannerInfo *root,
*/
switch (rc->strength)
{
+ case LCS_NONE:
+ /* No locking needed */
+ break;
case LCS_FORKEYSHARE:
case LCS_FORSHARE:
appendStringInfoString(&sql, " FOR SHARE");
@@ -1193,15 +1202,17 @@ postgresPlanForeignModify(PlannerInfo *root,
}
else if (operation == CMD_UPDATE)
{
- Bitmapset *tmpset = bms_copy(rte->modifiedCols);
- AttrNumber col;
+ int col;
- while ((col = bms_first_member(tmpset)) >= 0)
+ col = -1;
+ while ((col = bms_next_member(rte->modifiedCols, col)) >= 0)
{
- col += FirstLowInvalidHeapAttributeNumber;
- if (col <= InvalidAttrNumber) /* shouldn't happen */
+ /* bit numbers are offset by FirstLowInvalidHeapAttributeNumber */
+ AttrNumber attno = col + FirstLowInvalidHeapAttributeNumber;
+
+ if (attno <= InvalidAttrNumber) /* shouldn't happen */
elog(ERROR, "system-column update is not supported");
- targetAttrs = lappend_int(targetAttrs, col);
+ targetAttrs = lappend_int(targetAttrs, attno);
}
}
@@ -2099,15 +2110,15 @@ set_transmission_modes(void)
if (DateStyle != USE_ISO_DATES)
(void) set_config_option("datestyle", "ISO",
PGC_USERSET, PGC_S_SESSION,
- GUC_ACTION_SAVE, true, 0);
+ GUC_ACTION_SAVE, true, 0, false);
if (IntervalStyle != INTSTYLE_POSTGRES)
(void) set_config_option("intervalstyle", "postgres",
PGC_USERSET, PGC_S_SESSION,
- GUC_ACTION_SAVE, true, 0);
+ GUC_ACTION_SAVE, true, 0, false);
if (extra_float_digits < 3)
(void) set_config_option("extra_float_digits", "3",
PGC_USERSET, PGC_S_SESSION,
- GUC_ACTION_SAVE, true, 0);
+ GUC_ACTION_SAVE, true, 0, false);
return nestlevel;
}
@@ -2257,7 +2268,6 @@ static void
store_returning_result(PgFdwModifyState *fmstate,
TupleTableSlot *slot, PGresult *res)
{
- /* PGresult must be released before leaving this function. */
PG_TRY();
{
HeapTuple newtup;
@@ -2565,6 +2575,270 @@ analyze_row_processor(PGresult *res, int row, PgFdwAnalyzeState *astate)
}
/*
+ * Import a foreign schema
+ */
+static List *
+postgresImportForeignSchema(ImportForeignSchemaStmt *stmt, Oid serverOid)
+{
+ List *commands = NIL;
+ bool import_collate = true;
+ bool import_default = false;
+ bool import_not_null = true;
+ ForeignServer *server;
+ UserMapping *mapping;
+ PGconn *conn;
+ StringInfoData buf;
+ PGresult *volatile res = NULL;
+ int numrows,
+ i;
+ ListCell *lc;
+
+ /* Parse statement options */
+ foreach(lc, stmt->options)
+ {
+ DefElem *def = (DefElem *) lfirst(lc);
+
+ if (strcmp(def->defname, "import_collate") == 0)
+ import_collate = defGetBoolean(def);
+ else if (strcmp(def->defname, "import_default") == 0)
+ import_default = defGetBoolean(def);
+ else if (strcmp(def->defname, "import_not_null") == 0)
+ import_not_null = defGetBoolean(def);
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_FDW_INVALID_OPTION_NAME),
+ errmsg("invalid option \"%s\"", def->defname)));
+ }
+
+ /*
+ * Get connection to the foreign server. Connection manager will
+ * establish new connection if necessary.
+ */
+ server = GetForeignServer(serverOid);
+ mapping = GetUserMapping(GetUserId(), server->serverid);
+ conn = GetConnection(server, mapping, false);
+
+ /* Don't attempt to import collation if remote server hasn't got it */
+ if (PQserverVersion(conn) < 90100)
+ import_collate = false;
+
+ /* Create workspace for strings */
+ initStringInfo(&buf);
+
+ /* In what follows, do not risk leaking any PGresults. */
+ PG_TRY();
+ {
+ /* Check that the schema really exists */
+ appendStringInfoString(&buf, "SELECT 1 FROM pg_catalog.pg_namespace WHERE nspname = ");
+ deparseStringLiteral(&buf, stmt->remote_schema);
+
+ res = PQexec(conn, buf.data);
+ if (PQresultStatus(res) != PGRES_TUPLES_OK)
+ pgfdw_report_error(ERROR, res, conn, false, buf.data);
+
+ if (PQntuples(res) != 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_FDW_SCHEMA_NOT_FOUND),
+ errmsg("schema \"%s\" is not present on foreign server \"%s\"",
+ stmt->remote_schema, server->servername)));
+
+ PQclear(res);
+ res = NULL;
+ resetStringInfo(&buf);
+
+ /*
+ * Fetch all table data from this schema, possibly restricted by
+ * EXCEPT or LIMIT TO.
+ *
+ * Note: because we run the connection with search_path restricted to
+ * pg_catalog, the format_type() and pg_get_expr() outputs will always
+ * include a schema name for types/functions in other schemas, which
+ * is what we want.
+ */
+ if (import_collate)
+ appendStringInfoString(&buf,
+ "SELECT relname, "
+ " attname, "
+ " format_type(atttypid, atttypmod), "
+ " attnotnull, "
+ " pg_get_expr(adbin, adrelid), "
+ " collname, "
+ " collnsp.nspname "
+ "FROM pg_class c "
+ " JOIN pg_namespace n ON "
+ " relnamespace = n.oid "
+ " LEFT JOIN pg_attribute a ON "
+ " attrelid = c.oid AND attnum > 0 "
+ " AND NOT attisdropped "
+ " LEFT JOIN pg_attrdef ad ON "
+ " adrelid = c.oid AND adnum = attnum "
+ " LEFT JOIN pg_collation coll ON "
+ " coll.oid = attcollation "
+ " LEFT JOIN pg_namespace collnsp ON "
+ " collnsp.oid = collnamespace ");
+ else
+ appendStringInfoString(&buf,
+ "SELECT relname, "
+ " attname, "
+ " format_type(atttypid, atttypmod), "
+ " attnotnull, "
+ " pg_get_expr(adbin, adrelid), "
+ " NULL, NULL "
+ "FROM pg_class c "
+ " JOIN pg_namespace n ON "
+ " relnamespace = n.oid "
+ " LEFT JOIN pg_attribute a ON "
+ " attrelid = c.oid AND attnum > 0 "
+ " AND NOT attisdropped "
+ " LEFT JOIN pg_attrdef ad ON "
+ " adrelid = c.oid AND adnum = attnum ");
+
+ appendStringInfoString(&buf,
+ "WHERE c.relkind IN ('r', 'v', 'f', 'm') "
+ " AND n.nspname = ");
+ deparseStringLiteral(&buf, stmt->remote_schema);
+
+ /* Apply restrictions for LIMIT TO and EXCEPT */
+ if (stmt->list_type == FDW_IMPORT_SCHEMA_LIMIT_TO ||
+ stmt->list_type == FDW_IMPORT_SCHEMA_EXCEPT)
+ {
+ bool first_item = true;
+
+ appendStringInfoString(&buf, " AND c.relname ");
+ if (stmt->list_type == FDW_IMPORT_SCHEMA_EXCEPT)
+ appendStringInfoString(&buf, "NOT ");
+ appendStringInfoString(&buf, "IN (");
+
+ /* Append list of table names within IN clause */
+ foreach(lc, stmt->table_list)
+ {
+ RangeVar *rv = (RangeVar *) lfirst(lc);
+
+ if (first_item)
+ first_item = false;
+ else
+ appendStringInfoString(&buf, ", ");
+ deparseStringLiteral(&buf, rv->relname);
+ }
+ appendStringInfoString(&buf, ")");
+ }
+
+ /* Append ORDER BY at the end of query to ensure output ordering */
+ appendStringInfo(&buf, " ORDER BY c.relname, a.attnum");
+
+ /* Fetch the data */
+ res = PQexec(conn, buf.data);
+ if (PQresultStatus(res) != PGRES_TUPLES_OK)
+ pgfdw_report_error(ERROR, res, conn, false, buf.data);
+
+ /* Process results */
+ numrows = PQntuples(res);
+ /* note: incrementation of i happens in inner loop's while() test */
+ for (i = 0; i < numrows;)
+ {
+ char *tablename = PQgetvalue(res, i, 0);
+ bool first_item = true;
+
+ resetStringInfo(&buf);
+ appendStringInfo(&buf, "CREATE FOREIGN TABLE %s (\n",
+ quote_identifier(tablename));
+
+ /* Scan all rows for this table */
+ do
+ {
+ char *attname;
+ char *typename;
+ char *attnotnull;
+ char *attdefault;
+ char *collname;
+ char *collnamespace;
+
+ /* If table has no columns, we'll see nulls here */
+ if (PQgetisnull(res, i, 1))
+ continue;
+
+ attname = PQgetvalue(res, i, 1);
+ typename = PQgetvalue(res, i, 2);
+ attnotnull = PQgetvalue(res, i, 3);
+ attdefault = PQgetisnull(res, i, 4) ? (char *) NULL :
+ PQgetvalue(res, i, 4);
+ collname = PQgetisnull(res, i, 5) ? (char *) NULL :
+ PQgetvalue(res, i, 5);
+ collnamespace = PQgetisnull(res, i, 6) ? (char *) NULL :
+ PQgetvalue(res, i, 6);
+
+ if (first_item)
+ first_item = false;
+ else
+ appendStringInfoString(&buf, ",\n");
+
+ /* Print column name and type */
+ appendStringInfo(&buf, " %s %s",
+ quote_identifier(attname),
+ typename);
+
+ /*
+ * Add column_name option so that renaming the foreign table's
+ * column doesn't break the association to the underlying
+ * column.
+ */
+ appendStringInfoString(&buf, " OPTIONS (column_name ");
+ deparseStringLiteral(&buf, attname);
+ appendStringInfoString(&buf, ")");
+
+ /* Add COLLATE if needed */
+ if (import_collate && collname != NULL && collnamespace != NULL)
+ appendStringInfo(&buf, " COLLATE %s.%s",
+ quote_identifier(collnamespace),
+ quote_identifier(collname));
+
+ /* Add DEFAULT if needed */
+ if (import_default && attdefault != NULL)
+ appendStringInfo(&buf, " DEFAULT %s", attdefault);
+
+ /* Add NOT NULL if needed */
+ if (import_not_null && attnotnull[0] == 't')
+ appendStringInfoString(&buf, " NOT NULL");
+ }
+ while (++i < numrows &&
+ strcmp(PQgetvalue(res, i, 0), tablename) == 0);
+
+ /*
+ * Add server name and table-level options. We specify remote
+ * schema and table name as options (the latter to ensure that
+ * renaming the foreign table doesn't break the association).
+ */
+ appendStringInfo(&buf, "\n) SERVER %s\nOPTIONS (",
+ quote_identifier(server->servername));
+
+ appendStringInfoString(&buf, "schema_name ");
+ deparseStringLiteral(&buf, stmt->remote_schema);
+ appendStringInfoString(&buf, ", table_name ");
+ deparseStringLiteral(&buf, tablename);
+
+ appendStringInfoString(&buf, ");");
+
+ commands = lappend(commands, pstrdup(buf.data));
+ }
+
+ /* Clean up */
+ PQclear(res);
+ res = NULL;
+ }
+ PG_CATCH();
+ {
+ if (res)
+ PQclear(res);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+
+ ReleaseConnection(conn);
+
+ return commands;
+}
+
+/*
* Create a tuple from the specified row of the PGresult.
*
* rel is the local representation of the foreign table, attinmeta is