Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge load_pir branch

  • Loading branch information...
commit 60dcc2124ddd8d4f0152df3f6cf844275a9da2b0 2 parents a6a6a58 + 05dc842
@leto authored
View
18 HACKING.postgres
@@ -17,13 +17,17 @@ PostgreSQL, do:
When compiling PostgreSQL, here's a handy-ish configure invocation:
-CC="ccache gcc" ./configure \
- --prefix=$PG_PREFIX \
- --with-pgport=$PGPORT \
- --with-perl \
- --with-libxml \
- --enable-debug \
- --enable-cassert
+ ./configure \
+ --prefix=$PG_PREFIX \
+ --with-pgport=$PGPORT \
+ --with-perl \
+ --with-libxml \
+ --enable-debug \
+ --enable-cassert
+
+If you have ccache installed, you may also want to do
+
+ CC="ccache gcc" ./configure ...
To avoid confusion, you also might want to un-symlink plparrot from
beneath the other source tree and symlink it under contrib/ in the
View
4 Makefile
@@ -1,6 +1,6 @@
NAME = plparrot
-MODULE_big = src/plparrot
-OBJS= src/plparrot.o
+MODULE_big = plparrot
+OBJS= plparrot.o
DATA_built = plparrot.sql
REGRESS_OPTS = --dbname=$(PL_TESTDB) --load-language=plpgsql
TESTS = $(wildcard t/sql/*.sql)
View
6 ROADMAP
@@ -44,6 +44,12 @@ This document describes the roadmap for PL/Parrot. Please be as specific as poss
module Parrot code can load, containing those functions. I've no idea
how to build such a thing.
+ eggyknap idea:
+ Originally I'd thought we'd provide some module or something users
+ would load, and then use for database access. Perhaps it makes more
+ sense to build a PMC that provides db access functions, and make it
+ available in the default namespace
+
06:51:52 < eggyknap> I think we need to decide how parrot is going to get the code it wants to execute. In psql, you say CREATE FUNCTION ... AS $$ <function text>$$ ...
06:51:58 < eggyknap> ... and we need to know what that function text will be.
View
183 include/embed_string.h
@@ -0,0 +1,183 @@
+/* embed_string.h
+ * Copyright (C) 2001-2008, Parrot Foundation.
+ * SVN Info
+ * $Id$
+ * Overview:
+ * This is the api header for the string subsystem
+ * Data Structure and Algorithms:
+ * History:
+ * Notes:
+ * References:
+ */
+
+#ifndef PARROT_EMBED_STRING_H_GUARD
+#define PARROT_EMBED_STRING_H_GUARD
+
+#include "parrot/compiler.h"
+
+typedef struct parrot_string_t STRING;
+typedef Parrot_UInt UINTVAL;
+typedef Parrot_Int INTVAL;
+typedef Parrot_Float FLOATVAL;
+typedef unsigned long long UHUGEINTVAL;
+
+PARROT_EXPORT
+PARROT_WARN_UNUSED_RESULT
+/* PARROT_CAN_RETURN_NULL */
+STRING * Parrot_str_append(PARROT_INTERP,
+ ARGMOD_NULLOK(STRING *a),
+ ARGIN_NULLOK(STRING *b))
+ __attribute__nonnull__(1)
+ FUNC_MODIFIES(*a);
+
+PARROT_EXPORT
+PARROT_PURE_FUNCTION
+UINTVAL Parrot_str_byte_length(SHIM_INTERP, ARGIN_NULLOK(const STRING *s));
+
+PARROT_EXPORT
+PARROT_WARN_UNUSED_RESULT
+INTVAL Parrot_str_compare(PARROT_INTERP,
+ ARGIN_NULLOK(const STRING *s1),
+ ARGIN_NULLOK(const STRING *s2))
+ __attribute__nonnull__(1);
+
+PARROT_EXPORT
+PARROT_CANNOT_RETURN_NULL
+STRING * Parrot_str_concat(PARROT_INTERP,
+ ARGIN_NULLOK(STRING *a),
+ ARGIN_NULLOK(STRING *b),
+ UINTVAL Uflags)
+ __attribute__nonnull__(1);
+
+PARROT_EXPORT
+PARROT_CANNOT_RETURN_NULL
+PARROT_WARN_UNUSED_RESULT
+STRING * Parrot_str_copy(PARROT_INTERP, ARGMOD(STRING *s))
+ __attribute__nonnull__(1)
+ __attribute__nonnull__(2)
+ FUNC_MODIFIES(*s);
+
+PARROT_EXPORT
+PARROT_WARN_UNUSED_RESULT
+INTVAL Parrot_str_equal(PARROT_INTERP,
+ ARGIN_NULLOK(const STRING *s1),
+ ARGIN_NULLOK(const STRING *s2))
+ __attribute__nonnull__(1);
+
+PARROT_EXPORT
+void Parrot_str_free_cstring(ARGIN_NULLOK(char *p));
+
+PARROT_EXPORT
+PARROT_WARN_UNUSED_RESULT
+PARROT_CANNOT_RETURN_NULL
+STRING * Parrot_str_from_int(PARROT_INTERP, INTVAL i)
+ __attribute__nonnull__(1);
+
+PARROT_EXPORT
+PARROT_WARN_UNUSED_RESULT
+PARROT_CANNOT_RETURN_NULL
+STRING * Parrot_str_from_num(PARROT_INTERP, FLOATVAL f)
+ __attribute__nonnull__(1);
+
+PARROT_EXPORT
+PARROT_IGNORABLE_RESULT
+INTVAL /*@alt void@*/
+Parrot_str_length(PARROT_INTERP, ARGMOD(STRING *s))
+ __attribute__nonnull__(1)
+ __attribute__nonnull__(2)
+ FUNC_MODIFIES(*s);
+
+PARROT_EXPORT
+PARROT_WARN_UNUSED_RESULT
+PARROT_MALLOC
+PARROT_CANNOT_RETURN_NULL
+STRING * Parrot_str_new(PARROT_INTERP,
+ ARGIN_NULLOK(const char * const buffer),
+ const UINTVAL len)
+ __attribute__nonnull__(1);
+
+PARROT_EXPORT
+PARROT_WARN_UNUSED_RESULT
+PARROT_CANNOT_RETURN_NULL
+STRING * Parrot_str_new_constant(PARROT_INTERP, ARGIN(const char *buffer))
+ __attribute__nonnull__(1)
+ __attribute__nonnull__(2);
+
+PARROT_EXPORT
+PARROT_CANNOT_RETURN_NULL
+STRING * Parrot_str_set(PARROT_INTERP,
+ ARGIN_NULLOK(STRING *dest),
+ ARGMOD(STRING *src))
+ __attribute__nonnull__(1)
+ __attribute__nonnull__(3)
+ FUNC_MODIFIES(*src);
+
+PARROT_EXPORT
+PARROT_MALLOC
+PARROT_CANNOT_RETURN_NULL
+char * Parrot_str_to_cstring(PARROT_INTERP, ARGIN_NULLOK(const STRING *s))
+ __attribute__nonnull__(1);
+
+PARROT_EXPORT
+PARROT_PURE_FUNCTION
+PARROT_CANNOT_RETURN_NULL
+const char * Parrot_string_cstring(SHIM_INTERP, ARGIN(const STRING *str))
+ __attribute__nonnull__(2);
+
+PARROT_EXPORT
+INTVAL STRING_is_null(SHIM_INTERP, ARGIN_NULLOK(const STRING *s));
+
+PARROT_WARN_UNUSED_RESULT
+PARROT_CANNOT_RETURN_NULL
+STRING* Parrot_str_from_uint(PARROT_INTERP,
+ ARGOUT(char *tc),
+ UHUGEINTVAL num,
+ unsigned int base,
+ int minus)
+ __attribute__nonnull__(1)
+ __attribute__nonnull__(2)
+ FUNC_MODIFIES(*tc);
+
+#define ASSERT_ARGS_Parrot_str_append __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
+ PARROT_ASSERT_ARG(interp))
+#define ASSERT_ARGS_Parrot_str_byte_length __attribute__unused__ int _ASSERT_ARGS_CHECK = (0)
+#define ASSERT_ARGS_Parrot_str_compare __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
+ PARROT_ASSERT_ARG(interp))
+#define ASSERT_ARGS_Parrot_str_concat __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
+ PARROT_ASSERT_ARG(interp))
+#define ASSERT_ARGS_Parrot_str_copy __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
+ PARROT_ASSERT_ARG(interp) \
+ , PARROT_ASSERT_ARG(s))
+#define ASSERT_ARGS_Parrot_str_equal __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
+ PARROT_ASSERT_ARG(interp))
+#define ASSERT_ARGS_Parrot_str_free_cstring __attribute__unused__ int _ASSERT_ARGS_CHECK = (0)
+#define ASSERT_ARGS_Parrot_str_from_int __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
+ PARROT_ASSERT_ARG(interp))
+#define ASSERT_ARGS_Parrot_str_from_num __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
+ PARROT_ASSERT_ARG(interp))
+#define ASSERT_ARGS_Parrot_str_length __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
+ PARROT_ASSERT_ARG(interp) \
+ , PARROT_ASSERT_ARG(s))
+#define ASSERT_ARGS_Parrot_str_new __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
+ PARROT_ASSERT_ARG(interp))
+#define ASSERT_ARGS_Parrot_str_new_constant __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
+ PARROT_ASSERT_ARG(interp) \
+ , PARROT_ASSERT_ARG(buffer))
+#define ASSERT_ARGS_Parrot_str_set __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
+ PARROT_ASSERT_ARG(interp) \
+ , PARROT_ASSERT_ARG(src))
+#define ASSERT_ARGS_Parrot_str_to_cstring __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
+ PARROT_ASSERT_ARG(interp))
+#define ASSERT_ARGS_Parrot_str_to_int __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
+ PARROT_ASSERT_ARG(interp))
+#define ASSERT_ARGS_Parrot_str_to_num __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
+ PARROT_ASSERT_ARG(interp) \
+ , PARROT_ASSERT_ARG(s))
+#define ASSERT_ARGS_Parrot_string_cstring __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
+ PARROT_ASSERT_ARG(str))
+#define ASSERT_ARGS_STRING_is_null __attribute__unused__ int _ASSERT_ARGS_CHECK = (0)
+#define ASSERT_ARGS_Parrot_str_from_uint __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
+ PARROT_ASSERT_ARG(interp) \
+ , PARROT_ASSERT_ARG(tc))
+
+#endif /* PARROT_EMBED_STRING_H_GUARD */
View
18 load_pir.test.sql
@@ -0,0 +1,18 @@
+begin;
+-- handler function
+CREATE FUNCTION plparrot_call_handler ()
+RETURNS language_handler AS '$libdir/plparrot' LANGUAGE C;
+
+-- language
+CREATE LANGUAGE plparrot HANDLER plparrot_call_handler;
+
+create or replace function plp_test() RETURNS VOID language plparrot as $$
+ syntax error
+ my name is 'fred'
+ $P0 = open '/tmp/testfile.plparrot.txt', 'w'
+ print $P0, 'Nobody expects this to work'
+ close $P0
+$$;
+
+select plp_test();
+rollback;
View
132 src/plparrot.c → plparrot.c
@@ -1,6 +1,8 @@
/* Parrot header files */
#include "parrot/embed.h"
#include "parrot/extend.h"
+#include "parrot/imcc.h"
+#include "include/embed_string.h"
/* Postgres header files */
#include "postgres.h"
@@ -63,15 +65,23 @@ typedef struct plparrot_call_data
MemoryContext tmp_cxt;
} plparrot_call_data;
-int execq(text *sql, int cnt);
Parrot_Interp interp;
+void plparrot_elog(int level, char *message);
+
+Parrot_String create_string(Parrot_Interp interp, const char *name);
+/* this is saved and restored by plparrot_call_handler */
+static plparrot_call_data *current_call_data = NULL;
+
+/* Be sure we do initialization only once */
+static bool inited = false;
+
+void _PG_init(void);
+void _PG_fini(void);
+
void
_PG_init(void)
{
- /* Be sure we do initialization only once */
- static bool inited = false;
-
if (inited)
return;
@@ -80,83 +90,106 @@ _PG_init(void)
if (!interp) {
elog(ERROR,"Could not create a Parrot interpreter!\n");
- return 1;
+ return;
}
inited = true;
}
-int
-execq(text *sql, int cnt)
+/*
+ * Per PostgreSQL 9.0 documentation, _PG_fini only gets called when a module
+ * is un-loaded, which isn't yet supported. But I'm putting this here for good
+ * measure, anyway
+ */
+void
+_PG_fini(void)
{
- char *command;
- int proc;
- int ret;
-
- if (SPI_connect() != SPI_OK_CONNECT)
- ereport(ERROR, (errcode(ERRCODE_CONNECTION_EXCEPTION), errmsg("Couldn't connect to SPI")));
-
- /* Convert given text object to a C string */
- command = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(sql)));
-
- ret = SPI_exec(command, cnt);
-
- proc = SPI_processed;
-
- /* do stuff */
-
- SPI_finish();
- pfree(command);
-
- return (proc);
+ Parrot_destroy(interp);
+ inited = false;
}
Datum plparrot_call_handler(PG_FUNCTION_ARGS);
Datum plparrot_func_handler(PG_FUNCTION_ARGS);
-/* this is saved and restored by plparrot_call_handler */
-static plparrot_call_data *current_call_data = NULL;
-
-
/* The PostgreSQL function+trigger managers call this function for execution
of PL/Parrot procedures. */
PG_FUNCTION_INFO_V1(plparrot_call_handler);
+/*
+ * The PostgreSQL function+trigger managers call this function for execution of
+ * PL/Parrot procedures.
+ */
Datum
plparrot_call_handler(PG_FUNCTION_ARGS)
{
- Datum retval;
+ Datum retval, procsrc_datum;
Form_pg_proc procstruct;
HeapTuple proctup;
- Oid returntype;
+ Oid returntype, *argtypes;
+ int numargs, rc;
+ char **argnames, *argmodes;
plparrot_call_data *save_call_data = current_call_data;
+ char *proc_src, *errmsg, *tmp;
+ bool isnull;
+ Parrot_PMC func_pmc, func_args;
+ Parrot_Int pmctype;
+ Parrot_String err;
+ if ((rc = SPI_connect()) != SPI_OK_CONNECT)
+ elog(ERROR, "SPI_connect failed: %s", SPI_result_code_string(rc));
+
+ //elog(NOTICE,"enter plparrot_call_handler");
proctup = SearchSysCache(PROCOID, ObjectIdGetDatum(fcinfo->flinfo->fn_oid), 0, 0, 0);
if (!HeapTupleIsValid(proctup))
elog(ERROR, "Failed to look up procedure with OID %u", fcinfo->flinfo->fn_oid);
procstruct = (Form_pg_proc) GETSTRUCT(proctup);
returntype = procstruct->prorettype;
+ procsrc_datum = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_prosrc, &isnull);
+ numargs = get_func_arg_info(proctup, &argtypes, &argnames, &argmodes);
+ if (isnull)
+ elog(ERROR, "Couldn't load function source for function with OID %u", fcinfo->flinfo->fn_oid);
+#ifdef TextDatumGetCString
+ proc_src = pstrdup(TextDatumGetCString(procsrc_datum));
+#else
+ /* For PostgreSQL versions 8.3 and prior */
+ proc_src = pstrdup(DatumGetCString(DirectFunctionCall1(textout, procsrc_datum)));
+#endif
- /* procstruct probably isn't valid after this ReleaseSysCache call, so don't use it */
+ /* procstruct probably isn't valid after this ReleaseSysCache call, so don't use it anymore */
ReleaseSysCache(proctup);
- if (returntype == VOIDOID)
- PG_RETURN_VOID();
-
- if (fcinfo->nargs == 0)
- PG_RETURN_NULL();
-
/* Assume from here on out that the first argument type is the same as the return type */
retval = PG_GETARG_DATUM(0);
+ //elog(NOTICE,"entering PG_TRY");
PG_TRY();
{
if (CALLED_AS_TRIGGER(fcinfo)) {
TriggerData *tdata = (TriggerData *) fcinfo->context;
/* we need a trigger handler */
} else {
- retval = plparrot_func_handler(fcinfo);
+ // elog(NOTICE,"about to compile a PIR string: %s", proc_src);
+ /* Our current plan of attack is the pass along a ResizablePMCArray to all stored procedures */
+ func_pmc = Parrot_compile_string(interp, create_string(interp, "PIR"), proc_src, &err);
+ pmctype = Parrot_PMC_typenum(interp, "ResizablePMCArray");
+ func_args = Parrot_PMC_new(interp, pmctype);
+
+ /* TODO: fill func_args with PG_FUNCTION_ARGS */
+
+ // elog(NOTICE,"compiled a PIR string");
+ if (!STRING_is_null(interp, err)) {
+ // elog(NOTICE,"got an error compiling PIR string");
+ tmp = Parrot_str_to_cstring(interp, err);
+ errmsg = pstrdup(tmp);
+ // elog(NOTICE,"about to free parrot cstring");
+ Parrot_str_free_cstring(tmp);
+ elog(ERROR, "Error compiling PIR function");
+ }
+ // elog(NOTICE,"about to call compiled PIR string with Parrot_ext_call");
+ /* See Parrot's src/extend.c for interpretations of the third argument */
+ /* Pf => PMC with :flat attribute */
+ Parrot_ext_call(interp, func_pmc, "Pf", func_args);
}
}
PG_CATCH();
@@ -168,22 +201,13 @@ plparrot_call_handler(PG_FUNCTION_ARGS)
current_call_data = save_call_data;
- /* Free our intrepreter */
- Parrot_destroy(interp);
+ if ((rc = SPI_finish()) != SPI_OK_FINISH)
+ elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc));
return retval;
}
-Datum
-plparrot_func_handler(PG_FUNCTION_ARGS)
+Parrot_String create_string(Parrot_Interp interp, const char *name)
{
- plparrot_proc_desc *prodesc;
- Datum retval;
- ReturnSetInfo *rsi;
- ErrorContextCallback pl_error_context;
- /* what is the parrot equivalent of these?
- SV *array_ret = NULL;
- SV *perlret;
- */
- return retval;
+ return Parrot_new_string(interp, name, strlen(name), (const char *) NULL, 0);
}
View
37 t/sql/test.sql
@@ -24,23 +24,44 @@ AS $$
CREATE LANGUAGE plparrot;
SELECT true;
$$;
--- These functions should be written in PIR
-CREATE FUNCTION test_void() RETURNS void AS $$ FAIL $$ LANGUAGE plparrot;
+CREATE FUNCTION test_void() RETURNS void AS $$
+.sub foo_void
+ .return()
+.end
+$$ LANGUAGE plparrot;
-CREATE FUNCTION test_int() RETURNS int AS $$ select 1 as result $$ LANGUAGE plparrot;
+CREATE FUNCTION test_int() RETURNS int AS $$
+.sub foo_int
+ .return(1)
+.end
+$$ LANGUAGE plparrot;
-CREATE FUNCTION test_int_int(int) RETURNS int AS $$ select $1 as result $$ LANGUAGE plparrot;
+CREATE FUNCTION test_int_int(int) RETURNS int AS $$
+.sub foo_int_int
+ .param int x
+ .return(x)
+.end
+$$ LANGUAGE plparrot;
-CREATE FUNCTION test_float() RETURNS float AS $$ select 1.0::float as result $$ LANGUAGE plparrot;
+CREATE FUNCTION test_float() RETURNS float AS $$
+.sub foo_float
+ .return(1.0)
+.end
+$$ LANGUAGE plparrot;
-CREATE FUNCTION test_varchar() RETURNS varchar(5) AS $$ select 'cheese' as result $$ LANGUAGE plparrot;
+CREATE FUNCTION test_varchar() RETURNS varchar(5) AS $$
+.sub foo_varchar
+ $S0 = 'cheese'
+ .return($S0)
+.end
+$$ LANGUAGE plparrot;
-select is(test_void()::text,''::text,'We can return void');
select is(test_int(),1,'We can return an int');
-select is(test_int_int(42),42,'We can return an int that was passed as an arg');
+select is(test_void()::text,''::text,'We can return void');
select is(test_float(), 1.0::float ,'We can return a float');
select is(test_varchar(), 'cheese', 'We can return a varchar');
+select is(test_int_int(42),42,'We can return an int that was passed as an arg');
-- Finish the tests and clean up.
SELECT * FROM finish();
Please sign in to comment.
Something went wrong with that request. Please try again.