Peter Eisentraut wrote:
> Martin Pihlak wrote:
>>> I would call it something like
>>>
>>> pg_postgresql_fdw_options_string(server, user) returns text
>>
>> Hmm, it is probably a good idea to avoid the fdw abbreviation -- the term
>> "foreign data wrapper" is already confusing enough. My suggestion:
>>
>> pg_foreign_server_conninfo(server)
>> pg_foreign_server_conninfo(server,user)
>>
>> If there are no objections, I'll whack those functions out, and bring
>> the dblink
>> patch up to date.
> 
> Sure, propose some code.  (Note that you can use parameter default
> values now.)
> 

Proposal attached. This adds two C functions:

List *GetForeignConnectionOptions(Oid serverid, Oid userid);
char *GetForeignConnectionString(Oid serverid, Oid userid);

One for obtaining all of the connection related options as a list, and
another for transforming these options into a libpq conninfo string.
The latter should be useful for dblink (although the userid and serverid
need to be obtained first).

On top of those there are two SQL accessible functions:

pg_foreign_connection_options(server name, user name = current_user,
        OUT option_class text, OUT option_name text, OUT option_value text);

pg_foreign_connection_string(server name, user  name = current_user);

These should initially be restricted from ordinary users -- grant explicitly
if the user should see the connect strings. Otherwise use from security definer
functions. The pg_foreign_connection_options() exposes all of the connection
options and can be used by clients such as DBI link to construct the connect
string or equivalent. pg_foreign_connection_string() can be used for instance
by plpythonu or plperlu functions to connect to remote postgres database.

Example:

select * from pg_foreign_connection_options('foo');

 option_class | option_name | option_value
--------------+-------------+--------------
 server       | host        | localhost
 server       | port        | 54321
 server       | dbname      | foo
 user mapping | user        | bob
 user mapping | password    | secret
(5 rows)

select * from pg_foreign_connection_string('foo');

                      pg_foreign_connection_string
-------------------------------------------------------------------------
 host='localhost' port='54321' dbname='foo' user='bob' password='secret'
(1 row)

Will add regression and tests if this is acceptable.

PS. I'm not sure if I nailed the "proargdefaults" syntax correctly in pg_proc.h,
for now I just copied it out from a sample function with similar arguments.

regards,
Martin

*** a/src/backend/foreign/foreign.c
--- b/src/backend/foreign/foreign.c
***************
*** 31,36 ****
--- 31,38 ----
  
  
  extern Datum pg_options_to_table(PG_FUNCTION_ARGS);
+ extern Datum pg_foreign_connection_string(PG_FUNCTION_ARGS);
+ extern Datum pg_foreign_connection_options(PG_FUNCTION_ARGS);
  
  
  /* list of currently loaded foreign-data wrapper interfaces */
***************
*** 321,338 **** GetUserMapping(Oid userid, Oid serverid)
  }
  
  
  /*
!  * deflist_to_tuplestore - Helper function to convert DefElem list to
   * tuplestore usable in SRF.
   */
  static void
! deflist_to_tuplestore(ReturnSetInfo *rsinfo, List *options)
  {
  	ListCell   *cell;
  	TupleDesc	tupdesc;
  	Tuplestorestate *tupstore;
! 	Datum		values[2];
! 	bool		nulls[2] = { 0 };
  	MemoryContext per_query_ctx;
  	MemoryContext oldcontext;
  
--- 323,447 ----
  }
  
  
