Age Owner Branch data TLA Line data Source code
1 : : /*
2 : : * dblink.c
3 : : *
4 : : * Functions returning results from a remote database
5 : : *
6 : : * Joe Conway <mail@joeconway.com>
7 : : * And contributors:
8 : : * Darko Prenosil <Darko.Prenosil@finteh.hr>
9 : : * Shridhar Daithankar <shridhar_daithankar@persistent.co.in>
10 : : *
11 : : * contrib/dblink/dblink.c
12 : : * Copyright (c) 2001-2024, PostgreSQL Global Development Group
13 : : * ALL RIGHTS RESERVED;
14 : : *
15 : : * Permission to use, copy, modify, and distribute this software and its
16 : : * documentation for any purpose, without fee, and without a written agreement
17 : : * is hereby granted, provided that the above copyright notice and this
18 : : * paragraph and the following two paragraphs appear in all copies.
19 : : *
20 : : * IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR
21 : : * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
22 : : * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
23 : : * DOCUMENTATION, EVEN IF THE AUTHOR OR DISTRIBUTORS HAVE BEEN ADVISED OF THE
24 : : * POSSIBILITY OF SUCH DAMAGE.
25 : : *
26 : : * THE AUTHOR AND DISTRIBUTORS SPECIFICALLY DISCLAIMS ANY WARRANTIES,
27 : : * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
28 : : * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
29 : : * ON AN "AS IS" BASIS, AND THE AUTHOR AND DISTRIBUTORS HAS NO OBLIGATIONS TO
30 : : * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
31 : : *
32 : : */
33 : : #include "postgres.h"
34 : :
35 : : #include <limits.h>
36 : :
37 : : #include "access/htup_details.h"
38 : : #include "access/relation.h"
39 : : #include "access/reloptions.h"
40 : : #include "access/table.h"
41 : : #include "catalog/namespace.h"
42 : : #include "catalog/pg_foreign_data_wrapper.h"
43 : : #include "catalog/pg_foreign_server.h"
44 : : #include "catalog/pg_type.h"
45 : : #include "catalog/pg_user_mapping.h"
46 : : #include "executor/spi.h"
47 : : #include "foreign/foreign.h"
48 : : #include "funcapi.h"
49 : : #include "lib/stringinfo.h"
50 : : #include "libpq-fe.h"
51 : : #include "libpq/libpq-be.h"
52 : : #include "libpq/libpq-be-fe-helpers.h"
53 : : #include "mb/pg_wchar.h"
54 : : #include "miscadmin.h"
55 : : #include "parser/scansup.h"
56 : : #include "utils/acl.h"
57 : : #include "utils/builtins.h"
58 : : #include "utils/fmgroids.h"
59 : : #include "utils/guc.h"
60 : : #include "utils/lsyscache.h"
61 : : #include "utils/memutils.h"
62 : : #include "utils/rel.h"
63 : : #include "utils/varlena.h"
64 : : #include "utils/wait_event.h"
65 : :
6529 tgl@sss.pgh.pa.us 66 :CBC 9 : PG_MODULE_MAGIC;
67 : :
68 : : typedef struct remoteConn
69 : : {
70 : : PGconn *conn; /* Hold the remote connection */
71 : : int openCursorCount; /* The number of open cursors */
72 : : bool newXactForCursor; /* Opened a transaction for a cursor */
73 : : } remoteConn;
74 : :
75 : : typedef struct storeInfo
76 : : {
77 : : FunctionCallInfo fcinfo;
78 : : Tuplestorestate *tuplestore;
79 : : AttInMetadata *attinmeta;
80 : : MemoryContext tmpcontext;
81 : : char **cstrs;
82 : : /* temp storage for results to avoid leaks on exception */
83 : : PGresult *last_res;
84 : : PGresult *cur_res;
85 : : } storeInfo;
86 : :
87 : : /*
88 : : * Internal declarations
89 : : */
90 : : static Datum dblink_record_internal(FunctionCallInfo fcinfo, bool is_async);
91 : : static void prepTuplestoreResult(FunctionCallInfo fcinfo);
92 : : static void materializeResult(FunctionCallInfo fcinfo, PGconn *conn,
93 : : PGresult *res);
94 : : static void materializeQueryResult(FunctionCallInfo fcinfo,
95 : : PGconn *conn,
96 : : const char *conname,
97 : : const char *sql,
98 : : bool fail);
99 : : static PGresult *storeQueryResult(volatile storeInfo *sinfo, PGconn *conn, const char *sql);
100 : : static void storeRow(volatile storeInfo *sinfo, PGresult *res, bool first);
101 : : static remoteConn *getConnectionByName(const char *name);
102 : : static HTAB *createConnHash(void);
103 : : static void createNewConnection(const char *name, remoteConn *rconn);
104 : : static void deleteConnection(const char *name);
105 : : static char **get_pkey_attnames(Relation rel, int16 *indnkeyatts);
106 : : static char **get_text_array_contents(ArrayType *array, int *numitems);
107 : : static char *get_sql_insert(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals, char **tgt_pkattvals);
108 : : static char *get_sql_delete(Relation rel, int *pkattnums, int pknumatts, char **tgt_pkattvals);
109 : : static char *get_sql_update(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals, char **tgt_pkattvals);
110 : : static char *quote_ident_cstr(char *rawstr);
111 : : static int get_attnum_pk_pos(int *pkattnums, int pknumatts, int key);
112 : : static HeapTuple get_tuple_of_interest(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals);
113 : : static Relation get_rel_from_relname(text *relname_text, LOCKMODE lockmode, AclMode aclmode);
114 : : static char *generate_relation_name(Relation rel);
115 : : static void dblink_connstr_check(const char *connstr);
116 : : static bool dblink_connstr_has_pw(const char *connstr);
117 : : static void dblink_security_check(PGconn *conn, remoteConn *rconn, const char *connstr);
118 : : static void dblink_res_error(PGconn *conn, const char *conname, PGresult *res,
119 : : bool fail, const char *fmt,...) pg_attribute_printf(5, 6);
120 : : static char *get_connect_string(const char *servername);
121 : : static char *escape_param_str(const char *str);
122 : : static void validate_pkattnums(Relation rel,
123 : : int2vector *pkattnums_arg, int32 pknumatts_arg,
124 : : int **pkattnums, int *pknumatts);
125 : : static bool is_valid_dblink_option(const PQconninfoOption *options,
126 : : const char *option, Oid context);
127 : : static int applyRemoteGucs(PGconn *conn);
128 : : static void restoreLocalGucs(int nestlevel);
129 : :
130 : : /* Global */
131 : : static remoteConn *pconn = NULL;
132 : : static HTAB *remoteConnHash = NULL;
133 : :
134 : : /* custom wait event values, retrieved from shared memory */
135 : : static uint32 dblink_we_connect = 0;
136 : : static uint32 dblink_we_get_conn = 0;
137 : : static uint32 dblink_we_get_result = 0;
138 : :
139 : : /*
140 : : * Following is list that holds multiple remote connections.
141 : : * Calling convention of each dblink function changes to accept
142 : : * connection name as the first parameter. The connection list is
143 : : * much like ecpg e.g. a mapping between a name and a PGconn object.
144 : : */
145 : :
146 : : typedef struct remoteConnHashEnt
147 : : {
148 : : char name[NAMEDATALEN];
149 : : remoteConn *rconn;
150 : : } remoteConnHashEnt;
151 : :
152 : : /* initial number of connection hashes */
153 : : #define NUMCONN 16
154 : :
155 : : static char *
2667 peter_e@gmx.net 156 : 48 : xpstrdup(const char *in)
157 : : {
158 [ + + ]: 48 : if (in == NULL)
159 : 36 : return NULL;
160 : 12 : return pstrdup(in);
161 : : }
162 : :
163 : : static void
164 : : pg_attribute_noreturn()
2667 peter_e@gmx.net 165 :UBC 0 : dblink_res_internalerror(PGconn *conn, PGresult *res, const char *p2)
166 : : {
167 : 0 : char *msg = pchomp(PQerrorMessage(conn));
168 : :
651 peter@eisentraut.org 169 : 0 : PQclear(res);
2667 peter_e@gmx.net 170 [ # # ]: 0 : elog(ERROR, "%s: %s", p2, msg);
171 : : }
172 : :
173 : : static void
174 : : pg_attribute_noreturn()
2667 peter_e@gmx.net 175 :CBC 3 : dblink_conn_not_avail(const char *conname)
176 : : {
177 [ + + ]: 3 : if (conname)
178 [ + - ]: 1 : ereport(ERROR,
179 : : (errcode(ERRCODE_CONNECTION_DOES_NOT_EXIST),
180 : : errmsg("connection \"%s\" not available", conname)));
181 : : else
182 [ + - ]: 2 : ereport(ERROR,
183 : : (errcode(ERRCODE_CONNECTION_DOES_NOT_EXIST),
184 : : errmsg("connection not available")));
185 : : }
186 : :
187 : : static void
188 : 38 : dblink_get_conn(char *conname_or_str,
189 : : PGconn *volatile *conn_p, char **conname_p, volatile bool *freeconn_p)
190 : : {
191 : 38 : remoteConn *rconn = getConnectionByName(conname_or_str);
192 : : PGconn *conn;
193 : : char *conname;
194 : : bool freeconn;
195 : :
196 [ + + ]: 38 : if (rconn)
197 : : {
198 : 27 : conn = rconn->conn;
199 : 27 : conname = conname_or_str;
200 : 27 : freeconn = false;
201 : : }
202 : : else
203 : : {
204 : : const char *connstr;
205 : :
206 : 11 : connstr = get_connect_string(conname_or_str);
207 [ + - ]: 11 : if (connstr == NULL)
208 : 11 : connstr = conname_or_str;
209 : 11 : dblink_connstr_check(connstr);
210 : :
211 : : /* first time, allocate or get the custom wait event */
192 michael@paquier.xyz 212 [ + + ]:GNC 9 : if (dblink_we_get_conn == 0)
213 : 5 : dblink_we_get_conn = WaitEventExtensionNew("DblinkGetConnect");
214 : :
215 : : /* OK to make connection */
216 : 9 : conn = libpqsrv_connect(connstr, dblink_we_get_conn);
217 : :
2667 peter_e@gmx.net 218 [ + + ]:CBC 9 : if (PQstatus(conn) == CONNECTION_BAD)
219 : : {
220 : 2 : char *msg = pchomp(PQerrorMessage(conn));
221 : :
447 andres@anarazel.de 222 : 2 : libpqsrv_disconnect(conn);
2667 peter_e@gmx.net 223 [ + - ]: 2 : ereport(ERROR,
224 : : (errcode(ERRCODE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION),
225 : : errmsg("could not establish connection"),
226 : : errdetail_internal("%s", msg)));
227 : : }
367 sfrost@snowman.net 228 : 7 : dblink_security_check(conn, rconn, connstr);
2667 peter_e@gmx.net 229 [ - + ]: 6 : if (PQclientEncoding(conn) != GetDatabaseEncoding())
2667 peter_e@gmx.net 230 :UBC 0 : PQsetClientEncoding(conn, GetDatabaseEncodingName());
2667 peter_e@gmx.net 231 :CBC 6 : freeconn = true;
232 : 6 : conname = NULL;
233 : : }
234 : :
235 : 33 : *conn_p = conn;
236 : 33 : *conname_p = conname;
237 : 33 : *freeconn_p = freeconn;
238 : 33 : }
239 : :
240 : : static PGconn *
241 : 17 : dblink_get_named_conn(const char *conname)
242 : : {
243 : 17 : remoteConn *rconn = getConnectionByName(conname);
244 : :
245 [ + - ]: 17 : if (rconn)
246 : 17 : return rconn->conn;
247 : :
2589 peter_e@gmx.net 248 :UBC 0 : dblink_conn_not_avail(conname);
249 : : return NULL; /* keep compiler quiet */
250 : : }
251 : :
252 : : static void
2667 peter_e@gmx.net 253 :CBC 125 : dblink_init(void)
254 : : {
255 [ + + ]: 125 : if (!pconn)
256 : : {
97 noah@leadboat.com 257 [ + - ]:GNC 8 : if (dblink_we_get_result == 0)
258 : 8 : dblink_we_get_result = WaitEventExtensionNew("DblinkGetResult");
259 : :
2667 peter_e@gmx.net 260 :CBC 8 : pconn = (remoteConn *) MemoryContextAlloc(TopMemoryContext, sizeof(remoteConn));
261 : 8 : pconn->conn = NULL;
262 : 8 : pconn->openCursorCount = 0;
2433 263 : 8 : pconn->newXactForCursor = false;
264 : : }
2667 265 : 125 : }
266 : :
267 : : /*
268 : : * Create a persistent connection to another database
269 : : */
7895 bruce@momjian.us 270 : 13 : PG_FUNCTION_INFO_V1(dblink_connect);
271 : : Datum
272 : 16 : dblink_connect(PG_FUNCTION_ARGS)
273 : : {
5426 mail@joeconway.com 274 : 16 : char *conname_or_str = NULL;
7599 bruce@momjian.us 275 : 16 : char *connstr = NULL;
276 : 16 : char *connname = NULL;
277 : : char *msg;
278 : 16 : PGconn *conn = NULL;
6763 279 : 16 : remoteConn *rconn = NULL;
280 : :
2667 peter_e@gmx.net 281 : 16 : dblink_init();
282 : :
7559 bruce@momjian.us 283 [ + + ]: 16 : if (PG_NARGS() == 2)
284 : : {
5426 mail@joeconway.com 285 : 11 : conname_or_str = text_to_cstring(PG_GETARG_TEXT_PP(1));
5864 tgl@sss.pgh.pa.us 286 : 11 : connname = text_to_cstring(PG_GETARG_TEXT_PP(0));
287 : : }
7559 bruce@momjian.us 288 [ + - ]: 5 : else if (PG_NARGS() == 1)
5426 mail@joeconway.com 289 : 5 : conname_or_str = text_to_cstring(PG_GETARG_TEXT_PP(0));
290 : :
7559 bruce@momjian.us 291 [ + + ]: 16 : if (connname)
292 : : {
5614 tgl@sss.pgh.pa.us 293 : 11 : rconn = (remoteConn *) MemoryContextAlloc(TopMemoryContext,
294 : : sizeof(remoteConn));
1417 mail@joeconway.com 295 : 11 : rconn->conn = NULL;
296 : 11 : rconn->openCursorCount = 0;
297 : 11 : rconn->newXactForCursor = false;
298 : : }
299 : :
300 : : /* first check for valid foreign data server */
5426 301 : 16 : connstr = get_connect_string(conname_or_str);
302 [ + + ]: 16 : if (connstr == NULL)
303 : 14 : connstr = conname_or_str;
304 : :
305 : : /* check password in connection string if not superuser */
5683 tgl@sss.pgh.pa.us 306 : 16 : dblink_connstr_check(connstr);
307 : :
308 : : /* first time, allocate or get the custom wait event */
192 michael@paquier.xyz 309 [ + + ]:GNC 15 : if (dblink_we_connect == 0)
310 : 2 : dblink_we_connect = WaitEventExtensionNew("DblinkConnect");
311 : :
312 : : /* OK to make connection */
313 : 15 : conn = libpqsrv_connect(connstr, dblink_we_connect);
314 : :
7599 bruce@momjian.us 315 [ - + ]:CBC 15 : if (PQstatus(conn) == CONNECTION_BAD)
316 : : {
2603 peter_e@gmx.net 317 :UBC 0 : msg = pchomp(PQerrorMessage(conn));
447 andres@anarazel.de 318 : 0 : libpqsrv_disconnect(conn);
6763 bruce@momjian.us 319 [ # # ]: 0 : if (rconn)
320 : 0 : pfree(rconn);
321 : :
7570 tgl@sss.pgh.pa.us 322 [ # # ]: 0 : ereport(ERROR,
323 : : (errcode(ERRCODE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION),
324 : : errmsg("could not establish connection"),
325 : : errdetail_internal("%s", msg)));
326 : : }
327 : :
328 : : /* check password actually used if not superuser */
367 sfrost@snowman.net 329 :CBC 15 : dblink_security_check(conn, rconn, connstr);
330 : :
331 : : /* attempt to set client encoding to match server encoding, if needed */
3781 mail@joeconway.com 332 [ - + ]: 15 : if (PQclientEncoding(conn) != GetDatabaseEncoding())
3781 mail@joeconway.com 333 :UBC 0 : PQsetClientEncoding(conn, GetDatabaseEncodingName());
334 : :
7559 bruce@momjian.us 335 [ + + ]:CBC 15 : if (connname)
336 : : {
6763 337 : 10 : rconn->conn = conn;
338 : 10 : createNewConnection(connname, rconn);
339 : : }
340 : : else
341 : : {
2591 mail@joeconway.com 342 [ + + ]: 5 : if (pconn->conn)
439 andres@anarazel.de 343 : 1 : libpqsrv_disconnect(pconn->conn);
6753 mail@joeconway.com 344 : 5 : pconn->conn = conn;
345 : : }
346 : :
5864 tgl@sss.pgh.pa.us 347 : 14 : PG_RETURN_TEXT_P(cstring_to_text("OK"));
348 : : }
349 : :
350 : : /*
351 : : * Clear a persistent connection to another database
352 : : */
7895 bruce@momjian.us 353 : 8 : PG_FUNCTION_INFO_V1(dblink_disconnect);
354 : : Datum
355 : 13 : dblink_disconnect(PG_FUNCTION_ARGS)
356 : : {
7570 tgl@sss.pgh.pa.us 357 : 13 : char *conname = NULL;
6763 bruce@momjian.us 358 : 13 : remoteConn *rconn = NULL;
7599 359 : 13 : PGconn *conn = NULL;
360 : :
2667 peter_e@gmx.net 361 : 13 : dblink_init();
362 : :
7559 bruce@momjian.us 363 [ + + ]: 13 : if (PG_NARGS() == 1)
364 : : {
5864 tgl@sss.pgh.pa.us 365 : 9 : conname = text_to_cstring(PG_GETARG_TEXT_PP(0));
6763 bruce@momjian.us 366 : 9 : rconn = getConnectionByName(conname);
367 [ + + ]: 9 : if (rconn)
368 : 8 : conn = rconn->conn;
369 : : }
370 : : else
6753 mail@joeconway.com 371 : 4 : conn = pconn->conn;
372 : :
7599 bruce@momjian.us 373 [ + + ]: 13 : if (!conn)
2667 peter_e@gmx.net 374 : 1 : dblink_conn_not_avail(conname);
375 : :
447 andres@anarazel.de 376 : 12 : libpqsrv_disconnect(conn);
6763 bruce@momjian.us 377 [ + + ]: 12 : if (rconn)
378 : : {
7570 tgl@sss.pgh.pa.us 379 : 8 : deleteConnection(conname);
6763 bruce@momjian.us 380 : 8 : pfree(rconn);
381 : : }
382 : : else
6753 mail@joeconway.com 383 : 4 : pconn->conn = NULL;
384 : :
5864 tgl@sss.pgh.pa.us 385 : 12 : PG_RETURN_TEXT_P(cstring_to_text("OK"));
386 : : }
387 : :
388 : : /*
389 : : * opens a cursor using a persistent connection
390 : : */
7895 bruce@momjian.us 391 : 13 : PG_FUNCTION_INFO_V1(dblink_open);
392 : : Datum
393 : 9 : dblink_open(PG_FUNCTION_ARGS)
394 : : {
7893 395 : 9 : PGresult *res = NULL;
396 : : PGconn *conn;
7599 397 : 9 : char *curname = NULL;
398 : 9 : char *sql = NULL;
399 : 9 : char *conname = NULL;
400 : : StringInfoData buf;
6763 401 : 9 : remoteConn *rconn = NULL;
7343 mail@joeconway.com 402 : 9 : bool fail = true; /* default to backward compatible behavior */
403 : :
2667 peter_e@gmx.net 404 : 9 : dblink_init();
6619 neilc@samurai.com 405 : 9 : initStringInfo(&buf);
406 : :
7559 bruce@momjian.us 407 [ + + ]: 9 : if (PG_NARGS() == 2)
408 : : {
409 : : /* text,text */
5864 tgl@sss.pgh.pa.us 410 : 2 : curname = text_to_cstring(PG_GETARG_TEXT_PP(0));
411 : 2 : sql = text_to_cstring(PG_GETARG_TEXT_PP(1));
6753 mail@joeconway.com 412 : 2 : rconn = pconn;
413 : : }
7559 bruce@momjian.us 414 [ + + ]: 7 : else if (PG_NARGS() == 3)
415 : : {
416 : : /* might be text,text,text or text,text,bool */
7343 mail@joeconway.com 417 [ + + ]: 6 : if (get_fn_expr_argtype(fcinfo->flinfo, 2) == BOOLOID)
418 : : {
5864 tgl@sss.pgh.pa.us 419 : 1 : curname = text_to_cstring(PG_GETARG_TEXT_PP(0));
420 : 1 : sql = text_to_cstring(PG_GETARG_TEXT_PP(1));
7343 mail@joeconway.com 421 : 1 : fail = PG_GETARG_BOOL(2);
6753 422 : 1 : rconn = pconn;
423 : : }
424 : : else
425 : : {
5864 tgl@sss.pgh.pa.us 426 : 5 : conname = text_to_cstring(PG_GETARG_TEXT_PP(0));
427 : 5 : curname = text_to_cstring(PG_GETARG_TEXT_PP(1));
428 : 5 : sql = text_to_cstring(PG_GETARG_TEXT_PP(2));
6763 bruce@momjian.us 429 : 5 : rconn = getConnectionByName(conname);
430 : : }
431 : : }
7343 mail@joeconway.com 432 [ + - ]: 1 : else if (PG_NARGS() == 4)
433 : : {
434 : : /* text,text,text,bool */
5864 tgl@sss.pgh.pa.us 435 : 1 : conname = text_to_cstring(PG_GETARG_TEXT_PP(0));
436 : 1 : curname = text_to_cstring(PG_GETARG_TEXT_PP(1));
437 : 1 : sql = text_to_cstring(PG_GETARG_TEXT_PP(2));
7343 mail@joeconway.com 438 : 1 : fail = PG_GETARG_BOOL(3);
6763 bruce@momjian.us 439 : 1 : rconn = getConnectionByName(conname);
440 : : }
441 : :
6753 mail@joeconway.com 442 [ + - - + ]: 9 : if (!rconn || !rconn->conn)
2667 peter_e@gmx.net 443 :UBC 0 : dblink_conn_not_avail(conname);
444 : :
2566 peter_e@gmx.net 445 :CBC 9 : conn = rconn->conn;
446 : :
447 : : /* If we are not in a transaction, start one */
6753 mail@joeconway.com 448 [ + + ]: 9 : if (PQtransactionStatus(conn) == PQTRANS_IDLE)
449 : : {
97 noah@leadboat.com 450 :GNC 7 : res = libpqsrv_exec(conn, "BEGIN", dblink_we_get_result);
6753 mail@joeconway.com 451 [ - + ]:CBC 7 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
2667 peter_e@gmx.net 452 :UBC 0 : dblink_res_internalerror(conn, res, "begin error");
6753 mail@joeconway.com 453 :CBC 7 : PQclear(res);
2433 peter_e@gmx.net 454 : 7 : rconn->newXactForCursor = true;
455 : :
456 : : /*
457 : : * Since transaction state was IDLE, we force cursor count to
458 : : * initially be 0. This is needed as a previous ABORT might have wiped
459 : : * out our transaction without maintaining the cursor count for us.
460 : : */
6507 mail@joeconway.com 461 : 7 : rconn->openCursorCount = 0;
462 : : }
463 : :
464 : : /* if we started a transaction, increment cursor count */
6753 465 [ + - ]: 9 : if (rconn->newXactForCursor)
466 : 9 : (rconn->openCursorCount)++;
467 : :
6619 neilc@samurai.com 468 : 9 : appendStringInfo(&buf, "DECLARE %s CURSOR FOR %s", curname, sql);
97 noah@leadboat.com 469 :GNC 9 : res = libpqsrv_exec(conn, buf.data, dblink_we_get_result);
7343 mail@joeconway.com 470 [ + - + + ]:CBC 9 : if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
471 : : {
2215 tgl@sss.pgh.pa.us 472 : 2 : dblink_res_error(conn, conname, res, fail,
473 : : "while opening cursor \"%s\"", curname);
5764 mail@joeconway.com 474 : 2 : PG_RETURN_TEXT_P(cstring_to_text("ERROR"));
475 : : }
476 : :
7599 bruce@momjian.us 477 : 7 : PQclear(res);
5864 tgl@sss.pgh.pa.us 478 : 7 : PG_RETURN_TEXT_P(cstring_to_text("OK"));
479 : : }
480 : :
481 : : /*
482 : : * closes a cursor
483 : : */
7895 bruce@momjian.us 484 : 10 : PG_FUNCTION_INFO_V1(dblink_close);
485 : : Datum
486 : 5 : dblink_close(PG_FUNCTION_ARGS)
487 : : {
488 : : PGconn *conn;
7893 489 : 5 : PGresult *res = NULL;
7599 490 : 5 : char *curname = NULL;
491 : 5 : char *conname = NULL;
492 : : StringInfoData buf;
6763 493 : 5 : remoteConn *rconn = NULL;
7343 mail@joeconway.com 494 : 5 : bool fail = true; /* default to backward compatible behavior */
495 : :
2667 peter_e@gmx.net 496 : 5 : dblink_init();
6619 neilc@samurai.com 497 : 5 : initStringInfo(&buf);
498 : :
7599 bruce@momjian.us 499 [ - + ]: 5 : if (PG_NARGS() == 1)
500 : : {
501 : : /* text */
5864 tgl@sss.pgh.pa.us 502 :UBC 0 : curname = text_to_cstring(PG_GETARG_TEXT_PP(0));
6753 mail@joeconway.com 503 : 0 : rconn = pconn;
504 : : }
7559 bruce@momjian.us 505 [ + - ]:CBC 5 : else if (PG_NARGS() == 2)
506 : : {
507 : : /* might be text,text or text,bool */
7343 mail@joeconway.com 508 [ + + ]: 5 : if (get_fn_expr_argtype(fcinfo->flinfo, 1) == BOOLOID)
509 : : {
5864 tgl@sss.pgh.pa.us 510 : 2 : curname = text_to_cstring(PG_GETARG_TEXT_PP(0));
7343 mail@joeconway.com 511 : 2 : fail = PG_GETARG_BOOL(1);
6753 512 : 2 : rconn = pconn;
513 : : }
514 : : else
515 : : {
5864 tgl@sss.pgh.pa.us 516 : 3 : conname = text_to_cstring(PG_GETARG_TEXT_PP(0));
517 : 3 : curname = text_to_cstring(PG_GETARG_TEXT_PP(1));
6763 bruce@momjian.us 518 : 3 : rconn = getConnectionByName(conname);
519 : : }
520 : : }
7343 mail@joeconway.com 521 [ - + ]: 5 : if (PG_NARGS() == 3)
522 : : {
523 : : /* text,text,bool */
5864 tgl@sss.pgh.pa.us 524 :UBC 0 : conname = text_to_cstring(PG_GETARG_TEXT_PP(0));
525 : 0 : curname = text_to_cstring(PG_GETARG_TEXT_PP(1));
7343 mail@joeconway.com 526 : 0 : fail = PG_GETARG_BOOL(2);
6763 bruce@momjian.us 527 : 0 : rconn = getConnectionByName(conname);
528 : : }
529 : :
6753 mail@joeconway.com 530 [ + - - + ]:CBC 5 : if (!rconn || !rconn->conn)
2667 peter_e@gmx.net 531 :UBC 0 : dblink_conn_not_avail(conname);
532 : :
2566 peter_e@gmx.net 533 :CBC 5 : conn = rconn->conn;
534 : :
6619 neilc@samurai.com 535 : 5 : appendStringInfo(&buf, "CLOSE %s", curname);
536 : :
537 : : /* close the cursor */
97 noah@leadboat.com 538 :GNC 5 : res = libpqsrv_exec(conn, buf.data, dblink_we_get_result);
7893 bruce@momjian.us 539 [ + - + + ]:CBC 5 : if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
540 : : {
2215 tgl@sss.pgh.pa.us 541 : 1 : dblink_res_error(conn, conname, res, fail,
542 : : "while closing cursor \"%s\"", curname);
5764 mail@joeconway.com 543 : 1 : PG_RETURN_TEXT_P(cstring_to_text("ERROR"));
544 : : }
545 : :
7895 bruce@momjian.us 546 : 4 : PQclear(res);
547 : :
548 : : /* if we started a transaction, decrement cursor count */
6753 mail@joeconway.com 549 [ + - ]: 4 : if (rconn->newXactForCursor)
550 : : {
551 : 4 : (rconn->openCursorCount)--;
552 : :
553 : : /* if count is zero, commit the transaction */
554 [ + + ]: 4 : if (rconn->openCursorCount == 0)
555 : : {
2433 peter_e@gmx.net 556 : 2 : rconn->newXactForCursor = false;
557 : :
97 noah@leadboat.com 558 :GNC 2 : res = libpqsrv_exec(conn, "COMMIT", dblink_we_get_result);
6753 mail@joeconway.com 559 [ - + ]:CBC 2 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
2667 peter_e@gmx.net 560 :UBC 0 : dblink_res_internalerror(conn, res, "commit error");
6753 mail@joeconway.com 561 :CBC 2 : PQclear(res);
562 : : }
563 : : }
564 : :
5864 tgl@sss.pgh.pa.us 565 : 4 : PG_RETURN_TEXT_P(cstring_to_text("OK"));
566 : : }
567 : :
568 : : /*
569 : : * Fetch results from an open cursor
570 : : */
7895 bruce@momjian.us 571 : 13 : PG_FUNCTION_INFO_V1(dblink_fetch);
572 : : Datum
573 : 13 : dblink_fetch(PG_FUNCTION_ARGS)
574 : : {
5161 575 : 13 : PGresult *res = NULL;
576 : 13 : char *conname = NULL;
577 : 13 : remoteConn *rconn = NULL;
578 : 13 : PGconn *conn = NULL;
579 : : StringInfoData buf;
580 : 13 : char *curname = NULL;
581 : 13 : int howmany = 0;
582 : 13 : bool fail = true; /* default to backward compatible */
583 : :
4394 tgl@sss.pgh.pa.us 584 : 13 : prepTuplestoreResult(fcinfo);
585 : :
2667 peter_e@gmx.net 586 : 13 : dblink_init();
587 : :
5194 mail@joeconway.com 588 [ + + ]: 13 : if (PG_NARGS() == 4)
589 : : {
590 : : /* text,text,int,bool */
591 : 1 : conname = text_to_cstring(PG_GETARG_TEXT_PP(0));
592 : 1 : curname = text_to_cstring(PG_GETARG_TEXT_PP(1));
593 : 1 : howmany = PG_GETARG_INT32(2);
594 : 1 : fail = PG_GETARG_BOOL(3);
595 : :
596 : 1 : rconn = getConnectionByName(conname);
597 [ + - ]: 1 : if (rconn)
598 : 1 : conn = rconn->conn;
599 : : }
600 [ + + ]: 12 : else if (PG_NARGS() == 3)
601 : : {
602 : : /* text,text,int or text,int,bool */
603 [ + + ]: 8 : if (get_fn_expr_argtype(fcinfo->flinfo, 2) == BOOLOID)
604 : : {
605 : 2 : curname = text_to_cstring(PG_GETARG_TEXT_PP(0));
606 : 2 : howmany = PG_GETARG_INT32(1);
607 : 2 : fail = PG_GETARG_BOOL(2);
608 : 2 : conn = pconn->conn;
609 : : }
610 : : else
611 : : {
5864 tgl@sss.pgh.pa.us 612 : 6 : conname = text_to_cstring(PG_GETARG_TEXT_PP(0));
613 : 6 : curname = text_to_cstring(PG_GETARG_TEXT_PP(1));
7599 bruce@momjian.us 614 : 6 : howmany = PG_GETARG_INT32(2);
615 : :
6763 616 : 6 : rconn = getConnectionByName(conname);
617 [ + - ]: 6 : if (rconn)
618 : 6 : conn = rconn->conn;
619 : : }
620 : : }
5194 mail@joeconway.com 621 [ + - ]: 4 : else if (PG_NARGS() == 2)
622 : : {
623 : : /* text,int */
624 : 4 : curname = text_to_cstring(PG_GETARG_TEXT_PP(0));
625 : 4 : howmany = PG_GETARG_INT32(1);
626 : 4 : conn = pconn->conn;
627 : : }
628 : :
629 [ - + ]: 13 : if (!conn)
2667 peter_e@gmx.net 630 :UBC 0 : dblink_conn_not_avail(conname);
631 : :
5194 mail@joeconway.com 632 :CBC 13 : initStringInfo(&buf);
633 : 13 : appendStringInfo(&buf, "FETCH %d FROM %s", howmany, curname);
634 : :
635 : : /*
636 : : * Try to execute the query. Note that since libpq uses malloc, the
637 : : * PGresult will be long-lived even though we are still in a short-lived
638 : : * memory context.
639 : : */
97 noah@leadboat.com 640 :GNC 13 : res = libpqsrv_exec(conn, buf.data, dblink_we_get_result);
5194 mail@joeconway.com 641 [ + - + - ]:CBC 26 : if (!res ||
642 [ + + ]: 26 : (PQresultStatus(res) != PGRES_COMMAND_OK &&
643 : 13 : PQresultStatus(res) != PGRES_TUPLES_OK))
644 : : {
2215 tgl@sss.pgh.pa.us 645 : 5 : dblink_res_error(conn, conname, res, fail,
646 : : "while fetching from cursor \"%s\"", curname);
5194 mail@joeconway.com 647 : 3 : return (Datum) 0;
648 : : }
649 [ - + ]: 8 : else if (PQresultStatus(res) == PGRES_COMMAND_OK)
650 : : {
651 : : /* cursor does not exist - closed already or bad name */
7895 bruce@momjian.us 652 :UBC 0 : PQclear(res);
5194 mail@joeconway.com 653 [ # # ]: 0 : ereport(ERROR,
654 : : (errcode(ERRCODE_INVALID_CURSOR_NAME),
655 : : errmsg("cursor \"%s\" does not exist", curname)));
656 : : }
657 : :
4041 tgl@sss.pgh.pa.us 658 :CBC 8 : materializeResult(fcinfo, conn, res);
5194 mail@joeconway.com 659 : 7 : return (Datum) 0;
660 : : }
661 : :
662 : : /*
663 : : * Note: this is the new preferred version of dblink
664 : : */
7895 bruce@momjian.us 665 : 20 : PG_FUNCTION_INFO_V1(dblink_record);
666 : : Datum
667 : 30 : dblink_record(PG_FUNCTION_ARGS)
668 : : {
5430 mail@joeconway.com 669 : 30 : return dblink_record_internal(fcinfo, false);
670 : : }
671 : :
6434 672 : 4 : PG_FUNCTION_INFO_V1(dblink_send_query);
673 : : Datum
674 : 6 : dblink_send_query(PG_FUNCTION_ARGS)
675 : : {
676 : : PGconn *conn;
677 : : char *sql;
678 : : int retval;
679 : :
5430 680 [ + - ]: 6 : if (PG_NARGS() == 2)
681 : : {
2667 peter_e@gmx.net 682 : 6 : conn = dblink_get_named_conn(text_to_cstring(PG_GETARG_TEXT_PP(0)));
5430 mail@joeconway.com 683 : 6 : sql = text_to_cstring(PG_GETARG_TEXT_PP(1));
684 : : }
685 : : else
686 : : /* shouldn't happen */
5430 mail@joeconway.com 687 [ # # ]:UBC 0 : elog(ERROR, "wrong number of arguments");
688 : :
689 : : /* async query send */
5430 mail@joeconway.com 690 :CBC 6 : retval = PQsendQuery(conn, sql);
691 [ - + ]: 6 : if (retval != 1)
2603 peter_e@gmx.net 692 [ # # ]:UBC 0 : elog(NOTICE, "could not send query: %s", pchomp(PQerrorMessage(conn)));
693 : :
5430 mail@joeconway.com 694 :CBC 6 : PG_RETURN_INT32(retval);
695 : : }
696 : :
6434 697 : 6 : PG_FUNCTION_INFO_V1(dblink_get_result);
698 : : Datum
699 : 8 : dblink_get_result(PG_FUNCTION_ARGS)
700 : : {
5430 701 : 8 : return dblink_record_internal(fcinfo, true);
702 : : }
703 : :
704 : : static Datum
705 : 38 : dblink_record_internal(FunctionCallInfo fcinfo, bool is_async)
706 : : {
4393 tgl@sss.pgh.pa.us 707 : 38 : PGconn *volatile conn = NULL;
708 : 38 : volatile bool freeconn = false;
709 : :
4394 710 : 38 : prepTuplestoreResult(fcinfo);
711 : :
2667 peter_e@gmx.net 712 : 38 : dblink_init();
713 : :
4393 tgl@sss.pgh.pa.us 714 [ + + ]: 38 : PG_TRY();
715 : : {
716 : 38 : char *sql = NULL;
717 : 38 : char *conname = NULL;
718 : 38 : bool fail = true; /* default to backward compatible */
719 : :
720 [ + + ]: 38 : if (!is_async)
721 : : {
722 [ + + ]: 30 : if (PG_NARGS() == 3)
723 : : {
724 : : /* text,text,bool */
2574 peter_e@gmx.net 725 : 1 : conname = text_to_cstring(PG_GETARG_TEXT_PP(0));
4393 tgl@sss.pgh.pa.us 726 : 1 : sql = text_to_cstring(PG_GETARG_TEXT_PP(1));
727 : 1 : fail = PG_GETARG_BOOL(2);
2574 peter_e@gmx.net 728 : 1 : dblink_get_conn(conname, &conn, &conname, &freeconn);
729 : : }
4393 tgl@sss.pgh.pa.us 730 [ + + ]: 29 : else if (PG_NARGS() == 2)
731 : : {
732 : : /* text,text or text,bool */
733 [ + + ]: 22 : if (get_fn_expr_argtype(fcinfo->flinfo, 1) == BOOLOID)
734 : : {
735 : 1 : sql = text_to_cstring(PG_GETARG_TEXT_PP(0));
736 : 1 : fail = PG_GETARG_BOOL(1);
2574 peter_e@gmx.net 737 : 1 : conn = pconn->conn;
738 : : }
739 : : else
740 : : {
741 : 21 : conname = text_to_cstring(PG_GETARG_TEXT_PP(0));
4393 tgl@sss.pgh.pa.us 742 : 21 : sql = text_to_cstring(PG_GETARG_TEXT_PP(1));
2574 peter_e@gmx.net 743 : 21 : dblink_get_conn(conname, &conn, &conname, &freeconn);
744 : : }
745 : : }
4393 tgl@sss.pgh.pa.us 746 [ + - ]: 7 : else if (PG_NARGS() == 1)
747 : : {
748 : : /* text */
5194 mail@joeconway.com 749 : 7 : conn = pconn->conn;
750 : 7 : sql = text_to_cstring(PG_GETARG_TEXT_PP(0));
751 : : }
752 : : else
753 : : /* shouldn't happen */
4393 tgl@sss.pgh.pa.us 754 [ # # ]:UBC 0 : elog(ERROR, "wrong number of arguments");
755 : : }
756 : : else /* is_async */
757 : : {
758 : : /* get async result */
2574 peter_e@gmx.net 759 :CBC 8 : conname = text_to_cstring(PG_GETARG_TEXT_PP(0));
760 : :
4393 tgl@sss.pgh.pa.us 761 [ - + ]: 8 : if (PG_NARGS() == 2)
762 : : {
763 : : /* text,bool */
4393 tgl@sss.pgh.pa.us 764 :UBC 0 : fail = PG_GETARG_BOOL(1);
2574 peter_e@gmx.net 765 : 0 : conn = dblink_get_named_conn(conname);
766 : : }
4393 tgl@sss.pgh.pa.us 767 [ + - ]:CBC 8 : else if (PG_NARGS() == 1)
768 : : {
769 : : /* text */
2574 peter_e@gmx.net 770 : 8 : conn = dblink_get_named_conn(conname);
771 : : }
772 : : else
773 : : /* shouldn't happen */
4393 tgl@sss.pgh.pa.us 774 [ # # ]:UBC 0 : elog(ERROR, "wrong number of arguments");
775 : : }
776 : :
4393 tgl@sss.pgh.pa.us 777 [ + + ]:CBC 33 : if (!conn)
2667 peter_e@gmx.net 778 : 2 : dblink_conn_not_avail(conname);
779 : :
4393 tgl@sss.pgh.pa.us 780 [ + + ]: 31 : if (!is_async)
781 : : {
782 : : /* synchronous query, use efficient tuple collection method */
783 : 23 : materializeQueryResult(fcinfo, conn, conname, sql, fail);
784 : : }
785 : : else
786 : : {
787 : : /* async result retrieval, do it the old way */
97 noah@leadboat.com 788 :GNC 8 : PGresult *res = libpqsrv_get_result(conn, dblink_we_get_result);
789 : :
790 : : /* NULL means we're all done with the async results */
4393 tgl@sss.pgh.pa.us 791 [ + + ]:CBC 8 : if (res)
792 : : {
793 [ + - ]: 5 : if (PQresultStatus(res) != PGRES_COMMAND_OK &&
794 [ - + ]: 5 : PQresultStatus(res) != PGRES_TUPLES_OK)
795 : : {
2215 tgl@sss.pgh.pa.us 796 :UBC 0 : dblink_res_error(conn, conname, res, fail,
797 : : "while executing query");
798 : : /* if fail isn't set, we'll return an empty query result */
799 : : }
800 : : else
801 : : {
4041 tgl@sss.pgh.pa.us 802 :CBC 5 : materializeResult(fcinfo, conn, res);
803 : : }
804 : : }
805 : : }
806 : : }
1626 peter@eisentraut.org 807 : 7 : PG_FINALLY();
808 : : {
809 : : /* if needed, close the connection to the database */
4393 tgl@sss.pgh.pa.us 810 [ + + ]: 38 : if (freeconn)
447 andres@anarazel.de 811 : 5 : libpqsrv_disconnect(conn);
812 : : }
4393 tgl@sss.pgh.pa.us 813 [ + + ]: 38 : PG_END_TRY();
814 : :
5194 mail@joeconway.com 815 : 31 : return (Datum) 0;
816 : : }
817 : :
818 : : /*
819 : : * Verify function caller can handle a tuplestore result, and set up for that.
820 : : *
821 : : * Note: if the caller returns without actually creating a tuplestore, the
822 : : * executor will treat the function result as an empty set.
823 : : */
824 : : static void
4394 tgl@sss.pgh.pa.us 825 : 51 : prepTuplestoreResult(FunctionCallInfo fcinfo)
826 : : {
827 : 51 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
828 : :
829 : : /* check to see if query supports us returning a tuplestore */
830 [ + - - + ]: 51 : if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
4394 tgl@sss.pgh.pa.us 831 [ # # ]:UBC 0 : ereport(ERROR,
832 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
833 : : errmsg("set-valued function called in context that cannot accept a set")));
4394 tgl@sss.pgh.pa.us 834 [ - + ]:CBC 51 : if (!(rsinfo->allowedModes & SFRM_Materialize))
4394 tgl@sss.pgh.pa.us 835 [ # # ]:UBC 0 : ereport(ERROR,
836 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
837 : : errmsg("materialize mode required, but it is not allowed in this context")));
838 : :
839 : : /* let the executor know we're sending back a tuplestore */
4394 tgl@sss.pgh.pa.us 840 :CBC 51 : rsinfo->returnMode = SFRM_Materialize;
841 : :
842 : : /* caller must fill these to return a non-empty result */
843 : 51 : rsinfo->setResult = NULL;
844 : 51 : rsinfo->setDesc = NULL;
845 : 51 : }
846 : :
847 : : /*
848 : : * Copy the contents of the PGresult into a tuplestore to be returned
849 : : * as the result of the current function.
850 : : * The PGresult will be released in this function.
851 : : */
852 : : static void
4041 853 : 13 : materializeResult(FunctionCallInfo fcinfo, PGconn *conn, PGresult *res)
854 : : {
5161 bruce@momjian.us 855 : 13 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
856 : :
857 : : /* prepTuplestoreResult must have been called previously */
5194 mail@joeconway.com 858 [ - + ]: 13 : Assert(rsinfo->returnMode == SFRM_Materialize);
859 : :
860 [ + + ]: 13 : PG_TRY();
861 : : {
862 : : TupleDesc tupdesc;
863 : : bool is_sql_cmd;
864 : : int ntuples;
865 : : int nfields;
866 : :
5421 bruce@momjian.us 867 [ - + ]: 13 : if (PQresultStatus(res) == PGRES_COMMAND_OK)
868 : : {
5421 bruce@momjian.us 869 :UBC 0 : is_sql_cmd = true;
870 : :
871 : : /*
872 : : * need a tuple descriptor representing one TEXT column to return
873 : : * the command status string as our result tuple
874 : : */
1972 andres@anarazel.de 875 : 0 : tupdesc = CreateTemplateTupleDesc(1);
5421 bruce@momjian.us 876 : 0 : TupleDescInitEntry(tupdesc, (AttrNumber) 1, "status",
877 : : TEXTOID, -1, 0);
5194 mail@joeconway.com 878 : 0 : ntuples = 1;
879 : 0 : nfields = 1;
880 : : }
881 : : else
882 : : {
5194 mail@joeconway.com 883 [ - + ]:CBC 13 : Assert(PQresultStatus(res) == PGRES_TUPLES_OK);
884 : :
885 : 13 : is_sql_cmd = false;
886 : :
887 : : /* get a tuple descriptor for our result type */
5421 bruce@momjian.us 888 [ + - - ]: 13 : switch (get_call_result_type(fcinfo, NULL, &tupdesc))
889 : : {
890 : 13 : case TYPEFUNC_COMPOSITE:
891 : : /* success */
892 : 13 : break;
5421 bruce@momjian.us 893 :UBC 0 : case TYPEFUNC_RECORD:
894 : : /* failed to determine actual type of RECORD */
895 [ # # ]: 0 : ereport(ERROR,
896 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
897 : : errmsg("function returning record called in context "
898 : : "that cannot accept type record")));
899 : : break;
900 : 0 : default:
901 : : /* result type isn't composite */
902 [ # # ]: 0 : elog(ERROR, "return type must be a row type");
903 : : break;
904 : : }
905 : :
906 : : /* make sure we have a persistent copy of the tupdesc */
5421 bruce@momjian.us 907 :CBC 13 : tupdesc = CreateTupleDescCopy(tupdesc);
5194 mail@joeconway.com 908 : 13 : ntuples = PQntuples(res);
909 : 13 : nfields = PQnfields(res);
910 : : }
911 : :
912 : : /*
913 : : * check result and tuple descriptor have the same number of columns
914 : : */
915 [ - + ]: 13 : if (nfields != tupdesc->natts)
5421 bruce@momjian.us 916 [ # # ]:UBC 0 : ereport(ERROR,
917 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
918 : : errmsg("remote query result rowtype does not match "
919 : : "the specified FROM clause rowtype")));
920 : :
5194 mail@joeconway.com 921 [ + - ]:CBC 13 : if (ntuples > 0)
922 : : {
923 : : AttInMetadata *attinmeta;
4041 tgl@sss.pgh.pa.us 924 : 13 : int nestlevel = -1;
925 : : Tuplestorestate *tupstore;
926 : : MemoryContext oldcontext;
927 : : int row;
928 : : char **values;
929 : :
5194 mail@joeconway.com 930 : 13 : attinmeta = TupleDescGetAttInMetadata(tupdesc);
931 : :
932 : : /* Set GUCs to ensure we read GUC-sensitive data types correctly */
4041 tgl@sss.pgh.pa.us 933 [ + - ]: 13 : if (!is_sql_cmd)
934 : 13 : nestlevel = applyRemoteGucs(conn);
935 : :
1536 alvherre@alvh.no-ip. 936 : 13 : oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
5194 mail@joeconway.com 937 : 13 : tupstore = tuplestore_begin_heap(true, false, work_mem);
938 : 13 : rsinfo->setResult = tupstore;
939 : 13 : rsinfo->setDesc = tupdesc;
6434 940 : 13 : MemoryContextSwitchTo(oldcontext);
941 : :
580 peter@eisentraut.org 942 : 13 : values = palloc_array(char *, nfields);
943 : :
944 : : /* put all tuples into the tuplestore */
5194 mail@joeconway.com 945 [ + + ]: 49 : for (row = 0; row < ntuples; row++)
946 : : {
947 : : HeapTuple tuple;
948 : :
949 [ + - ]: 37 : if (!is_sql_cmd)
950 : : {
951 : : int i;
952 : :
953 [ + + ]: 138 : for (i = 0; i < nfields; i++)
954 : : {
955 [ - + ]: 101 : if (PQgetisnull(res, row, i))
5194 mail@joeconway.com 956 :UBC 0 : values[i] = NULL;
957 : : else
5194 mail@joeconway.com 958 :CBC 101 : values[i] = PQgetvalue(res, row, i);
959 : : }
960 : : }
961 : : else
962 : : {
5194 mail@joeconway.com 963 :UBC 0 : values[0] = PQcmdStatus(res);
964 : : }
965 : :
966 : : /* build the tuple and put it into the tuplestore. */
5194 mail@joeconway.com 967 :CBC 37 : tuple = BuildTupleFromCStrings(attinmeta, values);
968 : 36 : tuplestore_puttuple(tupstore, tuple);
969 : : }
970 : :
971 : : /* clean up GUC settings, if we changed any */
4041 tgl@sss.pgh.pa.us 972 : 12 : restoreLocalGucs(nestlevel);
973 : : }
974 : : }
1626 peter@eisentraut.org 975 : 1 : PG_FINALLY();
976 : : {
977 : : /* be sure to release the libpq result */
7895 bruce@momjian.us 978 : 13 : PQclear(res);
979 : : }
5194 mail@joeconway.com 980 [ + + ]: 13 : PG_END_TRY();
7895 bruce@momjian.us 981 : 12 : }
982 : :
983 : : /*
984 : : * Execute the given SQL command and store its results into a tuplestore
985 : : * to be returned as the result of the current function.
986 : : *
987 : : * This is equivalent to PQexec followed by materializeResult, but we make
988 : : * use of libpq's single-row mode to avoid accumulating the whole result
989 : : * inside libpq before it gets transferred to the tuplestore.
990 : : */
991 : : static void
4393 tgl@sss.pgh.pa.us 992 : 23 : materializeQueryResult(FunctionCallInfo fcinfo,
993 : : PGconn *conn,
994 : : const char *conname,
995 : : const char *sql,
996 : : bool fail)
997 : : {
998 : 23 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
999 : 23 : PGresult *volatile res = NULL;
1847 peter@eisentraut.org 1000 : 23 : volatile storeInfo sinfo = {0};
1001 : :
1002 : : /* prepTuplestoreResult must have been called previously */
4393 tgl@sss.pgh.pa.us 1003 [ - + ]: 23 : Assert(rsinfo->returnMode == SFRM_Materialize);
1004 : :
4273 1005 : 23 : sinfo.fcinfo = fcinfo;
1006 : :
4393 1007 [ + - ]: 23 : PG_TRY();
1008 : : {
1009 : : /* Create short-lived memory context for data conversions */
3586 mail@joeconway.com 1010 : 23 : sinfo.tmpcontext = AllocSetContextCreate(CurrentMemoryContext,
1011 : : "dblink temporary context",
1012 : : ALLOCSET_DEFAULT_SIZES);
1013 : :
1014 : : /* execute query, collecting any tuples into the tuplestore */
4273 tgl@sss.pgh.pa.us 1015 : 23 : res = storeQueryResult(&sinfo, conn, sql);
1016 : :
4393 1017 [ + - ]: 23 : if (!res ||
1018 [ + - ]: 23 : (PQresultStatus(res) != PGRES_COMMAND_OK &&
1019 [ + + ]: 23 : PQresultStatus(res) != PGRES_TUPLES_OK))
1020 : 2 : {
1021 : : /*
1022 : : * dblink_res_error will clear the passed PGresult, so we need
1023 : : * this ugly dance to avoid doing so twice during error exit
1024 : : */
1025 : 2 : PGresult *res1 = res;
1026 : :
1027 : 2 : res = NULL;
2215 1028 : 2 : dblink_res_error(conn, conname, res1, fail,
1029 : : "while executing query");
1030 : : /* if fail isn't set, we'll return an empty query result */
1031 : : }
4393 1032 [ - + ]: 21 : else if (PQresultStatus(res) == PGRES_COMMAND_OK)
1033 : : {
1034 : : /*
1035 : : * storeRow didn't get called, so we need to convert the command
1036 : : * status string to a tuple manually
1037 : : */
1038 : : TupleDesc tupdesc;
1039 : : AttInMetadata *attinmeta;
1040 : : Tuplestorestate *tupstore;
1041 : : HeapTuple tuple;
1042 : : char *values[1];
1043 : : MemoryContext oldcontext;
1044 : :
1045 : : /*
1046 : : * need a tuple descriptor representing one TEXT column to return
1047 : : * the command status string as our result tuple
1048 : : */
1972 andres@anarazel.de 1049 :UBC 0 : tupdesc = CreateTemplateTupleDesc(1);
4393 tgl@sss.pgh.pa.us 1050 : 0 : TupleDescInitEntry(tupdesc, (AttrNumber) 1, "status",
1051 : : TEXTOID, -1, 0);
1052 : 0 : attinmeta = TupleDescGetAttInMetadata(tupdesc);
1053 : :
1536 alvherre@alvh.no-ip. 1054 : 0 : oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
4393 tgl@sss.pgh.pa.us 1055 : 0 : tupstore = tuplestore_begin_heap(true, false, work_mem);
1056 : 0 : rsinfo->setResult = tupstore;
1057 : 0 : rsinfo->setDesc = tupdesc;
1058 : 0 : MemoryContextSwitchTo(oldcontext);
1059 : :
1060 : 0 : values[0] = PQcmdStatus(res);
1061 : :
1062 : : /* build the tuple and put it into the tuplestore. */
1063 : 0 : tuple = BuildTupleFromCStrings(attinmeta, values);
1064 : 0 : tuplestore_puttuple(tupstore, tuple);
1065 : :
1066 : 0 : PQclear(res);
4273 1067 : 0 : res = NULL;
1068 : : }
1069 : : else
1070 : : {
4393 tgl@sss.pgh.pa.us 1071 [ - + ]:CBC 21 : Assert(PQresultStatus(res) == PGRES_TUPLES_OK);
1072 : : /* storeRow should have created a tuplestore */
1073 [ - + ]: 21 : Assert(rsinfo->setResult != NULL);
1074 : :
1075 : 21 : PQclear(res);
4273 1076 : 21 : res = NULL;
1077 : : }
1078 : :
1079 : : /* clean up data conversion short-lived memory context */
3586 mail@joeconway.com 1080 [ + - ]: 23 : if (sinfo.tmpcontext != NULL)
1081 : 23 : MemoryContextDelete(sinfo.tmpcontext);
1082 : 23 : sinfo.tmpcontext = NULL;
1083 : :
4273 tgl@sss.pgh.pa.us 1084 : 23 : PQclear(sinfo.last_res);
1085 : 23 : sinfo.last_res = NULL;
1086 : 23 : PQclear(sinfo.cur_res);
1087 : 23 : sinfo.cur_res = NULL;
1088 : : }
4393 tgl@sss.pgh.pa.us 1089 :UBC 0 : PG_CATCH();
1090 : : {
1091 : : /* be sure to release any libpq result we collected */
4273 1092 : 0 : PQclear(res);
1093 : 0 : PQclear(sinfo.last_res);
1094 : 0 : PQclear(sinfo.cur_res);
1095 : : /* and clear out any pending data in libpq */
97 noah@leadboat.com 1096 [ # # ]:UNC 0 : while ((res = libpqsrv_get_result(conn, dblink_we_get_result)) !=
1097 : : NULL)
4393 tgl@sss.pgh.pa.us 1098 :UBC 0 : PQclear(res);
1099 : 0 : PG_RE_THROW();
1100 : : }
4393 tgl@sss.pgh.pa.us 1101 [ - + ]:CBC 23 : PG_END_TRY();
1102 : 23 : }
1103 : :
1104 : : /*
1105 : : * Execute query, and send any result rows to sinfo->tuplestore.
1106 : : */
1107 : : static PGresult *
3366 1108 : 23 : storeQueryResult(volatile storeInfo *sinfo, PGconn *conn, const char *sql)
1109 : : {
4273 1110 : 23 : bool first = true;
4041 1111 : 23 : int nestlevel = -1;
1112 : : PGresult *res;
1113 : :
4273 1114 [ - + ]: 23 : if (!PQsendQuery(conn, sql))
2603 peter_e@gmx.net 1115 [ # # ]:UBC 0 : elog(ERROR, "could not send query: %s", pchomp(PQerrorMessage(conn)));
1116 : :
2489 tgl@sss.pgh.pa.us 1117 [ - + ]:CBC 23 : if (!PQsetSingleRowMode(conn)) /* shouldn't fail */
4273 tgl@sss.pgh.pa.us 1118 [ # # ]:UBC 0 : elog(ERROR, "failed to set single-row mode for dblink query");
1119 : :
1120 : : for (;;)
1121 : : {
4273 tgl@sss.pgh.pa.us 1122 [ - + ]:CBC 180 : CHECK_FOR_INTERRUPTS();
1123 : :
97 noah@leadboat.com 1124 :GNC 180 : sinfo->cur_res = libpqsrv_get_result(conn, dblink_we_get_result);
4273 tgl@sss.pgh.pa.us 1125 [ + + ]:CBC 180 : if (!sinfo->cur_res)
1126 : 23 : break;
1127 : :
1128 [ + + ]: 157 : if (PQresultStatus(sinfo->cur_res) == PGRES_SINGLE_TUPLE)
1129 : : {
1130 : : /* got one row from possibly-bigger resultset */
1131 : :
1132 : : /*
1133 : : * Set GUCs to ensure we read GUC-sensitive data types correctly.
1134 : : * We shouldn't do this until we have a row in hand, to ensure
1135 : : * libpq has seen any earlier ParameterStatus protocol messages.
1136 : : */
4041 1137 [ + + + - ]: 134 : if (first && nestlevel < 0)
1138 : 21 : nestlevel = applyRemoteGucs(conn);
1139 : :
4273 1140 : 134 : storeRow(sinfo, sinfo->cur_res, first);
1141 : :
1142 : 134 : PQclear(sinfo->cur_res);
1143 : 134 : sinfo->cur_res = NULL;
1144 : 134 : first = false;
1145 : : }
1146 : : else
1147 : : {
1148 : : /* if empty resultset, fill tuplestore header */
1149 [ + + - + ]: 23 : if (first && PQresultStatus(sinfo->cur_res) == PGRES_TUPLES_OK)
4273 tgl@sss.pgh.pa.us 1150 :UBC 0 : storeRow(sinfo, sinfo->cur_res, first);
1151 : :
1152 : : /* store completed result at last_res */
4273 tgl@sss.pgh.pa.us 1153 :CBC 23 : PQclear(sinfo->last_res);
1154 : 23 : sinfo->last_res = sinfo->cur_res;
1155 : 23 : sinfo->cur_res = NULL;
1156 : 23 : first = true;
1157 : : }
1158 : : }
1159 : :
1160 : : /* clean up GUC settings, if we changed any */
4041 1161 : 23 : restoreLocalGucs(nestlevel);
1162 : :
1163 : : /* return last_res */
4273 1164 : 23 : res = sinfo->last_res;
1165 : 23 : sinfo->last_res = NULL;
1166 : 23 : return res;
1167 : : }
1168 : :
1169 : : /*
1170 : : * Send single row to sinfo->tuplestore.
1171 : : *
1172 : : * If "first" is true, create the tuplestore using PGresult's metadata
1173 : : * (in this case the PGresult might contain either zero or one row).
1174 : : */
1175 : : static void
3366 1176 : 134 : storeRow(volatile storeInfo *sinfo, PGresult *res, bool first)
1177 : : {
4393 1178 : 134 : int nfields = PQnfields(res);
1179 : : HeapTuple tuple;
1180 : : int i;
1181 : : MemoryContext oldcontext;
1182 : :
4273 1183 [ + + ]: 134 : if (first)
1184 : : {
1185 : : /* Prepare for new result set */
4393 1186 : 21 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) sinfo->fcinfo->resultinfo;
1187 : : TupleDesc tupdesc;
1188 : :
1189 : : /*
1190 : : * It's possible to get more than one result set if the query string
1191 : : * contained multiple SQL commands. In that case, we follow PQexec's
1192 : : * traditional behavior of throwing away all but the last result.
1193 : : */
1194 [ - + ]: 21 : if (sinfo->tuplestore)
4393 tgl@sss.pgh.pa.us 1195 :UBC 0 : tuplestore_end(sinfo->tuplestore);
4393 tgl@sss.pgh.pa.us 1196 :CBC 21 : sinfo->tuplestore = NULL;
1197 : :
1198 : : /* get a tuple descriptor for our result type */
1199 [ + - - ]: 21 : switch (get_call_result_type(sinfo->fcinfo, NULL, &tupdesc))
1200 : : {
1201 : 21 : case TYPEFUNC_COMPOSITE:
1202 : : /* success */
1203 : 21 : break;
4393 tgl@sss.pgh.pa.us 1204 :UBC 0 : case TYPEFUNC_RECORD:
1205 : : /* failed to determine actual type of RECORD */
1206 [ # # ]: 0 : ereport(ERROR,
1207 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1208 : : errmsg("function returning record called in context "
1209 : : "that cannot accept type record")));
1210 : : break;
1211 : 0 : default:
1212 : : /* result type isn't composite */
1213 [ # # ]: 0 : elog(ERROR, "return type must be a row type");
1214 : : break;
1215 : : }
1216 : :
1217 : : /* make sure we have a persistent copy of the tupdesc */
4393 tgl@sss.pgh.pa.us 1218 :CBC 21 : tupdesc = CreateTupleDescCopy(tupdesc);
1219 : :
1220 : : /* check result and tuple descriptor have the same number of columns */
1221 [ - + ]: 21 : if (nfields != tupdesc->natts)
4393 tgl@sss.pgh.pa.us 1222 [ # # ]:UBC 0 : ereport(ERROR,
1223 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
1224 : : errmsg("remote query result rowtype does not match "
1225 : : "the specified FROM clause rowtype")));
1226 : :
1227 : : /* Prepare attinmeta for later data conversions */
4393 tgl@sss.pgh.pa.us 1228 :CBC 21 : sinfo->attinmeta = TupleDescGetAttInMetadata(tupdesc);
1229 : :
1230 : : /* Create a new, empty tuplestore */
4273 1231 : 21 : oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
4393 1232 : 21 : sinfo->tuplestore = tuplestore_begin_heap(true, false, work_mem);
1233 : 21 : rsinfo->setResult = sinfo->tuplestore;
1234 : 21 : rsinfo->setDesc = tupdesc;
1235 : 21 : MemoryContextSwitchTo(oldcontext);
1236 : :
1237 : : /* Done if empty resultset */
4273 1238 [ - + ]: 21 : if (PQntuples(res) == 0)
4273 tgl@sss.pgh.pa.us 1239 :UBC 0 : return;
1240 : :
1241 : : /*
1242 : : * Set up sufficiently-wide string pointers array; this won't change
1243 : : * in size so it's easy to preallocate.
1244 : : */
4393 tgl@sss.pgh.pa.us 1245 [ - + ]:CBC 21 : if (sinfo->cstrs)
4393 tgl@sss.pgh.pa.us 1246 :UBC 0 : pfree(sinfo->cstrs);
580 peter@eisentraut.org 1247 :CBC 21 : sinfo->cstrs = palloc_array(char *, nfields);
1248 : : }
1249 : :
1250 : : /* Should have a single-row result if we get here */
4273 tgl@sss.pgh.pa.us 1251 [ - + ]: 134 : Assert(PQntuples(res) == 1);
1252 : :
1253 : : /*
1254 : : * Do the following work in a temp context that we reset after each tuple.
1255 : : * This cleans up not only the data we have direct access to, but any
1256 : : * cruft the I/O functions might leak.
1257 : : */
4393 1258 : 134 : oldcontext = MemoryContextSwitchTo(sinfo->tmpcontext);
1259 : :
1260 : : /*
1261 : : * Fill cstrs with null-terminated strings of column values.
1262 : : */
1263 [ + + ]: 514 : for (i = 0; i < nfields; i++)
1264 : : {
4273 1265 [ - + ]: 380 : if (PQgetisnull(res, 0, i))
4273 tgl@sss.pgh.pa.us 1266 :UBC 0 : sinfo->cstrs[i] = NULL;
1267 : : else
4273 tgl@sss.pgh.pa.us 1268 :CBC 380 : sinfo->cstrs[i] = PQgetvalue(res, 0, i);
1269 : : }
1270 : :
1271 : : /* Convert row to a tuple, and add it to the tuplestore */
1272 : 134 : tuple = BuildTupleFromCStrings(sinfo->attinmeta, sinfo->cstrs);
1273 : :
4393 1274 : 134 : tuplestore_puttuple(sinfo->tuplestore, tuple);
1275 : :
1276 : : /* Clean up */
1277 : 134 : MemoryContextSwitchTo(oldcontext);
1278 : 134 : MemoryContextReset(sinfo->tmpcontext);
1279 : : }
1280 : :
1281 : : /*
1282 : : * List all open dblink connections by name.
1283 : : * Returns an array of all connection names.
1284 : : * Takes no params
1285 : : */
6434 mail@joeconway.com 1286 : 3 : PG_FUNCTION_INFO_V1(dblink_get_connections);
1287 : : Datum
1288 : 1 : dblink_get_connections(PG_FUNCTION_ARGS)
1289 : : {
1290 : : HASH_SEQ_STATUS status;
1291 : : remoteConnHashEnt *hentry;
6402 bruce@momjian.us 1292 : 1 : ArrayBuildState *astate = NULL;
1293 : :
6434 mail@joeconway.com 1294 [ + - ]: 1 : if (remoteConnHash)
1295 : : {
1296 : 1 : hash_seq_init(&status, remoteConnHash);
1297 [ + + ]: 4 : while ((hentry = (remoteConnHashEnt *) hash_seq_search(&status)) != NULL)
1298 : : {
1299 : : /* stash away current value */
1300 : 3 : astate = accumArrayResult(astate,
5864 tgl@sss.pgh.pa.us 1301 : 3 : CStringGetTextDatum(hentry->name),
1302 : : false, TEXTOID, CurrentMemoryContext);
1303 : : }
1304 : : }
1305 : :
6434 mail@joeconway.com 1306 [ + - ]: 1 : if (astate)
595 peter@eisentraut.org 1307 : 1 : PG_RETURN_DATUM(makeArrayResult(astate,
1308 : : CurrentMemoryContext));
1309 : : else
6434 mail@joeconway.com 1310 :UBC 0 : PG_RETURN_NULL();
1311 : : }
1312 : :
1313 : : /*
1314 : : * Checks if a given remote connection is busy
1315 : : *
1316 : : * Returns 1 if the connection is busy, 0 otherwise
1317 : : * Params:
1318 : : * text connection_name - name of the connection to check
1319 : : *
1320 : : */
6434 mail@joeconway.com 1321 :CBC 3 : PG_FUNCTION_INFO_V1(dblink_is_busy);
1322 : : Datum
1323 : 1 : dblink_is_busy(PG_FUNCTION_ARGS)
1324 : : {
1325 : : PGconn *conn;
1326 : :
2667 peter_e@gmx.net 1327 : 1 : dblink_init();
1328 : 1 : conn = dblink_get_named_conn(text_to_cstring(PG_GETARG_TEXT_PP(0)));
1329 : :
6434 mail@joeconway.com 1330 : 1 : PQconsumeInput(conn);
1331 : 1 : PG_RETURN_INT32(PQisBusy(conn));
1332 : : }
1333 : :
1334 : : /*
1335 : : * Cancels a running request on a connection
1336 : : *
1337 : : * Returns text:
1338 : : * "OK" if the cancel request has been sent correctly,
1339 : : * an error message otherwise
1340 : : *
1341 : : * Params:
1342 : : * text connection_name - name of the connection to check
1343 : : *
1344 : : */
1345 : 3 : PG_FUNCTION_INFO_V1(dblink_cancel_query);
1346 : : Datum
1347 : 1 : dblink_cancel_query(PG_FUNCTION_ARGS)
1348 : : {
1349 : : PGconn *conn;
1350 : : const char *msg;
1351 : : TimestampTz endtime;
1352 : :
2667 peter_e@gmx.net 1353 : 1 : dblink_init();
1354 : 1 : conn = dblink_get_named_conn(text_to_cstring(PG_GETARG_TEXT_PP(0)));
17 alvherre@alvh.no-ip. 1355 :GNC 1 : endtime = TimestampTzPlusMilliseconds(GetCurrentTimestamp(),
1356 : : 30000);
1357 : 1 : msg = libpqsrv_cancel(conn, endtime);
1358 [ + - ]: 1 : if (msg == NULL)
1359 : 1 : msg = "OK";
1360 : :
27 1361 : 1 : PG_RETURN_TEXT_P(cstring_to_text(msg));
1362 : : }
1363 : :
1364 : :
1365 : : /*
1366 : : * Get error message from a connection
1367 : : *
1368 : : * Returns text:
1369 : : * "OK" if no error, an error message otherwise
1370 : : *
1371 : : * Params:
1372 : : * text connection_name - name of the connection to check
1373 : : *
1374 : : */
6434 mail@joeconway.com 1375 :CBC 3 : PG_FUNCTION_INFO_V1(dblink_error_message);
1376 : : Datum
1377 : 1 : dblink_error_message(PG_FUNCTION_ARGS)
1378 : : {
1379 : : char *msg;
1380 : : PGconn *conn;
1381 : :
2667 peter_e@gmx.net 1382 : 1 : dblink_init();
1383 : 1 : conn = dblink_get_named_conn(text_to_cstring(PG_GETARG_TEXT_PP(0)));
1384 : :
6434 mail@joeconway.com 1385 : 1 : msg = PQerrorMessage(conn);
5946 tgl@sss.pgh.pa.us 1386 [ + - + - ]: 1 : if (msg == NULL || msg[0] == '\0')
5864 1387 : 1 : PG_RETURN_TEXT_P(cstring_to_text("OK"));
1388 : : else
2603 peter_e@gmx.net 1389 :UBC 0 : PG_RETURN_TEXT_P(cstring_to_text(pchomp(msg)));
1390 : : }
1391 : :
1392 : : /*
1393 : : * Execute an SQL non-SELECT command
1394 : : */
7895 bruce@momjian.us 1395 :CBC 13 : PG_FUNCTION_INFO_V1(dblink_exec);
1396 : : Datum
1397 : 26 : dblink_exec(PG_FUNCTION_ARGS)
1398 : : {
4394 tgl@sss.pgh.pa.us 1399 : 26 : text *volatile sql_cmd_status = NULL;
1400 : 26 : PGconn *volatile conn = NULL;
1401 : 26 : volatile bool freeconn = false;
1402 : :
2667 peter_e@gmx.net 1403 : 26 : dblink_init();
1404 : :
4394 tgl@sss.pgh.pa.us 1405 [ + + ]: 26 : PG_TRY();
1406 : : {
1407 : 26 : PGresult *res = NULL;
1408 : 26 : char *sql = NULL;
1409 : 26 : char *conname = NULL;
1410 : 26 : bool fail = true; /* default to backward compatible behavior */
1411 : :
1412 [ - + ]: 26 : if (PG_NARGS() == 3)
1413 : : {
1414 : : /* must be text,text,bool */
2574 peter_e@gmx.net 1415 :UBC 0 : conname = text_to_cstring(PG_GETARG_TEXT_PP(0));
4394 tgl@sss.pgh.pa.us 1416 : 0 : sql = text_to_cstring(PG_GETARG_TEXT_PP(1));
1417 : 0 : fail = PG_GETARG_BOOL(2);
2574 peter_e@gmx.net 1418 : 0 : dblink_get_conn(conname, &conn, &conname, &freeconn);
1419 : : }
4394 tgl@sss.pgh.pa.us 1420 [ + + ]:CBC 26 : else if (PG_NARGS() == 2)
1421 : : {
1422 : : /* might be text,text or text,bool */
1423 [ + + ]: 17 : if (get_fn_expr_argtype(fcinfo->flinfo, 1) == BOOLOID)
1424 : : {
1425 : 1 : sql = text_to_cstring(PG_GETARG_TEXT_PP(0));
1426 : 1 : fail = PG_GETARG_BOOL(1);
2574 peter_e@gmx.net 1427 : 1 : conn = pconn->conn;
1428 : : }
1429 : : else
1430 : : {
1431 : 16 : conname = text_to_cstring(PG_GETARG_TEXT_PP(0));
4394 tgl@sss.pgh.pa.us 1432 : 16 : sql = text_to_cstring(PG_GETARG_TEXT_PP(1));
2574 peter_e@gmx.net 1433 : 16 : dblink_get_conn(conname, &conn, &conname, &freeconn);
1434 : : }
1435 : : }
4394 tgl@sss.pgh.pa.us 1436 [ + - ]: 9 : else if (PG_NARGS() == 1)
1437 : : {
1438 : : /* must be single text argument */
6753 mail@joeconway.com 1439 : 9 : conn = pconn->conn;
5864 tgl@sss.pgh.pa.us 1440 : 9 : sql = text_to_cstring(PG_GETARG_TEXT_PP(0));
1441 : : }
1442 : : else
1443 : : /* shouldn't happen */
4394 tgl@sss.pgh.pa.us 1444 [ # # ]:UBC 0 : elog(ERROR, "wrong number of arguments");
1445 : :
4394 tgl@sss.pgh.pa.us 1446 [ - + ]:CBC 26 : if (!conn)
2667 peter_e@gmx.net 1447 :UBC 0 : dblink_conn_not_avail(conname);
1448 : :
97 noah@leadboat.com 1449 :GNC 26 : res = libpqsrv_exec(conn, sql, dblink_we_get_result);
4394 tgl@sss.pgh.pa.us 1450 [ + - ]:CBC 26 : if (!res ||
1451 [ + + ]: 26 : (PQresultStatus(res) != PGRES_COMMAND_OK &&
1452 [ + - ]: 2 : PQresultStatus(res) != PGRES_TUPLES_OK))
1453 : : {
2215 1454 : 2 : dblink_res_error(conn, conname, res, fail,
1455 : : "while executing command");
1456 : :
1457 : : /*
1458 : : * and save a copy of the command status string to return as our
1459 : : * result tuple
1460 : : */
4394 1461 : 1 : sql_cmd_status = cstring_to_text("ERROR");
1462 : : }
1463 [ + - ]: 24 : else if (PQresultStatus(res) == PGRES_COMMAND_OK)
1464 : : {
1465 : : /*
1466 : : * and save a copy of the command status string to return as our
1467 : : * result tuple
1468 : : */
1469 : 24 : sql_cmd_status = cstring_to_text(PQcmdStatus(res));
1470 : 24 : PQclear(res);
1471 : : }
1472 : : else
1473 : : {
4394 tgl@sss.pgh.pa.us 1474 :UBC 0 : PQclear(res);
1475 [ # # ]: 0 : ereport(ERROR,
1476 : : (errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
1477 : : errmsg("statement returning results not allowed")));
1478 : : }
1479 : : }
1626 peter@eisentraut.org 1480 :CBC 1 : PG_FINALLY();
1481 : : {
1482 : : /* if needed, close the connection to the database */
4394 tgl@sss.pgh.pa.us 1483 [ + + ]: 26 : if (freeconn)
447 andres@anarazel.de 1484 : 1 : libpqsrv_disconnect(conn);
1485 : : }
4394 tgl@sss.pgh.pa.us 1486 [ + + ]: 26 : PG_END_TRY();
1487 : :
7599 bruce@momjian.us 1488 : 25 : PG_RETURN_TEXT_P(sql_cmd_status);
1489 : : }
1490 : :
1491 : :
1492 : : /*
1493 : : * dblink_get_pkey
1494 : : *
1495 : : * Return list of primary key fields for the supplied relation,
1496 : : * or NULL if none exists.
1497 : : */
8026 1498 : 3 : PG_FUNCTION_INFO_V1(dblink_get_pkey);
1499 : : Datum
1500 : 9 : dblink_get_pkey(PG_FUNCTION_ARGS)
1501 : : {
1502 : : int16 indnkeyatts;
1503 : : char **results;
1504 : : FuncCallContext *funcctx;
1505 : : int32 call_cntr;
1506 : : int32 max_calls;
1507 : : AttInMetadata *attinmeta;
1508 : : MemoryContext oldcontext;
1509 : :
1510 : : /* stuff done only on the first call of the function */
7893 1511 [ + + ]: 9 : if (SRF_IS_FIRSTCALL())
1512 : : {
1513 : : Relation rel;
1514 : : TupleDesc tupdesc;
1515 : :
1516 : : /* create a function context for cross-call persistence */
1517 : 3 : funcctx = SRF_FIRSTCALL_INIT();
1518 : :
1519 : : /*
1520 : : * switch to memory context appropriate for multiple function calls
1521 : : */
7895 1522 : 3 : oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1523 : :
1524 : : /* open target relation */
2590 noah@leadboat.com 1525 : 3 : rel = get_rel_from_relname(PG_GETARG_TEXT_PP(0), AccessShareLock, ACL_SELECT);
1526 : :
1527 : : /* get the array of attnums */
2199 teodor@sigaev.ru 1528 : 3 : results = get_pkey_attnames(rel, &indnkeyatts);
1529 : :
5053 tgl@sss.pgh.pa.us 1530 : 3 : relation_close(rel, AccessShareLock);
1531 : :
1532 : : /*
1533 : : * need a tuple descriptor representing one INT and one TEXT column
1534 : : */
1972 andres@anarazel.de 1535 : 3 : tupdesc = CreateTemplateTupleDesc(2);
7895 bruce@momjian.us 1536 : 3 : TupleDescInitEntry(tupdesc, (AttrNumber) 1, "position",
1537 : : INT4OID, -1, 0);
1538 : 3 : TupleDescInitEntry(tupdesc, (AttrNumber) 2, "colname",
1539 : : TEXTOID, -1, 0);
1540 : :
1541 : : /*
1542 : : * Generate attribute metadata needed later to produce tuples from raw
1543 : : * C strings
1544 : : */
1545 : 3 : attinmeta = TupleDescGetAttInMetadata(tupdesc);
1546 : 3 : funcctx->attinmeta = attinmeta;
1547 : :
2199 teodor@sigaev.ru 1548 [ + - + - ]: 3 : if ((results != NULL) && (indnkeyatts > 0))
1549 : : {
1550 : 3 : funcctx->max_calls = indnkeyatts;
1551 : :
1552 : : /* got results, keep track of them */
7895 bruce@momjian.us 1553 : 3 : funcctx->user_fctx = results;
1554 : : }
1555 : : else
1556 : : {
1557 : : /* fast track when no results */
5614 tgl@sss.pgh.pa.us 1558 :UBC 0 : MemoryContextSwitchTo(oldcontext);
7893 bruce@momjian.us 1559 : 0 : SRF_RETURN_DONE(funcctx);
1560 : : }
1561 : :
7895 bruce@momjian.us 1562 :CBC 3 : MemoryContextSwitchTo(oldcontext);
1563 : : }
1564 : :
1565 : : /* stuff done on every call of the function */
7893 1566 : 9 : funcctx = SRF_PERCALL_SETUP();
1567 : :
1568 : : /*
1569 : : * initialize per-call variables
1570 : : */
7895 1571 : 9 : call_cntr = funcctx->call_cntr;
1572 : 9 : max_calls = funcctx->max_calls;
1573 : :
1574 : 9 : results = (char **) funcctx->user_fctx;
1575 : 9 : attinmeta = funcctx->attinmeta;
1576 : :
1577 [ + + ]: 9 : if (call_cntr < max_calls) /* do when there is more left to send */
1578 : : {
1579 : : char **values;
1580 : : HeapTuple tuple;
1581 : : Datum result;
1582 : :
580 peter@eisentraut.org 1583 : 6 : values = palloc_array(char *, 2);
3751 peter_e@gmx.net 1584 : 6 : values[0] = psprintf("%d", call_cntr + 1);
7895 bruce@momjian.us 1585 : 6 : values[1] = results[call_cntr];
1586 : :
1587 : : /* build the tuple */
1588 : 6 : tuple = BuildTupleFromCStrings(attinmeta, values);
1589 : :
1590 : : /* make the tuple into a datum */
7318 tgl@sss.pgh.pa.us 1591 : 6 : result = HeapTupleGetDatum(tuple);
1592 : :
7893 bruce@momjian.us 1593 : 6 : SRF_RETURN_NEXT(funcctx, result);
1594 : : }
1595 : : else
1596 : : {
1597 : : /* do when there is no more left */
7599 1598 : 3 : SRF_RETURN_DONE(funcctx);
1599 : : }
1600 : : }
1601 : :
1602 : :
1603 : : /*
1604 : : * dblink_build_sql_insert
1605 : : *
1606 : : * Used to generate an SQL insert statement
1607 : : * based on an existing tuple in a local relation.
1608 : : * This is useful for selectively replicating data
1609 : : * to another server via dblink.
1610 : : *
1611 : : * API:
1612 : : * <relname> - name of local table of interest
1613 : : * <pkattnums> - an int2vector of attnums which will be used
1614 : : * to identify the local tuple of interest
1615 : : * <pknumatts> - number of attnums in pkattnums
1616 : : * <src_pkattvals_arry> - text array of key values which will be used
1617 : : * to identify the local tuple of interest
1618 : : * <tgt_pkattvals_arry> - text array of key values which will be used
1619 : : * to build the string for execution remotely. These are substituted
1620 : : * for their counterparts in src_pkattvals_arry
1621 : : */
8026 1622 : 4 : PG_FUNCTION_INFO_V1(dblink_build_sql_insert);
1623 : : Datum
1624 : 6 : dblink_build_sql_insert(PG_FUNCTION_ARGS)
1625 : : {
2590 noah@leadboat.com 1626 : 6 : text *relname_text = PG_GETARG_TEXT_PP(0);
5052 tgl@sss.pgh.pa.us 1627 : 6 : int2vector *pkattnums_arg = (int2vector *) PG_GETARG_POINTER(1);
1628 : 6 : int32 pknumatts_arg = PG_GETARG_INT32(2);
6722 1629 : 6 : ArrayType *src_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(3);
1630 : 6 : ArrayType *tgt_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(4);
1631 : : Relation rel;
1632 : : int *pkattnums;
1633 : : int pknumatts;
1634 : : char **src_pkattvals;
1635 : : char **tgt_pkattvals;
1636 : : int src_nitems;
1637 : : int tgt_nitems;
1638 : : char *sql;
1639 : :
1640 : : /*
1641 : : * Open target relation.
1642 : : */
5053 1643 : 6 : rel = get_rel_from_relname(relname_text, AccessShareLock, ACL_SELECT);
1644 : :
1645 : : /*
1646 : : * Process pkattnums argument.
1647 : : */
5052 1648 : 6 : validate_pkattnums(rel, pkattnums_arg, pknumatts_arg,
1649 : : &pkattnums, &pknumatts);
1650 : :
1651 : : /*
1652 : : * Source array is made up of key values that will be used to locate the
1653 : : * tuple of interest from the local system.
1654 : : */
6722 1655 : 4 : src_pkattvals = get_text_array_contents(src_pkattvals_arry, &src_nitems);
1656 : :
1657 : : /*
1658 : : * There should be one source array key value for each key attnum
1659 : : */
8026 bruce@momjian.us 1660 [ - + ]: 4 : if (src_nitems != pknumatts)
7570 tgl@sss.pgh.pa.us 1661 [ # # ]:UBC 0 : ereport(ERROR,
1662 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1663 : : errmsg("source key array length must match number of key attributes")));
1664 : :
1665 : : /*
1666 : : * Target array is made up of key values that will be used to build the
1667 : : * SQL string for use on the remote system.
1668 : : */
6722 tgl@sss.pgh.pa.us 1669 :CBC 4 : tgt_pkattvals = get_text_array_contents(tgt_pkattvals_arry, &tgt_nitems);
1670 : :
1671 : : /*
1672 : : * There should be one target array key value for each key attnum
1673 : : */
8026 bruce@momjian.us 1674 [ - + ]: 4 : if (tgt_nitems != pknumatts)
7570 tgl@sss.pgh.pa.us 1675 [ # # ]:UBC 0 : ereport(ERROR,
1676 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1677 : : errmsg("target key array length must match number of key attributes")));
1678 : :
1679 : : /*
1680 : : * Prep work is finally done. Go get the SQL string.
1681 : : */
5053 tgl@sss.pgh.pa.us 1682 :CBC 4 : sql = get_sql_insert(rel, pkattnums, pknumatts, src_pkattvals, tgt_pkattvals);
1683 : :
1684 : : /*
1685 : : * Now we can close the relation.
1686 : : */
1687 : 4 : relation_close(rel, AccessShareLock);
1688 : :
1689 : : /*
1690 : : * And send it
1691 : : */
5864 1692 : 4 : PG_RETURN_TEXT_P(cstring_to_text(sql));
1693 : : }
1694 : :
1695 : :
1696 : : /*
1697 : : * dblink_build_sql_delete
1698 : : *
1699 : : * Used to generate an SQL delete statement.
1700 : : * This is useful for selectively replicating a
1701 : : * delete to another server via dblink.
1702 : : *
1703 : : * API:
1704 : : * <relname> - name of remote table of interest
1705 : : * <pkattnums> - an int2vector of attnums which will be used
1706 : : * to identify the remote tuple of interest
1707 : : * <pknumatts> - number of attnums in pkattnums
1708 : : * <tgt_pkattvals_arry> - text array of key values which will be used
1709 : : * to build the string for execution remotely.
1710 : : */
8026 bruce@momjian.us 1711 : 4 : PG_FUNCTION_INFO_V1(dblink_build_sql_delete);
1712 : : Datum
1713 : 6 : dblink_build_sql_delete(PG_FUNCTION_ARGS)
1714 : : {
2590 noah@leadboat.com 1715 : 6 : text *relname_text = PG_GETARG_TEXT_PP(0);
5052 tgl@sss.pgh.pa.us 1716 : 6 : int2vector *pkattnums_arg = (int2vector *) PG_GETARG_POINTER(1);
1717 : 6 : int32 pknumatts_arg = PG_GETARG_INT32(2);
6722 1718 : 6 : ArrayType *tgt_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(3);
1719 : : Relation rel;
1720 : : int *pkattnums;
1721 : : int pknumatts;
1722 : : char **tgt_pkattvals;
1723 : : int tgt_nitems;
1724 : : char *sql;
1725 : :
1726 : : /*
1727 : : * Open target relation.
1728 : : */
5053 1729 : 6 : rel = get_rel_from_relname(relname_text, AccessShareLock, ACL_SELECT);
1730 : :
1731 : : /*
1732 : : * Process pkattnums argument.
1733 : : */
5052 1734 : 6 : validate_pkattnums(rel, pkattnums_arg, pknumatts_arg,
1735 : : &pkattnums, &pknumatts);
1736 : :
1737 : : /*
1738 : : * Target array is made up of key values that will be used to build the
1739 : : * SQL string for use on the remote system.
1740 : : */
6722 1741 : 4 : tgt_pkattvals = get_text_array_contents(tgt_pkattvals_arry, &tgt_nitems);
1742 : :
1743 : : /*
1744 : : * There should be one target array key value for each key attnum
1745 : : */
8026 bruce@momjian.us 1746 [ - + ]: 4 : if (tgt_nitems != pknumatts)
7570 tgl@sss.pgh.pa.us 1747 [ # # ]:UBC 0 : ereport(ERROR,
1748 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1749 : : errmsg("target key array length must match number of key attributes")));
1750 : :
1751 : : /*
1752 : : * Prep work is finally done. Go get the SQL string.
1753 : : */
5053 tgl@sss.pgh.pa.us 1754 :CBC 4 : sql = get_sql_delete(rel, pkattnums, pknumatts, tgt_pkattvals);
1755 : :
1756 : : /*
1757 : : * Now we can close the relation.
1758 : : */
1759 : 4 : relation_close(rel, AccessShareLock);
1760 : :
1761 : : /*
1762 : : * And send it
1763 : : */
5864 1764 : 4 : PG_RETURN_TEXT_P(cstring_to_text(sql));
1765 : : }
1766 : :
1767 : :
1768 : : /*
1769 : : * dblink_build_sql_update
1770 : : *
1771 : : * Used to generate an SQL update statement
1772 : : * based on an existing tuple in a local relation.
1773 : : * This is useful for selectively replicating data
1774 : : * to another server via dblink.
1775 : : *
1776 : : * API:
1777 : : * <relname> - name of local table of interest
1778 : : * <pkattnums> - an int2vector of attnums which will be used
1779 : : * to identify the local tuple of interest
1780 : : * <pknumatts> - number of attnums in pkattnums
1781 : : * <src_pkattvals_arry> - text array of key values which will be used
1782 : : * to identify the local tuple of interest
1783 : : * <tgt_pkattvals_arry> - text array of key values which will be used
1784 : : * to build the string for execution remotely. These are substituted
1785 : : * for their counterparts in src_pkattvals_arry
1786 : : */
8026 bruce@momjian.us 1787 : 4 : PG_FUNCTION_INFO_V1(dblink_build_sql_update);
1788 : : Datum
1789 : 6 : dblink_build_sql_update(PG_FUNCTION_ARGS)
1790 : : {
2590 noah@leadboat.com 1791 : 6 : text *relname_text = PG_GETARG_TEXT_PP(0);
5052 tgl@sss.pgh.pa.us 1792 : 6 : int2vector *pkattnums_arg = (int2vector *) PG_GETARG_POINTER(1);
1793 : 6 : int32 pknumatts_arg = PG_GETARG_INT32(2);
6722 1794 : 6 : ArrayType *src_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(3);
1795 : 6 : ArrayType *tgt_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(4);
1796 : : Relation rel;
1797 : : int *pkattnums;
1798 : : int pknumatts;
1799 : : char **src_pkattvals;
1800 : : char **tgt_pkattvals;
1801 : : int src_nitems;
1802 : : int tgt_nitems;
1803 : : char *sql;
1804 : :
1805 : : /*
1806 : : * Open target relation.
1807 : : */
5053 1808 : 6 : rel = get_rel_from_relname(relname_text, AccessShareLock, ACL_SELECT);
1809 : :
1810 : : /*
1811 : : * Process pkattnums argument.
1812 : : */
5052 1813 : 6 : validate_pkattnums(rel, pkattnums_arg, pknumatts_arg,
1814 : : &pkattnums, &pknumatts);
1815 : :
1816 : : /*
1817 : : * Source array is made up of key values that will be used to locate the
1818 : : * tuple of interest from the local system.
1819 : : */
6722 1820 : 4 : src_pkattvals = get_text_array_contents(src_pkattvals_arry, &src_nitems);
1821 : :
1822 : : /*
1823 : : * There should be one source array key value for each key attnum
1824 : : */
8026 bruce@momjian.us 1825 [ - + ]: 4 : if (src_nitems != pknumatts)
7570 tgl@sss.pgh.pa.us 1826 [ # # ]:UBC 0 : ereport(ERROR,
1827 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1828 : : errmsg("source key array length must match number of key attributes")));
1829 : :
1830 : : /*
1831 : : * Target array is made up of key values that will be used to build the
1832 : : * SQL string for use on the remote system.
1833 : : */
6722 tgl@sss.pgh.pa.us 1834 :CBC 4 : tgt_pkattvals = get_text_array_contents(tgt_pkattvals_arry, &tgt_nitems);
1835 : :
1836 : : /*
1837 : : * There should be one target array key value for each key attnum
1838 : : */
8026 bruce@momjian.us 1839 [ - + ]: 4 : if (tgt_nitems != pknumatts)
7570 tgl@sss.pgh.pa.us 1840 [ # # ]:UBC 0 : ereport(ERROR,
1841 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1842 : : errmsg("target key array length must match number of key attributes")));
1843 : :
1844 : : /*
1845 : : * Prep work is finally done. Go get the SQL string.
1846 : : */
5053 tgl@sss.pgh.pa.us 1847 :CBC 4 : sql = get_sql_update(rel, pkattnums, pknumatts, src_pkattvals, tgt_pkattvals);
1848 : :
1849 : : /*
1850 : : * Now we can close the relation.
1851 : : */
1852 : 4 : relation_close(rel, AccessShareLock);
1853 : :
1854 : : /*
1855 : : * And send it
1856 : : */
5864 1857 : 4 : PG_RETURN_TEXT_P(cstring_to_text(sql));
1858 : : }
1859 : :
1860 : : /*
1861 : : * dblink_current_query
1862 : : * return the current query string
1863 : : * to allow its use in (among other things)
1864 : : * rewrite rules
1865 : : */
5423 1866 : 2 : PG_FUNCTION_INFO_V1(dblink_current_query);
1867 : : Datum
5423 tgl@sss.pgh.pa.us 1868 :UBC 0 : dblink_current_query(PG_FUNCTION_ARGS)
1869 : : {
1870 : : /* This is now just an alias for the built-in function current_query() */
1871 : 0 : PG_RETURN_DATUM(current_query(fcinfo));
1872 : : }
1873 : :
1874 : : /*
1875 : : * Retrieve async notifications for a connection.
1876 : : *
1877 : : * Returns a setof record of notifications, or an empty set if none received.
1878 : : * Can optionally take a named connection as parameter, but uses the unnamed
1879 : : * connection per default.
1880 : : *
1881 : : */
1882 : : #define DBLINK_NOTIFY_COLS 3
1883 : :
5366 mail@joeconway.com 1884 :CBC 5 : PG_FUNCTION_INFO_V1(dblink_get_notify);
1885 : : Datum
1886 : 2 : dblink_get_notify(PG_FUNCTION_ARGS)
1887 : : {
1888 : : PGconn *conn;
1889 : : PGnotify *notify;
5161 bruce@momjian.us 1890 : 2 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
1891 : :
2667 peter_e@gmx.net 1892 : 2 : dblink_init();
5366 mail@joeconway.com 1893 [ - + ]: 2 : if (PG_NARGS() == 1)
2667 peter_e@gmx.net 1894 :UBC 0 : conn = dblink_get_named_conn(text_to_cstring(PG_GETARG_TEXT_PP(0)));
1895 : : else
5366 mail@joeconway.com 1896 :CBC 2 : conn = pconn->conn;
1897 : :
544 michael@paquier.xyz 1898 : 2 : InitMaterializedSRF(fcinfo, 0);
1899 : :
5366 mail@joeconway.com 1900 : 2 : PQconsumeInput(conn);
1901 [ + + ]: 4 : while ((notify = PQnotifies(conn)) != NULL)
1902 : : {
1903 : : Datum values[DBLINK_NOTIFY_COLS];
1904 : : bool nulls[DBLINK_NOTIFY_COLS];
1905 : :
1906 : 2 : memset(values, 0, sizeof(values));
1907 : 2 : memset(nulls, 0, sizeof(nulls));
1908 : :
1909 [ + - ]: 2 : if (notify->relname != NULL)
1910 : 2 : values[0] = CStringGetTextDatum(notify->relname);
1911 : : else
5366 mail@joeconway.com 1912 :UBC 0 : nulls[0] = true;
1913 : :
5366 mail@joeconway.com 1914 :CBC 2 : values[1] = Int32GetDatum(notify->be_pid);
1915 : :
1916 [ + - ]: 2 : if (notify->extra != NULL)
1917 : 2 : values[2] = CStringGetTextDatum(notify->extra);
1918 : : else
5366 mail@joeconway.com 1919 :UBC 0 : nulls[2] = true;
1920 : :
768 michael@paquier.xyz 1921 :CBC 2 : tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
1922 : :
5366 mail@joeconway.com 1923 : 2 : PQfreemem(notify);
1924 : 2 : PQconsumeInput(conn);
1925 : : }
1926 : :
1927 : 2 : return (Datum) 0;
1928 : : }
1929 : :
1930 : : /*
1931 : : * Validate the options given to a dblink foreign server or user mapping.
1932 : : * Raise an error if any option is invalid.
1933 : : *
1934 : : * We just check the names of options here, so semantic errors in options,
1935 : : * such as invalid numeric format, will be detected at the attempt to connect.
1936 : : */
4204 tgl@sss.pgh.pa.us 1937 : 5 : PG_FUNCTION_INFO_V1(dblink_fdw_validator);
1938 : : Datum
1939 : 6 : dblink_fdw_validator(PG_FUNCTION_ARGS)
1940 : : {
1941 : 6 : List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
1942 : 6 : Oid context = PG_GETARG_OID(1);
1943 : : ListCell *cell;
1944 : :
1945 : : static const PQconninfoOption *options = NULL;
1946 : :
1947 : : /*
1948 : : * Get list of valid libpq options.
1949 : : *
1950 : : * To avoid unnecessary work, we get the list once and use it throughout
1951 : : * the lifetime of this backend process. We don't need to care about
1952 : : * memory context issues, because PQconndefaults allocates with malloc.
1953 : : */
1954 [ + + ]: 6 : if (!options)
1955 : : {
1956 : 3 : options = PQconndefaults();
1957 [ - + ]: 3 : if (!options) /* assume reason for failure is OOM */
4204 tgl@sss.pgh.pa.us 1958 [ # # ]:UBC 0 : ereport(ERROR,
1959 : : (errcode(ERRCODE_FDW_OUT_OF_MEMORY),
1960 : : errmsg("out of memory"),
1961 : : errdetail("Could not get libpq's default connection options.")));
1962 : : }
1963 : :
1964 : : /* Validate each supplied option. */
4204 tgl@sss.pgh.pa.us 1965 [ + + + + :CBC 9 : foreach(cell, options_list)
+ + ]
1966 : : {
1967 : 5 : DefElem *def = (DefElem *) lfirst(cell);
1968 : :
1969 [ + + ]: 5 : if (!is_valid_dblink_option(options, def->defname, context))
1970 : : {
1971 : : /*
1972 : : * Unknown option, or invalid option for the context specified, so
1973 : : * complain about it. Provide a hint with a valid option that
1974 : : * looks similar, if there is one.
1975 : : */
1976 : : const PQconninfoOption *opt;
1977 : : const char *closest_match;
1978 : : ClosestMatchState match_state;
576 peter@eisentraut.org 1979 : 2 : bool has_valid_options = false;
1980 : :
1981 : 2 : initClosestMatch(&match_state, def->defname, 4);
4204 tgl@sss.pgh.pa.us 1982 [ + + ]: 84 : for (opt = options; opt->keyword; opt++)
1983 : : {
1984 [ + + ]: 82 : if (is_valid_dblink_option(options, opt->keyword, context))
1985 : : {
576 peter@eisentraut.org 1986 : 3 : has_valid_options = true;
1987 : 3 : updateClosestMatch(&match_state, opt->keyword);
1988 : : }
1989 : : }
1990 : :
1991 : 2 : closest_match = getClosestMatch(&match_state);
4204 tgl@sss.pgh.pa.us 1992 [ + - + + : 2 : ereport(ERROR,
- + ]
1993 : : (errcode(ERRCODE_FDW_OPTION_NAME_NOT_FOUND),
1994 : : errmsg("invalid option \"%s\"", def->defname),
1995 : : has_valid_options ? closest_match ?
1996 : : errhint("Perhaps you meant the option \"%s\".",
1997 : : closest_match) : 0 :
1998 : : errhint("There are no valid options in this context.")));
1999 : : }
2000 : : }
2001 : :
2002 : 4 : PG_RETURN_VOID();
2003 : : }
2004 : :
2005 : :
2006 : : /*************************************************************
2007 : : * internal functions
2008 : : */
2009 : :
2010 : :
2011 : : /*
2012 : : * get_pkey_attnames
2013 : : *
2014 : : * Get the primary key attnames for the given relation.
2015 : : * Return NULL, and set indnkeyatts = 0, if no primary key exists.
2016 : : */
2017 : : static char **
2199 teodor@sigaev.ru 2018 : 3 : get_pkey_attnames(Relation rel, int16 *indnkeyatts)
2019 : : {
2020 : : Relation indexRelation;
2021 : : ScanKeyData skey;
2022 : : SysScanDesc scan;
2023 : : HeapTuple indexTuple;
2024 : : int i;
7893 bruce@momjian.us 2025 : 3 : char **result = NULL;
2026 : : TupleDesc tupdesc;
2027 : :
2028 : : /* initialize indnkeyatts to 0 in case no primary key exists */
2199 teodor@sigaev.ru 2029 : 3 : *indnkeyatts = 0;
2030 : :
8026 bruce@momjian.us 2031 : 3 : tupdesc = rel->rd_att;
2032 : :
2033 : : /* Prepare to scan pg_index for entries having indrelid = this rel. */
1910 andres@anarazel.de 2034 : 3 : indexRelation = table_open(IndexRelationId, AccessShareLock);
5935 tgl@sss.pgh.pa.us 2035 : 3 : ScanKeyInit(&skey,
2036 : : Anum_pg_index_indrelid,
2037 : : BTEqualStrategyNumber, F_OIDEQ,
2038 : : ObjectIdGetDatum(RelationGetRelid(rel)));
2039 : :
2040 : 3 : scan = systable_beginscan(indexRelation, IndexIndrelidIndexId, true,
2041 : : NULL, 1, &skey);
2042 : :
2043 [ + - ]: 3 : while (HeapTupleIsValid(indexTuple = systable_getnext(scan)))
2044 : : {
7893 bruce@momjian.us 2045 : 3 : Form_pg_index index = (Form_pg_index) GETSTRUCT(indexTuple);
2046 : :
2047 : : /* we're only interested if it is the primary key */
5935 tgl@sss.pgh.pa.us 2048 [ + - ]: 3 : if (index->indisprimary)
2049 : : {
2199 teodor@sigaev.ru 2050 : 3 : *indnkeyatts = index->indnkeyatts;
2051 [ + - ]: 3 : if (*indnkeyatts > 0)
2052 : : {
580 peter@eisentraut.org 2053 : 3 : result = palloc_array(char *, *indnkeyatts);
2054 : :
2199 teodor@sigaev.ru 2055 [ + + ]: 9 : for (i = 0; i < *indnkeyatts; i++)
6956 tgl@sss.pgh.pa.us 2056 : 6 : result[i] = SPI_fname(tupdesc, index->indkey.values[i]);
2057 : : }
8026 bruce@momjian.us 2058 : 3 : break;
2059 : : }
2060 : : }
2061 : :
5935 tgl@sss.pgh.pa.us 2062 : 3 : systable_endscan(scan);
1910 andres@anarazel.de 2063 : 3 : table_close(indexRelation, AccessShareLock);
2064 : :
8026 bruce@momjian.us 2065 : 3 : return result;
2066 : : }
2067 : :
2068 : : /*
2069 : : * Deconstruct a text[] into C-strings (note any NULL elements will be
2070 : : * returned as NULL pointers)
2071 : : */
2072 : : static char **
6722 tgl@sss.pgh.pa.us 2073 : 20 : get_text_array_contents(ArrayType *array, int *numitems)
2074 : : {
2075 : 20 : int ndim = ARR_NDIM(array);
2076 : 20 : int *dims = ARR_DIMS(array);
2077 : : int nitems;
2078 : : int16 typlen;
2079 : : bool typbyval;
2080 : : char typalign;
2081 : : char **values;
2082 : : char *ptr;
2083 : : bits8 *bitmap;
2084 : : int bitmask;
2085 : : int i;
2086 : :
2087 [ - + ]: 20 : Assert(ARR_ELEMTYPE(array) == TEXTOID);
2088 : :
2089 : 20 : *numitems = nitems = ArrayGetNItems(ndim, dims);
2090 : :
2091 : 20 : get_typlenbyvalalign(ARR_ELEMTYPE(array),
2092 : : &typlen, &typbyval, &typalign);
2093 : :
580 peter@eisentraut.org 2094 : 20 : values = palloc_array(char *, nitems);
2095 : :
6722 tgl@sss.pgh.pa.us 2096 [ - + ]: 20 : ptr = ARR_DATA_PTR(array);
2097 [ - + ]: 20 : bitmap = ARR_NULLBITMAP(array);
2098 : 20 : bitmask = 1;
2099 : :
2100 [ + + ]: 55 : for (i = 0; i < nitems; i++)
2101 : : {
2102 [ - + - - ]: 35 : if (bitmap && (*bitmap & bitmask) == 0)
2103 : : {
6722 tgl@sss.pgh.pa.us 2104 :UBC 0 : values[i] = NULL;
2105 : : }
2106 : : else
2107 : : {
5864 tgl@sss.pgh.pa.us 2108 :CBC 35 : values[i] = TextDatumGetCString(PointerGetDatum(ptr));
6218 2109 [ - + + - : 35 : ptr = att_addlength_pointer(ptr, typlen, ptr);
- + - - -
- - - - +
- - ]
2110 [ + - - - : 35 : ptr = (char *) att_align_nominal(ptr, typalign);
- - - - ]
2111 : : }
2112 : :
2113 : : /* advance bitmap pointer if any */
6722 2114 [ - + ]: 35 : if (bitmap)
2115 : : {
6722 tgl@sss.pgh.pa.us 2116 :UBC 0 : bitmask <<= 1;
2117 [ # # ]: 0 : if (bitmask == 0x100)
2118 : : {
2119 : 0 : bitmap++;
2120 : 0 : bitmask = 1;
2121 : : }
2122 : : }
2123 : : }
2124 : :
6722 tgl@sss.pgh.pa.us 2125 :CBC 20 : return values;
2126 : : }
2127 : :
2128 : : static char *
5052 2129 : 4 : get_sql_insert(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals, char **tgt_pkattvals)
2130 : : {
2131 : : char *relname;
2132 : : HeapTuple tuple;
2133 : : TupleDesc tupdesc;
2134 : : int natts;
2135 : : StringInfoData buf;
2136 : : char *val;
2137 : : int key;
2138 : : int i;
2139 : : bool needComma;
2140 : :
6619 neilc@samurai.com 2141 : 4 : initStringInfo(&buf);
2142 : :
2143 : : /* get relation name including any needed schema prefix and quoting */
5053 tgl@sss.pgh.pa.us 2144 : 4 : relname = generate_relation_name(rel);
2145 : :
8026 bruce@momjian.us 2146 : 4 : tupdesc = rel->rd_att;
2147 : 4 : natts = tupdesc->natts;
2148 : :
5053 tgl@sss.pgh.pa.us 2149 : 4 : tuple = get_tuple_of_interest(rel, pkattnums, pknumatts, src_pkattvals);
7895 bruce@momjian.us 2150 [ - + ]: 4 : if (!tuple)
7570 tgl@sss.pgh.pa.us 2151 [ # # ]:UBC 0 : ereport(ERROR,
2152 : : (errcode(ERRCODE_CARDINALITY_VIOLATION),
2153 : : errmsg("source row not found")));
2154 : :
6619 neilc@samurai.com 2155 :CBC 4 : appendStringInfo(&buf, "INSERT INTO %s(", relname);
2156 : :
7926 tgl@sss.pgh.pa.us 2157 : 4 : needComma = false;
8026 bruce@momjian.us 2158 [ + + ]: 19 : for (i = 0; i < natts; i++)
2159 : : {
2429 andres@anarazel.de 2160 : 15 : Form_pg_attribute att = TupleDescAttr(tupdesc, i);
2161 : :
2162 [ + + ]: 15 : if (att->attisdropped)
7926 tgl@sss.pgh.pa.us 2163 : 2 : continue;
2164 : :
2165 [ + + ]: 13 : if (needComma)
3818 rhaas@postgresql.org 2166 : 9 : appendStringInfoChar(&buf, ',');
2167 : :
6619 neilc@samurai.com 2168 : 13 : appendStringInfoString(&buf,
2429 andres@anarazel.de 2169 : 13 : quote_ident_cstr(NameStr(att->attname)));
7926 tgl@sss.pgh.pa.us 2170 : 13 : needComma = true;
2171 : : }
2172 : :
3818 rhaas@postgresql.org 2173 : 4 : appendStringInfoString(&buf, ") VALUES(");
2174 : :
2175 : : /*
2176 : : * Note: i is physical column number (counting from 0).
2177 : : */
7926 tgl@sss.pgh.pa.us 2178 : 4 : needComma = false;
8026 bruce@momjian.us 2179 [ + + ]: 19 : for (i = 0; i < natts; i++)
2180 : : {
2429 andres@anarazel.de 2181 [ + + ]: 15 : if (TupleDescAttr(tupdesc, i)->attisdropped)
7926 tgl@sss.pgh.pa.us 2182 : 2 : continue;
2183 : :
2184 [ + + ]: 13 : if (needComma)
3818 rhaas@postgresql.org 2185 : 9 : appendStringInfoChar(&buf, ',');
2186 : :
5052 tgl@sss.pgh.pa.us 2187 : 13 : key = get_attnum_pk_pos(pkattnums, pknumatts, i);
2188 : :
2189 [ + + ]: 13 : if (key >= 0)
6722 2190 [ + - ]: 7 : val = tgt_pkattvals[key] ? pstrdup(tgt_pkattvals[key]) : NULL;
2191 : : else
8026 bruce@momjian.us 2192 : 6 : val = SPI_getvalue(tuple, tupdesc, i + 1);
2193 : :
2194 [ + - ]: 13 : if (val != NULL)
2195 : : {
6619 neilc@samurai.com 2196 : 13 : appendStringInfoString(&buf, quote_literal_cstr(val));
8026 bruce@momjian.us 2197 : 13 : pfree(val);
2198 : : }
2199 : : else
3818 rhaas@postgresql.org 2200 :UBC 0 : appendStringInfoString(&buf, "NULL");
7926 tgl@sss.pgh.pa.us 2201 :CBC 13 : needComma = true;
2202 : : }
3818 rhaas@postgresql.org 2203 : 4 : appendStringInfoChar(&buf, ')');
2204 : :
2432 peter_e@gmx.net 2205 : 4 : return buf.data;
2206 : : }
2207 : :
2208 : : static char *
5052 tgl@sss.pgh.pa.us 2209 : 4 : get_sql_delete(Relation rel, int *pkattnums, int pknumatts, char **tgt_pkattvals)
2210 : : {
2211 : : char *relname;
2212 : : TupleDesc tupdesc;
2213 : : StringInfoData buf;
2214 : : int i;
2215 : :
6619 neilc@samurai.com 2216 : 4 : initStringInfo(&buf);
2217 : :
2218 : : /* get relation name including any needed schema prefix and quoting */
5053 tgl@sss.pgh.pa.us 2219 : 4 : relname = generate_relation_name(rel);
2220 : :
8026 bruce@momjian.us 2221 : 4 : tupdesc = rel->rd_att;
2222 : :
6619 neilc@samurai.com 2223 : 4 : appendStringInfo(&buf, "DELETE FROM %s WHERE ", relname);
8026 bruce@momjian.us 2224 [ + + ]: 11 : for (i = 0; i < pknumatts; i++)
2225 : : {
5052 tgl@sss.pgh.pa.us 2226 : 7 : int pkattnum = pkattnums[i];
2429 andres@anarazel.de 2227 : 7 : Form_pg_attribute attr = TupleDescAttr(tupdesc, pkattnum);
2228 : :
8026 bruce@momjian.us 2229 [ + + ]: 7 : if (i > 0)
3818 rhaas@postgresql.org 2230 : 3 : appendStringInfoString(&buf, " AND ");
2231 : :
6619 neilc@samurai.com 2232 : 7 : appendStringInfoString(&buf,
2429 andres@anarazel.de 2233 : 7 : quote_ident_cstr(NameStr(attr->attname)));
2234 : :
6722 tgl@sss.pgh.pa.us 2235 [ + - ]: 7 : if (tgt_pkattvals[i] != NULL)
6619 neilc@samurai.com 2236 : 7 : appendStringInfo(&buf, " = %s",
6722 tgl@sss.pgh.pa.us 2237 : 7 : quote_literal_cstr(tgt_pkattvals[i]));
2238 : : else
3818 rhaas@postgresql.org 2239 :UBC 0 : appendStringInfoString(&buf, " IS NULL");
2240 : : }
2241 : :
2432 peter_e@gmx.net 2242 :CBC 4 : return buf.data;
2243 : : }
2244 : :
2245 : : static char *
5052 tgl@sss.pgh.pa.us 2246 : 4 : get_sql_update(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals, char **tgt_pkattvals)
2247 : : {
2248 : : char *relname;
2249 : : HeapTuple tuple;
2250 : : TupleDesc tupdesc;
2251 : : int natts;
2252 : : StringInfoData buf;
2253 : : char *val;
2254 : : int key;
2255 : : int i;
2256 : : bool needComma;
2257 : :
6619 neilc@samurai.com 2258 : 4 : initStringInfo(&buf);
2259 : :
2260 : : /* get relation name including any needed schema prefix and quoting */
5053 tgl@sss.pgh.pa.us 2261 : 4 : relname = generate_relation_name(rel);
2262 : :
8026 bruce@momjian.us 2263 : 4 : tupdesc = rel->rd_att;
2264 : 4 : natts = tupdesc->natts;
2265 : :
5053 tgl@sss.pgh.pa.us 2266 : 4 : tuple = get_tuple_of_interest(rel, pkattnums, pknumatts, src_pkattvals);
7895 bruce@momjian.us 2267 [ - + ]: 4 : if (!tuple)
7570 tgl@sss.pgh.pa.us 2268 [ # # ]:UBC 0 : ereport(ERROR,
2269 : : (errcode(ERRCODE_CARDINALITY_VIOLATION),
2270 : : errmsg("source row not found")));
2271 : :
6619 neilc@samurai.com 2272 :CBC 4 : appendStringInfo(&buf, "UPDATE %s SET ", relname);
2273 : :
2274 : : /*
2275 : : * Note: i is physical column number (counting from 0).
2276 : : */
7926 tgl@sss.pgh.pa.us 2277 : 4 : needComma = false;
8026 bruce@momjian.us 2278 [ + + ]: 19 : for (i = 0; i < natts; i++)
2279 : : {
2429 andres@anarazel.de 2280 : 15 : Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
2281 : :
2282 [ + + ]: 15 : if (attr->attisdropped)
7926 tgl@sss.pgh.pa.us 2283 : 2 : continue;
2284 : :
2285 [ + + ]: 13 : if (needComma)
3818 rhaas@postgresql.org 2286 : 9 : appendStringInfoString(&buf, ", ");
2287 : :
6619 neilc@samurai.com 2288 : 13 : appendStringInfo(&buf, "%s = ",
2429 andres@anarazel.de 2289 : 13 : quote_ident_cstr(NameStr(attr->attname)));
2290 : :
5052 tgl@sss.pgh.pa.us 2291 : 13 : key = get_attnum_pk_pos(pkattnums, pknumatts, i);
2292 : :
2293 [ + + ]: 13 : if (key >= 0)
6718 bruce@momjian.us 2294 [ + - ]: 7 : val = tgt_pkattvals[key] ? pstrdup(tgt_pkattvals[key]) : NULL;
2295 : : else
8026 2296 : 6 : val = SPI_getvalue(tuple, tupdesc, i + 1);
2297 : :
2298 [ + - ]: 13 : if (val != NULL)
2299 : : {
6619 neilc@samurai.com 2300 : 13 : appendStringInfoString(&buf, quote_literal_cstr(val));
8026 bruce@momjian.us 2301 : 13 : pfree(val);
2302 : : }
2303 : : else
6619 neilc@samurai.com 2304 :UBC 0 : appendStringInfoString(&buf, "NULL");
7926 tgl@sss.pgh.pa.us 2305 :CBC 13 : needComma = true;
2306 : : }
2307 : :
3818 rhaas@postgresql.org 2308 : 4 : appendStringInfoString(&buf, " WHERE ");
2309 : :
8026 bruce@momjian.us 2310 [ + + ]: 11 : for (i = 0; i < pknumatts; i++)
2311 : : {
5052 tgl@sss.pgh.pa.us 2312 : 7 : int pkattnum = pkattnums[i];
2429 andres@anarazel.de 2313 : 7 : Form_pg_attribute attr = TupleDescAttr(tupdesc, pkattnum);
2314 : :
8026 bruce@momjian.us 2315 [ + + ]: 7 : if (i > 0)
3818 rhaas@postgresql.org 2316 : 3 : appendStringInfoString(&buf, " AND ");
2317 : :
2318 : 7 : appendStringInfoString(&buf,
2429 andres@anarazel.de 2319 : 7 : quote_ident_cstr(NameStr(attr->attname)));
2320 : :
5052 tgl@sss.pgh.pa.us 2321 : 7 : val = tgt_pkattvals[i];
2322 : :
8026 bruce@momjian.us 2323 [ + - ]: 7 : if (val != NULL)
6619 neilc@samurai.com 2324 : 7 : appendStringInfo(&buf, " = %s", quote_literal_cstr(val));
2325 : : else
3818 rhaas@postgresql.org 2326 :UBC 0 : appendStringInfoString(&buf, " IS NULL");
2327 : : }
2328 : :
2432 peter_e@gmx.net 2329 :CBC 4 : return buf.data;
2330 : : }
2331 : :
2332 : : /*
2333 : : * Return a properly quoted identifier.
2334 : : * Uses quote_ident in quote.c
2335 : : */
2336 : : static char *
8026 bruce@momjian.us 2337 : 80 : quote_ident_cstr(char *rawstr)
2338 : : {
2339 : : text *rawstr_text;
2340 : : text *result_text;
2341 : : char *result;
2342 : :
5864 tgl@sss.pgh.pa.us 2343 : 80 : rawstr_text = cstring_to_text(rawstr);
2590 noah@leadboat.com 2344 : 80 : result_text = DatumGetTextPP(DirectFunctionCall1(quote_ident,
2345 : : PointerGetDatum(rawstr_text)));
5864 tgl@sss.pgh.pa.us 2346 : 80 : result = text_to_cstring(result_text);
2347 : :
8026 bruce@momjian.us 2348 : 80 : return result;
2349 : : }
2350 : :
2351 : : static int
5052 tgl@sss.pgh.pa.us 2352 : 26 : get_attnum_pk_pos(int *pkattnums, int pknumatts, int key)
2353 : : {
2354 : : int i;
2355 : :
2356 : : /*
2357 : : * Not likely a long list anyway, so just scan for the value
2358 : : */
8026 bruce@momjian.us 2359 [ + + ]: 50 : for (i = 0; i < pknumatts; i++)
5052 tgl@sss.pgh.pa.us 2360 [ + + ]: 38 : if (key == pkattnums[i])
8026 bruce@momjian.us 2361 : 14 : return i;
2362 : :
2363 : 12 : return -1;
2364 : : }
2365 : :
2366 : : static HeapTuple
5052 tgl@sss.pgh.pa.us 2367 : 8 : get_tuple_of_interest(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals)
2368 : : {
2369 : : char *relname;
2370 : : TupleDesc tupdesc;
2371 : : int natts;
2372 : : StringInfoData buf;
2373 : : int ret;
2374 : : HeapTuple tuple;
2375 : : int i;
2376 : :
2377 : : /*
2378 : : * Connect to SPI manager
2379 : : */
2380 [ - + ]: 8 : if ((ret = SPI_connect()) < 0)
2381 : : /* internal error */
5052 tgl@sss.pgh.pa.us 2382 [ # # ]:UBC 0 : elog(ERROR, "SPI connect failure - returned %d", ret);
2383 : :
6619 neilc@samurai.com 2384 :CBC 8 : initStringInfo(&buf);
2385 : :
2386 : : /* get relation name including any needed schema prefix and quoting */
5053 tgl@sss.pgh.pa.us 2387 : 8 : relname = generate_relation_name(rel);
2388 : :
2389 : 8 : tupdesc = rel->rd_att;
5052 2390 : 8 : natts = tupdesc->natts;
2391 : :
2392 : : /*
2393 : : * Build sql statement to look up tuple of interest, ie, the one matching
2394 : : * src_pkattvals. We used to use "SELECT *" here, but it's simpler to
2395 : : * generate a result tuple that matches the table's physical structure,
2396 : : * with NULLs for any dropped columns. Otherwise we have to deal with two
2397 : : * different tupdescs and everything's very confusing.
2398 : : */
2399 : 8 : appendStringInfoString(&buf, "SELECT ");
2400 : :
2401 [ + + ]: 38 : for (i = 0; i < natts; i++)
2402 : : {
2429 andres@anarazel.de 2403 : 30 : Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
2404 : :
5052 tgl@sss.pgh.pa.us 2405 [ + + ]: 30 : if (i > 0)
2406 : 22 : appendStringInfoString(&buf, ", ");
2407 : :
2429 andres@anarazel.de 2408 [ + + ]: 30 : if (attr->attisdropped)
5052 tgl@sss.pgh.pa.us 2409 : 4 : appendStringInfoString(&buf, "NULL");
2410 : : else
2411 : 26 : appendStringInfoString(&buf,
2429 andres@anarazel.de 2412 : 26 : quote_ident_cstr(NameStr(attr->attname)));
2413 : : }
2414 : :
5052 tgl@sss.pgh.pa.us 2415 : 8 : appendStringInfo(&buf, " FROM %s WHERE ", relname);
2416 : :
8026 bruce@momjian.us 2417 [ + + ]: 22 : for (i = 0; i < pknumatts; i++)
2418 : : {
5052 tgl@sss.pgh.pa.us 2419 : 14 : int pkattnum = pkattnums[i];
2429 andres@anarazel.de 2420 : 14 : Form_pg_attribute attr = TupleDescAttr(tupdesc, pkattnum);
2421 : :
8026 bruce@momjian.us 2422 [ + + ]: 14 : if (i > 0)
3818 rhaas@postgresql.org 2423 : 6 : appendStringInfoString(&buf, " AND ");
2424 : :
6619 neilc@samurai.com 2425 : 14 : appendStringInfoString(&buf,
2429 andres@anarazel.de 2426 : 14 : quote_ident_cstr(NameStr(attr->attname)));
2427 : :
6722 tgl@sss.pgh.pa.us 2428 [ + - ]: 14 : if (src_pkattvals[i] != NULL)
6619 neilc@samurai.com 2429 : 14 : appendStringInfo(&buf, " = %s",
6722 tgl@sss.pgh.pa.us 2430 : 14 : quote_literal_cstr(src_pkattvals[i]));
2431 : : else
3818 rhaas@postgresql.org 2432 :UBC 0 : appendStringInfoString(&buf, " IS NULL");
2433 : : }
2434 : :
2435 : : /*
2436 : : * Retrieve the desired tuple
2437 : : */
6619 neilc@samurai.com 2438 :CBC 8 : ret = SPI_exec(buf.data, 0);
2439 : 8 : pfree(buf.data);
2440 : :
2441 : : /*
2442 : : * Only allow one qualifying tuple
2443 : : */
8026 bruce@momjian.us 2444 [ + - - + ]: 8 : if ((ret == SPI_OK_SELECT) && (SPI_processed > 1))
7570 tgl@sss.pgh.pa.us 2445 [ # # ]:UBC 0 : ereport(ERROR,
2446 : : (errcode(ERRCODE_CARDINALITY_VIOLATION),
2447 : : errmsg("source criteria matched more than one record")));
2448 : :
8026 bruce@momjian.us 2449 [ + - + - ]:CBC 8 : else if (ret == SPI_OK_SELECT && SPI_processed == 1)
2450 : : {
2451 : 8 : SPITupleTable *tuptable = SPI_tuptable;
2452 : :
2453 : 8 : tuple = SPI_copytuple(tuptable->vals[0]);
7445 mail@joeconway.com 2454 : 8 : SPI_finish();
2455 : :
8026 bruce@momjian.us 2456 : 8 : return tuple;
2457 : : }
2458 : : else
2459 : : {
2460 : : /*
2461 : : * no qualifying tuples
2462 : : */
7445 mail@joeconway.com 2463 :UBC 0 : SPI_finish();
2464 : :
8026 bruce@momjian.us 2465 : 0 : return NULL;
2466 : : }
2467 : :
2468 : : /*
2469 : : * never reached, but keep compiler quiet
2470 : : */
2471 : : return NULL;
2472 : : }
2473 : :
2474 : : /*
2475 : : * Open the relation named by relname_text, acquire specified type of lock,
2476 : : * verify we have specified permissions.
2477 : : * Caller must close rel when done with it.
2478 : : */
2479 : : static Relation
5053 tgl@sss.pgh.pa.us 2480 :CBC 21 : get_rel_from_relname(text *relname_text, LOCKMODE lockmode, AclMode aclmode)
2481 : : {
2482 : : RangeVar *relvar;
2483 : : Relation rel;
2484 : : AclResult aclresult;
2485 : :
6897 neilc@samurai.com 2486 : 21 : relvar = makeRangeVarFromNameList(textToQualifiedNameList(relname_text));
1910 andres@anarazel.de 2487 : 21 : rel = table_openrv(relvar, lockmode);
2488 : :
5053 tgl@sss.pgh.pa.us 2489 : 21 : aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
2490 : : aclmode);
2491 [ - + ]: 21 : if (aclresult != ACLCHECK_OK)
2325 peter_e@gmx.net 2492 :UBC 0 : aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
5053 tgl@sss.pgh.pa.us 2493 : 0 : RelationGetRelationName(rel));
2494 : :
5053 tgl@sss.pgh.pa.us 2495 :CBC 21 : return rel;
2496 : : }
2497 : :
2498 : : /*
2499 : : * generate_relation_name - copied from ruleutils.c
2500 : : * Compute the name to display for a relation
2501 : : *
2502 : : * The result includes all necessary quoting and schema-prefixing.
2503 : : */
2504 : : static char *
2505 : 20 : generate_relation_name(Relation rel)
2506 : : {
2507 : : char *nspname;
2508 : : char *result;
2509 : :
2510 : : /* Qualify the name if not visible in search path */
2511 [ + + ]: 20 : if (RelationIsVisible(RelationGetRelid(rel)))
7813 2512 : 15 : nspname = NULL;
2513 : : else
5053 2514 : 5 : nspname = get_namespace_name(rel->rd_rel->relnamespace);
2515 : :
2516 : 20 : result = quote_qualified_identifier(nspname, RelationGetRelationName(rel));
2517 : :
7813 2518 : 20 : return result;
2519 : : }
2520 : :
2521 : :
2522 : : static remoteConn *
7599 bruce@momjian.us 2523 : 80 : getConnectionByName(const char *name)
2524 : : {
2525 : : remoteConnHashEnt *hentry;
2526 : : char *key;
2527 : :
7559 2528 [ + + ]: 80 : if (!remoteConnHash)
2529 : 7 : remoteConnHash = createConnHash();
2530 : :
5064 itagaki.takahiro@gma 2531 : 80 : key = pstrdup(name);
4889 2532 : 80 : truncate_identifier(key, strlen(key), false);
7559 bruce@momjian.us 2533 : 80 : hentry = (remoteConnHashEnt *) hash_search(remoteConnHash,
2534 : : key, HASH_FIND, NULL);
2535 : :
2536 [ + + ]: 80 : if (hentry)
2432 peter_e@gmx.net 2537 : 68 : return hentry->rconn;
2538 : :
2539 : 12 : return NULL;
2540 : : }
2541 : :
2542 : : static HTAB *
7599 bruce@momjian.us 2543 : 8 : createConnHash(void)
2544 : : {
2545 : : HASHCTL ctl;
2546 : :
2547 : 8 : ctl.keysize = NAMEDATALEN;
2548 : 8 : ctl.entrysize = sizeof(remoteConnHashEnt);
2549 : :
1216 tgl@sss.pgh.pa.us 2550 : 8 : return hash_create("Remote Con hash", NUMCONN, &ctl,
2551 : : HASH_ELEM | HASH_STRINGS);
2552 : : }
2553 : :
2554 : : static void
5421 bruce@momjian.us 2555 : 10 : createNewConnection(const char *name, remoteConn *rconn)
2556 : : {
2557 : : remoteConnHashEnt *hentry;
2558 : : bool found;
2559 : : char *key;
2560 : :
7559 2561 [ + + ]: 10 : if (!remoteConnHash)
7570 tgl@sss.pgh.pa.us 2562 : 1 : remoteConnHash = createConnHash();
2563 : :
5064 itagaki.takahiro@gma 2564 : 10 : key = pstrdup(name);
2565 : 10 : truncate_identifier(key, strlen(key), true);
7599 bruce@momjian.us 2566 : 10 : hentry = (remoteConnHashEnt *) hash_search(remoteConnHash, key,
2567 : : HASH_ENTER, &found);
2568 : :
7559 2569 [ + + ]: 10 : if (found)
2570 : : {
447 andres@anarazel.de 2571 : 1 : libpqsrv_disconnect(rconn->conn);
5058 itagaki.takahiro@gma 2572 : 1 : pfree(rconn);
2573 : :
7570 tgl@sss.pgh.pa.us 2574 [ + - ]: 1 : ereport(ERROR,
2575 : : (errcode(ERRCODE_DUPLICATE_OBJECT),
2576 : : errmsg("duplicate connection name")));
2577 : : }
2578 : :
6763 bruce@momjian.us 2579 : 9 : hentry->rconn = rconn;
7599 2580 : 9 : }
2581 : :
2582 : : static void
2583 : 8 : deleteConnection(const char *name)
2584 : : {
2585 : : remoteConnHashEnt *hentry;
2586 : : bool found;
2587 : : char *key;
2588 : :
7559 2589 [ - + ]: 8 : if (!remoteConnHash)
7559 bruce@momjian.us 2590 :UBC 0 : remoteConnHash = createConnHash();
2591 : :
5064 itagaki.takahiro@gma 2592 :CBC 8 : key = pstrdup(name);
4889 2593 : 8 : truncate_identifier(key, strlen(key), false);
7599 bruce@momjian.us 2594 : 8 : hentry = (remoteConnHashEnt *) hash_search(remoteConnHash,
2595 : : key, HASH_REMOVE, &found);
2596 : :
7559 2597 [ - + ]: 8 : if (!hentry)
7570 tgl@sss.pgh.pa.us 2598 [ # # ]:UBC 0 : ereport(ERROR,
2599 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
2600 : : errmsg("undefined connection name")));
7599 bruce@momjian.us 2601 :CBC 8 : }
2602 : :
2603 : : /*
2604 : : * We need to make sure that the connection made used credentials
2605 : : * which were provided by the user, so check what credentials were
2606 : : * used to connect and then make sure that they came from the user.
2607 : : */
2608 : : static void
367 sfrost@snowman.net 2609 : 22 : dblink_security_check(PGconn *conn, remoteConn *rconn, const char *connstr)
2610 : : {
2611 : : /* Superuser bypasses security check */
2612 [ + + ]: 22 : if (superuser())
2613 : 19 : return;
2614 : :
2615 : : /* If password was used to connect, make sure it was one provided */
2616 [ + + - + ]: 3 : if (PQconnectionUsedPassword(conn) && dblink_connstr_has_pw(connstr))
367 sfrost@snowman.net 2617 :UBC 0 : return;
2618 : :
2619 : : #ifdef ENABLE_GSS
2620 : : /* If GSSAPI creds used to connect, make sure it was one delegated */
330 bruce@momjian.us 2621 [ + + + - ]:CBC 3 : if (PQconnectionUsedGSSAPI(conn) && be_gssapi_get_delegation(MyProcPort))
367 sfrost@snowman.net 2622 : 2 : return;
2623 : : #endif
2624 : :
2625 : : /* Otherwise, fail out */
2626 : 1 : libpqsrv_disconnect(conn);
2627 [ - + ]: 1 : if (rconn)
367 sfrost@snowman.net 2628 :UBC 0 : pfree(rconn);
2629 : :
367 sfrost@snowman.net 2630 [ + - ]:CBC 1 : ereport(ERROR,
2631 : : (errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
2632 : : errmsg("password or GSSAPI delegated credentials required"),
2633 : : errdetail("Non-superusers may only connect using credentials they provide, eg: password in connection string or delegated GSSAPI credentials"),
2634 : : errhint("Ensure provided credentials match target server's authentication method.")));
2635 : : }
2636 : :
2637 : : /*
2638 : : * Function to check if the connection string includes an explicit
2639 : : * password, needed to ensure that non-superuser password-based auth
2640 : : * is using a provided password and not one picked up from the
2641 : : * environment.
2642 : : */
2643 : : static bool
2644 : 7 : dblink_connstr_has_pw(const char *connstr)
2645 : : {
2646 : : PQconninfoOption *options;
2647 : : PQconninfoOption *option;
2648 : 7 : bool connstr_gives_password = false;
2649 : :
2650 : 7 : options = PQconninfoParse(connstr, NULL);
2651 [ + - ]: 7 : if (options)
2652 : : {
2653 [ + + ]: 255 : for (option = options; option->keyword != NULL; option++)
2654 : : {
2655 [ + + ]: 249 : if (strcmp(option->keyword, "password") == 0)
2656 : : {
2657 [ + + + - ]: 7 : if (option->val != NULL && option->val[0] != '\0')
2658 : : {
2659 : 1 : connstr_gives_password = true;
2660 : 1 : break;
2661 : : }
2662 : : }
2663 : : }
2664 : 7 : PQconninfoFree(options);
2665 : : }
2666 : :
2667 : 7 : return connstr_gives_password;
2668 : : }
2669 : :
2670 : : /*
2671 : : * For non-superusers, insist that the connstr specify a password, except
2672 : : * if GSSAPI credentials have been delegated (and we check that they are used
2673 : : * for the connection in dblink_security_check later). This prevents a
2674 : : * password or GSSAPI credentials from being picked up from .pgpass, a
2675 : : * service file, the environment, etc. We don't want the postgres user's
2676 : : * passwords or Kerberos credentials to be accessible to non-superusers.
2677 : : */
2678 : : static void
2679 : 27 : dblink_connstr_check(const char *connstr)
2680 : : {
2681 [ + + ]: 27 : if (superuser())
2682 : 21 : return;
2683 : :
2684 [ + + ]: 6 : if (dblink_connstr_has_pw(connstr))
2685 : 1 : return;
2686 : :
2687 : : #ifdef ENABLE_GSS
330 bruce@momjian.us 2688 [ + + ]: 5 : if (be_gssapi_get_delegation(MyProcPort))
367 sfrost@snowman.net 2689 : 2 : return;
2690 : : #endif
2691 : :
2692 [ + - ]: 3 : ereport(ERROR,
2693 : : (errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
2694 : : errmsg("password or GSSAPI delegated credentials required"),
2695 : : errdetail("Non-superusers must provide a password in the connection string or send delegated GSSAPI credentials.")));
2696 : : }
2697 : :
2698 : : /*
2699 : : * Report an error received from the remote server
2700 : : *
2701 : : * res: the received error result (will be freed)
2702 : : * fail: true for ERROR ereport, false for NOTICE
2703 : : * fmt and following args: sprintf-style format and values for errcontext;
2704 : : * the resulting string should be worded like "while <some action>"
2705 : : */
2706 : : static void
2670 mail@joeconway.com 2707 : 12 : dblink_res_error(PGconn *conn, const char *conname, PGresult *res,
2708 : : bool fail, const char *fmt,...)
2709 : : {
2710 : : int level;
5764 2711 : 12 : char *pg_diag_sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE);
2712 : 12 : char *pg_diag_message_primary = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY);
2713 : 12 : char *pg_diag_message_detail = PQresultErrorField(res, PG_DIAG_MESSAGE_DETAIL);
2714 : 12 : char *pg_diag_message_hint = PQresultErrorField(res, PG_DIAG_MESSAGE_HINT);
2715 : 12 : char *pg_diag_context = PQresultErrorField(res, PG_DIAG_CONTEXT);
2716 : : int sqlstate;
2717 : : char *message_primary;
2718 : : char *message_detail;
2719 : : char *message_hint;
2720 : : char *message_context;
2721 : : va_list ap;
2722 : : char dblink_context_msg[512];
2723 : :
2724 [ + + ]: 12 : if (fail)
2725 : 3 : level = ERROR;
2726 : : else
2727 : 9 : level = NOTICE;
2728 : :
2729 [ + - ]: 12 : if (pg_diag_sqlstate)
2730 : 12 : sqlstate = MAKE_SQLSTATE(pg_diag_sqlstate[0],
2731 : : pg_diag_sqlstate[1],
2732 : : pg_diag_sqlstate[2],
2733 : : pg_diag_sqlstate[3],
2734 : : pg_diag_sqlstate[4]);
2735 : : else
5764 mail@joeconway.com 2736 :UBC 0 : sqlstate = ERRCODE_CONNECTION_FAILURE;
2737 : :
2667 peter_e@gmx.net 2738 :CBC 12 : message_primary = xpstrdup(pg_diag_message_primary);
2739 : 12 : message_detail = xpstrdup(pg_diag_message_detail);
2740 : 12 : message_hint = xpstrdup(pg_diag_message_hint);
2741 : 12 : message_context = xpstrdup(pg_diag_context);
2742 : :
2743 : : /*
2744 : : * If we don't get a message from the PGresult, try the PGconn. This is
2745 : : * needed because for connection-level failures, PQgetResult may just
2746 : : * return NULL, not a PGresult at all.
2747 : : */
2670 mail@joeconway.com 2748 [ - + ]: 12 : if (message_primary == NULL)
2603 peter_e@gmx.net 2749 :UBC 0 : message_primary = pchomp(PQerrorMessage(conn));
2750 : :
2751 : : /*
2752 : : * Now that we've copied all the data we need out of the PGresult, it's
2753 : : * safe to free it. We must do this to avoid PGresult leakage. We're
2754 : : * leaking all the strings too, but those are in palloc'd memory that will
2755 : : * get cleaned up eventually.
2756 : : */
651 peter@eisentraut.org 2757 :CBC 12 : PQclear(res);
2758 : :
2759 : : /*
2760 : : * Format the basic errcontext string. Below, we'll add on something
2761 : : * about the connection name. That's a violation of the translatability
2762 : : * guidelines about constructing error messages out of parts, but since
2763 : : * there's no translation support for dblink, there's no need to worry
2764 : : * about that (yet).
2765 : : */
2215 tgl@sss.pgh.pa.us 2766 : 12 : va_start(ap, fmt);
2767 : 12 : vsnprintf(dblink_context_msg, sizeof(dblink_context_msg), fmt, ap);
2768 : 12 : va_end(ap);
2769 : :
5764 mail@joeconway.com 2770 [ + - + - : 12 : ereport(level,
+ - - + -
+ - + +
+ ]
2771 : : (errcode(sqlstate),
2772 : : (message_primary != NULL && message_primary[0] != '\0') ?
2773 : : errmsg_internal("%s", message_primary) :
2774 : : errmsg("could not obtain message string for remote error"),
2775 : : message_detail ? errdetail_internal("%s", message_detail) : 0,
2776 : : message_hint ? errhint("%s", message_hint) : 0,
2777 : : message_context ? (errcontext("%s", message_context)) : 0,
2778 : : conname ?
2779 : : (errcontext("%s on dblink connection named \"%s\"",
2780 : : dblink_context_msg, conname)) :
2781 : : (errcontext("%s on unnamed dblink connection",
2782 : : dblink_context_msg))));
2783 : 9 : }
2784 : :
2785 : : /*
2786 : : * Obtain connection string for a foreign server
2787 : : */
2788 : : static char *
5426 2789 : 27 : get_connect_string(const char *servername)
2790 : : {
5421 bruce@momjian.us 2791 : 27 : ForeignServer *foreign_server = NULL;
2792 : : UserMapping *user_mapping;
2793 : : ListCell *cell;
2794 : : StringInfoData buf;
2795 : : ForeignDataWrapper *fdw;
2796 : : AclResult aclresult;
2797 : : char *srvname;
2798 : :
2799 : : static const PQconninfoOption *options = NULL;
2800 : :
2592 peter_e@gmx.net 2801 : 27 : initStringInfo(&buf);
2802 : :
2803 : : /*
2804 : : * Get list of valid libpq options.
2805 : : *
2806 : : * To avoid unnecessary work, we get the list once and use it throughout
2807 : : * the lifetime of this backend process. We don't need to care about
2808 : : * memory context issues, because PQconndefaults allocates with malloc.
2809 : : */
2670 mail@joeconway.com 2810 [ + + ]: 27 : if (!options)
2811 : : {
2812 : 8 : options = PQconndefaults();
2813 [ - + ]: 8 : if (!options) /* assume reason for failure is OOM */
2670 mail@joeconway.com 2814 [ # # ]:UBC 0 : ereport(ERROR,
2815 : : (errcode(ERRCODE_FDW_OUT_OF_MEMORY),
2816 : : errmsg("out of memory"),
2817 : : errdetail("Could not get libpq's default connection options.")));
2818 : : }
2819 : :
2820 : : /* first gather the server connstr options */
5064 itagaki.takahiro@gma 2821 :CBC 27 : srvname = pstrdup(servername);
5058 2822 : 27 : truncate_identifier(srvname, strlen(srvname), false);
5064 2823 : 27 : foreign_server = GetForeignServerByName(srvname, true);
2824 : :
5426 mail@joeconway.com 2825 [ + + ]: 27 : if (foreign_server)
2826 : : {
5421 bruce@momjian.us 2827 : 2 : Oid serverid = foreign_server->serverid;
2828 : 2 : Oid fdwid = foreign_server->fdwid;
2829 : 2 : Oid userid = GetUserId();
2830 : :
5426 mail@joeconway.com 2831 : 2 : user_mapping = GetUserMapping(userid, serverid);
5421 bruce@momjian.us 2832 : 2 : fdw = GetForeignDataWrapper(fdwid);
2833 : :
2834 : : /* Check permissions, user must have usage on the server. */
518 peter@eisentraut.org 2835 : 2 : aclresult = object_aclcheck(ForeignServerRelationId, serverid, userid, ACL_USAGE);
5426 mail@joeconway.com 2836 [ - + ]: 2 : if (aclresult != ACLCHECK_OK)
2325 peter_e@gmx.net 2837 :UBC 0 : aclcheck_error(aclresult, OBJECT_FOREIGN_SERVER, foreign_server->servername);
2838 : :
5421 bruce@momjian.us 2839 [ - + - - :CBC 2 : foreach(cell, fdw->options)
- + ]
2840 : : {
5421 bruce@momjian.us 2841 :UBC 0 : DefElem *def = lfirst(cell);
2842 : :
2670 mail@joeconway.com 2843 [ # # ]: 0 : if (is_valid_dblink_option(options, def->defname, ForeignDataWrapperRelationId))
2592 peter_e@gmx.net 2844 : 0 : appendStringInfo(&buf, "%s='%s' ", def->defname,
2670 mail@joeconway.com 2845 : 0 : escape_param_str(strVal(def->arg)));
2846 : : }
2847 : :
5421 bruce@momjian.us 2848 [ + - + + :CBC 6 : foreach(cell, foreign_server->options)
+ + ]
2849 : : {
2850 : 4 : DefElem *def = lfirst(cell);
2851 : :
2670 mail@joeconway.com 2852 [ + - ]: 4 : if (is_valid_dblink_option(options, def->defname, ForeignServerRelationId))
2592 peter_e@gmx.net 2853 : 4 : appendStringInfo(&buf, "%s='%s' ", def->defname,
2670 mail@joeconway.com 2854 : 4 : escape_param_str(strVal(def->arg)));
2855 : : }
2856 : :
5421 bruce@momjian.us 2857 [ + - + + : 4 : foreach(cell, user_mapping->options)
+ + ]
2858 : : {
2859 : :
2860 : 2 : DefElem *def = lfirst(cell);
2861 : :
2670 mail@joeconway.com 2862 [ + - ]: 2 : if (is_valid_dblink_option(options, def->defname, UserMappingRelationId))
2592 peter_e@gmx.net 2863 : 2 : appendStringInfo(&buf, "%s='%s' ", def->defname,
2670 mail@joeconway.com 2864 : 2 : escape_param_str(strVal(def->arg)));
2865 : : }
2866 : :
2592 peter_e@gmx.net 2867 : 2 : return buf.data;
2868 : : }
2869 : : else
5426 mail@joeconway.com 2870 : 25 : return NULL;
2871 : : }
2872 : :
2873 : : /*
2874 : : * Escaping libpq connect parameter strings.
2875 : : *
2876 : : * Replaces "'" with "\'" and "\" with "\\".
2877 : : */
2878 : : static char *
2879 : 6 : escape_param_str(const char *str)
2880 : : {
2881 : : const char *cp;
2882 : : StringInfoData buf;
2883 : :
2592 peter_e@gmx.net 2884 : 6 : initStringInfo(&buf);
2885 : :
5426 mail@joeconway.com 2886 [ + + ]: 62 : for (cp = str; *cp; cp++)
2887 : : {
2888 [ + - - + ]: 56 : if (*cp == '\\' || *cp == '\'')
2592 peter_e@gmx.net 2889 :UBC 0 : appendStringInfoChar(&buf, '\\');
2592 peter_e@gmx.net 2890 :CBC 56 : appendStringInfoChar(&buf, *cp);
2891 : : }
2892 : :
2893 : 6 : return buf.data;
2894 : : }
2895 : :
2896 : : /*
2897 : : * Validate the PK-attnums argument for dblink_build_sql_insert() and related
2898 : : * functions, and translate to the internal representation.
2899 : : *
2900 : : * The user supplies an int2vector of 1-based logical attnums, plus a count
2901 : : * argument (the need for the separate count argument is historical, but we
2902 : : * still check it). We check that each attnum corresponds to a valid,
2903 : : * non-dropped attribute of the rel. We do *not* prevent attnums from being
2904 : : * listed twice, though the actual use-case for such things is dubious.
2905 : : * Note that before Postgres 9.0, the user's attnums were interpreted as
2906 : : * physical not logical column numbers; this was changed for future-proofing.
2907 : : *
2908 : : * The internal representation is a palloc'd int array of 0-based physical
2909 : : * attnums.
2910 : : */
2911 : : static void
5052 tgl@sss.pgh.pa.us 2912 : 18 : validate_pkattnums(Relation rel,
2913 : : int2vector *pkattnums_arg, int32 pknumatts_arg,
2914 : : int **pkattnums, int *pknumatts)
2915 : : {
2916 : 18 : TupleDesc tupdesc = rel->rd_att;
2917 : 18 : int natts = tupdesc->natts;
2918 : : int i;
2919 : :
2920 : : /* Don't take more array elements than there are */
2921 : 18 : pknumatts_arg = Min(pknumatts_arg, pkattnums_arg->dim1);
2922 : :
2923 : : /* Must have at least one pk attnum selected */
2924 [ - + ]: 18 : if (pknumatts_arg <= 0)
5052 tgl@sss.pgh.pa.us 2925 [ # # ]:UBC 0 : ereport(ERROR,
2926 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2927 : : errmsg("number of key attributes must be > 0")));
2928 : :
2929 : : /* Allocate output array */
580 peter@eisentraut.org 2930 :CBC 18 : *pkattnums = palloc_array(int, pknumatts_arg);
5052 tgl@sss.pgh.pa.us 2931 : 18 : *pknumatts = pknumatts_arg;
2932 : :
2933 : : /* Validate attnums and convert to internal form */
2934 [ + + ]: 57 : for (i = 0; i < pknumatts_arg; i++)
2935 : : {
5031 bruce@momjian.us 2936 : 45 : int pkattnum = pkattnums_arg->values[i];
2937 : : int lnum;
2938 : : int j;
2939 : :
2940 : : /* Can throw error immediately if out of range */
5052 tgl@sss.pgh.pa.us 2941 [ + - + + ]: 45 : if (pkattnum <= 0 || pkattnum > natts)
2942 [ + - ]: 6 : ereport(ERROR,
2943 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2944 : : errmsg("invalid attribute number %d", pkattnum)));
2945 : :
2946 : : /* Identify which physical column has this logical number */
2947 : 39 : lnum = 0;
2948 [ + - ]: 69 : for (j = 0; j < natts; j++)
2949 : : {
2950 : : /* dropped columns don't count */
2429 andres@anarazel.de 2951 [ + + ]: 69 : if (TupleDescAttr(tupdesc, j)->attisdropped)
5052 tgl@sss.pgh.pa.us 2952 : 3 : continue;
2953 : :
2954 [ + + ]: 66 : if (++lnum == pkattnum)
2955 : 39 : break;
2956 : : }
2957 : :
2958 [ + - ]: 39 : if (j < natts)
2959 : 39 : (*pkattnums)[i] = j;
2960 : : else
5052 tgl@sss.pgh.pa.us 2961 [ # # ]:UBC 0 : ereport(ERROR,
2962 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2963 : : errmsg("invalid attribute number %d", pkattnum)));
2964 : : }
5184 mail@joeconway.com 2965 :CBC 12 : }
2966 : :
2967 : : /*
2968 : : * Check if the specified connection option is valid.
2969 : : *
2970 : : * We basically allow whatever libpq thinks is an option, with these
2971 : : * restrictions:
2972 : : * debug options: disallowed
2973 : : * "client_encoding": disallowed
2974 : : * "user": valid only in USER MAPPING options
2975 : : * secure options (eg password): valid only in USER MAPPING options
2976 : : * others: valid only in FOREIGN SERVER options
2977 : : *
2978 : : * We disallow client_encoding because it would be overridden anyway via
2979 : : * PQclientEncoding; allowing it to be specified would merely promote
2980 : : * confusion.
2981 : : */
2982 : : static bool
4204 tgl@sss.pgh.pa.us 2983 : 93 : is_valid_dblink_option(const PQconninfoOption *options, const char *option,
2984 : : Oid context)
2985 : : {
2986 : : const PQconninfoOption *opt;
2987 : :
2988 : : /* Look up the option in libpq result */
2989 [ + + ]: 1863 : for (opt = options; opt->keyword; opt++)
2990 : : {
2991 [ + + ]: 1861 : if (strcmp(opt->keyword, option) == 0)
2992 : 91 : break;
2993 : : }
2994 [ + + ]: 93 : if (opt->keyword == NULL)
2995 : 2 : return false;
2996 : :
2997 : : /* Disallow debug options (particularly "replication") */
2998 [ + + ]: 91 : if (strchr(opt->dispchar, 'D'))
2999 : 2 : return false;
3000 : :
3001 : : /* Disallow "client_encoding" */
3002 [ + + ]: 89 : if (strcmp(opt->keyword, "client_encoding") == 0)
3003 : 2 : return false;
3004 : :
3005 : : /*
3006 : : * If the option is "user" or marked secure, it should be specified only
3007 : : * in USER MAPPING. Others should be specified only in SERVER.
3008 : : */
3009 [ + + + + ]: 87 : if (strcmp(opt->keyword, "user") == 0 || strchr(opt->dispchar, '*'))
3010 : : {
3011 [ + + ]: 9 : if (context != UserMappingRelationId)
3012 : 3 : return false;
3013 : : }
3014 : : else
3015 : : {
3016 [ + + ]: 78 : if (context != ForeignServerRelationId)
3017 : 72 : return false;
3018 : : }
3019 : :
3020 : 12 : return true;
3021 : : }
3022 : :
3023 : : /*
3024 : : * Copy the remote session's values of GUCs that affect datatype I/O
3025 : : * and apply them locally in a new GUC nesting level. Returns the new
3026 : : * nestlevel (which is needed by restoreLocalGucs to undo the settings),
3027 : : * or -1 if no new nestlevel was needed.
3028 : : *
3029 : : * We use the equivalent of a function SET option to allow the settings to
3030 : : * persist only until the caller calls restoreLocalGucs. If an error is
3031 : : * thrown in between, guc.c will take care of undoing the settings.
3032 : : */
3033 : : static int
4041 3034 : 34 : applyRemoteGucs(PGconn *conn)
3035 : : {
3036 : : static const char *const GUCsAffectingIO[] = {
3037 : : "DateStyle",
3038 : : "IntervalStyle"
3039 : : };
3040 : :
3041 : 34 : int nestlevel = -1;
3042 : : int i;
3043 : :
3044 [ + + ]: 102 : for (i = 0; i < lengthof(GUCsAffectingIO); i++)
3045 : : {
3046 : 68 : const char *gucName = GUCsAffectingIO[i];
3047 : 68 : const char *remoteVal = PQparameterStatus(conn, gucName);
3048 : : const char *localVal;
3049 : :
3050 : : /*
3051 : : * If the remote server is pre-8.4, it won't have IntervalStyle, but
3052 : : * that's okay because its output format won't be ambiguous. So just
3053 : : * skip the GUC if we don't get a value for it. (We might eventually
3054 : : * need more complicated logic with remote-version checks here.)
3055 : : */
3056 [ - + ]: 68 : if (remoteVal == NULL)
4041 tgl@sss.pgh.pa.us 3057 :UBC 0 : continue;
3058 : :
3059 : : /*
3060 : : * Avoid GUC-setting overhead if the remote and local GUCs already
3061 : : * have the same value.
3062 : : */
4041 tgl@sss.pgh.pa.us 3063 :CBC 68 : localVal = GetConfigOption(gucName, false, false);
3064 [ - + ]: 68 : Assert(localVal != NULL);
3065 : :
3066 [ + + ]: 68 : if (strcmp(remoteVal, localVal) == 0)
3067 : 51 : continue;
3068 : :
3069 : : /* Create new GUC nest level if we didn't already */
3070 [ + + ]: 17 : if (nestlevel < 0)
3071 : 9 : nestlevel = NewGUCNestLevel();
3072 : :
3073 : : /* Apply the option (this will throw error on failure) */
3074 : 17 : (void) set_config_option(gucName, remoteVal,
3075 : : PGC_USERSET, PGC_S_SESSION,
3076 : : GUC_ACTION_SAVE, true, 0, false);
3077 : : }
3078 : :
3079 : 34 : return nestlevel;
3080 : : }
3081 : :
3082 : : /*
3083 : : * Restore local GUCs after they have been overlaid with remote settings.
3084 : : */
3085 : : static void
3086 : 35 : restoreLocalGucs(int nestlevel)
3087 : : {
3088 : : /* Do nothing if no new nestlevel was created */
3089 [ + + ]: 35 : if (nestlevel > 0)
3090 : 8 : AtEOXact_GUC(true, nestlevel);
3091 : 35 : }
|