* Darko Prenosil <Darko.Prenosil@finteh.hr>
* Shridhar Daithankar <shridhar_daithankar@persistent.co.in>
*
- * $PostgreSQL: pgsql/contrib/dblink/dblink.c,v 1.78 2009/06/02 03:21:56 joe Exp $
+ * $PostgreSQL: pgsql/contrib/dblink/dblink.c,v 1.79 2009/06/06 21:27:56 joe Exp $
* Copyright (c) 2001-2009, PostgreSQL Global Development Group
* ALL RIGHTS RESERVED;
*
#include "catalog/pg_type.h"
#include "executor/executor.h"
#include "executor/spi.h"
+#include "foreign/foreign.h"
#include "lib/stringinfo.h"
#include "miscadmin.h"
#include "nodes/execnodes.h"
static void dblink_connstr_check(const char *connstr);
static void dblink_security_check(PGconn *conn, remoteConn *rconn);
static void dblink_res_error(const char *conname, PGresult *res, const char *dblink_context_msg, bool fail);
+static char *get_connect_string(const char *servername);
+static char *escape_param_str(const char *from);
/* Global */
static remoteConn *pconn = NULL;
} \
else \
{ \
- connstr = conname_or_str; \
+ connstr = get_connect_string(conname_or_str); \
+ if (connstr == NULL) \
+ { \
+ connstr = conname_or_str; \
+ } \
dblink_connstr_check(connstr); \
conn = PQconnectdb(connstr); \
if (PQstatus(conn) == CONNECTION_BAD) \
Datum
dblink_connect(PG_FUNCTION_ARGS)
{
+ char *conname_or_str = NULL;
char *connstr = NULL;
char *connname = NULL;
char *msg;
if (PG_NARGS() == 2)
{
- connstr = text_to_cstring(PG_GETARG_TEXT_PP(1));
+ conname_or_str = text_to_cstring(PG_GETARG_TEXT_PP(1));
connname = text_to_cstring(PG_GETARG_TEXT_PP(0));
}
else if (PG_NARGS() == 1)
- connstr = text_to_cstring(PG_GETARG_TEXT_PP(0));
+ conname_or_str = text_to_cstring(PG_GETARG_TEXT_PP(0));
if (connname)
rconn = (remoteConn *) MemoryContextAlloc(TopMemoryContext,
sizeof(remoteConn));
+ /* first check for valid foreign data server */
+ connstr = get_connect_string(conname_or_str);
+ if (connstr == NULL)
+ connstr = conname_or_str;
+
/* check password in connection string if not superuser */
dblink_connstr_check(connstr);
conn = PQconnectdb(connstr);
errcontext("Error occurred on dblink connection named \"%s\": %s.",
dblink_context_conname, dblink_context_msg)));
}
+
+/*
+ * Obtain connection string for a foreign server
+ */
+static char *
+get_connect_string(const char *servername)
+{
+ ForeignServer *foreign_server = NULL;
+ UserMapping *user_mapping;
+ ListCell *cell;
+ StringInfo buf = makeStringInfo();
+ ForeignDataWrapper *fdw;
+ AclResult aclresult;
+
+ /* first gather the server connstr options */
+ if (strlen(servername) < NAMEDATALEN)
+ foreign_server = GetForeignServerByName(servername, true);
+
+ if (foreign_server)
+ {
+ Oid serverid = foreign_server->serverid;
+ Oid fdwid = foreign_server->fdwid;
+ Oid userid = GetUserId();
+
+ user_mapping = GetUserMapping(userid, serverid);
+ fdw = GetForeignDataWrapper(fdwid);
+
+ /* Check permissions, user must have usage on the server. */
+ aclresult = pg_foreign_server_aclcheck(serverid, userid, ACL_USAGE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_FOREIGN_SERVER, foreign_server->servername);
+
+ foreach (cell, fdw->options)
+ {
+ DefElem *def = lfirst(cell);
+
+ appendStringInfo(buf, "%s='%s' ", def->defname,
+ escape_param_str(strVal(def->arg)));
+ }
+
+ foreach (cell, foreign_server->options)
+ {
+ DefElem *def = lfirst(cell);
+
+ appendStringInfo(buf, "%s='%s' ", def->defname,
+ escape_param_str(strVal(def->arg)));
+ }
+
+ foreach (cell, user_mapping->options)
+ {
+
+ DefElem *def = lfirst(cell);
+
+ appendStringInfo(buf, "%s='%s' ", def->defname,
+ escape_param_str(strVal(def->arg)));
+ }
+
+ return buf->data;
+ }
+ else
+ return NULL;
+}
+
+/*
+ * Escaping libpq connect parameter strings.
+ *
+ * Replaces "'" with "\'" and "\" with "\\".
+ */
+static char *
+escape_param_str(const char *str)
+{
+ const char *cp;
+ StringInfo buf = makeStringInfo();
+
+ for (cp = str; *cp; cp++)
+ {
+ if (*cp == '\\' || *cp == '\'')
+ appendStringInfoChar(buf, '\\');
+ appendStringInfoChar(buf, *cp);
+ }
+
+ return buf->data;
+}
OK
(1 row)
+-- test foreign data wrapper functionality
+CREATE USER dblink_regression_test;
+CREATE FOREIGN DATA WRAPPER postgresql;
+CREATE SERVER fdtest FOREIGN DATA WRAPPER postgresql OPTIONS (dbname 'contrib_regression');
+CREATE USER MAPPING FOR public SERVER fdtest;
+GRANT USAGE ON FOREIGN SERVER fdtest TO dblink_regression_test;
+GRANT EXECUTE ON FUNCTION dblink_connect_u(text, text) TO dblink_regression_test;
+\set ORIGINAL_USER :USER
+\c - dblink_regression_test
+-- should fail
+SELECT dblink_connect('myconn', 'fdtest');
+ERROR: password is required
+DETAIL: Non-superusers must provide a password in the connection string.
+-- should succeed
+SELECT dblink_connect_u('myconn', 'fdtest');
+ dblink_connect_u
+------------------
+ OK
+(1 row)
+
+SELECT * FROM dblink('myconn','SELECT * FROM foo') AS t(a int, b text, c text[]);
+ a | b | c
+----+---+---------------
+ 0 | a | {a0,b0,c0}
+ 1 | b | {a1,b1,c1}
+ 2 | c | {a2,b2,c2}
+ 3 | d | {a3,b3,c3}
+ 4 | e | {a4,b4,c4}
+ 5 | f | {a5,b5,c5}
+ 6 | g | {a6,b6,c6}
+ 7 | h | {a7,b7,c7}
+ 8 | i | {a8,b8,c8}
+ 9 | j | {a9,b9,c9}
+ 10 | k | {a10,b10,c10}
+(11 rows)
+
+\c - :ORIGINAL_USER
+REVOKE USAGE ON FOREIGN SERVER fdtest FROM dblink_regression_test;
+REVOKE EXECUTE ON FUNCTION dblink_connect_u(text, text) FROM dblink_regression_test;
+DROP USER dblink_regression_test;
+DROP USER MAPPING FOR public SERVER fdtest;
+DROP SERVER fdtest;
+DROP FOREIGN DATA WRAPPER postgresql;
SELECT dblink_cancel_query('dtest1');
SELECT dblink_error_message('dtest1');
SELECT dblink_disconnect('dtest1');
+
+-- test foreign data wrapper functionality
+CREATE USER dblink_regression_test;
+
+CREATE FOREIGN DATA WRAPPER postgresql;
+CREATE SERVER fdtest FOREIGN DATA WRAPPER postgresql OPTIONS (dbname 'contrib_regression');
+CREATE USER MAPPING FOR public SERVER fdtest;
+GRANT USAGE ON FOREIGN SERVER fdtest TO dblink_regression_test;
+GRANT EXECUTE ON FUNCTION dblink_connect_u(text, text) TO dblink_regression_test;
+
+\set ORIGINAL_USER :USER
+\c - dblink_regression_test
+-- should fail
+SELECT dblink_connect('myconn', 'fdtest');
+-- should succeed
+SELECT dblink_connect_u('myconn', 'fdtest');
+SELECT * FROM dblink('myconn','SELECT * FROM foo') AS t(a int, b text, c text[]);
+
+\c - :ORIGINAL_USER
+REVOKE USAGE ON FOREIGN SERVER fdtest FROM dblink_regression_test;
+REVOKE EXECUTE ON FUNCTION dblink_connect_u(text, text) FROM dblink_regression_test;
+DROP USER dblink_regression_test;
+DROP USER MAPPING FOR public SERVER fdtest;
+DROP SERVER fdtest;
+DROP FOREIGN DATA WRAPPER postgresql;
-<!-- $PostgreSQL: pgsql/doc/src/sgml/dblink.sgml,v 1.6 2008/11/12 15:52:44 petere Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/dblink.sgml,v 1.7 2009/06/06 21:27:56 joe Exp $ -->
<sect1 id="dblink">
<title>dblink</title>
only one unnamed connection is permitted at a time. The connection
will persist until closed or until the database session is ended.
</para>
+
+ <para>
+ The connection string may also be the name of an existing foreign
+ server that utilizes the postgresql_fdw foreign data wrapper library.
+ See the example below, as well as the following:
+ <simplelist type="inline">
+ <member><xref linkend="sql-createforeigndatawrapper" endterm="sql-createforeigndatawrapper-title"></member>
+ <member><xref linkend="sql-createserver" endterm="sql-createserver-title"></member>
+ <member><xref linkend="sql-createusermapping" endterm="sql-createusermapping-title"></member>
+ </simplelist>
+ </para>
+
</refsect1>
<refsect1>
----------------
OK
(1 row)
+
+ -- FOREIGN DATA WRAPPER functionality
+ -- Note: local connection must require password authentication for this to work properly
+ -- Otherwise, you will receive the following error from dblink_connect():
+ -- ----------------------------------------------------------------------
+ -- 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 FOREIGN DATA WRAPPER postgresql;
+ CREATE SERVER fdtest FOREIGN DATA WRAPPER postgresql OPTIONS (hostaddr '127.0.0.1', dbname 'contrib_regression');
+
+ 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;
+
+ \set ORIGINAL_USER :USER
+ \c - dblink_regression_test
+ SELECT dblink_connect('myconn', 'fdtest');
+ dblink_connect
+ ----------------
+ OK
+ (1 row)
+
+ SELECT * FROM dblink('myconn','SELECT * FROM foo') AS t(a int, b text, c text[]);
+ a | b | c
+ ----+---+---------------
+ 0 | a | {a0,b0,c0}
+ 1 | b | {a1,b1,c1}
+ 2 | c | {a2,b2,c2}
+ 3 | d | {a3,b3,c3}
+ 4 | e | {a4,b4,c4}
+ 5 | f | {a5,b5,c5}
+ 6 | g | {a6,b6,c6}
+ 7 | h | {a7,b7,c7}
+ 8 | i | {a8,b8,c8}
+ 9 | j | {a9,b9,c9}
+ 10 | k | {a10,b10,c10}
+ (11 rows)
+
+ \c - :ORIGINAL_USER
+ REVOKE USAGE ON FOREIGN SERVER fdtest FROM dblink_regression_test;
+ REVOKE SELECT ON TABLE foo FROM dblink_regression_test;
+ DROP USER MAPPING FOR dblink_regression_test SERVER fdtest;
+ DROP USER dblink_regression_test;
+ DROP SERVER fdtest;
+ DROP FOREIGN DATA WRAPPER postgresql;
</programlisting>
</refsect1>
</refentry>