+ /* 
+  * Helper for appending a ForeignConnectionOption node to a list.
+  */
+ static List *
+ append_option_list(List *options, GenericOptionFlags type, DefElem *def)
+ {
+ 	ForeignConnectionOption	   *opt = makeNode(ForeignConnectionOption);
+ 
+ 	opt->opttype = type;
+ 	opt->option = def;
+ 	return lappend(options, opt);
+ }
+ 
+ 
+ /*
+  * GetForeignConnectionOptions - look up the options for foreign connection.
+  *
+  * Foreign connection is defined by the foreign data wrapper, server and
+  * user mapping triple. The options are simply merged together into a list
+  * of ForeignConnectionOption nodes. 
+  *
+  * The role specified by userid must have a user mapping and USAGE privilege
+  * on the server.
+  */
+ List *
+ GetForeignConnectionOptions(Oid serverid, Oid userid)
+ {
+ 	ForeignServer	   *server;
+ 	UserMapping		   *um;
+ 	ForeignDataWrapper *fdw;
+ 	List			   *result = NIL;
+ 	ListCell		   *cell;
+ 	AclResult			aclresult;
+ 
+ 	server = GetForeignServer(serverid);
+ 	um = GetUserMapping(userid, serverid);
+ 	fdw	= GetForeignDataWrapper(server->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, server->servername);
+ 
+ 	/* Seems OK, prepare a list of all the options */
+ 	foreach (cell, fdw->options)
+ 		result = append_option_list(result, FdwOpt, lfirst(cell));
+ 	foreach (cell, server->options)
+ 		result = append_option_list(result, ServerOpt, lfirst(cell));
+ 	foreach (cell, um->options)
+ 		result = append_option_list(result, UserMappingOpt, lfirst(cell));
+ 
+ 	return result;
+ }
+ 
+ 
+ /*
+  * GetForeignConnectionString - return a libpq conninfo string for the 
+  * foreign connection.
+  *
+  * Currently this just means transforming all of the fdw, server and user
+  * mapping options into a single string. No attempt is made to check if the
+  * resulting conninfo string is valid.
+  */
+ char *
+ GetForeignConnectionString(Oid serverid, Oid userid)
+ {
+ 	StringInfo		stringptr = makeStringInfo();
+ 	ListCell	   *cell;
+ 
+ 	foreach (cell, GetForeignConnectionOptions(serverid, userid))
+ 	{
+ 		ForeignConnectionOption	   *opt = lfirst(cell);
+ 
+ 		appendStringInfo(stringptr, "%s%s='%s'",
+ 						 (stringptr->len > 0) ? " " : "",
+ 						 opt->option->defname,
+ 						 strVal(opt->option->arg));
+ 	}
+ 
+ 	return stringptr->data;
+ }
+ 
+ 
+ /* 
+  * Helper function for obtaining description of the option type.
+  */
+ static const char *
+ optclass_name(GenericOptionFlags opttype)
+ {
+ 	switch (opttype)
+ 	{
+ 		case FdwOpt:
+ 			return "foreign data wrapper";
+ 		case ServerOpt:
+ 			return "server";
+ 		case UserMappingOpt:
+ 			return "user mapping";
+ 		default:
+ 			return "unknown";
+ 	}
+ 
+ 	/* not reached */
+ 	return NULL;
+ }
+ 
  /*
!  * list_to_tuplestore - Helper function to convert a list to
   * tuplestore usable in SRF.
+  *
+  * All the list elements are assumed to nodes of type "type".
   */
  static void
