Permalink
Browse files

Add executeSql(sql, args). $n in sql is parsed as placeholder.

Values in args must be null, string, numeric, or boolean.
Other types including object, array, and date are not supported yet.
  • Loading branch information...
1 parent a12c9ae commit 50a4bcaf9d0afd2109228ce26bf628276ffbe2e1 @ItGacky ItGacky committed Aug 26, 2010
Showing with 175 additions and 26 deletions.
  1. +17 −0 expected/plv8.out
  2. +143 −26 plv8.cc
  3. +5 −0 plv8.h
  4. +10 −0 sql/plv8.sql
View
@@ -261,6 +261,23 @@ SELECT * FROM test_tbl;
4 | s4
(4 rows)
+CREATE FUNCTION return_sql() RETURNS SETOF test_tbl AS
+$$
+ return executeSql(
+ "SELECT i, $1 || i AS s FROM generate_series(1, $2) AS t(i)",
+ [ 's', 4 ]
+ );
+$$
+LANGUAGE plv8;
+SELECT * FROM return_sql();
+ i | s
+---+----
+ 1 | s1
+ 2 | s2
+ 3 | s3
+ 4 | s4
+(4 rows)
+
CREATE FUNCTION test_sql_error() RETURNS void AS $$ executeSql("ERROR") $$ LANGUAGE plv8;
SELECT test_sql_error();
ERROR: syntax error at or near "ERROR"
View
169 plv8.cc
@@ -88,10 +88,12 @@ static Handle<v8::Value> Print(const Arguments& args) throw();
static Handle<v8::Value> PrintInternal(const Arguments& args);
static Handle<v8::Value> ExecuteSql(const Arguments& args) throw();
static Handle<v8::Value> ExecuteSqlInternal(const Arguments& args);
+static Handle<v8::Value> ExecuteSqlWithArgs(const Arguments& args);
static Handle<v8::Value> Yield(const Arguments& args) throw();
static Handle<v8::Value> YieldInternal(const Arguments& args);
static Handle<Context> GetGlobalContext() throw();
+
struct YieldState
{
Converter conv;
@@ -103,7 +105,6 @@ struct YieldState
}
};
-
Datum
plv8_call_handler(PG_FUNCTION_ARGS) throw()
{
@@ -124,6 +125,7 @@ plv8_call_handler(PG_FUNCTION_ARGS) throw()
else if (proc->retset)
return CallSRFunction(fcinfo, proc->function,
proc->nargs, proc->argtypes, &proc->rettype);
+ else
return CallFunction(fcinfo, proc->function,
proc->nargs, proc->argtypes, &proc->rettype);
}
@@ -254,6 +256,28 @@ CallSRFunction(PG_FUNCTION_ARGS, Handle<Function> fn,
if (result.IsEmpty())
throw js_error(try_catch);
+ if (result->IsUndefined())
+ {
+ // no additinal return values
+ }
+ else if (result->IsArray())
+ {
+ Handle<Array> array = Handle<Array>::Cast(result);
+ // return an array of recoreds.
+ int length = array->Length();
+ for (int i = 0; i < length; i++)
+ state.conv.ToDatum(array->Get(i), tupstore);
+ }
+ else if (result->IsObject())
+ {
+ // return a record
+ state.conv.ToDatum(result, tupstore);
+ }
+ else
+ {
+ throw js_error("SETOF function cannot return scalar value");
+ }
+
/* clean up and return the tuplestore */
tuplestore_donestoring(tupstore);
@@ -754,35 +778,27 @@ PrintInternal(const Arguments& args)
static Handle<v8::Value>
ExecuteSql(const Arguments& args) throw()
{
- return SafeCall(ExecuteSqlInternal, args);
-}
+ if (args.Length() != 1 && (args.Length() != 2 || !args[1]->IsArray()))
+ return ThrowException(String::New("usage: executeSql(sql [, args])"));
-/*
- * executeSql(string sql) returns array<json> for query, or integer for command.
- *
- * TODO: support query arguments with SPI_execute_with_args().
- */
-static Handle<v8::Value>
-ExecuteSqlInternal(const Arguments& args)
-{
- if (args.Length() != 1)
- return ThrowException(String::New("usage: executeSql(sql)"));
+ Handle<v8::Value> result;
SPI_connect();
+ if (args.Length() == 1)
+ result = SafeCall(ExecuteSqlInternal, args);
+ else
+ result = SafeCall(ExecuteSqlWithArgs, args);
+ SPI_finish();
+ return result;
+}
- CString sql(args[0]);
+static Handle<v8::Value>
+SPIResultToValue(int status)
+{
Handle<v8::Value> result;
- int status;
- PG_TRY();
- {
- status = SPI_exec(sql, 0);
- }
- PG_CATCH();
- {
- throw pg_error();
- }
- PG_END_TRY();
+ if (status < 0)
+ return ThrowException(String::New("SPI failed"));
switch (status)
{
@@ -806,11 +822,112 @@ ExecuteSqlInternal(const Arguments& args)
break;
}
- SPI_finish();
-
return result;
}
+/*
+ * executeSql(string sql) returns array<json> for query, or integer for command.
+ */
+static Handle<v8::Value>
+ExecuteSqlInternal(const Arguments& args)
+{
+ SPI_connect();
+
+ int status;
+ CString sql(args[0]);
+
+ PG_TRY();
+ {
+ status = SPI_exec(sql, 0);
+ }
+ PG_CATCH();
+ {
+ throw pg_error();
+ }
+ PG_END_TRY();
+
+ return SPIResultToValue(status);
+}
+
+static Handle<v8::Value>
+ExecuteSqlWithArgs(const Arguments& args)
+{
+ int status;
+ CString sql(args[0]);
+ Handle<Array> array = Handle<Array>::Cast(args[1]);
+
+ int nargs = array->Length();
+ Datum *values = (Datum *) palloc(sizeof(Datum) * nargs);
+ char *nulls = (char *) palloc(sizeof(char) * nargs);
+ Oid *argtypes = (Oid *) palloc(sizeof(Oid) * nargs);
+
+ memset(nulls, ' ', sizeof(char) * nargs);
+ for (int i = 0; i < nargs; i++)
+ {
+ Handle<v8::Value> value = array->Get(i);
+
+ if (value->IsNull() || value->IsUndefined())
+ {
+ nulls[i] = 'n';
+ argtypes[i] = TEXTOID;
+ }
+ else if (value->IsBoolean())
+ {
+ values[i] = BoolGetDatum(value->ToBoolean()->Value());
+ argtypes[i] = BOOLOID;
+ }
+ else if (value->IsInt32())
+ {
+ values[i] = Int32GetDatum(value->ToInt32()->Value());
+ argtypes[i] = INT4OID;
+ }
+ else if (value->IsUint32())
+ {
+ values[i] = Int64GetDatum(value->ToUint32()->Value());
+ argtypes[i] = INT8OID;
+ }
+ else if (value->IsNumber())
+ {
+ values[i] = Float8GetDatum(value->ToNumber()->Value());
+ argtypes[i] = FLOAT8OID;
+ }
+ else if (value->IsDate())
+ {
+ // TODO: Date
+ return ThrowException(String::New("Date is not supported as arguments for executeSql()"));
+ }
+ else if (value->IsObject())
+ {
+ // TODO: Object
+ return ThrowException(String::New("Object is not supported as arguments for executeSql()"));
+ }
+ else if (value->IsArray())
+ {
+ // TODO: Array
+ return ThrowException(String::New("Array is not supported as arguments for executeSql()"));
+ }
+ else
+ {
+ CString str(value);
+ values[i] = CStringGetTextDatum(str);
+ argtypes[i] = TEXTOID;
+ }
+ }
+
+ PG_TRY();
+ {
+ status = SPI_execute_with_args(sql, nargs,
+ argtypes, values, nulls, false, 0);
+ }
+ PG_CATCH();
+ {
+ throw pg_error();
+ }
+ PG_END_TRY();
+
+ return SPIResultToValue(status);
+}
+
static Handle<v8::Value>
Yield(const Arguments& args) throw()
{
View
5 plv8.h
@@ -1,3 +1,6 @@
+#ifndef _PLV8_
+#define _PLV8_
+
#include <v8.h>
#include <vector>
@@ -106,3 +109,5 @@ extern v8::Handle<v8::String> ToString(Datum value, plv8_type *type);
extern v8::Handle<v8::String> ToString(const char *str, int len = -1, int encoding = GetDatabaseEncoding());
extern char *ToCString(const v8::String::Utf8Value &value);
extern char *ToCStringCopy(const v8::String::Utf8Value &value);
+
+#endif // _PLV8_
View
@@ -139,6 +139,16 @@ LANGUAGE plv8;
SELECT test_sql();
SELECT * FROM test_tbl;
+CREATE FUNCTION return_sql() RETURNS SETOF test_tbl AS
+$$
+ return executeSql(
+ "SELECT i, $1 || i AS s FROM generate_series(1, $2) AS t(i)",
+ [ 's', 4 ]
+ );
+$$
+LANGUAGE plv8;
+SELECT * FROM return_sql();
+
CREATE FUNCTION test_sql_error() RETURNS void AS $$ executeSql("ERROR") $$ LANGUAGE plv8;
SELECT test_sql_error();

0 comments on commit 50a4bca

Please sign in to comment.