/*------------------------------------------------------------------------- * * plv8.cc : PL/v8 handler routines. * * Copyright (c) 2009-2012, the PLV8JS Development Group. *------------------------------------------------------------------------- */ #include "plv8.h" #include extern "C" { #define delete delete_ #define namespace namespace_ #define typeid typeid_ #define typename typename_ #define using using_ #if PG_VERSION_NUM >= 90300 #include "access/htup_details.h" #endif #include "access/xact.h" #include "catalog/pg_proc.h" #include "catalog/pg_type.h" #include "commands/trigger.h" #include "executor/spi.h" #include "funcapi.h" #include "miscadmin.h" #include "utils/builtins.h" #include "utils/guc.h" #include "utils/memutils.h" #include "utils/lsyscache.h" #include "utils/rel.h" #include "utils/syscache.h" #undef delete #undef namespace #undef typeid #undef typename #undef using PG_MODULE_MAGIC; PG_FUNCTION_INFO_V1(plv8_call_handler); PG_FUNCTION_INFO_V1(plv8_call_validator); PG_FUNCTION_INFO_V1(plcoffee_call_handler); PG_FUNCTION_INFO_V1(plcoffee_call_validator); PG_FUNCTION_INFO_V1(plls_call_handler); PG_FUNCTION_INFO_V1(plls_call_validator); Datum plv8_call_handler(PG_FUNCTION_ARGS); Datum plv8_call_validator(PG_FUNCTION_ARGS); Datum plcoffee_call_handler(PG_FUNCTION_ARGS); Datum plcoffee_call_validator(PG_FUNCTION_ARGS); Datum plls_call_handler(PG_FUNCTION_ARGS); Datum plls_call_validator(PG_FUNCTION_ARGS); void _PG_init(void); #if PG_VERSION_NUM >= 90000 PG_FUNCTION_INFO_V1(plv8_inline_handler); PG_FUNCTION_INFO_V1(plcoffee_inline_handler); PG_FUNCTION_INFO_V1(plls_inline_handler); Datum plv8_inline_handler(PG_FUNCTION_ARGS); Datum plcoffee_inline_handler(PG_FUNCTION_ARGS); Datum plls_inline_handler(PG_FUNCTION_ARGS); #endif } // extern "C" using namespace v8; typedef struct plv8_proc_cache { Oid fn_oid; Persistent function; char proname[NAMEDATALEN]; char *prosrc; TransactionId fn_xmin; ItemPointerData fn_tid; Oid user_id; int nargs; bool retset; /* true if SRF */ Oid rettype; Oid argtypes[FUNC_MAX_ARGS]; } plv8_proc_cache; /* * The function and context are created at the first invocation. Their * lifetime is same as plv8_proc, but they are not palloc'ed memory, * so we need to clear them at the end of transaction. */ typedef struct plv8_exec_env { Persistent recv; Persistent context; struct plv8_exec_env *next; } plv8_exec_env; /* * We cannot cache plv8_type inter executions because it has FmgrInfo fields. * So, we cache rettype and argtype in fn_extra only during one execution. */ typedef struct plv8_proc { plv8_proc_cache *cache; plv8_exec_env *xenv; TypeFuncClass functypclass; /* For SRF */ plv8_type rettype; plv8_type argtypes[FUNC_MAX_ARGS]; } plv8_proc; /* * For the security reasons, the global context is separated * between users and it's associated with user id. */ typedef struct plv8_context { Persistent context; Oid user_id; } plv8_context; static HTAB *plv8_proc_cache_hash = NULL; static plv8_exec_env *exec_env_head = NULL; extern const unsigned char coffee_script_binary_data[]; extern const unsigned char livescript_binary_data[]; /* * lower_case_functions are postgres-like C functions. * They could raise errors with elog/ereport(ERROR). */ static plv8_proc *plv8_get_proc(Oid fn_oid, FunctionCallInfo fcinfo, bool validate, char ***argnames) throw(); static void plv8_xact_cb(XactEvent event, void *arg); /* * CamelCaseFunctions are C++ functions. * They could raise errors with C++ throw statements, or never throw exceptions. */ static plv8_exec_env *CreateExecEnv(Handle script); static plv8_proc *Compile(Oid fn_oid, FunctionCallInfo fcinfo, bool validate, bool is_trigger, Dialect dialect); static Local CompileFunction(Handle global_context, const char *proname, int proarglen, const char *proargs[], const char *prosrc, bool is_trigger, bool retset, Dialect dialect); static Datum CallFunction(PG_FUNCTION_ARGS, plv8_exec_env *xenv, int nargs, plv8_type argtypes[], plv8_type *rettype); static Datum CallSRFunction(PG_FUNCTION_ARGS, plv8_exec_env *xenv, int nargs, plv8_type argtypes[], plv8_type *rettype); static Datum CallTrigger(PG_FUNCTION_ARGS, plv8_exec_env *xenv); static Persistent GetGlobalContext(); static Persistent GetGlobalObjectTemplate(); /* A GUC to specify a custom start up function to call */ static char *plv8_start_proc = NULL; /* A GUC to specify the remote debugger port */ static int plv8_debugger_port; /* * We use vector instead of hash since the size of this array * is expected to be short in most cases. */ static std::vector ContextVector; #ifdef ENABLE_DEBUGGER_SUPPORT v8::Persistent debug_message_context; void DispatchDebugMessages() { // We are in some random thread. We should already have v8::Locker acquired // (we requested this when registered this callback). We was called // because new debug messages arrived; they may have already been processed, // but we shouldn't worry about this. // // All we have to do is to set context and call ProcessDebugMessages. // // We should decide which V8 context to use here. This is important for // "evaluate" command, because it must be executed some context. // In our sample we have only one context, so there is nothing really to // think about. v8::Context::Scope scope(debug_message_context); v8::Debug::ProcessDebugMessages(); } #endif // ENABLE_DEBUGGER_SUPPORT void _PG_init(void) { HASHCTL hash_ctl = { 0 }; hash_ctl.keysize = sizeof(Oid); hash_ctl.entrysize = sizeof(plv8_proc_cache); hash_ctl.hash = oid_hash; plv8_proc_cache_hash = hash_create("PLv8 Procedures", 32, &hash_ctl, HASH_ELEM | HASH_FUNCTION); DefineCustomStringVariable("plv8.start_proc", gettext_noop("PLV8 function to run once when PLV8 is first used."), NULL, &plv8_start_proc, NULL, PGC_USERSET, 0, #if PG_VERSION_NUM >= 90100 NULL, #endif NULL, NULL); DefineCustomIntVariable("plv8.debugger_port", gettext_noop("V8 remote debug port."), gettext_noop("The default value is 35432. " "This is effective only if PLV8 is built with ENABLE_DEBUGGER_SUPPORT."), &plv8_debugger_port, 35432, 0, 65536, PGC_USERSET, 0, #if PG_VERSION_NUM >= 90100 NULL, #endif NULL, NULL); RegisterXactCallback(plv8_xact_cb, NULL); EmitWarningsOnPlaceholders("plv8"); } static void plv8_xact_cb(XactEvent event, void *arg) { plv8_exec_env *env = exec_env_head; while (env) { if (!env->recv.IsEmpty()) { env->recv.Dispose(); env->recv.Clear(); } env = env->next; /* * Each item was allocated in TopTransactionContext, so * it will be freed eventually. */ } exec_env_head = NULL; } static inline plv8_exec_env * plv8_new_exec_env() { plv8_exec_env *xenv = (plv8_exec_env *) MemoryContextAllocZero(TopTransactionContext, sizeof(plv8_exec_env)); new(&xenv->context) Persistent(); new(&xenv->recv) Persistent(); /* * Add it to the list, which will be freed in the end of top transaction. */ xenv->next = exec_env_head; exec_env_head = xenv; return xenv; } static Datum common_pl_call_handler(PG_FUNCTION_ARGS, Dialect dialect) throw() { Oid fn_oid = fcinfo->flinfo->fn_oid; bool is_trigger = CALLED_AS_TRIGGER(fcinfo); try { #ifdef ENABLE_DEBUGGER_SUPPORT Locker lock; #endif // ENABLE_DEBUGGER_SUPPORT HandleScope handle_scope; if (!fcinfo->flinfo->fn_extra) { plv8_proc *proc = Compile(fn_oid, fcinfo, false, is_trigger, dialect); proc->xenv = CreateExecEnv(proc->cache->function); fcinfo->flinfo->fn_extra = proc; } plv8_proc *proc = (plv8_proc *) fcinfo->flinfo->fn_extra; plv8_proc_cache *cache = proc->cache; if (is_trigger) return CallTrigger(fcinfo, proc->xenv); else if (cache->retset) return CallSRFunction(fcinfo, proc->xenv, cache->nargs, proc->argtypes, &proc->rettype); else return CallFunction(fcinfo, proc->xenv, cache->nargs, proc->argtypes, &proc->rettype); } catch (js_error& e) { e.rethrow(); } catch (pg_error& e) { e.rethrow(); } return (Datum) 0; // keep compiler quiet } Datum plv8_call_handler(PG_FUNCTION_ARGS) { return common_pl_call_handler(fcinfo, PLV8_DIALECT_NONE); } Datum plcoffee_call_handler(PG_FUNCTION_ARGS) { return common_pl_call_handler(fcinfo, PLV8_DIALECT_COFFEE); } Datum plls_call_handler(PG_FUNCTION_ARGS) { return common_pl_call_handler(fcinfo, PLV8_DIALECT_LIVESCRIPT); } #if PG_VERSION_NUM >= 90000 static Datum common_pl_inline_handler(PG_FUNCTION_ARGS, Dialect dialect) throw() { InlineCodeBlock *codeblock = (InlineCodeBlock *) DatumGetPointer(PG_GETARG_DATUM(0)); Assert(IsA(codeblock, InlineCodeBlock)); try { #ifdef ENABLE_DEBUGGER_SUPPORT Locker lock; #endif // ENABLE_DEBUGGER_SUPPORT HandleScope handle_scope; char *source_text = codeblock->source_text; Handle global_context = GetGlobalContext(); Handle function = CompileFunction(global_context, NULL, 0, NULL, source_text, false, false, dialect); plv8_exec_env *xenv = CreateExecEnv(function); return CallFunction(fcinfo, xenv, 0, NULL, NULL); } catch (js_error& e) { e.rethrow(); } catch (pg_error& e) { e.rethrow(); } return (Datum) 0; // keep compiler quiet } Datum plv8_inline_handler(PG_FUNCTION_ARGS) { return common_pl_inline_handler(fcinfo, PLV8_DIALECT_NONE); } Datum plcoffee_inline_handler(PG_FUNCTION_ARGS) { return common_pl_inline_handler(fcinfo, PLV8_DIALECT_COFFEE); } Datum plls_inline_handler(PG_FUNCTION_ARGS) { return common_pl_inline_handler(fcinfo, PLV8_DIALECT_LIVESCRIPT); } #endif /* * DoCall -- Call a JS function with SPI support. * * This function could throw C++ exceptions, but must not throw PG exceptions. */ static Local DoCall(Handle fn, Handle receiver, int nargs, Handle args[]) { TryCatch try_catch; if (SPI_connect() != SPI_OK_CONNECT) throw js_error("could not connect to SPI manager"); Local result = fn->Call(receiver, nargs, args); int status = SPI_finish(); if (result.IsEmpty()) throw js_error(try_catch); if (status < 0) throw js_error(FormatSPIStatus(status)); return result; } static Datum CallFunction(PG_FUNCTION_ARGS, plv8_exec_env *xenv, int nargs, plv8_type argtypes[], plv8_type *rettype) { Handle context = xenv->context; Context::Scope context_scope(context); Handle args[FUNC_MAX_ARGS]; Handle plv8obj; WindowFunctionSupport support(context, fcinfo); /* * In window function case, we cannot see the argument datum * in fcinfo. Instead, get them by WinGetFuncArgCurrent(). */ if (support.IsWindowCall()) { WindowObject winobj = support.GetWindowObject(); for (int i = 0; i < nargs; i++) { bool isnull; Datum arg = WinGetFuncArgCurrent(winobj, i, &isnull); args[i] = ToValue(arg, isnull, &argtypes[i]); } } else { for (int i = 0; i < nargs; i++) args[i] = ToValue(fcinfo->arg[i], fcinfo->argnull[i], &argtypes[i]); } Local fn = Local::Cast(xenv->recv->GetInternalField(0)); Local result = DoCall(fn, xenv->recv, nargs, args); if (rettype) return ToDatum(result, &fcinfo->isnull, rettype); else PG_RETURN_VOID(); } static Tuplestorestate * CreateTupleStore(PG_FUNCTION_ARGS, TupleDesc *tupdesc) { Tuplestorestate *tupstore; PG_TRY(); { ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; MemoryContext per_query_ctx; MemoryContext oldcontext; plv8_proc *proc = (plv8_proc *) fcinfo->flinfo->fn_extra; /* check to see if caller supports us returning a tuplestore */ if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that cannot accept a set"))); if (!(rsinfo->allowedModes & SFRM_Materialize)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("materialize mode required, but it is not " \ "allowed in this context"))); if (!proc->functypclass) proc->functypclass = get_call_result_type(fcinfo, NULL, NULL); per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); tupstore = tuplestore_begin_heap(true, false, work_mem); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; /* Build a tuple descriptor for our result type */ if (proc->rettype.typid == RECORDOID) { if (proc->functypclass != TYPEFUNC_COMPOSITE) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning record called in context " "that cannot accept type record"))); } if (!rsinfo->setDesc) { *tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc); rsinfo->setDesc = *tupdesc; } else *tupdesc = rsinfo->setDesc; MemoryContextSwitchTo(oldcontext); } PG_CATCH(); { throw pg_error(); } PG_END_TRY(); return tupstore; } static Datum CallSRFunction(PG_FUNCTION_ARGS, plv8_exec_env *xenv, int nargs, plv8_type argtypes[], plv8_type *rettype) { plv8_proc *proc = (plv8_proc *) fcinfo->flinfo->fn_extra; TupleDesc tupdesc; Tuplestorestate *tupstore; tupstore = CreateTupleStore(fcinfo, &tupdesc); Handle context = xenv->context; Context::Scope context_scope(context); Converter conv(tupdesc, proc->functypclass == TYPEFUNC_SCALAR); Handle args[FUNC_MAX_ARGS + 1]; /* * In case this is nested via SPI, stash pre-registered converters * for the previous SRF. */ SRFSupport support(context, &conv, tupstore); for (int i = 0; i < nargs; i++) args[i] = ToValue(fcinfo->arg[i], fcinfo->argnull[i], &argtypes[i]); Local fn = Local::Cast(xenv->recv->GetInternalField(0)); Handle result = DoCall(fn, xenv->recv, nargs, args); if (result->IsUndefined()) { // no additional values } else if (result->IsArray()) { Handle array = Handle::Cast(result); // return an array of records. int length = array->Length(); for (int i = 0; i < length; i++) conv.ToDatum(array->Get(i), tupstore); } else { // return a record or a scalar conv.ToDatum(result, tupstore); } /* clean up and return the tuplestore */ tuplestore_donestoring(tupstore); return (Datum) 0; } static Datum CallTrigger(PG_FUNCTION_ARGS, plv8_exec_env *xenv) { // trigger arguments are: // 0: NEW // 1: OLD // 2: TG_NAME // 3: TG_WHEN // 4: TG_LEVEL // 5: TG_OP // 6: TG_RELID // 7: TG_TABLE_NAME // 8: TG_TABLE_SCHEMA // 9: TG_ARGV TriggerData *trig = (TriggerData *) fcinfo->context; Relation rel = trig->tg_relation; TriggerEvent event = trig->tg_event; Handle args[10]; Datum result = (Datum) 0; Handle context = xenv->context; Context::Scope context_scope(context); if (TRIGGER_FIRED_FOR_ROW(event)) { TupleDesc tupdesc = RelationGetDescr(rel); Converter conv(tupdesc); if (TRIGGER_FIRED_BY_INSERT(event)) { result = PointerGetDatum(trig->tg_trigtuple); // NEW args[0] = conv.ToValue(trig->tg_trigtuple); // OLD args[1] = Undefined(); } else if (TRIGGER_FIRED_BY_DELETE(event)) { result = PointerGetDatum(trig->tg_trigtuple); // NEW args[0] = Undefined(); // OLD args[1] = conv.ToValue(trig->tg_trigtuple); } else if (TRIGGER_FIRED_BY_UPDATE(event)) { result = PointerGetDatum(trig->tg_newtuple); // NEW args[0] = conv.ToValue(trig->tg_newtuple); // OLD args[1] = conv.ToValue(trig->tg_trigtuple); } } else { args[0] = args[1] = Undefined(); } // 2: TG_NAME args[2] = ToString(trig->tg_trigger->tgname); // 3: TG_WHEN if (TRIGGER_FIRED_BEFORE(event)) args[3] = String::New("BEFORE"); else args[3] = String::New("AFTER"); // 4: TG_LEVEL if (TRIGGER_FIRED_FOR_ROW(event)) args[4] = String::New("ROW"); else args[4] = String::New("STATEMENT"); // 5: TG_OP if (TRIGGER_FIRED_BY_INSERT(event)) args[5] = String::New("INSERT"); else if (TRIGGER_FIRED_BY_DELETE(event)) args[5] = String::New("DELETE"); else if (TRIGGER_FIRED_BY_UPDATE(event)) args[5] = String::New("UPDATE"); #ifdef TRIGGER_FIRED_BY_TRUNCATE else if (TRIGGER_FIRED_BY_TRUNCATE(event)) args[5] = String::New("TRUNCATE"); #endif else args[5] = String::New("?"); // 6: TG_RELID args[6] = Uint32::New(RelationGetRelid(rel)); // 7: TG_TABLE_NAME args[7] = ToString(RelationGetRelationName(rel)); // 8: TG_TABLE_SCHEMA args[8] = ToString(get_namespace_name(RelationGetNamespace(rel))); // 9: TG_ARGV Handle tgargs = Array::New(trig->tg_trigger->tgnargs); for (int i = 0; i < trig->tg_trigger->tgnargs; i++) tgargs->Set(i, ToString(trig->tg_trigger->tgargs[i])); args[9] = tgargs; TryCatch try_catch; Local fn = Local::Cast(xenv->recv->GetInternalField(0)); Handle newtup = DoCall(fn, xenv->recv, lengthof(args), args); if (newtup.IsEmpty()) throw js_error(try_catch); /* * If the function specifically returned null, return NULL to * tell executor to skip the operation. Otherwise, the function * result is the tuple to be returned. */ if (newtup->IsNull() || !TRIGGER_FIRED_FOR_ROW(event)) { result = PointerGetDatum(NULL); } else if (!newtup->IsUndefined()) { TupleDesc tupdesc = RelationGetDescr(rel); Converter conv(tupdesc); HeapTupleHeader header; header = DatumGetHeapTupleHeader(conv.ToDatum(newtup)); /* We know it's there; heap_form_tuple stores with this layout. */ result = PointerGetDatum((char *) header - HEAPTUPLESIZE); } return result; } static Datum common_pl_call_validator(PG_FUNCTION_ARGS, Dialect dialect) throw() { Oid fn_oid = PG_GETARG_OID(0); HeapTuple tuple; Form_pg_proc proc; char functyptype; bool is_trigger = false; if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, fn_oid)) PG_RETURN_VOID(); /* Get the new function's pg_proc entry */ tuple = SearchSysCache(PROCOID, ObjectIdGetDatum(fn_oid), 0, 0, 0); if (!HeapTupleIsValid(tuple)) elog(ERROR, "cache lookup failed for function %u", fn_oid); proc = (Form_pg_proc) GETSTRUCT(tuple); functyptype = get_typtype(proc->prorettype); /* Disallow pseudotype result */ /* except for TRIGGER, RECORD, INTERNAL, VOID or polymorphic types */ if (functyptype == TYPTYPE_PSEUDO) { /* we assume OPAQUE with no arguments means a trigger */ if (proc->prorettype == TRIGGEROID || (proc->prorettype == OPAQUEOID && proc->pronargs == 0)) is_trigger = true; else if (proc->prorettype != RECORDOID && proc->prorettype != VOIDOID && proc->prorettype != INTERNALOID && !IsPolymorphicType(proc->prorettype)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("PL/v8 functions cannot return type %s", format_type_be(proc->prorettype)))); } ReleaseSysCache(tuple); try { #ifdef ENABLE_DEBUGGER_SUPPORT Locker lock; #endif // ENABLE_DEBUGGER_SUPPORT /* Don't use validator's fcinfo */ plv8_proc *proc = Compile(fn_oid, NULL, true, is_trigger, dialect); (void) CreateExecEnv(proc->cache->function); /* the result of a validator is ignored */ PG_RETURN_VOID(); } catch (js_error& e) { e.rethrow(); } catch (pg_error& e) { e.rethrow(); } return (Datum) 0; // keep compiler quiet } Datum plv8_call_validator(PG_FUNCTION_ARGS) { return common_pl_call_validator(fcinfo, PLV8_DIALECT_NONE); } Datum plcoffee_call_validator(PG_FUNCTION_ARGS) { return common_pl_call_validator(fcinfo, PLV8_DIALECT_COFFEE); } Datum plls_call_validator(PG_FUNCTION_ARGS) { return common_pl_call_validator(fcinfo, PLV8_DIALECT_LIVESCRIPT); } static plv8_proc * plv8_get_proc(Oid fn_oid, FunctionCallInfo fcinfo, bool validate, char ***argnames) throw() { HeapTuple procTup; plv8_proc_cache *cache; bool found; bool isnull; Datum prosrc; Oid *argtypes; char *argmodes; MemoryContext oldcontext; procTup = SearchSysCache(PROCOID, ObjectIdGetDatum(fn_oid), 0, 0, 0); if (!HeapTupleIsValid(procTup)) elog(ERROR, "cache lookup failed for function %u", fn_oid); cache = (plv8_proc_cache *) hash_search(plv8_proc_cache_hash,&fn_oid, HASH_ENTER, &found); if (found) { bool uptodate; /* * We need to check user id and dispose it if it's different from * the previous cache user id, as the V8 function is associated * with the context where it was generated. In most cases, * we can expect this doesn't affect runtime performance. */ uptodate = (!cache->function.IsEmpty() && cache->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) && ItemPointerEquals(&cache->fn_tid, &procTup->t_self) && cache->user_id == GetUserId()); if (!uptodate) { if (cache->prosrc) { pfree(cache->prosrc); cache->prosrc = NULL; } cache->function.Dispose(); cache->function.Clear(); } else { ReleaseSysCache(procTup); } } else { new(&cache->function) Persistent(); cache->prosrc = NULL; } if (cache->function.IsEmpty()) { Form_pg_proc procStruct; procStruct = (Form_pg_proc) GETSTRUCT(procTup); prosrc = SysCacheGetAttr(PROCOID, procTup, Anum_pg_proc_prosrc, &isnull); if (isnull) elog(ERROR, "null prosrc"); cache->retset = procStruct->proretset; cache->rettype = procStruct->prorettype; strlcpy(cache->proname, NameStr(procStruct->proname), NAMEDATALEN); cache->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data); cache->fn_tid = procTup->t_self; cache->user_id = GetUserId(); int nargs = get_func_arg_info(procTup, &argtypes, argnames, &argmodes); if (validate) { /* * Disallow non-polymorphic pseudotypes in arguments * (either IN or OUT). Internal type is used to declare * js functions for find_function(). */ for (int i = 0; i < nargs; i++) { if (get_typtype(argtypes[i]) == TYPTYPE_PSEUDO && argtypes[i] != INTERNALOID && !IsPolymorphicType(argtypes[i])) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("PL/v8 functions cannot accept type %s", format_type_be(argtypes[i])))); } } oldcontext = MemoryContextSwitchTo(TopMemoryContext); cache->prosrc = TextDatumGetCString(prosrc); MemoryContextSwitchTo(oldcontext); ReleaseSysCache(procTup); int inargs = 0; for (int i = 0; i < nargs; i++) { Oid argtype = argtypes[i]; char argmode = argmodes ? argmodes[i] : PROARGMODE_IN; switch (argmode) { case PROARGMODE_IN: case PROARGMODE_INOUT: case PROARGMODE_VARIADIC: break; default: continue; } if (*argnames) (*argnames)[inargs] = (*argnames)[i]; cache->argtypes[inargs] = argtype; inargs++; } cache->nargs = inargs; } MemoryContext mcxt = CurrentMemoryContext; if (fcinfo) mcxt = fcinfo->flinfo->fn_mcxt; plv8_proc *proc = (plv8_proc *) MemoryContextAllocZero(mcxt, offsetof(plv8_proc, argtypes) + sizeof(plv8_type) * cache->nargs); proc->cache = cache; for (int i = 0; i < cache->nargs; i++) { Oid argtype = cache->argtypes[i]; /* Resolve polymorphic types, if this is an actual call context. */ if (fcinfo && IsPolymorphicType(argtype)) argtype = get_fn_expr_argtype(fcinfo->flinfo, i); plv8_fill_type(&proc->argtypes[i], argtype, mcxt); } Oid rettype = cache->rettype; /* Resolve polymorphic return type if this is an actual call context. */ if (fcinfo && IsPolymorphicType(rettype)) rettype = get_fn_expr_rettype(fcinfo->flinfo); plv8_fill_type(&proc->rettype, rettype, mcxt); return proc; } static plv8_exec_env * CreateExecEnv(Handle function) { plv8_exec_env *xenv; HandleScope handle_scope; PG_TRY(); { xenv = plv8_new_exec_env(); } PG_CATCH(); { throw pg_error(); } PG_END_TRY(); xenv->context = GetGlobalContext(); Context::Scope scope(xenv->context); static Persistent recv_templ; if (recv_templ.IsEmpty()) { recv_templ = Persistent::New(ObjectTemplate::New()); recv_templ->SetInternalFieldCount(1); } xenv->recv = Persistent::New(recv_templ->NewInstance()); xenv->recv->SetInternalField(0, function); return xenv; } /* Source transformation from a dialect (coffee or ls) to js */ static char * CompileDialect(const char *src, Dialect dialect) { HandleScope handle_scope; static Persistent context = Context::New((ExtensionConfiguration*)NULL); Context::Scope context_scope(context); TryCatch try_catch; Local key; char *cresult; const char *dialect_binary_data; switch (dialect) { case PLV8_DIALECT_COFFEE: if (coffee_script_binary_data[0] == '\0') throw js_error("CoffeeScript is not enabled"); key = String::NewSymbol("CoffeeScript"); dialect_binary_data = (const char *) coffee_script_binary_data; break; case PLV8_DIALECT_LIVESCRIPT: if (livescript_binary_data[0] == '\0') throw js_error("LiveScript is not enabled"); key = String::NewSymbol("LiveScript"); dialect_binary_data = (const char *) livescript_binary_data; break; default: throw js_error("Unknown Dialect"); } if (context->Global()->Get(key)->IsUndefined()) { HandleScope handle_scope; Local