! list_to_tuplestore(ReturnSetInfo *rsinfo, List *options, NodeTag type)
  {
  	ListCell   *cell;
  	TupleDesc	tupdesc;
  	Tuplestorestate *tupstore;
! 	Datum		values[3];
! 	bool		nulls[3] = { 0 };
  	MemoryContext per_query_ctx;
  	MemoryContext oldcontext;
  
***************
*** 360,369 **** deflist_to_tuplestore(ReturnSetInfo *rsinfo, List *options)
  
  	foreach (cell, options)
  	{
! 		DefElem	   *def = lfirst(cell);
  
- 		values[0] = CStringGetTextDatum(def->defname);
- 		values[1] = CStringGetTextDatum(((Value *)def->arg)->val.str);
  		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
  	}
  
--- 469,502 ----
  
  	foreach (cell, options)
  	{
! 		NodeTag		nodeType = ((Node *)lfirst(cell))->type;
! 
! 		Assert(nodeType == type);
! 
! 		if (nodeType == T_DefElem)
! 		{
! 			/* Simple key/value list */
! 			DefElem *def = lfirst(cell);
! 
! 			values[0] = CStringGetTextDatum(def->defname);
! 			values[1] = CStringGetTextDatum(strVal(def->arg));
! 		}
! 		else if (nodeType == T_ForeignConnectionOption)
! 		{
! 			/* Foreign connection option list - class, key, value */
! 			ForeignConnectionOption *opt = lfirst(cell);
! 
! 			values[0] = CStringGetTextDatum(optclass_name(opt->opttype));
! 			values[1] = CStringGetTextDatum(opt->option->defname);
! 			values[2] = CStringGetTextDatum(strVal(opt->option->arg));
! 		}
! 		else
! 		{
! 			ereport(ERROR,
! 					(errcode(ERRCODE_INTERNAL_ERROR),
! 					 errmsg("unrecognized node type %d in list_to_tuplestore", type)));
! 		}
  
  		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
  	}
  
***************
*** 383,389 **** pg_options_to_table(PG_FUNCTION_ARGS)
  {
  	Datum array = PG_GETARG_DATUM(0);
  
! 	deflist_to_tuplestore((ReturnSetInfo *) fcinfo->resultinfo, untransformRelOptions(array));
  
  	return (Datum) 0;
  }
--- 516,562 ----
  {
  	Datum array = PG_GETARG_DATUM(0);
  
! 	list_to_tuplestore((ReturnSetInfo *) fcinfo->resultinfo, untransformRelOptions(array), T_DefElem);
! 
! 	return (Datum) 0;
! }
! 
! 
! /*
!  * Return the libpq connection string for specified server and user.
!  */
! Datum
! pg_foreign_connection_string(PG_FUNCTION_ARGS)
! {
! 	Name	servername = PG_GETARG_NAME(0);
! 	Name	username = PG_GETARG_NAME(1);
! 	Oid		serverid;
! 	Oid		userid;
! 
! 	userid = get_roleid_checked(NameStr(*username));
! 	serverid = GetForeignServerOidByName(NameStr(*servername), false);
! 
! 	return CStringGetTextDatum(GetForeignConnectionString(serverid, userid));
! }
! 
! 
! /*
!  * Return the connection options for specified server and user.
!  */
! Datum
! pg_foreign_connection_options(PG_FUNCTION_ARGS)
! {
! 	Name	servername = PG_GETARG_NAME(0);
! 	Name	username = PG_GETARG_NAME(1);
! 	Oid		serverid;
! 	Oid		userid;
! 	List   *options;
! 
! 	userid = get_roleid_checked(NameStr(*username));
! 	serverid = GetForeignServerOidByName(NameStr(*servername), false);
! 	options = GetForeignConnectionOptions(serverid, userid);
! 
! 	list_to_tuplestore((ReturnSetInfo *) fcinfo->resultinfo, options, T_ForeignConnectionOption);
  
  	return (Datum) 0;
  }
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 2319,2324 **** DESCR("list of SQL keywords");
--- 2319,2330 ----
  DATA(insert OID = 2289 (  pg_options_to_table		PGNSP PGUID 12 1 3 0 f f f t t s 1 0 2249 "1009" "{1009,25,25}" "{i,o,o}" "{options_array,option_name,option_value}" _null_ pg_options_to_table _null_ _null_ _null_ ));
  DESCR("convert generic options array to name/value table");
  
+ DATA(insert OID = 2316 (  pg_foreign_connection_string		PGNSP PGUID 12 1 0 0 f f f t f s 2 1 25 "19 19" _null_ _null_ "{servername,username}" "({FUNCEXPR :funcid 745 :funcresulttype 19 :funcretset false :funcformat 0 :args <> :location 33})" pg_foreign_connection_string _null_ _null_ _null_ ));
+ DESCR("returns the libpq conninfo string for the server and username");
+ 
+ DATA(insert OID = 2319 (  pg_foreign_connection_options		PGNSP PGUID 12 1 5 0 f f f t t s 2 1 2249 "19 19" "{19,19,25,25,25}" "{i,i,o,o,o}" "{servername,username,option_class,option_name,option_value}" "({FUNCEXPR :funcid 745 :funcresulttype 19 :funcretset false :funcformat 0 :args <> :location 33})" pg_foreign_connection_options _null_ _null_ _null_ ));
+ DESCR("returns the foreign connection options for the server and username");
+ 
  DATA(insert OID = 1619 (  pg_typeof				PGNSP PGUID 12 1 0 0 f f f f f s 1 0 2206 "2276" _null_ _null_ _null_ _null_  pg_typeof _null_ _null_ _null_ ));
  DESCR("returns the type of the argument");
  
*** a/src/include/foreign/foreign.h
--- b/src/include/foreign/foreign.h
***************
*** 65,70 **** typedef struct UserMapping
--- 65,78 ----
  } UserMapping;
  
  
+ typedef struct ForeignConnectionOption
+ {
+ 	NodeTag				type;		/* Node type */
+ 	GenericOptionFlags	opttype;	/* Option type, used as integer value, not flags */
+ 	DefElem			   *option;		/* Option name and value */
+ } ForeignConnectionOption;
+ 
+ 
  /*
   * Foreign-data wrapper library function types.
   */
***************
*** 93,98 **** extern ForeignDataWrapper *GetForeignDataWrapperByName(const char *name,
--- 101,108 ----
  													   bool missing_ok);
  extern Oid GetForeignDataWrapperOidByName(const char *name, bool missing_ok);
  extern ForeignDataWrapperLibrary *GetForeignDataWrapperLibrary(const char *libname);
+ extern List *GetForeignConnectionOptions(Oid serverid, Oid userid);
+ extern char *GetForeignConnectionString(Oid serverid, Oid userid);
  
  /* Foreign data wrapper interface functions */
  extern void _pg_validateOptionList(ForeignDataWrapper *fdw,
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
***************
*** 390,396 **** typedef enum NodeTag
  	T_TriggerData = 950,		/* in commands/trigger.h */
  	T_ReturnSetInfo,			/* in nodes/execnodes.h */
  	T_WindowObjectData,			/* private in nodeWindowAgg.c */
! 	T_TIDBitmap					/* in nodes/tidbitmap.h */
  } NodeTag;
  
  /*
--- 390,397 ----
  	T_TriggerData = 950,		/* in commands/trigger.h */
  	T_ReturnSetInfo,			/* in nodes/execnodes.h */
  	T_WindowObjectData,			/* private in nodeWindowAgg.c */
! 	T_TIDBitmap,				/* in nodes/tidbitmap.h */
! 	T_ForeignConnectionOption 	/* private in foreign/foreign.h */
  } NodeTag;
  
  /*
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to