diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index c5b0a75..a3abb56 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -19,7 +19,7 @@ OBJS = acl.o arrayfuncs.o array_selfuncs.o array_typanalyze.o \
array_userfuncs.o arrayutils.o bool.o \
cash.o char.o date.o datetime.o datum.o domains.o \
enum.o float.o format_type.o \
- geo_ops.o geo_selfuncs.o int.o int8.o json.o like.o lockfuncs.o \
+ geo_ops.o geo_selfuncs.o hostvars.o int.o int8.o json.o like.o lockfuncs.o \
misc.o nabstime.o name.o numeric.o numutils.o \
oid.o oracle_compat.o pseudotypes.o rangetypes.o rangetypes_gist.o \
rowtypes.o regexp.o regproc.o ruleutils.o selfuncs.o \
diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c
index 9a6306b..65f3569 100644
--- a/src/bin/psql/startup.c
+++ b/src/bin/psql/startup.c
@@ -77,6 +77,24 @@ static void process_psqlrc_file(char *filename);
static void showVersion(void);
static void EstablishVariableSpace(void);
+static void
+hostvarReceiver(const char *varname, const char *value)
+{
+ SetVariable(pset.vars, varname, value);
+}
+
+static void
+hostvarSender(const char *varname, const char **value)
+{
+ char *content;
+
+ content = GetVariable(pset.vars, varname);
+ if (content == NULL)
+ *value = "";
+ else
+ *value = content;
+}
+
/*
*
* main
@@ -230,6 +248,9 @@ main(int argc, char *argv[])
PQsetNoticeProcessor(pset.db, NoticeProcessor, NULL);
+ PQsetHostvarReceiver(pset.db, hostvarReceiver);
+ PQsetHostvarSender(pset.db, hostvarSender);
+
SyncVariables();
if (options.action == ACT_LIST_DB)
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index bee7154..cc43e39 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -4646,6 +4646,11 @@ DATA(insert OID = 4031 ( spg_text_leaf_consistent PGNSP PGUID 12 1 0 0 0 f f f
DESCR("SP-GiST support for suffix tree over text");
+DATA(insert OID = 3920 ( hgetvar PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 25 "25" _null_ _null_ _null_ _null_ get_hostvar_text _null_ _null_ _null_ ));
+DATA(insert OID = 3921 ( hsetvar PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 2278 "25 25" _null_ _null_ _null_ _null_ set_hostvar_text _null_ _null_ _null_ ));
+
+
+
/*
* Symbolic values for provolatile column: these indicate whether the result
* of a function is dependent *only* on the values of its explicit arguments,
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 1063403..c79a2b4 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -1142,4 +1142,8 @@ extern Datum pg_prepared_statement(PG_FUNCTION_ARGS);
/* utils/mmgr/portalmem.c */
extern Datum pg_cursor(PG_FUNCTION_ARGS);
+/* adt/hostvars.c */
+extern Datum get_hostvar_text(PG_FUNCTION_ARGS);
+extern Datum set_hostvar_text(PG_FUNCTION_ARGS);
+
#endif /* BUILTINS_H */
diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt
index 1251455..a0009c0 100644
--- a/src/interfaces/libpq/exports.txt
+++ b/src/interfaces/libpq/exports.txt
@@ -163,3 +163,5 @@ PQlibVersion 160
PQsetRowProcessor 161
PQgetRowProcessor 162
PQskipResult 163
+PQsetHostvarReceiver 164
+PQsetHostvarSender 165
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 7c9fa34..143d641 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -2714,6 +2714,9 @@ makeEmptyPGconn(void)
conn->noticeHooks.noticeRec = defaultNoticeReceiver;
conn->noticeHooks.noticeProc = defaultNoticeProcessor;
+ conn->hostvarReceiver = NULL;
+ conn->hostvarSender = NULL;
+
conn->status = CONNECTION_BAD;
conn->asyncStatus = PGASYNC_IDLE;
conn->xactStatus = PQTRANS_IDLE;
@@ -5383,6 +5386,27 @@ PQsetNoticeProcessor(PGconn *conn, PQnoticeProcessor proc, void *arg)
return old;
}
+void
+PQsetHostvarReceiver(PGconn *conn, PQhostvarReceiver proc)
+{
+ if (conn == NULL)
+ return;
+
+ if (proc)
+ conn->hostvarReceiver = proc;
+}
+
+void
+PQsetHostvarSender(PGconn *conn, PQhostvarSender proc)
+{
+ if (conn == NULL)
+ return;
+
+ if (proc)
+ conn->hostvarSender = proc;
+}
+
+
/*
* The default notice message receiver just gets the standard notice text
* and sends it to the notice processor. This two-level setup exists
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
index 173af2e..f49d353 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -148,7 +148,61 @@ pqParseInput3(PGconn *conn)
* from config file due to SIGHUP), but otherwise we hold off until
* BUSY state.
*/
- if (id == 'A')
+
+ if (id == 'v')
+ {
+ PQExpBufferData varnameBuf, valueBuf;
+ char result;
+ char *var;
+
+ initPQExpBuffer(&varnameBuf);
+ initPQExpBuffer(&valueBuf);
+
+ if (pqGetc(&result, conn))
+ return;
+
+ if (result == 1)
+ {
+ if (pqGets(&varnameBuf, conn))
+ return;
+
+ if (conn->hostvarSender != NULL)
+ (*conn->hostvarSender) (varnameBuf.data, &var);
+
+ if (pqPutMsgStart('v', false, conn) < 0 ||
+ pqPuts(var, conn) < 0 ||
+ pqPutMsgEnd(conn) < 0 ||
+ pqFlush(conn) < 0)
+ {
+
+ pqHandleSendFailure(conn);
+ return;
+ }
+
+ resetPQExpBuffer(&varnameBuf);
+ }
+ else if (result == 2)
+ {
+ if (pqGets(&varnameBuf, conn))
+ return;
+ if (pqGets(&valueBuf, conn))
+ return;
+
+ if (conn->hostvarReceiver != NULL)
+ (*conn->hostvarReceiver) (varnameBuf.data, valueBuf.data);
+
+ resetPQExpBuffer(&varnameBuf);
+ resetPQExpBuffer(&valueBuf);
+ }
+
+ if (pqGetc(&result, conn))
+ return;
+
+ if (result != '\0')
+ return;
+
+ }
+ else if (id == 'A')
{
if (getNotify(conn))
return;
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 67db611..1c8ba3b 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -169,6 +169,11 @@ typedef int (*PQrowProcessor) (PGresult *res, const PGdataValue *columns,
typedef void (*PQnoticeReceiver) (void *arg, const PGresult *res);
typedef void (*PQnoticeProcessor) (void *arg, const char *message);
+/* Function types for hostvar handling callbacks */
+typedef void (*PQhostvarReceiver) (const char *varname, const char *value);
+typedef void (*PQhostvarSender) (const char *varname, const char **value);
+
+
/* Print options for PQprint() */
typedef char pqbool;
@@ -349,6 +354,10 @@ extern PQnoticeProcessor PQsetNoticeProcessor(PGconn *conn,
PQnoticeProcessor proc,
void *arg);
+extern void PQsetHostvarReceiver(PGconn *conn, PQhostvarReceiver proc);
+extern void PQsetHostvarSender(PGconn *conn, PQhostvarSender proc);
+
+
/*
* Used to set callback that prevents concurrent access to
* non-thread safe functions that libpq needs.
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 4bc8926..0ed4761 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -157,6 +157,7 @@ typedef struct
void *noticeProcArg;
} PGNoticeHooks;
+
typedef struct PGEvent
{
PGEventProc proc; /* the function to call on events */
@@ -336,6 +337,10 @@ struct pg_conn
int nEvents; /* number of active events */
int eventArraySize; /* allocated array size */
+ /* hostvar processing */
+ PQhostvarReceiver hostvarReceiver;
+ PQhostvarSender hostvarSender;
+
/* Status indicators */
ConnStatusType status;
PGAsyncStatusType asyncStatus;
diff --git a/src/test.sql b/src/test.sql
new file mode 100644
index 0000000..c3764ba
--- /dev/null
+++ b/src/test.sql
@@ -0,0 +1,14 @@
+\echo value of external paremeter is :"myvar"
+
+do $$
+begin
+ -- we can take any session variable on client side
+ -- it is safe against to SQL injection
+ raise notice 'external parameter accessed from plpgsql is "%"', hgetvar('myvar');
+
+ -- we can change this session variable and finish transaction
+ perform hsetvar('myvar', 'Hello, World');
+end;
+$$ language plpgsql;
+
+\echo new value of session variable is :"myvar"