Sometimes it is useful to have some global status data that is held
between two calls to a procedure or is shared between different
procedures. Equally useful is the ability to create functions that
your PL/R functions can share. This is easily done since all PL/R
procedures executed in one backend share the same R interpreter. So,
any global R variable is accessible to all PL/R procedure calls, and
will persist for the duration of the SQL client connection. An example
of using a global object appears in the
pg.spi.execp
example, in
Chapter 6.
A globally available, user named, R function (the R function name of PL/R
functions is not the same as its PostgreSQL function name; see:
Chapter 11) can be created dynamically
using the provided PostgreSQL function
install_rcmd(text)
. Here is an example:
select install_rcmd('pg.test.install <-function(msg) {print(msg)}'); install_rcmd -------------- OK (1 row) create or replace function pg_test_install(text) returns text as ' pg.test.install(arg1) ' language 'plr'; select pg_test_install('hello world'); pg_test_install ----------------- hello world (1 row)
A globally available, user named, R function can also be automatically created and installed in the R interpreter. See: Chapter 10
PL/R also provides a global variable called pg.state.firstpass. This variable is reset to TRUE the first time each PL/R function is called, for a particular query. On subsequent calls the value is left unchanged. This allows one or more PL/R functions to perform a possibly expensive initialization on the first call, and reuse the results for the remaining rows in the query. For example:
create table t (f1 int); insert into t values (1); insert into t values (2); insert into t values (3); create or replace function f1() returns int as ' msg <- paste("enter f1, pg.state.firstpass is", pg.state.firstpass) pg.thrownotice(msg) if (pg.state.firstpass == TRUE) pg.state.firstpass <<- FALSE msg <- paste("exit f1, pg.state.firstpass is", pg.state.firstpass) pg.thrownotice(msg) return(0) ' language plr; create or replace function f2() returns int as ' msg <- paste("enter f2, pg.state.firstpass is", pg.state.firstpass) pg.thrownotice(msg) if (pg.state.firstpass == TRUE) pg.state.firstpass <<- FALSE msg <- paste("exit f2, pg.state.firstpass is", pg.state.firstpass) pg.thrownotice(msg) return(0) ' language plr; select f1(), f2(), f1 from t; NOTICE: enter f1, pg.state.firstpass is TRUE NOTICE: exit f1, pg.state.firstpass is FALSE NOTICE: enter f2, pg.state.firstpass is TRUE NOTICE: exit f2, pg.state.firstpass is FALSE NOTICE: enter f1, pg.state.firstpass is FALSE NOTICE: exit f1, pg.state.firstpass is FALSE NOTICE: enter f2, pg.state.firstpass is FALSE NOTICE: exit f2, pg.state.firstpass is FALSE NOTICE: enter f1, pg.state.firstpass is FALSE NOTICE: exit f1, pg.state.firstpass is FALSE NOTICE: enter f2, pg.state.firstpass is FALSE NOTICE: exit f2, pg.state.firstpass is FALSE f1 | f2 | f1 ----+----+---- 0 | 0 | 1 0 | 0 | 2 0 | 0 | 3 (3 rows) create or replace function row_number() returns int as ' if (pg.state.firstpass) { assign("pg.state.firstpass", FALSE, env=.GlobalEnv) lclcntr <- 1 } else lclcntr <- plrcounter + 1 assign("plrcounter", lclcntr, env=.GlobalEnv) return(lclcntr) ' language 'plr'; SELECT row_number(), f1 from t; row_number | f1 ------------+---- 1 | 1 2 | 2 3 | 3 (3 rows)