Commit d0d75c40 authored by Tom Lane's avatar Tom Lane

Add postgres_fdw contrib module.

There's still a lot of room for improvement, but it basically works,
and we need this to be present before we can do anything much with the
writable-foreign-tables patch.  So let's commit it and get on with testing.

Shigeru Hanada, reviewed by KaiGai Kohei and Tom Lane
parent f435cd1d
......@@ -43,6 +43,7 @@ SUBDIRS = \
pgcrypto \
pgrowlocks \
pgstattuple \
postgres_fdw \
seg \
spi \
tablefunc \
......
# Generated subdirectories
/log/
/results/
/tmp_check/
# contrib/postgres_fdw/Makefile
MODULE_big = postgres_fdw
OBJS = postgres_fdw.o option.o deparse.o connection.o
PG_CPPFLAGS = -I$(libpq_srcdir)
SHLIB_LINK = $(libpq)
SHLIB_PREREQS = submake-libpq
EXTENSION = postgres_fdw
DATA = postgres_fdw--1.0.sql
REGRESS = postgres_fdw
# the db name is hard-coded in the tests
override USE_MODULE_DB =
ifdef USE_PGXS
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
else
subdir = contrib/postgres_fdw
top_builddir = ../..
include $(top_builddir)/src/Makefile.global
include $(top_srcdir)/contrib/contrib-global.mk
endif
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/*-------------------------------------------------------------------------
*
* option.c
* FDW option handling for postgres_fdw
*
* Portions Copyright (c) 2012-2013, PostgreSQL Global Development Group
*
* IDENTIFICATION
* contrib/postgres_fdw/option.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "postgres_fdw.h"
#include "access/reloptions.h"
#include "catalog/pg_foreign_server.h"
#include "catalog/pg_foreign_table.h"
#include "catalog/pg_user_mapping.h"
#include "commands/defrem.h"
/*
* Describes the valid options for objects that this wrapper uses.
*/
typedef struct PgFdwOption
{
const char *keyword;
Oid optcontext; /* OID of catalog in which option may appear */
bool is_libpq_opt; /* true if it's used in libpq */
} PgFdwOption;
/*
* Valid options for postgres_fdw.
* Allocated and filled in InitPgFdwOptions.
*/
static PgFdwOption *postgres_fdw_options;
/*
* Valid options for libpq.
* Allocated and filled in InitPgFdwOptions.
*/
static PQconninfoOption *libpq_options;
/*
* Helper functions
*/
static void InitPgFdwOptions(void);
static bool is_valid_option(const char *keyword, Oid context);
static bool is_libpq_option(const char *keyword);
/*
* Validate the generic options given to a FOREIGN DATA WRAPPER, SERVER,
* USER MAPPING or FOREIGN TABLE that uses postgres_fdw.
*
* Raise an ERROR if the option or its value is considered invalid.
*/
extern Datum postgres_fdw_validator(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(postgres_fdw_validator);
Datum
postgres_fdw_validator(PG_FUNCTION_ARGS)
{
List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
ListCell *cell;
/* Build our options lists if we didn't yet. */
InitPgFdwOptions();
/*
* Check that only options supported by postgres_fdw, and allowed for the
* current object type, are given.
*/
foreach(cell, options_list)
{
DefElem *def = (DefElem *) lfirst(cell);
if (!is_valid_option(def->defname, catalog))
{
/*
* Unknown option specified, complain about it. Provide a hint
* with list of valid options for the object.
*/
PgFdwOption *opt;
StringInfoData buf;
initStringInfo(&buf);
for (opt = postgres_fdw_options; opt->keyword; opt++)
{
if (catalog == opt->optcontext)
appendStringInfo(&buf, "%s%s", (buf.len > 0) ? ", " : "",
opt->keyword);
}
ereport(ERROR,
(errcode(ERRCODE_FDW_INVALID_OPTION_NAME),
errmsg("invalid option \"%s\"", def->defname),
errhint("Valid options in this context are: %s",
buf.data)));
}
/*
* Validate option value, when we can do so without any context.
*/
if (strcmp(def->defname, "use_remote_explain") == 0)
{
/* use_remote_explain accepts only boolean values */
(void) defGetBoolean(def);
}
else if (strcmp(def->defname, "fdw_startup_cost") == 0 ||
strcmp(def->defname, "fdw_tuple_cost") == 0)
{
/* these must have a non-negative numeric value */
double val;
char *endp;
val = strtod(defGetString(def), &endp);
if (*endp || val < 0)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("%s requires a non-negative numeric value",
def->defname)));
}
}
PG_RETURN_VOID();
}
/*
* Initialize option lists.
*/
static void
InitPgFdwOptions(void)
{
int num_libpq_opts;
PQconninfoOption *lopt;
PgFdwOption *popt;
/* non-libpq FDW-specific FDW options */
static const PgFdwOption non_libpq_options[] = {
{"schema_name", ForeignTableRelationId, false},
{"table_name", ForeignTableRelationId, false},
{"column_name", AttributeRelationId, false},
/* use_remote_explain is available on both server and table */
{"use_remote_explain", ForeignServerRelationId, false},
{"use_remote_explain", ForeignTableRelationId, false},
/* cost factors */
{"fdw_startup_cost", ForeignServerRelationId, false},
{"fdw_tuple_cost", ForeignServerRelationId, false},
{NULL, InvalidOid, false}
};
/* Prevent redundant initialization. */
if (postgres_fdw_options)
return;
/*
* Get list of valid libpq options.
*
* To avoid unnecessary work, we get the list once and use it throughout
* the lifetime of this backend process. We don't need to care about
* memory context issues, because PQconndefaults allocates with malloc.
*/
libpq_options = PQconndefaults();
if (!libpq_options) /* assume reason for failure is OOM */
ereport(ERROR,
(errcode(ERRCODE_FDW_OUT_OF_MEMORY),
errmsg("out of memory"),
errdetail("could not get libpq's default connection options")));
/* Count how many libpq options are available. */
num_libpq_opts = 0;
for (lopt = libpq_options; lopt->keyword; lopt++)
num_libpq_opts++;
/*
* Construct an array which consists of all valid options for
* postgres_fdw, by appending FDW-specific options to libpq options.
*
* We use plain malloc here to allocate postgres_fdw_options because it
* lives as long as the backend process does. Besides, keeping
* libpq_options in memory allows us to avoid copying every keyword
* string.
*/
postgres_fdw_options = (PgFdwOption *)
malloc(sizeof(PgFdwOption) * num_libpq_opts +
sizeof(non_libpq_options));
if (postgres_fdw_options == NULL)
ereport(ERROR,
(errcode(ERRCODE_FDW_OUT_OF_MEMORY),
errmsg("out of memory")));
popt = postgres_fdw_options;
for (lopt = libpq_options; lopt->keyword; lopt++)
{
/* Hide debug options, as well as settings we override internally. */
if (strchr(lopt->dispchar, 'D') ||
strcmp(lopt->keyword, "fallback_application_name") == 0 ||
strcmp(lopt->keyword, "client_encoding") == 0)
continue;
/* We don't have to copy keyword string, as described above. */
popt->keyword = lopt->keyword;
/*
* "user" and any secret options are allowed only on user mappings.
* Everything else is a server option.
*/
if (strcmp(lopt->keyword, "user") == 0 || strchr(lopt->dispchar, '*'))
popt->optcontext = UserMappingRelationId;
else
popt->optcontext = ForeignServerRelationId;
popt->is_libpq_opt = true;
popt++;
}
/* Append FDW-specific options and dummy terminator. */
memcpy(popt, non_libpq_options, sizeof(non_libpq_options));
}
/*
* Check whether the given option is one of the valid postgres_fdw options.
* context is the Oid of the catalog holding the object the option is for.
*/
static bool
is_valid_option(const char *keyword, Oid context)
{
PgFdwOption *opt;
Assert(postgres_fdw_options); /* must be initialized already */
for (opt = postgres_fdw_options; opt->keyword; opt++)
{
if (context == opt->optcontext && strcmp(opt->keyword, keyword) == 0)
return true;
}
return false;
}
/*
* Check whether the given option is one of the valid libpq options.
*/
static bool
is_libpq_option(const char *keyword)
{
PgFdwOption *opt;
Assert(postgres_fdw_options); /* must be initialized already */
for (opt = postgres_fdw_options; opt->keyword; opt++)
{
if (opt->is_libpq_opt && strcmp(opt->keyword, keyword) == 0)
return true;
}
return false;
}
/*
* Generate key-value arrays which include only libpq options from the
* given list (which can contain any kind of options). Caller must have
* allocated large-enough arrays. Returns number of options found.
*/
int
ExtractConnectionOptions(List *defelems, const char **keywords,
const char **values)
{
ListCell *lc;
int i;
/* Build our options lists if we didn't yet. */
InitPgFdwOptions();
i = 0;
foreach(lc, defelems)
{
DefElem *d = (DefElem *) lfirst(lc);
if (is_libpq_option(d->defname))
{
keywords[i] = d->defname;
values[i] = defGetString(d);
i++;
}
}
return i;
}
/* contrib/postgres_fdw/postgres_fdw--1.0.sql */
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "CREATE EXTENSION postgres_fdw" to load this file. \quit
CREATE FUNCTION postgres_fdw_handler()
RETURNS fdw_handler
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
CREATE FUNCTION postgres_fdw_validator(text[], oid)
RETURNS void
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
CREATE FOREIGN DATA WRAPPER postgres_fdw
HANDLER postgres_fdw_handler
VALIDATOR postgres_fdw_validator;
This diff is collapsed.
# postgres_fdw extension
comment = 'foreign-data wrapper for remote PostgreSQL servers'
default_version = '1.0'
module_pathname = '$libdir/postgres_fdw'
relocatable = true
/*-------------------------------------------------------------------------
*
* postgres_fdw.h
* Foreign-data wrapper for remote PostgreSQL servers
*
* Portions Copyright (c) 2012-2013, PostgreSQL Global Development Group
*
* IDENTIFICATION
* contrib/postgres_fdw/postgres_fdw.h
*
*-------------------------------------------------------------------------
*/
#ifndef POSTGRES_FDW_H
#define POSTGRES_FDW_H
#include "foreign/foreign.h"
#include "lib/stringinfo.h"
#include "nodes/relation.h"
#include "utils/rel.h"
#include "libpq-fe.h"
/* in connection.c */
extern PGconn *GetConnection(ForeignServer *server, UserMapping *user);
extern void ReleaseConnection(PGconn *conn);
extern unsigned int GetCursorNumber(PGconn *conn);
extern void pgfdw_report_error(int elevel, PGresult *res, bool clear,
const char *sql);
/* in option.c */
extern int ExtractConnectionOptions(List *defelems,
const char **keywords,
const char **values);
/* in deparse.c */
extern void classifyConditions(PlannerInfo *root,
RelOptInfo *baserel,
List **remote_conds,
List **param_conds,
List **local_conds,
List **param_numbers);
extern void deparseSimpleSql(StringInfo buf,
PlannerInfo *root,
RelOptInfo *baserel,
List *local_conds);
extern void appendWhereClause(StringInfo buf,
bool has_where,
List *exprs,
PlannerInfo *root);
extern void deparseAnalyzeSql(StringInfo buf, Relation rel);
#endif /* POSTGRES_FDW_H */
This diff is collapsed.
......@@ -1098,7 +1098,7 @@ omicron bryanh guest1
<replaceable>servicename</> can be set on the server side using the
<xref linkend="guc-krb-srvname"> configuration parameter, and on the
client side using the <literal>krbsrvname</> connection parameter. (See
also <xref linkend="libpq-connect">.) The installation default can be
also <xref linkend="libpq-paramkeywords">.) The installation default can be
changed from the default <literal>postgres</literal> at build time using
<literal>./configure --with-krb-srvnam=</><replaceable>whatever</>.
In most environments,
......
......@@ -132,6 +132,7 @@ CREATE EXTENSION <replaceable>module_name</> FROM unpackaged;
&pgstatstatements;
&pgstattuple;
&pgtrgm;
&postgres-fdw;
&seg;
&sepgsql;
&contrib-spi;
......
......@@ -8,11 +8,16 @@
</indexterm>
<para>
<filename>dblink</> is a module which supports connections to
<filename>dblink</> is a module that supports connections to
other <productname>PostgreSQL</> databases from within a database
session.
</para>
<para>
See also <xref linkend="postgres-fdw">, which provides roughly the same
functionality using a more modern and standards-compliant infrastructure.
</para>
<refentry id="CONTRIB-DBLINK-CONNECT">
<refmeta>
<refentrytitle>dblink_connect</refentrytitle>
......@@ -47,12 +52,10 @@ dblink_connect(text connname, text connstr) returns text
<para>
The connection string may also be the name of an existing foreign
server. It is recommended to use the foreign-data wrapper
<literal>dblink_fdw</literal> when defining the corresponding foreign
server. See the example below, as well as the following:
<simplelist type="inline">
<member><xref linkend="sql-createserver"></member>
<member><xref linkend="sql-createusermapping"></member>
</simplelist>
<literal>dblink_fdw</literal> when defining the foreign
server. See the example below, as well as
<xref linkend="sql-createserver"> and
<xref linkend="sql-createusermapping">.
</para>
</refsect1>
......@@ -77,8 +80,8 @@ dblink_connect(text connname, text connstr) returns text
<para><application>libpq</>-style connection info string, for example
<literal>hostaddr=127.0.0.1 port=5432 dbname=mydb user=postgres
password=mypasswd</>.
For details see <function>PQconnectdb</> in
<xref linkend="libpq-connect">.
For details see <xref linkend="libpq-connstring">.
Alternatively, the name of a foreign server.
</para>
</listitem>
</varlistentry>
......@@ -133,9 +136,10 @@ SELECT dblink_connect('myconn', 'dbname=postgres');
-- ERROR: password is required
-- DETAIL: Non-superuser cannot connect if the server does not request a password.
-- HINT: Target server's authentication method must be changed.
CREATE USER dblink_regression_test WITH PASSWORD 'secret';
CREATE SERVER fdtest FOREIGN DATA WRAPPER dblink_fdw OPTIONS (hostaddr '127.0.0.1', dbname 'contrib_regression');
CREATE USER dblink_regression_test WITH PASSWORD 'secret';
CREATE USER MAPPING FOR dblink_regression_test SERVER fdtest OPTIONS (user 'dblink_regression_test', password 'secret');
GRANT USAGE ON FOREIGN SERVER fdtest TO dblink_regression_test;
GRANT SELECT ON TABLE foo TO dblink_regression_test;
......
......@@ -134,6 +134,7 @@
<!ENTITY pgtesttiming SYSTEM "pgtesttiming.sgml">
<!ENTITY pgtrgm SYSTEM "pgtrgm.sgml">
<!ENTITY pgupgrade SYSTEM "pgupgrade.sgml">
<!ENTITY postgres-fdw SYSTEM "postgres-fdw.sgml">
<!ENTITY seg SYSTEM "seg.sgml">
<!ENTITY contrib-spi SYSTEM "contrib-spi.sgml">
<!ENTITY sepgsql SYSTEM "sepgsql.sgml">
......
......@@ -6941,7 +6941,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
<para>
The file uses an <quote>INI file</quote> format where the section
name is the service name and the parameters are connection
parameters; see <xref linkend="libpq-connect"> for a list. For
parameters; see <xref linkend="libpq-paramkeywords"> for a list. For
example:
<programlisting>
# comment
......
This diff is collapsed.
......@@ -699,7 +699,7 @@ SELECT *
WHERE proname LIKE 'bytea%';
</programlisting>
The <xref linkend="CONTRIB-DBLINK-FUNCTION"> function
(part of the <xref linkend="dblink"> module>) executes
(part of the <xref linkend="dblink"> module) executes
a remote query. It is declared to return
<type>record</> since it might be used for any kind of query.
The actual column set must be specified in the calling query so
......
......@@ -314,8 +314,7 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows
<para>
Specifies a connection string to be used for the standby server
to connect with the primary. This string is in the format
accepted by the libpq <function>PQconnectdb</function> function,
described in <xref linkend="libpq-connect">. If any option is
described in <xref linkend="libpq-connstring">. If any option is
unspecified in this string, then the corresponding environment
variable (see <xref linkend="libpq-envars">) is checked. If the
environment variable is not set either, then
......
......@@ -121,14 +121,6 @@ CREATE FOREIGN DATA WRAPPER <replaceable class="parameter">name</replaceable>
There is no support for updating a foreign table, and optimization of
queries is primitive (and mostly left to the wrapper, too).
</para>
<para>
There is one built-in foreign-data wrapper validator function
provided:
<filename>postgresql_fdw_validator</filename>, which accepts
options corresponding to <application>libpq</> connection
parameters.
</para>
</refsect1>
<refsect1>
......
......@@ -32,7 +32,7 @@ CREATE FOREIGN TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name
<title>Description</title>
<para>
<command>CREATE FOREIGN TABLE</command> will create a new foreign table
<command>CREATE FOREIGN TABLE</command> creates a new foreign table
in the current database. The table will be owned by the user issuing the
command.
</para>
......@@ -54,8 +54,9 @@ CREATE FOREIGN TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name
</para>
<para>
To be able to create a table, you must have <literal>USAGE</literal>
privilege on all column types.
To be able to create a foreign table, you must have <literal>USAGE</literal>
privilege on the foreign server, as well as <literal>USAGE</literal>
privilege on all column types used in the table.
</para>
</refsect1>
......@@ -134,7 +135,7 @@ CREATE FOREIGN TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name
<term><replaceable class="PARAMETER">server_name</replaceable></term>
<listitem>
<para>
The name of an existing server for the foreign table.
The name of an existing foreign server to use for the foreign table.
For details on defining a server, see <xref
linkend="SQL-CREATESERVER">.
</para>
......@@ -164,7 +165,8 @@ CREATE FOREIGN TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name
<title>Examples</title>
<para>
Create foreign table <structname>films</> with <structname>film_server</>:
Create foreign table <structname>films</>, which will be accessed through
the server <structname>film_server</>:
<programlisting>
CREATE FOREIGN TABLE films (
......
......@@ -110,11 +110,10 @@ CREATE SERVER <replaceable class="parameter">server_name</replaceable> [ TYPE '<
<title>Notes</title>
<para>
When using the <application>dblink</application> module
(see <xref linkend="dblink">), the foreign server name can be used
When using the <xref linkend="dblink"> module,
a foreign server's name can be used
as an argument of the <xref linkend="contrib-dblink-connect">
function to indicate the connection parameters. See also there for
more examples. It is necessary to have
function to indicate the connection parameters. It is necessary to have
the <literal>USAGE</literal> privilege on the foreign server to be
able to use it in this way.
</para>
......@@ -124,19 +123,13 @@ CREATE SERVER <replaceable class="parameter">server_name</replaceable> [ TYPE '<
<title>Examples</title>
<para>
Create a server <literal>foo</> that uses the built-in foreign-data
wrapper <literal>default</>:
Create a server <literal>myserver</> that uses the
foreign-data wrapper <literal>postgres_fdw</>:
<programlisting>
CREATE SERVER foo FOREIGN DATA WRAPPER "default";
CREATE SERVER myserver FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host 'foo', dbname 'foodb', port '5432');
</programlisting>
See <xref linkend="postgres-fdw"> for more details.
</para>
<para>
Create a server <literal>myserver</> that uses the
foreign-data wrapper <literal>pgsql</>:
<programlisting>
CREATE SERVER myserver FOREIGN DATA WRAPPER pgsql OPTIONS (host 'foo', dbname 'foodb', port '5432');
</programlisting></para>
</refsect1>
<refsect1>
......@@ -154,6 +147,7 @@ CREATE SERVER myserver FOREIGN DATA WRAPPER pgsql OPTIONS (host 'foo', dbname 'f
<member><xref linkend="sql-alterserver"></member>
<member><xref linkend="sql-dropserver"></member>
<member><xref linkend="sql-createforeigndatawrapper"></member>
<member><xref linkend="sql-createforeigntable"></member>
<member><xref linkend="sql-createusermapping"></member>
</simplelist>
</refsect1>
......
......@@ -357,10 +357,9 @@ GRANT <replaceable class="PARAMETER">role_name</replaceable> [, ...] TO <replace
to create new servers using that foreign-data wrapper.
</para>
<para>
For servers, this privilege enables the grantee to create,
alter, and drop his own user's user mappings associated with
that server. Also, it enables the grantee to query the options
of the server and associated user mappings.
For servers, this privilege enables the grantee to create foreign
tables using the server, and also to create, alter, or drop his own
user's user mappings associated with that server.
</para>
</listitem>
</varlistentry>
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment