Index: contrib/dblink/README.dblink =================================================================== RCS file: /cvsroot/pgsql-server/contrib/dblink/README.dblink,v retrieving revision 1.9 diff -c -r1.9 README.dblink *** contrib/dblink/README.dblink 4 Aug 2003 23:59:37 -0000 1.9 --- contrib/dblink/README.dblink 5 Mar 2004 05:55:45 -0000 *************** *** 30,42 **** * */ - Version 0.6 (14 June, 2003): - Completely removed previously deprecated functions. Added ability - to create "named" persistent connections in addition to the single global - "unnamed" persistent connection. - Tested under Linux (Red Hat 9) and PostgreSQL 7.4devel. - Release Notes: Version 0.6 - functions deprecated in 0.5 have been removed - added ability to create "named" persistent connections --- 30,40 ---- * */ Release Notes: + Version 0.7 (as of 25 Feb, 2004) + - Added new version of dblink, dblink_exec, dblink_open, dblink_close, + and, dblink_fetch -- allows ERROR on remote side of connection to + throw NOTICE locally instead of ERROR Version 0.6 - functions deprecated in 0.5 have been removed - added ability to create "named" persistent connections *************** *** 85,91 **** You can use dblink.sql to create the functions in your database of choice, e.g. ! psql -U postgres template1 < dblink.sql installs following functions into database template1: --- 83,89 ---- You can use dblink.sql to create the functions in your database of choice, e.g. ! psql template1 < dblink.sql installs following functions into database template1: *************** *** 104,143 **** cursor ------------ ! dblink_open(text,text) RETURNS text - opens a cursor using unnamed connection already opened with dblink_connect() that will persist for duration of current backend or until it is closed ! dblink_open(text,text,text) RETURNS text - opens a cursor using a named connection already opened with dblink_connect() that will persist for duration of current backend or until it is closed ! dblink_fetch(text, int) RETURNS setof record - fetches data from an already opened cursor on the unnamed connection ! dblink_fetch(text, text, int) RETURNS setof record - fetches data from an already opened cursor on a named connection ! dblink_close(text) RETURNS text - closes a cursor on the unnamed connection ! dblink_close(text,text) RETURNS text - closes a cursor on a named connection query ------------ ! dblink(text,text) RETURNS setof record - returns a set of results from remote SELECT query; the first argument is either a connection string, or the name of an already opened persistant connection ! dblink(text) RETURNS setof record - returns a set of results from remote SELECT query, using the unnamed connection already opened with dblink_connect() execute ------------ ! dblink_exec(text, text) RETURNS text - executes an INSERT/UPDATE/DELETE query remotely; the first argument is either a connection string, or the name of an already opened persistant connection ! dblink_exec(text) RETURNS text - executes an INSERT/UPDATE/DELETE query remotely, using connection already opened with dblink_connect() --- 102,141 ---- cursor ------------ ! dblink_open(text,text [, bool fail_on_error]) RETURNS text - opens a cursor using unnamed connection already opened with dblink_connect() that will persist for duration of current backend or until it is closed ! dblink_open(text,text,text [, bool fail_on_error]) RETURNS text - opens a cursor using a named connection already opened with dblink_connect() that will persist for duration of current backend or until it is closed ! dblink_fetch(text, int [, bool fail_on_error]) RETURNS setof record - fetches data from an already opened cursor on the unnamed connection ! dblink_fetch(text, text, int [, bool fail_on_error]) RETURNS setof record - fetches data from an already opened cursor on a named connection ! dblink_close(text [, bool fail_on_error]) RETURNS text - closes a cursor on the unnamed connection ! dblink_close(text,text [, bool fail_on_error]) RETURNS text - closes a cursor on a named connection query ------------ ! dblink(text,text [, bool fail_on_error]) RETURNS setof record - returns a set of results from remote SELECT query; the first argument is either a connection string, or the name of an already opened persistant connection ! dblink(text [, bool fail_on_error]) RETURNS setof record - returns a set of results from remote SELECT query, using the unnamed connection already opened with dblink_connect() execute ------------ ! dblink_exec(text, text [, bool fail_on_error]) RETURNS text - executes an INSERT/UPDATE/DELETE query remotely; the first argument is either a connection string, or the name of an already opened persistant connection ! dblink_exec(text [, bool fail_on_error]) RETURNS text - executes an INSERT/UPDATE/DELETE query remotely, using connection already opened with dblink_connect() *************** *** 169,175 **** doc/query doc/execute doc/misc - doc/deprecated ================================================================== -- Joe Conway --- 167,172 ---- Index: contrib/dblink/dblink.c =================================================================== RCS file: /cvsroot/pgsql-server/contrib/dblink/dblink.c,v retrieving revision 1.30 diff -c -r1.30 dblink.c *** contrib/dblink/dblink.c 24 Feb 2004 06:07:18 -0000 1.30 --- contrib/dblink/dblink.c 5 Mar 2004 05:55:46 -0000 *************** *** 134,139 **** --- 134,149 ---- errmsg("%s", p2), \ errdetail("%s", msg))); \ } while (0) + #define DBLINK_RES_ERROR_AS_NOTICE(p2) \ + do { \ + msg = pstrdup(PQerrorMessage(conn)); \ + if (res) \ + PQclear(res); \ + ereport(NOTICE, \ + (errcode(ERRCODE_SYNTAX_ERROR), \ + errmsg("%s", p2), \ + errdetail("%s", msg))); \ + } while (0) #define DBLINK_CONN_NOT_AVAIL \ do { \ if(conname) \ *************** *** 152,158 **** if(rcon) \ { \ conn = rcon->con; \ - freeconn = false; \ } \ else \ { \ --- 162,167 ---- *************** *** 167,172 **** --- 176,182 ---- errmsg("could not establish connection"), \ errdetail("%s", msg))); \ } \ + freeconn = true; \ } \ } while (0) *************** *** 276,293 **** --- 286,327 ---- char *conname = NULL; StringInfo str = makeStringInfo(); remoteConn *rcon = NULL; + bool fail = true; /* default to backward compatible behavior */ if (PG_NARGS() == 2) { + /* text,text */ curname = GET_STR(PG_GETARG_TEXT_P(0)); sql = GET_STR(PG_GETARG_TEXT_P(1)); conn = persistent_conn; } else if (PG_NARGS() == 3) { + /* might be text,text,text or text,text,bool */ + if (get_fn_expr_argtype(fcinfo->flinfo, 2) == BOOLOID) + { + curname = GET_STR(PG_GETARG_TEXT_P(0)); + sql = GET_STR(PG_GETARG_TEXT_P(1)); + fail = PG_GETARG_BOOL(2); + conn = persistent_conn; + } + else + { + conname = GET_STR(PG_GETARG_TEXT_P(0)); + curname = GET_STR(PG_GETARG_TEXT_P(1)); + sql = GET_STR(PG_GETARG_TEXT_P(2)); + } + rcon = getConnectionByName(conname); + if (rcon) + conn = rcon->con; + } + else if (PG_NARGS() == 4) + { + /* text,text,text,bool */ conname = GET_STR(PG_GETARG_TEXT_P(0)); curname = GET_STR(PG_GETARG_TEXT_P(1)); sql = GET_STR(PG_GETARG_TEXT_P(2)); + fail = PG_GETARG_BOOL(3); rcon = getConnectionByName(conname); if (rcon) conn = rcon->con; *************** *** 304,316 **** appendStringInfo(str, "DECLARE %s CURSOR FOR %s", curname, sql); res = PQexec(conn, str->data); ! if (!res || ! (PQresultStatus(res) != PGRES_COMMAND_OK && ! PQresultStatus(res) != PGRES_TUPLES_OK)) ! DBLINK_RES_ERROR("sql error"); PQclear(res); - PG_RETURN_TEXT_P(GET_TEXT("OK")); } --- 338,356 ---- appendStringInfo(str, "DECLARE %s CURSOR FOR %s", curname, sql); res = PQexec(conn, str->data); ! if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) ! { ! if (fail) ! DBLINK_RES_ERROR("sql error"); ! else ! { ! DBLINK_RES_ERROR_AS_NOTICE("sql error"); ! PQclear(res); ! PG_RETURN_TEXT_P(GET_TEXT("ERROR")); ! } ! } PQclear(res); PG_RETURN_TEXT_P(GET_TEXT("OK")); } *************** *** 328,343 **** --- 368,405 ---- StringInfo str = makeStringInfo(); char *msg; remoteConn *rcon = NULL; + bool fail = true; /* default to backward compatible behavior */ if (PG_NARGS() == 1) { + /* text */ curname = GET_STR(PG_GETARG_TEXT_P(0)); conn = persistent_conn; } else if (PG_NARGS() == 2) { + /* might be text,text or text,bool */ + if (get_fn_expr_argtype(fcinfo->flinfo, 1) == BOOLOID) + { + curname = GET_STR(PG_GETARG_TEXT_P(0)); + fail = PG_GETARG_BOOL(1); + conn = persistent_conn; + } + else + { + conname = GET_STR(PG_GETARG_TEXT_P(0)); + curname = GET_STR(PG_GETARG_TEXT_P(1)); + rcon = getConnectionByName(conname); + if (rcon) + conn = rcon->con; + } + } + if (PG_NARGS() == 3) + { + /* text,text,bool */ conname = GET_STR(PG_GETARG_TEXT_P(0)); curname = GET_STR(PG_GETARG_TEXT_P(1)); + fail = PG_GETARG_BOOL(2); rcon = getConnectionByName(conname); if (rcon) conn = rcon->con; *************** *** 351,357 **** /* close the cursor */ res = PQexec(conn, str->data); if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) ! DBLINK_RES_ERROR("sql error"); PQclear(res); --- 413,428 ---- /* close the cursor */ res = PQexec(conn, str->data); if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) ! { ! if (fail) ! DBLINK_RES_ERROR("sql error"); ! else ! { ! DBLINK_RES_ERROR_AS_NOTICE("sql error"); ! PQclear(res); ! PG_RETURN_TEXT_P(GET_TEXT("ERROR")); ! } ! } PQclear(res); *************** *** 395,413 **** char *curname = NULL; int howmany = 0; ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; ! if (PG_NARGS() == 3) { conname = GET_STR(PG_GETARG_TEXT_P(0)); curname = GET_STR(PG_GETARG_TEXT_P(1)); howmany = PG_GETARG_INT32(2); rcon = getConnectionByName(conname); if (rcon) conn = rcon->con; } else if (PG_NARGS() == 2) { curname = GET_STR(PG_GETARG_TEXT_P(0)); howmany = PG_GETARG_INT32(1); conn = persistent_conn; --- 466,509 ---- char *curname = NULL; int howmany = 0; ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; + bool fail = true; /* default to backward compatible */ ! if (PG_NARGS() == 4) { + /* text,text,int,bool */ conname = GET_STR(PG_GETARG_TEXT_P(0)); curname = GET_STR(PG_GETARG_TEXT_P(1)); howmany = PG_GETARG_INT32(2); + fail = PG_GETARG_BOOL(3); rcon = getConnectionByName(conname); if (rcon) conn = rcon->con; } + else if (PG_NARGS() == 3) + { + /* text,text,int or text,int,bool */ + if (get_fn_expr_argtype(fcinfo->flinfo, 2) == BOOLOID) + { + curname = GET_STR(PG_GETARG_TEXT_P(0)); + howmany = PG_GETARG_INT32(1); + fail = PG_GETARG_BOOL(2); + conn = persistent_conn; + } + else + { + conname = GET_STR(PG_GETARG_TEXT_P(0)); + curname = GET_STR(PG_GETARG_TEXT_P(1)); + howmany = PG_GETARG_INT32(2); + + rcon = getConnectionByName(conname); + if (rcon) + conn = rcon->con; + } + } else if (PG_NARGS() == 2) { + /* text,int */ curname = GET_STR(PG_GETARG_TEXT_P(0)); howmany = PG_GETARG_INT32(1); conn = persistent_conn; *************** *** 431,437 **** if (!res || (PQresultStatus(res) != PGRES_COMMAND_OK && PQresultStatus(res) != PGRES_TUPLES_OK)) ! DBLINK_RES_ERROR("sql error"); else if (PQresultStatus(res) == PGRES_COMMAND_OK) { /* cursor does not exist - closed already or bad name */ --- 527,543 ---- if (!res || (PQresultStatus(res) != PGRES_COMMAND_OK && PQresultStatus(res) != PGRES_TUPLES_OK)) ! { ! if (fail) ! DBLINK_RES_ERROR("sql error"); ! else ! { ! if (res) ! PQclear(res); ! DBLINK_RES_ERROR_AS_NOTICE("sql error"); ! SRF_RETURN_DONE(funcctx); ! } ! } else if (PQresultStatus(res) == PGRES_COMMAND_OK) { /* cursor does not exist - closed already or bad name */ *************** *** 448,454 **** --- 554,564 ---- /* fast track when no results */ if (funcctx->max_calls < 1) + { + if (res) + PQclear(res); SRF_RETURN_DONE(funcctx); + } /* check typtype to see if we have a predetermined return type */ functypeid = get_func_rettype(funcid); *************** *** 546,552 **** bool is_sql_cmd = false; char *sql_cmd_status = NULL; MemoryContext oldcontext; ! bool freeconn = true; /* stuff done only on the first call of the function */ if (SRF_IS_FIRSTCALL()) --- 656,662 ---- bool is_sql_cmd = false; char *sql_cmd_status = NULL; MemoryContext oldcontext; ! bool freeconn = false; /* stuff done only on the first call of the function */ if (SRF_IS_FIRSTCALL()) *************** *** 560,565 **** --- 670,676 ---- char *conname = NULL; remoteConn *rcon = NULL; ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; + bool fail = true; /* default to backward compatible */ /* create a function context for cross-call persistence */ funcctx = SRF_FIRSTCALL_INIT(); *************** *** 570,582 **** */ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); ! if (PG_NARGS() == 2) { DBLINK_GET_CONN; sql = GET_STR(PG_GETARG_TEXT_P(1)); } else if (PG_NARGS() == 1) { conn = persistent_conn; sql = GET_STR(PG_GETARG_TEXT_P(0)); } --- 681,711 ---- */ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); ! if (PG_NARGS() == 3) { + /* text,text,bool */ DBLINK_GET_CONN; sql = GET_STR(PG_GETARG_TEXT_P(1)); + fail = PG_GETARG_BOOL(2); + } + else if (PG_NARGS() == 2) + { + /* text,text or text,bool */ + if (get_fn_expr_argtype(fcinfo->flinfo, 1) == BOOLOID) + { + conn = persistent_conn; + sql = GET_STR(PG_GETARG_TEXT_P(0)); + fail = PG_GETARG_BOOL(1); + } + else + { + DBLINK_GET_CONN; + sql = GET_STR(PG_GETARG_TEXT_P(1)); + } } else if (PG_NARGS() == 1) { + /* text */ conn = persistent_conn; sql = GET_STR(PG_GETARG_TEXT_P(0)); } *************** *** 588,595 **** DBLINK_CONN_NOT_AVAIL; res = PQexec(conn, sql); ! if (!res || (PQresultStatus(res) != PGRES_COMMAND_OK && PQresultStatus(res) != PGRES_TUPLES_OK)) ! DBLINK_RES_ERROR("sql error"); if (PQresultStatus(res) == PGRES_COMMAND_OK) { --- 717,738 ---- DBLINK_CONN_NOT_AVAIL; res = PQexec(conn, sql); ! if (!res || ! (PQresultStatus(res) != PGRES_COMMAND_OK && ! PQresultStatus(res) != PGRES_TUPLES_OK)) ! { ! if (fail) ! DBLINK_RES_ERROR("sql error"); ! else ! { ! if (res) ! PQclear(res); ! if (freeconn) ! PQfinish(conn); ! DBLINK_RES_ERROR_AS_NOTICE("sql error"); ! SRF_RETURN_DONE(funcctx); ! } ! } if (PQresultStatus(res) == PGRES_COMMAND_OK) { *************** *** 614,625 **** funcctx->user_fctx = res; /* if needed, close the connection to the database and cleanup */ ! if (freeconn && PG_NARGS() == 2) PQfinish(conn); /* fast track when no results */ if (funcctx->max_calls < 1) SRF_RETURN_DONE(funcctx); /* check typtype to see if we have a predetermined return type */ functypeid = get_func_rettype(funcid); --- 757,772 ---- funcctx->user_fctx = res; /* if needed, close the connection to the database and cleanup */ ! if (freeconn) PQfinish(conn); /* fast track when no results */ if (funcctx->max_calls < 1) + { + if (res) + PQclear(res); SRF_RETURN_DONE(funcctx); + } /* check typtype to see if we have a predetermined return type */ functypeid = get_func_rettype(funcid); *************** *** 727,741 **** char *sql = NULL; char *conname = NULL; remoteConn *rcon = NULL; ! bool freeconn = true; ! if (PG_NARGS() == 2) { DBLINK_GET_CONN; sql = GET_STR(PG_GETARG_TEXT_P(1)); } else if (PG_NARGS() == 1) { conn = persistent_conn; sql = GET_STR(PG_GETARG_TEXT_P(0)); } --- 874,907 ---- char *sql = NULL; char *conname = NULL; remoteConn *rcon = NULL; ! bool freeconn = false; ! bool fail = true; /* default to backward compatible behavior */ ! if (PG_NARGS() == 3) { + /* must be text,text,bool */ DBLINK_GET_CONN; sql = GET_STR(PG_GETARG_TEXT_P(1)); + fail = PG_GETARG_BOOL(2); + } + else if (PG_NARGS() == 2) + { + /* might be text,text or text,bool */ + if (get_fn_expr_argtype(fcinfo->flinfo, 1) == BOOLOID) + { + conn = persistent_conn; + sql = GET_STR(PG_GETARG_TEXT_P(0)); + fail = PG_GETARG_BOOL(1); + } + else + { + DBLINK_GET_CONN; + sql = GET_STR(PG_GETARG_TEXT_P(1)); + } } else if (PG_NARGS() == 1) { + /* must be single text argument */ conn = persistent_conn; sql = GET_STR(PG_GETARG_TEXT_P(0)); } *************** *** 750,758 **** if (!res || (PQresultStatus(res) != PGRES_COMMAND_OK && PQresultStatus(res) != PGRES_TUPLES_OK)) ! DBLINK_RES_ERROR("sql error"); ! if (PQresultStatus(res) == PGRES_COMMAND_OK) { /* need a tuple descriptor representing one TEXT column */ tupdesc = CreateTemplateTupleDesc(1, false); --- 916,940 ---- if (!res || (PQresultStatus(res) != PGRES_COMMAND_OK && PQresultStatus(res) != PGRES_TUPLES_OK)) ! { ! if (fail) ! DBLINK_RES_ERROR("sql error"); ! else ! DBLINK_RES_ERROR_AS_NOTICE("sql error"); ! ! /* need a tuple descriptor representing one TEXT column */ ! tupdesc = CreateTemplateTupleDesc(1, false); ! TupleDescInitEntry(tupdesc, (AttrNumber) 1, "status", ! TEXTOID, -1, 0, false); ! /* ! * and save a copy of the command status string to return as our ! * result tuple ! */ ! sql_cmd_status = GET_TEXT("ERROR"); ! ! } ! else if (PQresultStatus(res) == PGRES_COMMAND_OK) { /* need a tuple descriptor representing one TEXT column */ tupdesc = CreateTemplateTupleDesc(1, false); *************** *** 773,779 **** PQclear(res); /* if needed, close the connection to the database and cleanup */ ! if (freeconn && fcinfo->nargs == 2) PQfinish(conn); PG_RETURN_TEXT_P(sql_cmd_status); --- 955,961 ---- PQclear(res); /* if needed, close the connection to the database and cleanup */ ! if (freeconn) PQfinish(conn); PG_RETURN_TEXT_P(sql_cmd_status); Index: contrib/dblink/dblink.sql.in =================================================================== RCS file: /cvsroot/pgsql-server/contrib/dblink/dblink.sql.in,v retrieving revision 1.8 diff -c -r1.8 dblink.sql.in *** contrib/dblink/dblink.sql.in 25 Jun 2003 01:10:15 -0000 1.8 --- contrib/dblink/dblink.sql.in 5 Mar 2004 05:55:46 -0000 *************** *** 1,94 **** CREATE OR REPLACE FUNCTION dblink_connect (text) RETURNS text AS 'MODULE_PATHNAME','dblink_connect' ! LANGUAGE 'C' WITH (isstrict); CREATE OR REPLACE FUNCTION dblink_connect (text, text) RETURNS text AS 'MODULE_PATHNAME','dblink_connect' ! LANGUAGE 'C' WITH (isstrict); CREATE OR REPLACE FUNCTION dblink_disconnect () RETURNS text AS 'MODULE_PATHNAME','dblink_disconnect' ! LANGUAGE 'C' WITH (isstrict); CREATE OR REPLACE FUNCTION dblink_disconnect (text) RETURNS text AS 'MODULE_PATHNAME','dblink_disconnect' ! LANGUAGE 'C' WITH (isstrict); CREATE OR REPLACE FUNCTION dblink_open (text,text) RETURNS text AS 'MODULE_PATHNAME','dblink_open' ! LANGUAGE 'C' WITH (isstrict); CREATE OR REPLACE FUNCTION dblink_open (text,text,text) RETURNS text AS 'MODULE_PATHNAME','dblink_open' ! LANGUAGE 'C' WITH (isstrict); CREATE OR REPLACE FUNCTION dblink_fetch (text,int) RETURNS setof record AS 'MODULE_PATHNAME','dblink_fetch' ! LANGUAGE 'C' WITH (isstrict); CREATE OR REPLACE FUNCTION dblink_fetch (text,text,int) RETURNS setof record AS 'MODULE_PATHNAME','dblink_fetch' ! LANGUAGE 'C' WITH (isstrict); CREATE OR REPLACE FUNCTION dblink_close (text) RETURNS text AS 'MODULE_PATHNAME','dblink_close' ! LANGUAGE 'C' WITH (isstrict); CREATE OR REPLACE FUNCTION dblink_close (text,text) RETURNS text AS 'MODULE_PATHNAME','dblink_close' ! LANGUAGE 'C' WITH (isstrict); CREATE OR REPLACE FUNCTION dblink (text,text) RETURNS setof record AS 'MODULE_PATHNAME','dblink_record' ! LANGUAGE 'C' WITH (isstrict); CREATE OR REPLACE FUNCTION dblink (text) RETURNS setof record AS 'MODULE_PATHNAME','dblink_record' ! LANGUAGE 'C' WITH (isstrict); CREATE OR REPLACE FUNCTION dblink_exec (text,text) RETURNS text AS 'MODULE_PATHNAME','dblink_exec' ! LANGUAGE 'C' WITH (isstrict); CREATE OR REPLACE FUNCTION dblink_exec (text) RETURNS text AS 'MODULE_PATHNAME','dblink_exec' ! LANGUAGE 'c' WITH (isstrict); CREATE TYPE dblink_pkey_results AS (position int4, colname text); CREATE OR REPLACE FUNCTION dblink_get_pkey (text) RETURNS setof dblink_pkey_results AS 'MODULE_PATHNAME','dblink_get_pkey' ! LANGUAGE 'c' WITH (isstrict); CREATE OR REPLACE FUNCTION dblink_build_sql_insert (text, int2vector, int4, _text, _text) RETURNS text AS 'MODULE_PATHNAME','dblink_build_sql_insert' ! LANGUAGE 'C' WITH (isstrict); CREATE OR REPLACE FUNCTION dblink_build_sql_delete (text, int2vector, int4, _text) RETURNS text AS 'MODULE_PATHNAME','dblink_build_sql_delete' ! LANGUAGE 'C' WITH (isstrict); CREATE OR REPLACE FUNCTION dblink_build_sql_update (text, int2vector, int4, _text, _text) RETURNS text AS 'MODULE_PATHNAME','dblink_build_sql_update' ! LANGUAGE 'C' WITH (isstrict); CREATE OR REPLACE FUNCTION dblink_current_query () RETURNS text --- 1,144 ---- CREATE OR REPLACE FUNCTION dblink_connect (text) RETURNS text AS 'MODULE_PATHNAME','dblink_connect' ! LANGUAGE 'C' STRICT; CREATE OR REPLACE FUNCTION dblink_connect (text, text) RETURNS text AS 'MODULE_PATHNAME','dblink_connect' ! LANGUAGE 'C' STRICT; CREATE OR REPLACE FUNCTION dblink_disconnect () RETURNS text AS 'MODULE_PATHNAME','dblink_disconnect' ! LANGUAGE 'C' STRICT; CREATE OR REPLACE FUNCTION dblink_disconnect (text) RETURNS text AS 'MODULE_PATHNAME','dblink_disconnect' ! LANGUAGE 'C' STRICT; CREATE OR REPLACE FUNCTION dblink_open (text,text) RETURNS text AS 'MODULE_PATHNAME','dblink_open' ! LANGUAGE 'C' STRICT; ! ! CREATE OR REPLACE FUNCTION dblink_open (text,text,bool) ! RETURNS text ! AS 'MODULE_PATHNAME','dblink_open' ! LANGUAGE 'C' STRICT; CREATE OR REPLACE FUNCTION dblink_open (text,text,text) RETURNS text AS 'MODULE_PATHNAME','dblink_open' ! LANGUAGE 'C' STRICT; ! ! CREATE OR REPLACE FUNCTION dblink_open (text,text,text,bool) ! RETURNS text ! AS 'MODULE_PATHNAME','dblink_open' ! LANGUAGE 'C' STRICT; CREATE OR REPLACE FUNCTION dblink_fetch (text,int) RETURNS setof record AS 'MODULE_PATHNAME','dblink_fetch' ! LANGUAGE 'C' STRICT; ! ! CREATE OR REPLACE FUNCTION dblink_fetch (text,int,bool) ! RETURNS setof record ! AS 'MODULE_PATHNAME','dblink_fetch' ! LANGUAGE 'C' STRICT; CREATE OR REPLACE FUNCTION dblink_fetch (text,text,int) RETURNS setof record AS 'MODULE_PATHNAME','dblink_fetch' ! LANGUAGE 'C' STRICT; ! ! CREATE OR REPLACE FUNCTION dblink_fetch (text,text,int,bool) ! RETURNS setof record ! AS 'MODULE_PATHNAME','dblink_fetch' ! LANGUAGE 'C' STRICT; CREATE OR REPLACE FUNCTION dblink_close (text) RETURNS text AS 'MODULE_PATHNAME','dblink_close' ! LANGUAGE 'C' STRICT; ! ! CREATE OR REPLACE FUNCTION dblink_close (text,bool) ! RETURNS text ! AS 'MODULE_PATHNAME','dblink_close' ! LANGUAGE 'C' STRICT; CREATE OR REPLACE FUNCTION dblink_close (text,text) RETURNS text AS 'MODULE_PATHNAME','dblink_close' ! LANGUAGE 'C' STRICT; ! ! CREATE OR REPLACE FUNCTION dblink_close (text,text,bool) ! RETURNS text ! AS 'MODULE_PATHNAME','dblink_close' ! LANGUAGE 'C' STRICT; CREATE OR REPLACE FUNCTION dblink (text,text) RETURNS setof record AS 'MODULE_PATHNAME','dblink_record' ! LANGUAGE 'C' STRICT; ! ! CREATE OR REPLACE FUNCTION dblink (text,text,bool) ! RETURNS setof record ! AS 'MODULE_PATHNAME','dblink_record' ! LANGUAGE 'C' STRICT; CREATE OR REPLACE FUNCTION dblink (text) RETURNS setof record AS 'MODULE_PATHNAME','dblink_record' ! LANGUAGE 'C' STRICT; ! ! CREATE OR REPLACE FUNCTION dblink (text,bool) ! RETURNS setof record ! AS 'MODULE_PATHNAME','dblink_record' ! LANGUAGE 'C' STRICT; CREATE OR REPLACE FUNCTION dblink_exec (text,text) RETURNS text AS 'MODULE_PATHNAME','dblink_exec' ! LANGUAGE 'C' STRICT; ! ! CREATE OR REPLACE FUNCTION dblink_exec (text,text,bool) ! RETURNS text ! AS 'MODULE_PATHNAME','dblink_exec' ! LANGUAGE 'C' STRICT; CREATE OR REPLACE FUNCTION dblink_exec (text) RETURNS text AS 'MODULE_PATHNAME','dblink_exec' ! LANGUAGE 'c' STRICT; ! ! CREATE OR REPLACE FUNCTION dblink_exec (text,bool) ! RETURNS text ! AS 'MODULE_PATHNAME','dblink_exec' ! LANGUAGE 'c' STRICT; CREATE TYPE dblink_pkey_results AS (position int4, colname text); CREATE OR REPLACE FUNCTION dblink_get_pkey (text) RETURNS setof dblink_pkey_results AS 'MODULE_PATHNAME','dblink_get_pkey' ! LANGUAGE 'c' STRICT; CREATE OR REPLACE FUNCTION dblink_build_sql_insert (text, int2vector, int4, _text, _text) RETURNS text AS 'MODULE_PATHNAME','dblink_build_sql_insert' ! LANGUAGE 'C' STRICT; CREATE OR REPLACE FUNCTION dblink_build_sql_delete (text, int2vector, int4, _text) RETURNS text AS 'MODULE_PATHNAME','dblink_build_sql_delete' ! LANGUAGE 'C' STRICT; CREATE OR REPLACE FUNCTION dblink_build_sql_update (text, int2vector, int4, _text, _text) RETURNS text AS 'MODULE_PATHNAME','dblink_build_sql_update' ! LANGUAGE 'C' STRICT; CREATE OR REPLACE FUNCTION dblink_current_query () RETURNS text Index: contrib/dblink/doc/cursor =================================================================== RCS file: /cvsroot/pgsql-server/contrib/dblink/doc/cursor,v retrieving revision 1.2 diff -c -r1.2 cursor *** contrib/dblink/doc/cursor 25 Jun 2003 01:10:15 -0000 1.2 --- contrib/dblink/doc/cursor 5 Mar 2004 05:55:46 -0000 *************** *** 5,12 **** Synopsis ! dblink_open(text cursorname, text sql) ! dblink_open(text connname, text cursorname, text sql) Inputs --- 5,12 ---- Synopsis ! dblink_open(text cursorname, text sql [, bool fail_on_error]) ! dblink_open(text connname, text cursorname, text sql [, bool fail_on_error]) Inputs *************** *** 23,28 **** --- 23,35 ---- sql statement that you wish to execute on the remote host e.g. "select * from pg_class" + fail_on_error + + If true (default when not present) then an ERROR thrown on the remote side + of the connection causes an ERROR to also be thrown locally. If false, the + remote ERROR is locally treated as a NOTICE, and the return value is set + to 'ERROR'. + Outputs Returns status = "OK" *************** *** 56,63 **** Synopsis ! dblink_fetch(text cursorname, int32 howmany) ! dblink_fetch(text connname, text cursorname, int32 howmany) Inputs --- 63,70 ---- Synopsis ! dblink_fetch(text cursorname, int32 howmany [, bool fail_on_error]) ! dblink_fetch(text connname, text cursorname, int32 howmany [, bool fail_on_error]) Inputs *************** *** 75,80 **** --- 82,93 ---- starting at the current cursor position, moving forward. Once the cursor has positioned to the end, no more rows are produced. + fail_on_error + + If true (default when not present) then an ERROR thrown on the remote side + of the connection causes an ERROR to also be thrown locally. If false, the + remote ERROR is locally treated as a NOTICE, and no rows are returned. + Outputs Returns setof record *************** *** 132,139 **** Synopsis ! dblink_close(text cursorname) ! dblink_close(text connname, text cursorname) Inputs --- 145,152 ---- Synopsis ! dblink_close(text cursorname [, bool fail_on_error]) ! dblink_close(text connname, text cursorname [, bool fail_on_error]) Inputs *************** *** 144,149 **** --- 157,169 ---- cursorname a reference name for the cursor + + fail_on_error + + If true (default when not present) then an ERROR thrown on the remote side + of the connection causes an ERROR to also be thrown locally. If false, the + remote ERROR is locally treated as a NOTICE, and the return value is set + to 'ERROR'. Outputs Index: contrib/dblink/doc/execute =================================================================== RCS file: /cvsroot/pgsql-server/contrib/dblink/doc/execute,v retrieving revision 1.2 diff -c -r1.2 execute *** contrib/dblink/doc/execute 25 Jun 2003 01:10:15 -0000 1.2 --- contrib/dblink/doc/execute 5 Mar 2004 05:55:46 -0000 *************** *** 5,18 **** Synopsis ! dblink_exec(text connstr, text sql) ! dblink_exec(text connname, text sql) ! dblink_exec(text sql) Inputs connname connstr If two arguments are present, the first is first assumed to be a specific connection name to use. If the name is not found, the argument is then assumed to be a valid connection string, of standard libpq format, --- 5,19 ---- Synopsis ! dblink_exec(text connstr, text sql [, bool fail_on_error]) ! dblink_exec(text connname, text sql [, bool fail_on_error]) ! dblink_exec(text sql [, bool fail_on_error]) Inputs connname connstr + If two arguments are present, the first is first assumed to be a specific connection name to use. If the name is not found, the argument is then assumed to be a valid connection string, of standard libpq format, *************** *** 25,33 **** sql statement that you wish to execute on the remote host, e.g.: insert into foo values(0,'a','{"a0","b0","c0"}'); Outputs ! Returns status of the command Notes 1) dblink_open starts an explicit transaction. If, after using dblink_open, --- 26,41 ---- sql statement that you wish to execute on the remote host, e.g.: insert into foo values(0,'a','{"a0","b0","c0"}'); + fail_on_error + + If true (default when not present) then an ERROR thrown on the remote side + of the connection causes an ERROR to also be thrown locally. If false, the + remote ERROR is locally treated as a NOTICE, and the return value is set + to 'ERROR'. + Outputs ! Returns status of the command, or 'ERROR' if the command failed. Notes 1) dblink_open starts an explicit transaction. If, after using dblink_open, *************** *** 59,62 **** --- 67,79 ---- dblink_exec ------------------ INSERT 6432584 1 + (1 row) + + select dblink_exec('myconn','insert into pg_class values (''foo'')',false); + NOTICE: sql error + DETAIL: ERROR: null value in column "relnamespace" violates not-null constraint + + dblink_exec + ------------- + ERROR (1 row) Index: contrib/dblink/doc/query =================================================================== RCS file: /cvsroot/pgsql-server/contrib/dblink/doc/query,v retrieving revision 1.2 diff -c -r1.2 query *** contrib/dblink/doc/query 25 Jun 2003 01:10:15 -0000 1.2 --- contrib/dblink/doc/query 5 Mar 2004 05:55:46 -0000 *************** *** 5,13 **** Synopsis ! dblink(text connstr, text sql) ! dblink(text connname, text sql) ! dblink(text sql) Inputs --- 5,13 ---- Synopsis ! dblink(text connstr, text sql [, bool fail_on_error]) ! dblink(text connname, text sql [, bool fail_on_error]) ! dblink(text sql [, bool fail_on_error]) Inputs *************** *** 24,29 **** --- 24,35 ---- sql statement that you wish to execute on the remote host e.g. "select * from pg_class" + + fail_on_error + + If true (default when not present) then an ERROR thrown on the remote side + of the connection causes an ERROR to also be thrown locally. If false, the + remote ERROR is locally treated as a NOTICE, and no rows are returned. Outputs Index: contrib/dblink/expected/dblink.out =================================================================== RCS file: /cvsroot/pgsql-server/contrib/dblink/expected/dblink.out,v retrieving revision 1.12 diff -c -r1.12 dblink.out *** contrib/dblink/expected/dblink.out 28 Nov 2003 05:03:01 -0000 1.12 --- contrib/dblink/expected/dblink.out 5 Mar 2004 05:55:46 -0000 *************** *** 128,133 **** --- 128,150 ---- 9 | j | {a9,b9,c9} (2 rows) + -- open a cursor with bad SQL and fail_on_error set to false + SELECT dblink_open('rmt_foo_cursor','SELECT * FROM foobar',false); + NOTICE: sql error + DETAIL: ERROR: relation "foobar" does not exist + + dblink_open + ------------- + ERROR + (1 row) + + -- reset remote transaction state + SELECT dblink_exec('ABORT'); + dblink_exec + ------------- + ROLLBACK + (1 row) + -- open a cursor SELECT dblink_open('rmt_foo_cursor','SELECT * FROM foo'); dblink_open *************** *** 135,140 **** --- 152,171 ---- OK (1 row) + -- close the cursor + SELECT dblink_close('rmt_foo_cursor',false); + dblink_close + -------------- + OK + (1 row) + + -- open the cursor again + SELECT dblink_open('rmt_foo_cursor','SELECT * FROM foo'); + dblink_open + ------------- + OK + (1 row) + -- fetch some data SELECT * FROM dblink_fetch('rmt_foo_cursor',4) AS t(a int, b text, c text[]); *************** *** 165,175 **** 9 | j | {a9,b9,c9} (2 rows) ! -- close the cursor ! SELECT dblink_close('rmt_foo_cursor'); dblink_close -------------- ! OK (1 row) -- should generate 'cursor "rmt_foo_cursor" not found' error --- 196,233 ---- 9 | j | {a9,b9,c9} (2 rows) ! -- intentionally botch a fetch ! SELECT * ! FROM dblink_fetch('rmt_foobar_cursor',4,false) AS t(a int, b text, c text[]); ! NOTICE: sql error ! DETAIL: ERROR: cursor "rmt_foobar_cursor" does not exist ! ! a | b | c ! ---+---+--- ! (0 rows) ! ! -- reset remote transaction state ! SELECT dblink_exec('ABORT'); ! dblink_exec ! ------------- ! ROLLBACK ! (1 row) ! ! -- close the wrong cursor ! SELECT dblink_close('rmt_foobar_cursor',false); ! NOTICE: sql error ! DETAIL: ERROR: cursor "rmt_foobar_cursor" does not exist ! dblink_close -------------- ! ERROR ! (1 row) ! ! -- reset remote transaction state ! SELECT dblink_exec('ABORT'); ! dblink_exec ! ------------- ! ROLLBACK (1 row) -- should generate 'cursor "rmt_foo_cursor" not found' error *************** *** 178,183 **** --- 236,251 ---- ERROR: sql error DETAIL: ERROR: cursor "rmt_foo_cursor" does not exist + -- this time, 'cursor "rmt_foo_cursor" not found' as a notice + SELECT * + FROM dblink_fetch('rmt_foo_cursor',4,false) AS t(a int, b text, c text[]); + NOTICE: sql error + DETAIL: ERROR: cursor "rmt_foo_cursor" does not exist + + a | b | c + ---+---+--- + (0 rows) + -- close the persistent connection SELECT dblink_disconnect(); dblink_disconnect *************** *** 232,237 **** --- 300,322 ---- 11 | l | {a11,b11,c11} (12 rows) + -- bad remote select + SELECT * + FROM dblink('SELECT * FROM foobar',false) AS t(a int, b text, c text[]); + NOTICE: sql error + DETAIL: ERROR: relation "foobar" does not exist + + a | b | c + ---+---+--- + (0 rows) + + -- reset remote transaction state + SELECT dblink_exec('ABORT'); + dblink_exec + ------------- + ROLLBACK + (1 row) + -- change some data SELECT dblink_exec('UPDATE foo SET f3[2] = ''b99'' WHERE f1 = 11'); dblink_exec *************** *** 248,253 **** --- 333,355 ---- 11 | l | {a11,b99,c11} (1 row) + -- botch a change to some other data + SELECT dblink_exec('UPDATE foobar SET f3[2] = ''b99'' WHERE f1 = 11',false); + NOTICE: sql error + DETAIL: ERROR: relation "foobar" does not exist + + dblink_exec + ------------- + ERROR + (1 row) + + -- reset remote transaction state + SELECT dblink_exec('ABORT'); + dblink_exec + ------------- + ROLLBACK + (1 row) + -- delete some data SELECT dblink_exec('DELETE FROM foo WHERE f1 = 11'); dblink_exec *************** *** 298,303 **** --- 400,423 ---- 10 | k | {a10,b10,c10} (3 rows) + -- use the named persistent connection, but get it wrong + SELECT * + FROM dblink('myconn','SELECT * FROM foobar',false) AS t(a int, b text, c text[]) + WHERE t.a > 7; + NOTICE: sql error + DETAIL: ERROR: relation "foobar" does not exist + + a | b | c + ---+---+--- + (0 rows) + + -- reset remote transaction state + SELECT dblink_exec('myconn','ABORT'); + dblink_exec + ------------- + ROLLBACK + (1 row) + -- create a second named persistent connection -- should error with "duplicate connection name" SELECT dblink_connect('myconn','dbname=regression'); *************** *** 327,332 **** --- 447,469 ---- OK (1 row) + -- open a cursor incorrectly + SELECT dblink_open('myconn','rmt_foo_cursor','SELECT * FROM foobar',false); + NOTICE: sql error + DETAIL: ERROR: relation "foobar" does not exist + + dblink_open + ------------- + ERROR + (1 row) + + -- reset remote transaction state + SELECT dblink_exec('myconn','ABORT'); + dblink_exec + ------------- + ROLLBACK + (1 row) + -- open a cursor SELECT dblink_open('myconn','rmt_foo_cursor','SELECT * FROM foo'); dblink_open *************** *** 365,375 **** 10 | k | {a10,b10,c10} (3 rows) ! -- close the cursor ! SELECT dblink_close('myconn','rmt_foo_cursor'); ! dblink_close ! -------------- ! OK (1 row) -- should generate 'cursor "rmt_foo_cursor" not found' error --- 502,522 ---- 10 | k | {a10,b10,c10} (3 rows) ! -- fetch some data incorrectly ! SELECT * ! FROM dblink_fetch('myconn','rmt_foobar_cursor',4,false) AS t(a int, b text, c text[]); ! NOTICE: sql error ! DETAIL: ERROR: cursor "rmt_foobar_cursor" does not exist ! ! a | b | c ! ---+---+--- ! (0 rows) ! ! -- reset remote transaction state ! SELECT dblink_exec('myconn','ABORT'); ! dblink_exec ! ------------- ! ROLLBACK (1 row) -- should generate 'cursor "rmt_foo_cursor" not found' error Index: contrib/dblink/sql/dblink.sql =================================================================== RCS file: /cvsroot/pgsql-server/contrib/dblink/sql/dblink.sql,v retrieving revision 1.11 diff -c -r1.11 dblink.sql *** contrib/dblink/sql/dblink.sql 28 Nov 2003 05:03:02 -0000 1.11 --- contrib/dblink/sql/dblink.sql 5 Mar 2004 05:55:46 -0000 *************** *** 81,89 **** --- 81,101 ---- FROM dblink('SELECT * FROM foo') AS t(a int, b text, c text[]) WHERE t.a > 7; + -- open a cursor with bad SQL and fail_on_error set to false + SELECT dblink_open('rmt_foo_cursor','SELECT * FROM foobar',false); + + -- reset remote transaction state + SELECT dblink_exec('ABORT'); + -- open a cursor SELECT dblink_open('rmt_foo_cursor','SELECT * FROM foo'); + -- close the cursor + SELECT dblink_close('rmt_foo_cursor',false); + + -- open the cursor again + SELECT dblink_open('rmt_foo_cursor','SELECT * FROM foo'); + -- fetch some data SELECT * FROM dblink_fetch('rmt_foo_cursor',4) AS t(a int, b text, c text[]); *************** *** 95,107 **** SELECT * FROM dblink_fetch('rmt_foo_cursor',4) AS t(a int, b text, c text[]); ! -- close the cursor ! SELECT dblink_close('rmt_foo_cursor'); -- should generate 'cursor "rmt_foo_cursor" not found' error SELECT * FROM dblink_fetch('rmt_foo_cursor',4) AS t(a int, b text, c text[]); -- close the persistent connection SELECT dblink_disconnect(); --- 107,133 ---- SELECT * FROM dblink_fetch('rmt_foo_cursor',4) AS t(a int, b text, c text[]); ! -- intentionally botch a fetch ! SELECT * ! FROM dblink_fetch('rmt_foobar_cursor',4,false) AS t(a int, b text, c text[]); ! ! -- reset remote transaction state ! SELECT dblink_exec('ABORT'); ! ! -- close the wrong cursor ! SELECT dblink_close('rmt_foobar_cursor',false); ! ! -- reset remote transaction state ! SELECT dblink_exec('ABORT'); -- should generate 'cursor "rmt_foo_cursor" not found' error SELECT * FROM dblink_fetch('rmt_foo_cursor',4) AS t(a int, b text, c text[]); + -- this time, 'cursor "rmt_foo_cursor" not found' as a notice + SELECT * + FROM dblink_fetch('rmt_foo_cursor',4,false) AS t(a int, b text, c text[]); + -- close the persistent connection SELECT dblink_disconnect(); *************** *** 125,130 **** --- 151,163 ---- SELECT * FROM dblink('SELECT * FROM foo') AS t(a int, b text, c text[]); + -- bad remote select + SELECT * + FROM dblink('SELECT * FROM foobar',false) AS t(a int, b text, c text[]); + + -- reset remote transaction state + SELECT dblink_exec('ABORT'); + -- change some data SELECT dblink_exec('UPDATE foo SET f3[2] = ''b99'' WHERE f1 = 11'); *************** *** 133,138 **** --- 166,177 ---- FROM dblink('SELECT * FROM foo') AS t(a int, b text, c text[]) WHERE a = 11; + -- botch a change to some other data + SELECT dblink_exec('UPDATE foobar SET f3[2] = ''b99'' WHERE f1 = 11',false); + + -- reset remote transaction state + SELECT dblink_exec('ABORT'); + -- delete some data SELECT dblink_exec('DELETE FROM foo WHERE f1 = 11'); *************** *** 161,166 **** --- 200,213 ---- FROM dblink('myconn','SELECT * FROM foo') AS t(a int, b text, c text[]) WHERE t.a > 7; + -- use the named persistent connection, but get it wrong + SELECT * + FROM dblink('myconn','SELECT * FROM foobar',false) AS t(a int, b text, c text[]) + WHERE t.a > 7; + + -- reset remote transaction state + SELECT dblink_exec('myconn','ABORT'); + -- create a second named persistent connection -- should error with "duplicate connection name" SELECT dblink_connect('myconn','dbname=regression'); *************** *** 176,181 **** --- 223,234 ---- -- close the second named persistent connection SELECT dblink_disconnect('myconn2'); + -- open a cursor incorrectly + SELECT dblink_open('myconn','rmt_foo_cursor','SELECT * FROM foobar',false); + + -- reset remote transaction state + SELECT dblink_exec('myconn','ABORT'); + -- open a cursor SELECT dblink_open('myconn','rmt_foo_cursor','SELECT * FROM foo'); *************** *** 190,197 **** SELECT * FROM dblink_fetch('myconn','rmt_foo_cursor',4) AS t(a int, b text, c text[]); ! -- close the cursor ! SELECT dblink_close('myconn','rmt_foo_cursor'); -- should generate 'cursor "rmt_foo_cursor" not found' error SELECT * --- 243,254 ---- SELECT * FROM dblink_fetch('myconn','rmt_foo_cursor',4) AS t(a int, b text, c text[]); ! -- fetch some data incorrectly ! SELECT * ! FROM dblink_fetch('myconn','rmt_foobar_cursor',4,false) AS t(a int, b text, c text[]); ! ! -- reset remote transaction state ! SELECT dblink_exec('myconn','ABORT'); -- should generate 'cursor "rmt_foo_cursor" not found' error SELECT *