summaryrefslogtreecommitdiff
path: root/contrib/dblink/dblink.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/dblink/dblink.c')
-rw-r--r--contrib/dblink/dblink.c1429
1 files changed, 0 insertions, 1429 deletions
diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
deleted file mode 100644
index 26fa62417fe..00000000000
--- a/contrib/dblink/dblink.c
+++ /dev/null
@@ -1,1429 +0,0 @@
-/*
- * dblink.c
- *
- * Functions returning results from a remote database
- *
- * Copyright (c) Joseph Conway <mail@joeconway.com>, 2001, 2002,
- * ALL RIGHTS RESERVED;
- *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation for any purpose, without fee, and without a written agreement
- * is hereby granted, provided that the above copyright notice and this
- * paragraph and the following two paragraphs appear in all copies.
- *
- * IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
- * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
- * DOCUMENTATION, EVEN IF THE AUTHOR OR DISTRIBUTORS HAVE BEEN ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- * THE AUTHOR AND DISTRIBUTORS SPECIFICALLY DISCLAIMS ANY WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE AUTHOR AND DISTRIBUTORS HAS NO OBLIGATIONS TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- */
-
-#include "dblink.h"
-
-
-/*
- * Internal declarations
- */
-static dblink_results *init_dblink_results(MemoryContext fn_mcxt);
-static dblink_array_results *init_dblink_array_results(MemoryContext fn_mcxt);
-static char **get_pkey_attnames(Oid relid, int16 *numatts);
-static char *get_strtok(char *fldtext, char *fldsep, int fldnum);
-static char *get_sql_insert(Oid relid, int16 *pkattnums, int16 pknumatts, char **src_pkattvals, char **tgt_pkattvals);
-static char *get_sql_delete(Oid relid, int16 *pkattnums, int16 pknumatts, char **tgt_pkattvals);
-static char *get_sql_update(Oid relid, int16 *pkattnums, int16 pknumatts, char **src_pkattvals, char **tgt_pkattvals);
-static char *quote_literal_cstr(char *rawstr);
-static char *quote_ident_cstr(char *rawstr);
-static int16 get_attnum_pk_pos(int16 *pkattnums, int16 pknumatts, int16 key);
-static HeapTuple get_tuple_of_interest(Oid relid, int16 *pkattnums, int16 pknumatts, char **src_pkattvals);
-static Oid get_relid_from_relname(text *relname_text);
-static dblink_results *get_res_ptr(int32 res_id_index);
-static void append_res_ptr(dblink_results *results);
-static void remove_res_ptr(dblink_results *results);
-
-/* Global */
-List *res_id = NIL;
-int res_id_index = 0;
-
-PG_FUNCTION_INFO_V1(dblink);
-Datum
-dblink(PG_FUNCTION_ARGS)
-{
- PGconn *conn = NULL;
- PGresult *res = NULL;
- dblink_results *results;
- char *optstr;
- char *sqlstatement;
- char *execstatement;
- char *msg;
- int ntuples = 0;
- ReturnSetInfo *rsi;
-
- if (fcinfo->resultinfo == NULL || !IsA(fcinfo->resultinfo, ReturnSetInfo))
- elog(ERROR, "dblink: function called in context that does not accept a set result");
-
- optstr = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(PG_GETARG_TEXT_P(0))));
- sqlstatement = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(PG_GETARG_TEXT_P(1))));
-
- if (fcinfo->flinfo->fn_extra == NULL)
- {
-
- conn = PQconnectdb(optstr);
- if (PQstatus(conn) == CONNECTION_BAD)
- {
- msg = pstrdup(PQerrorMessage(conn));
- PQfinish(conn);
- elog(ERROR, "dblink: connection error: %s", msg);
- }
-
- execstatement = (char *) palloc(strlen(sqlstatement) + 1);
- if (execstatement != NULL)
- {
- strcpy(execstatement, sqlstatement);
- strcat(execstatement, "\0");
- }
- else
- elog(ERROR, "dblink: insufficient memory");
-
- res = PQexec(conn, execstatement);
- if (!res || (PQresultStatus(res) != PGRES_COMMAND_OK && PQresultStatus(res) != PGRES_TUPLES_OK))
- {
- msg = pstrdup(PQerrorMessage(conn));
- PQclear(res);
- PQfinish(conn);
- elog(ERROR, "dblink: sql error: %s", msg);
- }
- else
- {
- /*
- * got results, start fetching them
- */
- ntuples = PQntuples(res);
-
- /*
- * increment resource index
- */
- res_id_index++;
-
- results = init_dblink_results(fcinfo->flinfo->fn_mcxt);
- results->tup_num = 0;
- results->res_id_index = res_id_index;
- results->res = res;
-
- /*
- * Append node to res_id to hold pointer to results.
- * Needed by dblink_tok to access the data
- */
- append_res_ptr(results);
-
- /*
- * save pointer to results for the next function manager call
- */
- fcinfo->flinfo->fn_extra = (void *) results;
-
- /* close the connection to the database and cleanup */
- PQfinish(conn);
-
- rsi = (ReturnSetInfo *) fcinfo->resultinfo;
- rsi->isDone = ExprMultipleResult;
-
- PG_RETURN_INT32(res_id_index);
- }
- }
- else
- {
- /*
- * check for more results
- */
- results = fcinfo->flinfo->fn_extra;
-
- results->tup_num++;
- res_id_index = results->res_id_index;
- ntuples = PQntuples(results->res);
-
- if (results->tup_num < ntuples)
- {
- /*
- * fetch them if available
- */
-
- rsi = (ReturnSetInfo *) fcinfo->resultinfo;
- rsi->isDone = ExprMultipleResult;
-
- PG_RETURN_INT32(res_id_index);
- }
- else
- {
- /*
- * or if no more, clean things up
- */
- results = fcinfo->flinfo->fn_extra;
-
- remove_res_ptr(results);
- PQclear(results->res);
- pfree(results);
- fcinfo->flinfo->fn_extra = NULL;
-
- rsi = (ReturnSetInfo *) fcinfo->resultinfo;
- rsi->isDone = ExprEndResult;
-
- PG_RETURN_NULL();
- }
- }
- PG_RETURN_NULL();
-}
-
-
-/*
- * dblink_tok
- * parse dblink output string
- * return fldnum item (0 based)
- * based on provided field separator
- */
-
-PG_FUNCTION_INFO_V1(dblink_tok);
-Datum
-dblink_tok(PG_FUNCTION_ARGS)
-{
- dblink_results *results;
- int fldnum;
- text *result_text;
- char *result;
- int nfields = 0;
- int text_len = 0;
-
- results = get_res_ptr(PG_GETARG_INT32(0));
- if (results == NULL)
- {
- if (res_id != NIL)
- {
- freeList(res_id);
- res_id = NIL;
- res_id_index = 0;
- }
-
- elog(ERROR, "dblink_tok: function called with invalid resource id");
- }
-
- fldnum = PG_GETARG_INT32(1);
- if (fldnum < 0)
- elog(ERROR, "dblink_tok: field number < 0 not permitted");
-
- nfields = PQnfields(results->res);
- if (fldnum > (nfields - 1))
- elog(ERROR, "dblink_tok: field number %d does not exist", fldnum);
-
- if (PQgetisnull(results->res, results->tup_num, fldnum) == 1)
- PG_RETURN_NULL();
- else
- {
- text_len = PQgetlength(results->res, results->tup_num, fldnum);
-
- result = (char *) palloc(text_len + 1);
-
- if (result != NULL)
- {
- strcpy(result, PQgetvalue(results->res, results->tup_num, fldnum));
- strcat(result, "\0");
- }
- else
- elog(ERROR, "dblink: insufficient memory");
-
- result_text = DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(result)));
-
- PG_RETURN_TEXT_P(result_text);
- }
-}
-
-
-/*
- * dblink_strtok
- * parse input string
- * return ord item (0 based)
- * based on provided field separator
- */
-PG_FUNCTION_INFO_V1(dblink_strtok);
-Datum
-dblink_strtok(PG_FUNCTION_ARGS)
-{
- char *fldtext;
- char *fldsep;
- int fldnum;
- char *buffer;
- text *result_text;
-
- fldtext = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(PG_GETARG_TEXT_P(0))));
- fldsep = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(PG_GETARG_TEXT_P(1))));
- fldnum = PG_GETARG_INT32(2);
-
- if (fldtext[0] == '\0')
- {
- elog(ERROR, "get_strtok: blank list not permitted");
- }
- if (fldsep[0] == '\0')
- {
- elog(ERROR, "get_strtok: blank field separator not permitted");
- }
-
- buffer = get_strtok(fldtext, fldsep, fldnum);
-
- pfree(fldtext);
- pfree(fldsep);
-
- if (buffer == NULL)
- {
- PG_RETURN_NULL();
- }
- else
- {
- result_text = DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(buffer)));
- pfree(buffer);
-
- PG_RETURN_TEXT_P(result_text);
- }
-}
-
-
-/*
- * dblink_get_pkey
- *
- * Return comma delimited list of primary key
- * fields for the supplied relation,
- * or NULL if none exists.
- */
-PG_FUNCTION_INFO_V1(dblink_get_pkey);
-Datum
-dblink_get_pkey(PG_FUNCTION_ARGS)
-{
- text *relname_text;
- Oid relid;
- char **result;
- text *result_text;
- int16 numatts;
- ReturnSetInfo *rsi;
- dblink_array_results *ret_set;
-
- if (fcinfo->resultinfo == NULL || !IsA(fcinfo->resultinfo, ReturnSetInfo))
- elog(ERROR, "dblink: function called in context that does not accept a set result");
-
- if (fcinfo->flinfo->fn_extra == NULL)
- {
- relname_text = PG_GETARG_TEXT_P(0);
-
- /*
- * Convert relname to rel OID.
- */
- relid = get_relid_from_relname(relname_text);
- if (!OidIsValid(relid))
- elog(ERROR, "dblink_get_pkey: relation does not exist");
-
- /*
- * get an array of attnums.
- */
- result = get_pkey_attnames(relid, &numatts);
-
- if ((result != NULL) && (numatts > 0))
- {
- ret_set = init_dblink_array_results(fcinfo->flinfo->fn_mcxt);
-
- ret_set->elem_num = 0;
- ret_set->num_elems = numatts;
- ret_set->res = result;
-
- fcinfo->flinfo->fn_extra = (void *) ret_set;
-
- rsi = (ReturnSetInfo *) fcinfo->resultinfo;
- rsi->isDone = ExprMultipleResult;
-
- result_text = DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(result[ret_set->elem_num])));
-
- PG_RETURN_TEXT_P(result_text);
- }
- else
- {
- rsi = (ReturnSetInfo *) fcinfo->resultinfo;
- rsi->isDone = ExprEndResult;
-
- PG_RETURN_NULL();
- }
- }
- else
- {
- /*
- * check for more results
- */
- ret_set = fcinfo->flinfo->fn_extra;
- ret_set->elem_num++;
- result = ret_set->res;
-
- if (ret_set->elem_num < ret_set->num_elems)
- {
- /*
- * fetch next one
- */
- rsi = (ReturnSetInfo *) fcinfo->resultinfo;
- rsi->isDone = ExprMultipleResult;
-
- result_text = DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(result[ret_set->elem_num])));
- PG_RETURN_TEXT_P(result_text);
- }
- else
- {
- int i;
-
- /*
- * or if no more, clean things up
- */
- for (i = 0; i < ret_set->num_elems; i++)
- pfree(result[i]);
-
- pfree(ret_set->res);
- pfree(ret_set);
-
- rsi = (ReturnSetInfo *) fcinfo->resultinfo;
- rsi->isDone = ExprEndResult;
-
- PG_RETURN_NULL();
- }
- }
- PG_RETURN_NULL();
-}
-
-
-/*
- * dblink_last_oid
- * return last inserted oid
- */
-PG_FUNCTION_INFO_V1(dblink_last_oid);
-Datum
-dblink_last_oid(PG_FUNCTION_ARGS)
-{
- dblink_results *results;
-
- results = get_res_ptr(PG_GETARG_INT32(0));
- if (results == NULL)
- {
- if (res_id != NIL)
- {
- freeList(res_id);
- res_id = NIL;
- res_id_index = 0;
- }
-
- elog(ERROR, "dblink_tok: function called with invalid resource id");
- }
-
- PG_RETURN_OID(PQoidValue(results->res));
-}
-
-
-/*
- * dblink_build_sql_insert
- *
- * Used to generate an SQL insert statement
- * based on an existing tuple in a local relation.
- * This is useful for selectively replicating data
- * to another server via dblink.
- *
- * API:
- * <relname> - name of local table of interest
- * <pkattnums> - an int2vector of attnums which will be used
- * to identify the local tuple of interest
- * <pknumatts> - number of attnums in pkattnums
- * <src_pkattvals_arry> - text array of key values which will be used
- * to identify the local tuple of interest
- * <tgt_pkattvals_arry> - text array of key values which will be used
- * to build the string for execution remotely. These are substituted
- * for their counterparts in src_pkattvals_arry
- */
-PG_FUNCTION_INFO_V1(dblink_build_sql_insert);
-Datum
-dblink_build_sql_insert(PG_FUNCTION_ARGS)
-{
- Oid relid;
- text *relname_text;
- int16 *pkattnums;
- int16 pknumatts;
- char **src_pkattvals;
- char **tgt_pkattvals;
- ArrayType *src_pkattvals_arry;
- ArrayType *tgt_pkattvals_arry;
- int src_ndim;
- int *src_dim;
- int src_nitems;
- int tgt_ndim;
- int *tgt_dim;
- int tgt_nitems;
- int i;
- char *ptr;
- char *sql;
- text *sql_text;
-
- relname_text = PG_GETARG_TEXT_P(0);
-
- /*
- * Convert relname to rel OID.
- */
- relid = get_relid_from_relname(relname_text);
- if (!OidIsValid(relid))
- elog(ERROR, "dblink_build_sql_insert: relation does not exist");
-
- pkattnums = (int16 *) PG_GETARG_POINTER(1);
- pknumatts = PG_GETARG_INT16(2);
- /*
- * There should be at least one key attribute
- */
- if (pknumatts == 0)
- elog(ERROR, "dblink_build_sql_insert: number of key attributes must be > 0.");
-
- src_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(3);
- tgt_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(4);
-
- /*
- * Source array is made up of key values that will be used to
- * locate the tuple of interest from the local system.
- */
- src_ndim = ARR_NDIM(src_pkattvals_arry);
- src_dim = ARR_DIMS(src_pkattvals_arry);
- src_nitems = ArrayGetNItems(src_ndim, src_dim);
-
- /*
- * There should be one source array key value for each key attnum
- */
- if (src_nitems != pknumatts)
- elog(ERROR, "dblink_build_sql_insert: source key array length does not match number of key attributes.");
-
- /*
- * get array of pointers to c-strings from the input source array
- */
- src_pkattvals = (char **) palloc(src_nitems * sizeof(char *));
- ptr = ARR_DATA_PTR(src_pkattvals_arry);
- for (i = 0; i < src_nitems; i++)
- {
- src_pkattvals[i] = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(ptr)));
- ptr += INTALIGN(*(int32 *) ptr);
- }
-
- /*
- * Target array is made up of key values that will be used to
- * build the SQL string for use on the remote system.
- */
- tgt_ndim = ARR_NDIM(tgt_pkattvals_arry);
- tgt_dim = ARR_DIMS(tgt_pkattvals_arry);
- tgt_nitems = ArrayGetNItems(tgt_ndim, tgt_dim);
-
- /*
- * There should be one target array key value for each key attnum
- */
- if (tgt_nitems != pknumatts)
- elog(ERROR, "dblink_build_sql_insert: target key array length does not match number of key attributes.");
-
- /*
- * get array of pointers to c-strings from the input target array
- */
- tgt_pkattvals = (char **) palloc(tgt_nitems * sizeof(char *));
- ptr = ARR_DATA_PTR(tgt_pkattvals_arry);
- for (i = 0; i < tgt_nitems; i++)
- {
- tgt_pkattvals[i] = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(ptr)));
- ptr += INTALIGN(*(int32 *) ptr);
- }
-
- /*
- * Prep work is finally done. Go get the SQL string.
- */
- sql = get_sql_insert(relid, pkattnums, pknumatts, src_pkattvals, tgt_pkattvals);
-
- /*
- * Make it into TEXT for return to the client
- */
- sql_text = DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(sql)));
-
- /*
- * And send it
- */
- PG_RETURN_TEXT_P(sql_text);
-}
-
-
-/*
- * dblink_build_sql_delete
- *
- * Used to generate an SQL delete statement.
- * This is useful for selectively replicating a
- * delete to another server via dblink.
- *
- * API:
- * <relname> - name of remote table of interest
- * <pkattnums> - an int2vector of attnums which will be used
- * to identify the remote tuple of interest
- * <pknumatts> - number of attnums in pkattnums
- * <tgt_pkattvals_arry> - text array of key values which will be used
- * to build the string for execution remotely.
- */
-PG_FUNCTION_INFO_V1(dblink_build_sql_delete);
-Datum
-dblink_build_sql_delete(PG_FUNCTION_ARGS)
-{
- Oid relid;
- text *relname_text;
- int16 *pkattnums;
- int16 pknumatts;
- char **tgt_pkattvals;
- ArrayType *tgt_pkattvals_arry;
- int tgt_ndim;
- int *tgt_dim;
- int tgt_nitems;
- int i;
- char *ptr;
- char *sql;
- text *sql_text;
-
- relname_text = PG_GETARG_TEXT_P(0);
-
- /*
- * Convert relname to rel OID.
- */
- relid = get_relid_from_relname(relname_text);
- if (!OidIsValid(relid))
- elog(ERROR, "dblink_build_sql_delete: relation does not exist");
-
- pkattnums = (int16 *) PG_GETARG_POINTER(1);
- pknumatts = PG_GETARG_INT16(2);
- /*
- * There should be at least one key attribute
- */
- if (pknumatts == 0)
- elog(ERROR, "dblink_build_sql_insert: number of key attributes must be > 0.");
-
- tgt_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(3);
-
- /*
- * Target array is made up of key values that will be used to
- * build the SQL string for use on the remote system.
- */
- tgt_ndim = ARR_NDIM(tgt_pkattvals_arry);
- tgt_dim = ARR_DIMS(tgt_pkattvals_arry);
- tgt_nitems = ArrayGetNItems(tgt_ndim, tgt_dim);
-
- /*
- * There should be one target array key value for each key attnum
- */
- if (tgt_nitems != pknumatts)
- elog(ERROR, "dblink_build_sql_insert: target key array length does not match number of key attributes.");
-
- /*
- * get array of pointers to c-strings from the input target array
- */
- tgt_pkattvals = (char **) palloc(tgt_nitems * sizeof(char *));
- ptr = ARR_DATA_PTR(tgt_pkattvals_arry);
- for (i = 0; i < tgt_nitems; i++)
- {
- tgt_pkattvals[i] = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(ptr)));
- ptr += INTALIGN(*(int32 *) ptr);
- }
-
- /*
- * Prep work is finally done. Go get the SQL string.
- */
- sql = get_sql_delete(relid, pkattnums, pknumatts, tgt_pkattvals);
-
- /*
- * Make it into TEXT for return to the client
- */
- sql_text = DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(sql)));
-
- /*
- * And send it
- */
- PG_RETURN_TEXT_P(sql_text);
-}
-
-
-/*
- * dblink_build_sql_update
- *
- * Used to generate an SQL update statement
- * based on an existing tuple in a local relation.
- * This is useful for selectively replicating data
- * to another server via dblink.
- *
- * API:
- * <relname> - name of local table of interest
- * <pkattnums> - an int2vector of attnums which will be used
- * to identify the local tuple of interest
- * <pknumatts> - number of attnums in pkattnums
- * <src_pkattvals_arry> - text array of key values which will be used
- * to identify the local tuple of interest
- * <tgt_pkattvals_arry> - text array of key values which will be used
- * to build the string for execution remotely. These are substituted
- * for their counterparts in src_pkattvals_arry
- */
-PG_FUNCTION_INFO_V1(dblink_build_sql_update);
-Datum
-dblink_build_sql_update(PG_FUNCTION_ARGS)
-{
- Oid relid;
- text *relname_text;
- int16 *pkattnums;
- int16 pknumatts;
- char **src_pkattvals;
- char **tgt_pkattvals;
- ArrayType *src_pkattvals_arry;
- ArrayType *tgt_pkattvals_arry;
- int src_ndim;
- int *src_dim;
- int src_nitems;
- int tgt_ndim;
- int *tgt_dim;
- int tgt_nitems;
- int i;
- char *ptr;
- char *sql;
- text *sql_text;
-
- relname_text = PG_GETARG_TEXT_P(0);
-
- /*
- * Convert relname to rel OID.
- */
- relid = get_relid_from_relname(relname_text);
- if (!OidIsValid(relid))
- elog(ERROR, "dblink_build_sql_update: relation does not exist");
-
- pkattnums = (int16 *) PG_GETARG_POINTER(1);
- pknumatts = PG_GETARG_INT16(2);
- /*
- * There should be one source array key values for each key attnum
- */
- if (pknumatts == 0)
- elog(ERROR, "dblink_build_sql_insert: number of key attributes must be > 0.");
-
- src_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(3);
- tgt_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(4);
-
- /*
- * Source array is made up of key values that will be used to
- * locate the tuple of interest from the local system.
- */
- src_ndim = ARR_NDIM(src_pkattvals_arry);
- src_dim = ARR_DIMS(src_pkattvals_arry);
- src_nitems = ArrayGetNItems(src_ndim, src_dim);
-
- /*
- * There should be one source array key value for each key attnum
- */
- if (src_nitems != pknumatts)
- elog(ERROR, "dblink_build_sql_insert: source key array length does not match number of key attributes.");
-
- /*
- * get array of pointers to c-strings from the input source array
- */
- src_pkattvals = (char **) palloc(src_nitems * sizeof(char *));
- ptr = ARR_DATA_PTR(src_pkattvals_arry);
- for (i = 0; i < src_nitems; i++)
- {
- src_pkattvals[i] = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(ptr)));
- ptr += INTALIGN(*(int32 *) ptr);
- }
-
- /*
- * Target array is made up of key values that will be used to
- * build the SQL string for use on the remote system.
- */
- tgt_ndim = ARR_NDIM(tgt_pkattvals_arry);
- tgt_dim = ARR_DIMS(tgt_pkattvals_arry);
- tgt_nitems = ArrayGetNItems(tgt_ndim, tgt_dim);
-
- /*
- * There should be one target array key value for each key attnum
- */
- if (tgt_nitems != pknumatts)
- elog(ERROR, "dblink_build_sql_insert: target key array length does not match number of key attributes.");
-
- /*
- * get array of pointers to c-strings from the input target array
- */
- tgt_pkattvals = (char **) palloc(tgt_nitems * sizeof(char *));
- ptr = ARR_DATA_PTR(tgt_pkattvals_arry);
- for (i = 0; i < tgt_nitems; i++)
- {
- tgt_pkattvals[i] = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(ptr)));
- ptr += INTALIGN(*(int32 *) ptr);
- }
-
- /*
- * Prep work is finally done. Go get the SQL string.
- */
- sql = get_sql_update(relid, pkattnums, pknumatts, src_pkattvals, tgt_pkattvals);
-
- /*
- * Make it into TEXT for return to the client
- */
- sql_text = DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(sql)));
-
- /*
- * And send it
- */
- PG_RETURN_TEXT_P(sql_text);
-}
-
-
-/*
- * dblink_current_query
- * return the current query string
- * to allow its use in (among other things)
- * rewrite rules
- */
-PG_FUNCTION_INFO_V1(dblink_current_query);
-Datum
-dblink_current_query(PG_FUNCTION_ARGS)
-{
- text *result_text;
-
- result_text = DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(debug_query_string)));
- PG_RETURN_TEXT_P(result_text);
-}
-
-
-/*
- * dblink_replace_text
- * replace all occurences of 'old_sub_str' in 'orig_str'
- * with 'new_sub_str' to form 'new_str'
- *
- * returns 'orig_str' if 'old_sub_str' == '' or 'orig_str' == ''
- * otherwise returns 'new_str'
- */
-PG_FUNCTION_INFO_V1(dblink_replace_text);
-Datum
-dblink_replace_text(PG_FUNCTION_ARGS)
-{
- text *left_text;
- text *right_text;
- text *buf_text;
- text *ret_text;
- char *ret_str;
- int curr_posn;
- text *src_text = PG_GETARG_TEXT_P(0);
- int src_text_len = DatumGetInt32(DirectFunctionCall1(textlen, PointerGetDatum(src_text)));
- text *from_sub_text = PG_GETARG_TEXT_P(1);
- int from_sub_text_len = DatumGetInt32(DirectFunctionCall1(textlen, PointerGetDatum(from_sub_text)));
- text *to_sub_text = PG_GETARG_TEXT_P(2);
- char *to_sub_str = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(to_sub_text)));
- StringInfo str = makeStringInfo();
-
- if (src_text_len == 0 || from_sub_text_len == 0)
- PG_RETURN_TEXT_P(src_text);
-
- buf_text = DatumGetTextPCopy(PointerGetDatum(src_text));
- curr_posn = DatumGetInt32(DirectFunctionCall2(textpos, PointerGetDatum(buf_text), PointerGetDatum(from_sub_text)));
-
- while (curr_posn > 0)
- {
- left_text = DatumGetTextP(DirectFunctionCall3(text_substr, PointerGetDatum(buf_text), 1, DatumGetInt32(DirectFunctionCall2(textpos, PointerGetDatum(buf_text), PointerGetDatum(from_sub_text))) - 1));
- right_text = DatumGetTextP(DirectFunctionCall3(text_substr, PointerGetDatum(buf_text), DatumGetInt32(DirectFunctionCall2(textpos, PointerGetDatum(buf_text), PointerGetDatum(from_sub_text))) + from_sub_text_len, -1));
-
- appendStringInfo(str, DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(left_text))));
- appendStringInfo(str, to_sub_str);
-
- pfree(buf_text);
- pfree(left_text);
- buf_text = right_text;
- curr_posn = DatumGetInt32(DirectFunctionCall2(textpos, PointerGetDatum(buf_text), PointerGetDatum(from_sub_text)));
- }
-
- appendStringInfo(str, DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(buf_text))));
- pfree(buf_text);
-
- ret_str = pstrdup(str->data);
- ret_text = DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(ret_str)));
-
- PG_RETURN_TEXT_P(ret_text);
-}
-
-
-/*************************************************************
- * internal functions
- */
-
-
-/*
- * init_dblink_results
- * - create an empty dblink_results data structure
- */
-static dblink_results *
-init_dblink_results(MemoryContext fn_mcxt)
-{
- MemoryContext oldcontext;
- dblink_results *retval;
-
- oldcontext = MemoryContextSwitchTo(fn_mcxt);
-
- retval = (dblink_results *) palloc(sizeof(dblink_results));
- MemSet(retval, 0, sizeof(dblink_results));
-
- retval->tup_num = -1;
- retval->res_id_index =-1;
- retval->res = NULL;
-
- MemoryContextSwitchTo(oldcontext);
-
- return retval;
-}
-
-
-/*
- * init_dblink_array_results
- * - create an empty dblink_array_results data structure
- */
-static dblink_array_results *
-init_dblink_array_results(MemoryContext fn_mcxt)
-{
- MemoryContext oldcontext;
- dblink_array_results *retval;
-
- oldcontext = MemoryContextSwitchTo(fn_mcxt);
-
- retval = (dblink_array_results *) palloc(sizeof(dblink_array_results));
- MemSet(retval, 0, sizeof(dblink_array_results));
-
- retval->elem_num = -1;
- retval->num_elems = 0;
- retval->res = NULL;
-
- MemoryContextSwitchTo(oldcontext);
-
- return retval;
-}
-
-/*
- * get_pkey_attnames
- *
- * Get the primary key attnames for the given relation.
- * Return NULL, and set numatts = 0, if no primary key exists.
- */
-static char **
-get_pkey_attnames(Oid relid, int16 *numatts)
-{
- Relation indexRelation;
- ScanKeyData entry;
- HeapScanDesc scan;
- HeapTuple indexTuple;
- int i;
- char **result = NULL;
- Relation rel;
- TupleDesc tupdesc;
-
- /*
- * Open relation using relid, get tupdesc
- */
- rel = relation_open(relid, AccessShareLock);
- tupdesc = rel->rd_att;
-
- /*
- * Initialize numatts to 0 in case no primary key
- * exists
- */
- *numatts = 0;
-
- /*
- * Use relid to get all related indexes
- */
- indexRelation = heap_openr(IndexRelationName, AccessShareLock);
- ScanKeyEntryInitialize(&entry, 0, Anum_pg_index_indrelid,
- F_OIDEQ, ObjectIdGetDatum(relid));
- scan = heap_beginscan(indexRelation, SnapshotNow, 1, &entry);
-
- while ((indexTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
- {
- Form_pg_index index = (Form_pg_index) GETSTRUCT(indexTuple);
-
- /*
- * We're only interested if it is the primary key
- */
- if (index->indisprimary == TRUE)
- {
- i = 0;
- while (index->indkey[i++] != 0)
- (*numatts)++;
-
- if (*numatts > 0)
- {
- result = (char **) palloc(*numatts * sizeof(char *));
- for (i = 0; i < *numatts; i++)
- result[i] = SPI_fname(tupdesc, index->indkey[i]);
- }
- break;
- }
- }
- heap_endscan(scan);
- heap_close(indexRelation, AccessShareLock);
- relation_close(rel, AccessShareLock);
-
- return result;
-}
-
-
-/*
- * get_strtok
- *
- * parse input string
- * return ord item (0 based)
- * based on provided field separator
- */
-static char *
-get_strtok(char *fldtext, char *fldsep, int fldnum)
-{
- int j = 0;
- char *result;
-
- if (fldnum < 0)
- {
- elog(ERROR, "get_strtok: field number < 0 not permitted");
- }
-
- if (fldsep[0] == '\0')
- {
- elog(ERROR, "get_strtok: blank field separator not permitted");
- }
-
- result = strtok(fldtext, fldsep);
- for (j = 1; j < fldnum + 1; j++)
- {
- result = strtok(NULL, fldsep);
- if (result == NULL)
- return NULL;
- }
-
- return pstrdup(result);
-}
-
-static char *
-get_sql_insert(Oid relid, int16 *pkattnums, int16 pknumatts, char **src_pkattvals, char **tgt_pkattvals)
-{
- Relation rel;
- char *relname;
- HeapTuple tuple;
- TupleDesc tupdesc;
- int natts;
- StringInfo str = makeStringInfo();
- char *sql = NULL;
- char *val = NULL;
- int16 key;
- unsigned int i;
-
- /*
- * Open relation using relid
- */
- rel = relation_open(relid, AccessShareLock);
- relname = RelationGetRelationName(rel);
- tupdesc = rel->rd_att;
- natts = tupdesc->natts;
-
- tuple = get_tuple_of_interest(relid, pkattnums, pknumatts, src_pkattvals);
-
- appendStringInfo(str, "INSERT INTO %s(", quote_ident_cstr(relname));
- for (i = 0; i < natts; i++)
- {
- if (i > 0)
- appendStringInfo(str, ",");
-
- appendStringInfo(str, NameStr(tupdesc->attrs[i]->attname));
- }
-
- appendStringInfo(str, ") VALUES(");
-
- /*
- * remember attvals are 1 based
- */
- for (i = 0; i < natts; i++)
- {
- if (i > 0)
- appendStringInfo(str, ",");
-
- if (tgt_pkattvals != NULL)
- key = get_attnum_pk_pos(pkattnums, pknumatts, i + 1);
- else
- key = -1;
-
- if (key > -1)
- val = pstrdup(tgt_pkattvals[key]);
- else
- val = SPI_getvalue(tuple, tupdesc, i + 1);
-
- if (val != NULL)
- {
- appendStringInfo(str, quote_literal_cstr(val));
- pfree(val);
- }
- else
- appendStringInfo(str, "NULL");
- }
- appendStringInfo(str, ")");
-
- sql = pstrdup(str->data);
- pfree(str->data);
- pfree(str);
- relation_close(rel, AccessShareLock);
-
- return (sql);
-}
-
-static char *
-get_sql_delete(Oid relid, int16 *pkattnums, int16 pknumatts, char **tgt_pkattvals)
-{
- Relation rel;
- char *relname;
- TupleDesc tupdesc;
- int natts;
- StringInfo str = makeStringInfo();
- char *sql = NULL;
- char *val = NULL;
- unsigned int i;
-
- /*
- * Open relation using relid
- */
- rel = relation_open(relid, AccessShareLock);
- relname = RelationGetRelationName(rel);
- tupdesc = rel->rd_att;
- natts = tupdesc->natts;
-
- appendStringInfo(str, "DELETE FROM %s WHERE ", quote_ident_cstr(relname));
- for (i = 0; i < pknumatts; i++)
- {
- int16 pkattnum = pkattnums[i];
-
- if (i > 0)
- appendStringInfo(str, " AND ");
-
- appendStringInfo(str, NameStr(tupdesc->attrs[pkattnum - 1]->attname));
-
- if (tgt_pkattvals != NULL)
- val = pstrdup(tgt_pkattvals[i]);
- else
- elog(ERROR, "Target key array must not be NULL");
-
- if (val != NULL)
- {
- appendStringInfo(str, "=");
- appendStringInfo(str, quote_literal_cstr(val));
- pfree(val);
- }
- else
- appendStringInfo(str, "IS NULL");
- }
-
- sql = pstrdup(str->data);
- pfree(str->data);
- pfree(str);
- relation_close(rel, AccessShareLock);
-
- return (sql);
-}
-
-static char *
-get_sql_update(Oid relid, int16 *pkattnums, int16 pknumatts, char **src_pkattvals, char **tgt_pkattvals)
-{
- Relation rel;
- char *relname;
- HeapTuple tuple;
- TupleDesc tupdesc;
- int natts;
- StringInfo str = makeStringInfo();
- char *sql = NULL;
- char *val = NULL;
- int16 key;
- int i;
-
- /*
- * Open relation using relid
- */
- rel = relation_open(relid, AccessShareLock);
- relname = RelationGetRelationName(rel);
- tupdesc = rel->rd_att;
- natts = tupdesc->natts;
-
- tuple = get_tuple_of_interest(relid, pkattnums, pknumatts, src_pkattvals);
-
- appendStringInfo(str, "UPDATE %s SET ", quote_ident_cstr(relname));
-
- for (i = 0; i < natts; i++)
- {
- if (i > 0)
- appendStringInfo(str, ",");
-
- appendStringInfo(str, NameStr(tupdesc->attrs[i]->attname));
- appendStringInfo(str, "=");
-
- if (tgt_pkattvals != NULL)
- key = get_attnum_pk_pos(pkattnums, pknumatts, i + 1);
- else
- key = -1;
-
- if (key > -1)
- val = pstrdup(tgt_pkattvals[key]);
- else
- val = SPI_getvalue(tuple, tupdesc, i + 1);
-
- if (val != NULL)
- {
- appendStringInfo(str, quote_literal_cstr(val));
- pfree(val);
- }
- else
- appendStringInfo(str, "NULL");
- }
-
- appendStringInfo(str, " WHERE ");
-
- for (i = 0; i < pknumatts; i++)
- {
- int16 pkattnum = pkattnums[i];
-
- if (i > 0)
- appendStringInfo(str, " AND ");
-
- appendStringInfo(str, NameStr(tupdesc->attrs[pkattnum - 1]->attname));
-
- if (tgt_pkattvals != NULL)
- val = pstrdup(tgt_pkattvals[i]);
- else
- val = SPI_getvalue(tuple, tupdesc, pkattnum);
-
- if (val != NULL)
- {
- appendStringInfo(str, "=");
- appendStringInfo(str, quote_literal_cstr(val));
- pfree(val);
- }
- else
- appendStringInfo(str, "IS NULL");
- }
-
- sql = pstrdup(str->data);
- pfree(str->data);
- pfree(str);
- relation_close(rel, AccessShareLock);
-
- return (sql);
-}
-
-/*
- * Return a properly quoted literal value.
- * Uses quote_literal in quote.c
- */
-static char *
-quote_literal_cstr(char *rawstr)
-{
- text *rawstr_text;
- text *result_text;
- char *result;
-
- rawstr_text = DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(rawstr)));
- result_text = DatumGetTextP(DirectFunctionCall1(quote_literal, PointerGetDatum(rawstr_text)));
- result = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(result_text)));
-
- return result;
-}
-
-/*
- * Return a properly quoted identifier.
- * Uses quote_ident in quote.c
- */
-static char *
-quote_ident_cstr(char *rawstr)
-{
- text *rawstr_text;
- text *result_text;
- char *result;
-
- rawstr_text = DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(rawstr)));
- result_text = DatumGetTextP(DirectFunctionCall1(quote_ident, PointerGetDatum(rawstr_text)));
- result = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(result_text)));
-
- return result;
-}
-
-static int16
-get_attnum_pk_pos(int16 *pkattnums, int16 pknumatts, int16 key)
-{
- int i;
-
- /*
- * Not likely a long list anyway, so just scan for
- * the value
- */
- for (i = 0; i < pknumatts; i++)
- if (key == pkattnums[i])
- return i;
-
- return -1;
-}
-
-static HeapTuple
-get_tuple_of_interest(Oid relid, int16 *pkattnums, int16 pknumatts, char **src_pkattvals)
-{
- Relation rel;
- char *relname;
- TupleDesc tupdesc;
- StringInfo str = makeStringInfo();
- char *sql = NULL;
- int ret;
- HeapTuple tuple;
- int i;
- char *val = NULL;
-
- /*
- * Open relation using relid
- */
- rel = relation_open(relid, AccessShareLock);
- relname = RelationGetRelationName(rel);
- tupdesc = rel->rd_att;
-
- /*
- * Connect to SPI manager
- */
- if ((ret = SPI_connect()) < 0)
- elog(ERROR, "get_tuple_of_interest: SPI_connect returned %d", ret);
-
- /*
- * Build sql statement to look up tuple of interest
- * Use src_pkattvals as the criteria.
- */
- appendStringInfo(str, "SELECT * from %s WHERE ", relname);
-
- for (i = 0; i < pknumatts; i++)
- {
- int16 pkattnum = pkattnums[i];
-
- if (i > 0)
- appendStringInfo(str, " AND ");
-
- appendStringInfo(str, NameStr(tupdesc->attrs[pkattnum - 1]->attname));
-
- val = pstrdup(src_pkattvals[i]);
- if (val != NULL)
- {
- appendStringInfo(str, "=");
- appendStringInfo(str, quote_literal_cstr(val));
- pfree(val);
- }
- else
- appendStringInfo(str, "IS NULL");
- }
-
- sql = pstrdup(str->data);
- pfree(str->data);
- pfree(str);
- /*
- * Retrieve the desired tuple
- */
- ret = SPI_exec(sql, 0);
- pfree(sql);
-
- /*
- * Only allow one qualifying tuple
- */
- if ((ret == SPI_OK_SELECT) && (SPI_processed > 1))
- {
- elog(ERROR, "get_tuple_of_interest: Source criteria may not match more than one record.");
- }
- else if (ret == SPI_OK_SELECT && SPI_processed == 1)
- {
- SPITupleTable *tuptable = SPI_tuptable;
- tuple = SPI_copytuple(tuptable->vals[0]);
-
- return tuple;
- }
- else
- {
- /*
- * no qualifying tuples
- */
- return NULL;
- }
-
- /*
- * never reached, but keep compiler quiet
- */
- return NULL;
-}
-
-static Oid
-get_relid_from_relname(text *relname_text)
-{
-#ifdef NamespaceRelationName
- RangeVar *relvar;
- Relation rel;
- Oid relid;
-
- relvar = makeRangeVarFromNameList(textToQualifiedNameList(relname_text, "get_relid_from_relname"));
- rel = heap_openrv(relvar, AccessShareLock);
- relid = RelationGetRelid(rel);
- relation_close(rel, AccessShareLock);
-#else
- char *relname;
- Relation rel;
- Oid relid;
-
- relname = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(relname_text)));
- rel = relation_openr(relname, AccessShareLock);
- relid = RelationGetRelid(rel);
- relation_close(rel, AccessShareLock);
-#endif /* NamespaceRelationName */
-
- return relid;
-}
-
-static dblink_results *
-get_res_ptr(int32 res_id_index)
-{
- List *ptr;
-
- /*
- * short circuit empty list
- */
- if(res_id == NIL)
- return NULL;
-
- /*
- * OK, should be good to go
- */
- foreach(ptr, res_id)
- {
- dblink_results *this_res_id = (dblink_results *) lfirst(ptr);
- if (this_res_id->res_id_index == res_id_index)
- return this_res_id;
- }
- return NULL;
-}
-
-/*
- * Add node to global List res_id
- */
-static void
-append_res_ptr(dblink_results *results)
-{
- res_id = lappend(res_id, results);
-}
-
-/*
- * Remove node from global List
- * using res_id_index
- */
-static void
-remove_res_ptr(dblink_results *results)
-{
- res_id = lremove(results, res_id);
-
- if (res_id == NIL)
- res_id_index = 0;
-}
-