mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +03:00
Add support for using SQL/MED compliant FOREIGN DATA WRAPPER, SERVER,
and USER MAPPING as method to supply dblink connect parameters. Per mailing list and PGCon discussions.
This commit is contained in:
@ -8,7 +8,7 @@
|
|||||||
* Darko Prenosil <Darko.Prenosil@finteh.hr>
|
* Darko Prenosil <Darko.Prenosil@finteh.hr>
|
||||||
* Shridhar Daithankar <shridhar_daithankar@persistent.co.in>
|
* 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
|
* Copyright (c) 2001-2009, PostgreSQL Global Development Group
|
||||||
* ALL RIGHTS RESERVED;
|
* ALL RIGHTS RESERVED;
|
||||||
*
|
*
|
||||||
@ -46,6 +46,7 @@
|
|||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "executor/executor.h"
|
#include "executor/executor.h"
|
||||||
#include "executor/spi.h"
|
#include "executor/spi.h"
|
||||||
|
#include "foreign/foreign.h"
|
||||||
#include "lib/stringinfo.h"
|
#include "lib/stringinfo.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "nodes/execnodes.h"
|
#include "nodes/execnodes.h"
|
||||||
@ -96,6 +97,8 @@ static char *generate_relation_name(Oid relid);
|
|||||||
static void dblink_connstr_check(const char *connstr);
|
static void dblink_connstr_check(const char *connstr);
|
||||||
static void dblink_security_check(PGconn *conn, remoteConn *rconn);
|
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 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 */
|
/* Global */
|
||||||
static remoteConn *pconn = NULL;
|
static remoteConn *pconn = NULL;
|
||||||
@ -165,7 +168,11 @@ typedef struct remoteConnHashEnt
|
|||||||
} \
|
} \
|
||||||
else \
|
else \
|
||||||
{ \
|
{ \
|
||||||
connstr = conname_or_str; \
|
connstr = get_connect_string(conname_or_str); \
|
||||||
|
if (connstr == NULL) \
|
||||||
|
{ \
|
||||||
|
connstr = conname_or_str; \
|
||||||
|
} \
|
||||||
dblink_connstr_check(connstr); \
|
dblink_connstr_check(connstr); \
|
||||||
conn = PQconnectdb(connstr); \
|
conn = PQconnectdb(connstr); \
|
||||||
if (PQstatus(conn) == CONNECTION_BAD) \
|
if (PQstatus(conn) == CONNECTION_BAD) \
|
||||||
@ -210,6 +217,7 @@ PG_FUNCTION_INFO_V1(dblink_connect);
|
|||||||
Datum
|
Datum
|
||||||
dblink_connect(PG_FUNCTION_ARGS)
|
dblink_connect(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
|
char *conname_or_str = NULL;
|
||||||
char *connstr = NULL;
|
char *connstr = NULL;
|
||||||
char *connname = NULL;
|
char *connname = NULL;
|
||||||
char *msg;
|
char *msg;
|
||||||
@ -220,16 +228,21 @@ dblink_connect(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
if (PG_NARGS() == 2)
|
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));
|
connname = text_to_cstring(PG_GETARG_TEXT_PP(0));
|
||||||
}
|
}
|
||||||
else if (PG_NARGS() == 1)
|
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)
|
if (connname)
|
||||||
rconn = (remoteConn *) MemoryContextAlloc(TopMemoryContext,
|
rconn = (remoteConn *) MemoryContextAlloc(TopMemoryContext,
|
||||||
sizeof(remoteConn));
|
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 */
|
/* check password in connection string if not superuser */
|
||||||
dblink_connstr_check(connstr);
|
dblink_connstr_check(connstr);
|
||||||
conn = PQconnectdb(connstr);
|
conn = PQconnectdb(connstr);
|
||||||
@ -2353,3 +2366,86 @@ dblink_res_error(const char *conname, PGresult *res, const char *dblink_context_
|
|||||||
errcontext("Error occurred on dblink connection named \"%s\": %s.",
|
errcontext("Error occurred on dblink connection named \"%s\": %s.",
|
||||||
dblink_context_conname, dblink_context_msg)));
|
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;
|
||||||
|
}
|
||||||
|
@ -784,3 +784,46 @@ SELECT dblink_disconnect('dtest1');
|
|||||||
OK
|
OK
|
||||||
(1 row)
|
(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;
|
||||||
|
@ -364,3 +364,28 @@ SELECT * from
|
|||||||
SELECT dblink_cancel_query('dtest1');
|
SELECT dblink_cancel_query('dtest1');
|
||||||
SELECT dblink_error_message('dtest1');
|
SELECT dblink_error_message('dtest1');
|
||||||
SELECT dblink_disconnect('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;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<!-- $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">
|
<sect1 id="dblink">
|
||||||
<title>dblink</title>
|
<title>dblink</title>
|
||||||
@ -42,6 +42,18 @@
|
|||||||
only one unnamed connection is permitted at a time. The connection
|
only one unnamed connection is permitted at a time. The connection
|
||||||
will persist until closed or until the database session is ended.
|
will persist until closed or until the database session is ended.
|
||||||
</para>
|
</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>
|
||||||
|
|
||||||
<refsect1>
|
<refsect1>
|
||||||
@ -113,6 +125,53 @@
|
|||||||
----------------
|
----------------
|
||||||
OK
|
OK
|
||||||
(1 row)
|
(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>
|
</programlisting>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
</refentry>
|
</refentry>
|
||||||
|
Reference in New Issue
Block a user