Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

- Update all Copyright notices.

- New (undocumented) functions r_version(), plr_set_rhome(),
  and plr_unset_rhome().
- Use Rembedded.h if available
- If R_HOME environment variable is not defined, attempt to find it using
  pkg-config. (idea from Dirk Eddelbuettel)
- In any case, create define for default R_HOME based on pkg-config. Use
  the default R_HOME during R interpreter init if the environment variable
  is unset. (idea from Dirk Eddelbuettel)
- Use PGDLLIMPORT instead of DLLIMPORT is it is defined.
- Switch to Rf_isVectorList instead of IS_LIST for spi_execp argument test.
  Prior to R-2.4.0, the latter allows bad arguments to get past, causing
  a segfault in an R internal type coersion function. (found by Steve Singer)
- New spi cursor manupulation functions (patch courtesy of Steve Singer).
- Force R interpreter non-interactive mode. Fixes some cases that previously
  appeared to be hung postgres backends in certain errors occured in R
  (R was actually waiting for user input). On some platforms this situation
  caused segfaults instead. (found by Jie Zhang)
- When a plr function source is empty, plr tries to find a function by the
  same name within the R interpreter environment. If the function could not
  be found, it would cause a hang or segfault. This was not easily trapped
  in the R interpreter. Now, build and compile the equivalent plr source.
  This allows the R interpreter to trap the error properly when the function
  does not exist.
- PG_VERSION_NUM if available. (patch courtesy of Neal Conway)
- Plug memory leak in POP_PLERRCONTEXT. (patch courtesy of Steve Singer)
  • Loading branch information...
