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"