commit d78ee6ab0963737f600c0e41260193eacdfcf21e 1 parent 5ac6902
jconway authored
View
25 Makefile
@@ -1,8 +1,20 @@
# location of R library
+
+ifdef R_HOME
r_libdir1x = ${R_HOME}/bin
r_libdir2x = ${R_HOME}/lib
# location of R includes
r_includespec = ${R_HOME}/include
+else
+R_HOME := $(shell pkg-config --variable=rhome libR)
+r_libdir1x := $(shell pkg-config --variable=rlibdir libR)
+r_libdir2x := $(shell pkg-config --variable=rlibdir libR)
+r_includespec := $(shell pkg-config --variable=rincludedir libR)
+endif
+
+rhomedef := $(shell pkg-config --variable=rhome libR)
+
+ifneq (,${R_HOME})
MODULE_big := plr
PG_CPPFLAGS += -I$(r_includespec)
@@ -55,6 +67,7 @@ ifneq (,$(findstring yes, $(shared_libr)$(allow_nonpic_in_shlib)))
override CPPFLAGS := -I$(srcdir) -I$(r_includespec) $(CPPFLAGS)
override CPPFLAGS += -DPKGLIBDIR=\"$(pkglibdir)\" -DDLSUFFIX=\"$(DLSUFFIX)\"
+override CPPFLAGS += -DR_HOME_DEFAULT=\"$(rhomedef)\"
else # can't build
@@ -65,4 +78,14 @@ all:
echo "*** the documentation for details."; \
echo ""
-endif # can't build
+endif # can't build - cannot find libR
+
+else # can't build - no R_HOME
+
+all:
+ @echo ""; \
+ echo "*** Cannot build PL/R because R_HOME cannot be found." ; \
+ echo "*** Refer to the documentation for details."; \
+ echo ""
+
+endif
View
4 README.plr
@@ -2,7 +2,7 @@
* PL/R - PostgreSQL support for R as a
* procedural language (PL)
*
- * Copyright (c) 2003-2006 by Joseph E. Conway
+ * Copyright (c) 2003-2007 by Joseph E. Conway
* ALL RIGHTS RESERVED
*
* Joe Conway <mail@joeconway.com>
@@ -30,7 +30,7 @@
*
*/
-See http://www.joeconway.com/plr/ for release notes
+See http://www.joeconway.com/plr/ for release notes and latest docs
Notes:
- R headers are required. Download and install R prior to building
View
71 doc/plr.sgml
@@ -790,6 +790,77 @@ select * from test_spi_execp('sp') as t(typeid oid, typename name);
</varlistentry>
<varlistentry>
+ <term>
+ <function>pg.spi.cursor_open</function>(
+ <type> character</type> <replaceable>cursor_name</replaceable>,
+ <type>external pointer</type> <replaceable>saved_plan</replaceable>,
+ <type>variable list</type> <replaceable>value_list</replaceable>)
+ </term>
+ <listitem>
+ <para>
+ Opens a cursor identified by cursor_name. The cursor can then be used to scroll through
+ the results of a query plan previously prepared by pg.spi.prepare. Any arguments to
+ the plan should be specified in argvalues similar to pg.spi.execp. Only read-only cursors
+ are supported at the moment.
+ <programlisting>
+plan <- pg.spi.prepare('SELECT * FROM pg_class');
+cursor_obj <- pg.spi.cursor_open('my_cursor',plan);
+ </programlisting>
+ </para>
+ <para>
+ Returns a cursor object that be be passed to <function>pg.spi.cursor_fetch</function>
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <function>pg.spi.cursor_fetch</function>(
+ <type>external pointer</type> <replaceable>cursor</replaceable>,
+ <type>boolean</type> <replaceable>forward</replaceable>,
+ <type>integer</type> <replaceable>rows</replaceable>)
+ </term>
+ <listitem>
+ <para>
+ Fetches rows from the cursor object previosuly returned by <function>pg.spi.cursor_open
+ </function>. If forward is TRUE then the cursor is moved forward to
+ fetch at most the number of rows required by the rows parameter. If forward is
+ FALSE then the cursor is moved backrwards at most the number of rows specified.
+ </para>
+ <para>
+ rows indicates the maximum number of rows that should be returned.
+ </para>
+ <para>
+ <programlisting>
+plan <- pg.spi.prepare('SELECT * FROM pg_class');
+cursor_obj <- pg.spi.cursor_open('my_cursor',plan);
+data <- pg.spi.cursor_forward(cursor_obj,TRUE,10);
+ </programlisting>
+ </para>
+ <para>
+ Returns a data frame containing the results.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <function>pg.spi.cursor_close</function>(
+ <type>external pointer</type><replaceable>cursor</replaceable>)
+ </term>
+ <listitem>
+ <para>
+ Closes a cursor previously opened by <function>pg.spi.cursor_open</function>
+ <programlisting>
+plan <- pg.spi.prepare('SELECT * FROM pg_class');
+cursor_obj <- pg.spi.cursor_open('my_cursor',plan);
+pg.spi.cursor_close(cursor_obj);
+ </programlisting>
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><function>pg.spi.lastoid</function>()</term>
<listitem>
<para>
View
49 expected/plr.out
@@ -819,3 +819,52 @@ select count(*) from foo;
(1 row)
drop trigger footrig on foo;
+-- Test cursors: creating, scrolling forward, closing
+CREATE OR REPLACE FUNCTION cursor_fetch_test(integer,boolean) RETURNS SETOF integer AS 'plan<-pg.spi.prepare("SELECT * FROM generate_series(1,10)"); cursor<-pg.spi.cursor_open("curs",plan); dat<-pg.spi.cursor_fetch(cursor,arg2,arg1); pg.spi.cursor_close(cursor); return (dat);' language 'plr';
+SELECT * FROM cursor_fetch_test(1,true);
+ cursor_fetch_test
+-------------------
+ 1
+(1 row)
+
+SELECT * FROM cursor_fetch_test(2,true);
+ cursor_fetch_test
+-------------------
+ 1
+ 2
+(2 rows)
+
+SELECT * FROM cursor_fetch_test(20,true);
+ cursor_fetch_test
+-------------------
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ 10
+(10 rows)
+
+--Test cursors: scrolling backwards
+CREATE OR REPLACE FUNCTION cursor_direction_test() RETURNS SETOF integer AS'plan<-pg.spi.prepare("SELECT * FROM generate_series(1,10)"); cursor<-pg.spi.cursor_open("curs",plan); dat<-pg.spi.cursor_fetch(cursor,TRUE,as.integer(3)); dat2<-pg.spi.cursor_fetch(cursor,FALSE,as.integer(3)); pg.spi.cursor_close(cursor); return (dat2);' language 'plr';
+SELECT * FROM cursor_direction_test();
+ cursor_direction_test
+-----------------------
+ 2
+ 1
+(2 rows)
+
+--Test cursors: Passing arguments to a plan
+CREATE OR REPLACE FUNCTION cursor_fetch_test_arg(integer) RETURNS SETOF integer AS 'plan<-pg.spi.prepare("SELECT * FROM generate_series(1,$1)",c(INT4OID)); cursor<-pg.spi.cursor_open("curs",plan,list(arg1)); dat<-pg.spi.cursor_fetch(cursor,TRUE,arg1); pg.spi.cursor_close(cursor); return (dat);' language 'plr';
+SELECT * FROM cursor_fetch_test_arg(3);
+ cursor_fetch_test_arg
+-----------------------
+ 1
+ 2
+ 3
+(3 rows)
+
View
9 pg_backend_support.c
@@ -2,7 +2,7 @@
* PL/R - PostgreSQL support for R as a
* procedural language (PL)
*
- * Copyright (c) 2003-2006 by Joseph E. Conway
+ * Copyright (c) 2003-2007 by Joseph E. Conway
* ALL RIGHTS RESERVED
*
* Joe Conway <mail@joeconway.com>
@@ -36,10 +36,17 @@
#include "optimizer/clauses.h"
#include "utils/memutils.h"
+#ifdef PGDLLIMPORT
+/* GUC variable */
+extern PGDLLIMPORT char *Dynamic_library_path;
+/* Postgres global */
+extern PGDLLIMPORT char pkglib_path[];
+#else
/* GUC variable */
extern DLLIMPORT char *Dynamic_library_path;
/* Postgres global */
extern DLLIMPORT char pkglib_path[];
+#endif /* PGDLLIMPORT */
/* compiled function hash table */
extern HTAB *plr_HashTable;
View
2  pg_conversion.c
@@ -2,7 +2,7 @@
* PL/R - PostgreSQL support for R as a
* procedural language (PL)
*
- * Copyright (c) 2003-2006 by Joseph E. Conway
+ * Copyright (c) 2003-2007 by Joseph E. Conway
* ALL RIGHTS RESERVED
*
* Joe Conway <mail@joeconway.com>
View
203 pg_rsupport.c
@@ -2,7 +2,7 @@
* PL/R - PostgreSQL support for R as a
* procedural language (PL)
*
- * Copyright (c) 2003-2006 by Joseph E. Conway
+ * Copyright (c) 2003-2007 by Joseph E. Conway
* ALL RIGHTS RESERVED
*
* Joe Conway <mail@joeconway.com>
@@ -475,7 +475,7 @@ plr_SPI_execp(SEXP rsaved_plan, SEXP rargvalues)
if (nargs > 0)
{
- if (!IS_LIST(rargvalues))
+ if (!Rf_isVectorList(rargvalues))
error("%s", "second parameter must be a list of arguments " \
"to the prepared plan");
@@ -589,7 +589,7 @@ plr_SPI_execp(SEXP rsaved_plan, SEXP rargvalues)
return result;
}
-/*
+/*
* plr_SPI_lastoid - return the last oid. To be used after insert queries.
*/
SEXP
@@ -604,6 +604,203 @@ plr_SPI_lastoid(void)
return result;
}
+/*
+ * Takes the prepared plan rsaved_plan and creates a cursor
+ * for it using the values specified in ragvalues.
+ *
+ */
+SEXP
+plr_SPI_cursor_open(SEXP cursor_name_arg,SEXP rsaved_plan, SEXP rargvalues)
+{
+ saved_plan_desc *plan_desc = (saved_plan_desc *) R_ExternalPtrAddr(rsaved_plan);
+ void *saved_plan = plan_desc->saved_plan;
+ int nargs = plan_desc->nargs;
+ Oid *typelems = plan_desc->typelems;
+ FmgrInfo *typinfuncs = plan_desc->typinfuncs;
+ int i;
+ Datum *argvalues = NULL;
+ char *nulls = NULL;
+ bool isnull = false;
+ SEXP obj;
+ SEXP result = NULL;
+ MemoryContext oldcontext;
+ char cursor_name[64];
+ Portal portal=NULL;
+ PREPARE_PG_TRY;
+ PUSH_PLERRCONTEXT(rsupport_error_callback, "pg.spi.cursor_open");
+
+ /* Divide rargvalues */
+ if (nargs > 0)
+ {
+ if (!Rf_isVectorList(rargvalues))
+ error("%s", "second parameter must be a list of arguments " \
+ "to the prepared plan");
+
+ if (length(rargvalues) != nargs)
+ error("list of arguments (%d) is not the same length " \
+ "as that of the prepared plan (%d)",
+ length(rargvalues), nargs);
+
+ argvalues = (Datum *) palloc(nargs * sizeof(Datum));
+ nulls = (char *) palloc(nargs * sizeof(char));
+ }
+
+ for (i = 0; i < nargs; i++)
+ {
+ PROTECT(obj = VECTOR_ELT(rargvalues, i));
+
+ argvalues[i] = get_scalar_datum(obj, typinfuncs[i], typelems[i], &isnull);
+ if (!isnull)
+ nulls[i] = ' ';
+ else
+ nulls[i] = 'n';
+
+ UNPROTECT(1);
+ }
+ strncpy(cursor_name, CHAR(STRING_ELT(cursor_name_arg,0)), 64);
+
+ /* switch to SPI memory context */
+ oldcontext = MemoryContextSwitchTo(plr_SPI_context);
+
+ /*
+ * trap elog/ereport so we can let R finish up gracefully
+ * and generate the error once we exit the interpreter
+ */
+ PG_TRY();
+ {
+ /* Open the cursor */
+ portal = SPI_cursor_open(cursor_name,saved_plan, argvalues, nulls,1);
+
+ }
+ PLR_PG_CATCH();
+ PLR_PG_END_TRY();
+
+ /* back to caller's memory context */
+ MemoryContextSwitchTo(oldcontext);
+
+ if(portal==NULL)
+ error("SPI_cursor_open() failed");
+ else
+ result = R_MakeExternalPtr(portal, R_NilValue, R_NilValue);
+
+ POP_PLERRCONTEXT;
+ return result;
+}
+
+SEXP
+plr_SPI_cursor_fetch(SEXP cursor_in,SEXP forward_in, SEXP rows_in)
+{
+ Portal portal=NULL;
+ int ntuples;
+ SEXP result = NULL;
+ MemoryContext oldcontext;
+ int forward;
+ int rows;
+ PREPARE_PG_TRY;
+ PUSH_PLERRCONTEXT(rsupport_error_callback, "pg.spi.cursor_fetch");
+
+ portal = R_ExternalPtrAddr(cursor_in);
+ if(!IS_LOGICAL(forward_in))
+ {
+ error("pg.spi.cursor_fetch arg2 must be boolean");
+ return result;
+ }
+ if(!IS_INTEGER(rows_in))
+ {
+ error("pg.spi.cursor_fetch arg3 must be an integer");
+ return result;
+ }
+ forward = LOGICAL_DATA(forward_in)[0];
+ rows = INTEGER_DATA(rows_in)[0];
+
+ /* switch to SPI memory context */
+ oldcontext = MemoryContextSwitchTo(plr_SPI_context);
+ PG_TRY();
+ {
+ /* Open the cursor */
+ SPI_cursor_fetch(portal,forward,rows);
+
+ }
+ PLR_PG_CATCH();
+ PLR_PG_END_TRY();
+ /* back to caller's memory context */
+ MemoryContextSwitchTo(oldcontext);
+
+ /* check the result */
+ ntuples = SPI_processed;
+ if (ntuples > 0)
+ {
+ result = rpgsql_get_results(ntuples, SPI_tuptable);
+ SPI_freetuptable(SPI_tuptable);
+ }
+ else
+ result = R_NilValue;
+
+ POP_PLERRCONTEXT;
+ return result;
+}
+
+void
+plr_SPI_cursor_close(SEXP cursor_in)
+{
+ Portal portal=NULL;
+ MemoryContext oldcontext;
+ PREPARE_PG_TRY;
+ PUSH_PLERRCONTEXT(rsupport_error_callback, "pg.spi.cursor_close");
+
+ portal = R_ExternalPtrAddr(cursor_in);
+
+ /* switch to SPI memory context */
+ oldcontext = MemoryContextSwitchTo(plr_SPI_context);
+ PG_TRY();
+ {
+ /* Open the cursor */
+ SPI_cursor_close(portal);
+ }
+ PLR_PG_CATCH();
+ PLR_PG_END_TRY();
+ /* back to caller's memory context */
+ MemoryContextSwitchTo(oldcontext);
+}
+
+void
+plr_SPI_cursor_move(SEXP cursor_in,SEXP forward_in, SEXP rows_in)
+{
+ Portal portal=NULL;
+ MemoryContext oldcontext;
+ int forward;
+ int rows;
+ PREPARE_PG_TRY;
+ PUSH_PLERRCONTEXT(rsupport_error_callback, "pg.spi.cursor_move");
+
+ portal = R_ExternalPtrAddr(cursor_in);
+ if(!IS_LOGICAL(forward_in))
+ {
+ error("pg.spi.cursor_move arg2 must be boolean");
+ return;
+ }
+ if(!IS_INTEGER(rows_in))
+ {
+ error("pg.spi.cursor_move arg3 must be an integer");
+ return;
+ }
+ forward = LOGICAL(forward_in)[0];
+ rows = INTEGER(rows_in)[0];
+
+ /* switch to SPI memory context */
+ oldcontext = MemoryContextSwitchTo(plr_SPI_context);
+ PG_TRY();
+ {
+ /* Open the cursor */
+ SPI_cursor_move(portal, forward, rows);
+ }
+ PLR_PG_CATCH();
+ PLR_PG_END_TRY();
+
+ /* back to caller's memory context */
+ MemoryContextSwitchTo(oldcontext);
+}
+
void
throw_r_error(const char **msg)
{
View
33 pg_userfuncs.c
@@ -2,7 +2,7 @@
* PL/R - PostgreSQL support for R as a
* procedural language (PL)
*
- * Copyright (c) 2003-2006 by Joseph E. Conway
+ * Copyright (c) 2003-2007 by Joseph E. Conway
* ALL RIGHTS RESERVED
*
* Joe Conway <mail@joeconway.com>
@@ -362,3 +362,34 @@ plr_environ(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+/*-----------------------------------------------------------------------------
+ * plr_set_rhome :
+ * utility function to set the R_HOME environment variable under
+ * which the postmaster is running.
+ *----------------------------------------------------------------------------
+ */
+PG_FUNCTION_INFO_V1(plr_set_rhome);
+Datum
+plr_set_rhome(PG_FUNCTION_ARGS)
+{
+ char *rhome = PG_TEXT_GET_STR(PG_GETARG_TEXT_P(0));
+
+ setenv("R_HOME", rhome, 1);
+
+ PG_RETURN_TEXT_P(PG_STR_GET_TEXT("OK"));
+}
+
+/*-----------------------------------------------------------------------------
+ * plr_unset_rhome :
+ * utility function to unset the R_HOME environment variable under
+ * which the postmaster is running.
+ *----------------------------------------------------------------------------
+ */
+PG_FUNCTION_INFO_V1(plr_unset_rhome);
+Datum
+plr_unset_rhome(PG_FUNCTION_ARGS)
+{
+ unsetenv("R_HOME");
+
+ PG_RETURN_TEXT_P(PG_STR_GET_TEXT("OK"));
+}
View
54 plr.c
@@ -2,7 +2,7 @@
* PL/R - PostgreSQL support for R as a
* procedural language (PL)
*
- * Copyright (c) 2003-2006 by Joseph E. Conway
+ * Copyright (c) 2003-2007 by Joseph E. Conway
* ALL RIGHTS RESERVED
*
* Joe Conway <mail@joeconway.com>
@@ -76,6 +76,8 @@ static Oid plr_nspOid = InvalidOid;
#define THROWERROR_CMD \
"pg.throwerror <-function(msg) " \
"{stop(msg, call. = FALSE)}"
+#define OPTIONS_THROWWARN_CMD \
+ "options(warning.expression = expression(pg.thrownotice(last.warning)))"
#define QUOTE_LITERAL_CMD \
"pg.quoteliteral <-function(sql) " \
"{.Call(\"plr_quote_literal\", sql)}"
@@ -90,6 +92,18 @@ static Oid plr_nspOid = InvalidOid;
#define SPI_EXECP_CMD \
"pg.spi.execp <-function(sql, argvalues = NA) " \
"{.Call(\"plr_SPI_execp\", sql, argvalues)}"
+#define SPI_CURSOR_OPEN_CMD \
+ "pg.spi.cursor_open<-function(cursor_name,plan,argvalues=NA) " \
+ "{.Call(\"plr_SPI_cursor_open\",cursor_name,plan,argvalues)}"
+#define SPI_CURSOR_FETCH_CMD \
+ "pg.spi.cursor_fetch<-function(cursor,forward,rows) " \
+ "{.Call(\"plr_SPI_cursor_fetch\",cursor,forward,rows)}"
+#define SPI_CURSOR_MOVE_CMD \
+ "pg.spi.cursor_move<-function(cursor,forward,rows) " \
+ "{.Call(\"plr_SPI_cursor_move\",cursor,forward,rows)}"
+#define SPI_CURSOR_CLOSE_CMD \
+ "pg.spi.cursor_close<-function(cursor) " \
+ "{.Call(\"plr_SPI_cursor_close\",cursor)}"
#define SPI_LASTOID_CMD \
"pg.spi.lastoid <-function() " \
"{.Call(\"plr_SPI_lastoid\")}"
@@ -300,11 +314,17 @@ plr_init(void)
/* refuse to start if R_HOME is not defined */
r_home = getenv("R_HOME");
if (r_home == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
- errmsg("environment variable R_HOME not defined"),
- errhint("R_HOME must be defined in the environment " \
- "of the user that starts the postmaster process.")));
+ {
+ /* see if this is a compiled in default R_HOME */
+ if (strlen(R_HOME_DEFAULT))
+ setenv("R_HOME", R_HOME_DEFAULT, 0);
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("environment variable R_HOME not defined"),
+ errhint("R_HOME must be defined in the environment " \
+ "of the user that starts the postmaster process.")));
+ }
rargc = sizeof(rargv)/sizeof(rargv[0]);
@@ -328,6 +348,14 @@ plr_init(void)
/* arrange for automatic cleanup at proc_exit */
on_proc_exit(plr_cleanup, 0);
+ /*
+ * Force non-interactive mode since R may not do so.
+ * See comment in Rembedded.c just after R_Interactive = TRUE:
+ * "Rf_initialize_R set this based on isatty"
+ * If Postgres still has the tty attached, R_Interactive remains TRUE
+ */
+ R_Interactive = false;
+
plr_pm_init_done = true;
}
@@ -349,6 +377,7 @@ plr_load_builtins(Oid funcid)
OPTIONS_THROWRERROR_CMD,
THROWNOTICE_CMD,
THROWERROR_CMD,
+ OPTIONS_THROWWARN_CMD,
/* install the commands for SPI support in the interpreter */
QUOTE_LITERAL_CMD,
@@ -356,6 +385,10 @@ plr_load_builtins(Oid funcid)
SPI_EXEC_CMD,
SPI_PREPARE_CMD,
SPI_EXECP_CMD,
+ SPI_CURSOR_OPEN_CMD,
+ SPI_CURSOR_FETCH_CMD,
+ SPI_CURSOR_MOVE_CMD,
+ SPI_CURSOR_CLOSE_CMD,
SPI_LASTOID_CMD,
SPI_FACTOR_CMD,
@@ -1218,13 +1251,14 @@ do_compile(FunctionCallInfo fcinfo,
p++;
}
- appendStringInfo(proc_internal_def, "%s}", proc_source);
-
/* parse or find the R function */
if(proc_source && proc_source[0])
- function->fun = plr_parse_func_body(proc_internal_def->data);
+ appendStringInfo(proc_internal_def, "%s}", proc_source);
else
- function->fun = Rf_findFun(Rf_install(proname), R_GlobalEnv);
+ appendStringInfo(proc_internal_def, "%s(%s)}",
+ function->proname,
+ proc_internal_args->data);
+ function->fun = plr_parse_func_body(proc_internal_def->data);
R_PreserveObject(function->fun);
View
26 plr.h
@@ -2,7 +2,7 @@
* PL/R - PostgreSQL support for R as a
* procedural language (PL)
*
- * Copyright (c) 2003-2006 by Joseph E. Conway
+ * Copyright (c) 2003-2007 by Joseph E. Conway
* ALL RIGHTS RESERVED
*
* Joe Conway <mail@joeconway.com>
@@ -39,6 +39,11 @@
#include <sys/stat.h>
#include "R.h"
+/* R version is calculated as shown below */
+#if (R_VERSION >= 132096) /* R_VERSION >= 2.4.0 */
+#include "Rembedded.h"
+#endif
+#include "Rinterface.h"
#include "Rinternals.h"
#include "Rdefines.h"
#include "Rdevices.h"
@@ -81,9 +86,9 @@
#include "utils/syscache.h"
/* working with postgres 7.3 compatible sources */
-#if (CATALOG_VERSION_NO <= 200510211)
-#error "This version of PL/R only builds with PostgreSQL 8.2"
-#elif (CATALOG_VERSION_NO <= 200611241)
+#if !defined(PG_VERSION_NUM) || PG_VERSION_NUM < 80200
+#error "This version of PL/R only builds with PostgreSQL 8.2 or later"
+#elif PG_VERSION_NUM < 80300
#define PG_VERSION_82_COMPAT
#else
#error "This version of PL/R only builds with PostgreSQL 8.2"
@@ -114,12 +119,6 @@ extern void pg_unprotect(int n, char *fn, int ln);
xpfree(mystr_); \
} while (0)
-#define resetStringInfo(mystr_) \
- do { \
- xpfree((mystr_)->data); \
- initStringInfo(mystr_); \
- } while (0)
-
#define NEXT_STR_ELEMENT " %s"
/*
@@ -217,6 +216,7 @@ extern void R_RunExitFinalizers(void);
#define POP_PLERRCONTEXT \
do { \
+ pfree(plerrcontext.arg); \
error_context_stack = plerrcontext.previous; \
} while (0)
@@ -449,6 +449,10 @@ extern SEXP plr_quote_ident(SEXP rawstr);
extern SEXP plr_SPI_exec(SEXP rsql);
extern SEXP plr_SPI_prepare(SEXP rsql, SEXP rargtypes);
extern SEXP plr_SPI_execp(SEXP rsaved_plan, SEXP rargvalues);
+extern SEXP plr_SPI_cursor_open(SEXP cursor_name_arg,SEXP rsaved_plan, SEXP rargvalues);
+extern SEXP plr_SPI_cursor_fetch(SEXP cursor_in,SEXP forward_in, SEXP rows_in);
+extern void plr_SPI_cursor_close(SEXP cursor_in);
+extern void plr_SPI_cursor_move(SEXP cursor_in, SEXP forward_in, SEXP rows_in);
extern SEXP plr_SPI_lastoid(void);
extern void throw_r_error(const char **msg);
@@ -459,6 +463,8 @@ extern Datum plr_array_push(PG_FUNCTION_ARGS);
extern Datum plr_array(PG_FUNCTION_ARGS);
extern Datum plr_array_accum(PG_FUNCTION_ARGS);
extern Datum plr_environ(PG_FUNCTION_ARGS);
+extern Datum plr_set_rhome(PG_FUNCTION_ARGS);
+extern Datum plr_unset_rhome(PG_FUNCTION_ARGS);
/* Postgres backend support functions */
extern void compute_function_hashkey(FunctionCallInfo fcinfo,
View
18 plr.sql.in
@@ -63,3 +63,21 @@ RETURNS text AS '
}
return("OK")
' language 'plr';
+
+CREATE TYPE r_version_type AS (name text, value text);
+CREATE OR REPLACE FUNCTION r_version()
+RETURNS setof r_version_type as '
+ cbind(names(version),unlist(version))
+' language 'plr';
+
+CREATE OR REPLACE FUNCTION plr_set_rhome (text)
+RETURNS text
+AS 'MODULE_PATHNAME','plr_set_rhome'
+LANGUAGE 'C' WITH (isstrict);
+REVOKE EXECUTE ON FUNCTION plr_set_rhome (text) FROM PUBLIC;
+
+CREATE OR REPLACE FUNCTION plr_unset_rhome ()
+RETURNS text
+AS 'MODULE_PATHNAME','plr_unset_rhome'
+LANGUAGE 'C';
+REVOKE EXECUTE ON FUNCTION plr_unset_rhome () FROM PUBLIC;
View
13 sql/plr.sql
@@ -358,4 +358,17 @@ delete from foo where f0 = 11;
select count(*) from foo;
drop trigger footrig on foo;
+-- Test cursors: creating, scrolling forward, closing
+CREATE OR REPLACE FUNCTION cursor_fetch_test(integer,boolean) RETURNS SETOF integer AS 'plan<-pg.spi.prepare("SELECT * FROM generate_series(1,10)"); cursor<-pg.spi.cursor_open("curs",plan); dat<-pg.spi.cursor_fetch(cursor,arg2,arg1); pg.spi.cursor_close(cursor); return (dat);' language 'plr';
+SELECT * FROM cursor_fetch_test(1,true);
+SELECT * FROM cursor_fetch_test(2,true);
+SELECT * FROM cursor_fetch_test(20,true);
+
+--Test cursors: scrolling backwards
+CREATE OR REPLACE FUNCTION cursor_direction_test() RETURNS SETOF integer AS'plan<-pg.spi.prepare("SELECT * FROM generate_series(1,10)"); cursor<-pg.spi.cursor_open("curs",plan); dat<-pg.spi.cursor_fetch(cursor,TRUE,as.integer(3)); dat2<-pg.spi.cursor_fetch(cursor,FALSE,as.integer(3)); pg.spi.cursor_close(cursor); return (dat2);' language 'plr';
+SELECT * FROM cursor_direction_test();
+
+--Test cursors: Passing arguments to a plan
+CREATE OR REPLACE FUNCTION cursor_fetch_test_arg(integer) RETURNS SETOF integer AS 'plan<-pg.spi.prepare("SELECT * FROM generate_series(1,$1)",c(INT4OID)); cursor<-pg.spi.cursor_open("curs",plan,list(arg1)); dat<-pg.spi.cursor_fetch(cursor,TRUE,arg1); pg.spi.cursor_close(cursor); return (dat);' language 'plr';
+SELECT * FROM cursor_fetch_test_arg(3);
Please sign in to comment.
Something went wrong with that request. Please try again.