1 /*-------------------------------------------------------------------------
2 *
3 * plv8.cc : PL/v8 handler routines.
4 *
5 * Copyright (c) 2009-2012, the PLV8JS Development Group.
6 *-------------------------------------------------------------------------
7 */
8 #include "plv8.h"
9
10 #ifdef _MSC_VER
11 #undef open
12 #endif
13
14 #include "libplatform/libplatform.h"
15 #include "plv8_allocator.h"
16
17 #include <new>
18
19 extern "C" {
20 #if PG_VERSION_NUM >= 90300
21 #include "access/htup_details.h"
22 #endif
23 #include "access/xact.h"
24 #include "catalog/pg_proc.h"
25 #include "catalog/pg_type.h"
26 #include "commands/trigger.h"
27 #include "executor/spi.h"
28 #include "funcapi.h"
29 #include "miscadmin.h"
30 #include "utils/builtins.h"
31 #include "utils/guc.h"
32 #include "utils/memutils.h"
33 #include "utils/lsyscache.h"
34 #include "utils/rel.h"
35 #include "utils/syscache.h"
36
37 #if PG_VERSION_NUM >= 120000
38 #include "catalog/pg_database.h"
39 #endif
40
41 #if PG_VERSION_NUM >= 130000
42 #include "common/hashfn.h"
43 #endif
44
45 #include <signal.h>
46
47 #ifdef EXECUTION_TIMEOUT
48 #ifdef _MSC_VER
49 #include <windows.h>
50 #else
51 #include <unistd.h>
52 #endif
53 #endif
54
55 PG_MODULE_MAGIC;
56
57 PGDLLEXPORT Datum plv8_call_handler(PG_FUNCTION_ARGS);
58 PGDLLEXPORT Datum plv8_call_validator(PG_FUNCTION_ARGS);
59 PGDLLEXPORT Datum plcoffee_call_handler(PG_FUNCTION_ARGS);
60 PGDLLEXPORT Datum plcoffee_call_validator(PG_FUNCTION_ARGS);
61 PGDLLEXPORT Datum plls_call_handler(PG_FUNCTION_ARGS);
62 PGDLLEXPORT Datum plls_call_validator(PG_FUNCTION_ARGS);
63 PGDLLEXPORT Datum plv8_reset(PG_FUNCTION_ARGS);
64 PGDLLEXPORT Datum plv8_info(PG_FUNCTION_ARGS);
65
66 PG_FUNCTION_INFO_V1(plv8_call_handler);
67 PG_FUNCTION_INFO_V1(plv8_call_validator);
68 PG_FUNCTION_INFO_V1(plcoffee_call_handler);
69 PG_FUNCTION_INFO_V1(plcoffee_call_validator);
70 PG_FUNCTION_INFO_V1(plls_call_handler);
71 PG_FUNCTION_INFO_V1(plls_call_validator);
72 PG_FUNCTION_INFO_V1(plv8_reset);
73 PG_FUNCTION_INFO_V1(plv8_info);
74
75
76 PGDLLEXPORT void _PG_init(void);
77
78 #if PG_VERSION_NUM >= 90000
79 PGDLLEXPORT Datum plv8_inline_handler(PG_FUNCTION_ARGS);
80 PGDLLEXPORT Datum plcoffee_inline_handler(PG_FUNCTION_ARGS);
81 PGDLLEXPORT Datum plls_inline_handler(PG_FUNCTION_ARGS);
82
83 PG_FUNCTION_INFO_V1(plv8_inline_handler);
84 PG_FUNCTION_INFO_V1(plcoffee_inline_handler);
85 PG_FUNCTION_INFO_V1(plls_inline_handler);
86 #endif
87 } // extern "C"
88
89 using namespace v8;
90
91 typedef struct plv8_proc_cache
92 {
93 Oid fn_oid;
94
95 Persistent<Function> function;
96 char proname[NAMEDATALEN];
97 char *prosrc;
98
99 TransactionId fn_xmin;
100 ItemPointerData fn_tid;
101 Oid user_id;
102
103 int nargs;
104 bool retset; /* true if SRF */
105 Oid rettype;
106 Oid argtypes[FUNC_MAX_ARGS];
107 } plv8_proc_cache;
108
109 plv8_context *current_context = nullptr;
110 size_t plv8_memory_limit = 0;
111 size_t plv8_last_heap_size = 0;
112
113 /*
114 * The function and context are created at the first invocation. Their
115 * lifetime is same as plv8_proc, but they are not palloc'ed memory,
116 * so we need to clear them at the end of transaction.
117 */
118 typedef struct plv8_exec_env
119 {
120 Isolate *isolate;
121 Persistent<Object> recv;
122 Persistent<Context> context;
localContextplv8_exec_env123 Local<Context> localContext() { return Local<Context>::New(isolate, context) ; }
124 struct plv8_exec_env *next;
125 } plv8_exec_env;
126
127 /*
128 * We cannot cache plv8_type inter executions because it has FmgrInfo fields.
129 * So, we cache rettype and argtype in fn_extra only during one execution.
130 */
131 typedef struct plv8_proc
132 {
133 plv8_proc_cache *cache;
134 plv8_exec_env *xenv;
135 TypeFuncClass functypclass; /* For SRF */
136 plv8_type rettype;
137 plv8_type argtypes[FUNC_MAX_ARGS];
138 } plv8_proc;
139
140 static HTAB *plv8_proc_cache_hash = NULL;
141
142 static plv8_exec_env *exec_env_head = NULL;
143
144 extern const unsigned char coffee_script_binary_data[];
145 extern const unsigned char livescript_binary_data[];
146
147 /*
148 * lower_case_functions are postgres-like C functions.
149 * They could raise errors with elog/ereport(ERROR).
150 */
151 static plv8_proc *plv8_get_proc(Oid fn_oid, FunctionCallInfo fcinfo,
152 bool validate, char ***argnames) throw();
153 static void plv8_xact_cb(XactEvent event, void *arg);
154
155 /*
156 * CamelCaseFunctions are C++ functions.
157 * They could raise errors with C++ throw statements, or never throw exceptions.
158 */
159 static plv8_exec_env *CreateExecEnv(Handle<Function> function, plv8_context *context);
160 static plv8_exec_env *CreateExecEnv(Persistent<Function>& function, plv8_context *context);
161 static plv8_proc *Compile(Oid fn_oid, FunctionCallInfo fcinfo,
162 bool validate, bool is_trigger, Dialect dialect);
163 static Local<Function> CompileFunction(plv8_context *global_context,
164 const char *proname, int proarglen,
165 const char *proargs[], const char *prosrc,
166 bool is_trigger, bool retset, Dialect dialect);
167 static Datum CallFunction(PG_FUNCTION_ARGS, plv8_exec_env *xenv,
168 int nargs, plv8_type argtypes[], plv8_type *rettype);
169 static Datum CallSRFunction(PG_FUNCTION_ARGS, plv8_exec_env *xenv,
170 int nargs, plv8_type argtypes[], plv8_type *rettype);
171 static Datum CallTrigger(PG_FUNCTION_ARGS, plv8_exec_env *xenv);
172 static plv8_context *GetPlv8Context();
173 static Local<ObjectTemplate> GetGlobalObjectTemplate(Isolate *isolate);
174 static void CreateIsolate(plv8_context *context);
175
176 /* A GUC to specify a custom start up function to call */
177 static char *plv8_start_proc = NULL;
178
179 /* A GUC to specify V8 flags (e.g. --es_staging) */
180 static char *plv8_v8_flags = NULL;
181
182 /* A GUC to specify the ICU data directory */
183 static char *plv8_icu_data = NULL;
184
185 /* A GUC to specify the remote debugger port */
186 static int plv8_debugger_port;
187
188 #ifdef EXECUTION_TIMEOUT
189 static int plv8_execution_timeout = 300;
190 #endif
191
192 static std::unique_ptr<v8::Platform> v8_platform = NULL;
193
194 /*
195 * We use vector instead of hash since the size of this array
196 * is expected to be short in most cases.
197 */
198 static std::vector<plv8_context *> ContextVector;
199
200 #ifdef ENABLE_DEBUGGER_SUPPORT
201 v8::Persistent<v8::Context> debug_message_context;
202
DispatchDebugMessages()203 void DispatchDebugMessages() {
204 // We are in some random thread. We should already have v8::Locker acquired
205 // (we requested this when registered this callback). We was called
206 // because new debug messages arrived; they may have already been processed,
207 // but we shouldn't worry about this.
208 //
209 // All we have to do is to set context and call ProcessDebugMessages.
210 //
211 // We should decide which V8 context to use here. This is important for
212 // "evaluate" command, because it must be executed some context.
213 // In our sample we have only one context, so there is nothing really to
214 // think about.
215 v8::Context::Scope scope(debug_message_context);
216
217 v8::Debug::ProcessDebugMessages();
218 }
219 #endif // ENABLE_DEBUGGER_SUPPORT
220
OOMErrorHandler(const char * location,bool is_heap_oom)221 void OOMErrorHandler(const char* location, bool is_heap_oom) {
222 Isolate *isolate = Isolate::GetCurrent();
223 isolate->TerminateExecution();
224 throw js_error("OOM error");
225 }
226
GCEpilogueCallback(Isolate * isolate,GCType type,GCCallbackFlags)227 void GCEpilogueCallback(Isolate* isolate, GCType type, GCCallbackFlags /* flags */) {
228 HeapStatistics heap_statistics;
229 isolate->GetHeapStatistics(&heap_statistics);
230 if (type != GCType::kGCTypeIncrementalMarking
231 && heap_statistics.used_heap_size() > plv8_memory_limit * 1_MB) {
232 isolate->TerminateExecution();
233 throw js_error("OOM error in GC");
234 }
235 if (heap_statistics.used_heap_size() > plv8_memory_limit * 1_MB / 0.9
236 && plv8_last_heap_size < plv8_memory_limit * 1_MB / 0.9) {
237 isolate->LowMemoryNotification();
238 }
239 plv8_last_heap_size = heap_statistics.used_heap_size();
240 }
241
NearHeapLimitHandler(void * data,size_t current_heap_limit,size_t initial_heap_limit)242 size_t NearHeapLimitHandler(void* data, size_t current_heap_limit,
243 size_t initial_heap_limit) {
244 Isolate *isolate = Isolate::GetCurrent();
245 isolate->TerminateExecution();
246 // need to give back more space
247 // to make sure it can unwind the stack and process exceptions
248 return current_heap_limit + 1_MB;
249 }
250
251 static void
CreateIsolate(plv8_context * context)252 CreateIsolate(plv8_context *context) {
253 Isolate *isolate;
254 Isolate::CreateParams params;
255 params.array_buffer_allocator = new ArrayAllocator(plv8_memory_limit * 1_MB);
256 ResourceConstraints rc;
257 rc.ConfigureDefaults(plv8_memory_limit * 1_MB * 2, plv8_memory_limit * 1_MB * 2);
258 params.constraints = rc;
259 isolate = Isolate::New(params);
260 isolate->SetOOMErrorHandler(OOMErrorHandler);
261 isolate->AddGCEpilogueCallback(GCEpilogueCallback);
262 isolate->AddNearHeapLimitCallback(NearHeapLimitHandler, NULL);
263 context->isolate = isolate;
264 context->array_buffer_allocator = params.array_buffer_allocator;
265 }
266
267 void
_PG_init(void)268 _PG_init(void)
269 {
270 HASHCTL hash_ctl = { 0 };
271
272 hash_ctl.keysize = sizeof(Oid);
273 hash_ctl.entrysize = sizeof(plv8_proc_cache);
274 hash_ctl.hash = oid_hash;
275 plv8_proc_cache_hash = hash_create("PLv8 Procedures", 32,
276 &hash_ctl, HASH_ELEM | HASH_FUNCTION);
277
278 DefineCustomStringVariable("plv8.start_proc",
279 gettext_noop("PLV8 function to run once when PLV8 is first used."),
280 NULL,
281 &plv8_start_proc,
282 NULL,
283 PGC_USERSET, 0,
284 #if PG_VERSION_NUM >= 90100
285 NULL,
286 #endif
287 NULL,
288 NULL);
289
290 DefineCustomStringVariable("plv8.icu_data",
291 gettext_noop("ICU data file directory."),
292 NULL,
293 &plv8_icu_data,
294 NULL,
295 PGC_USERSET, 0,
296 #if PG_VERSION_NUM >= 90100
297 NULL,
298 #endif
299 NULL,
300 NULL);
301
302 DefineCustomStringVariable("plv8.v8_flags",
303 gettext_noop("V8 engine initialization flags (e.g. --harmony for all current harmony features)."),
304 NULL,
305 &plv8_v8_flags,
306 NULL,
307 PGC_USERSET, 0,
308 #if PG_VERSION_NUM >= 90100
309 NULL,
310 #endif
311 NULL,
312 NULL);
313
314 DefineCustomIntVariable("plv8.debugger_port",
315 gettext_noop("V8 remote debug port."),
316 gettext_noop("The default value is 35432. "
317 "This is effective only if PLV8 is built with ENABLE_DEBUGGER_SUPPORT."),
318 &plv8_debugger_port,
319 35432, 0, 65536,
320 PGC_USERSET, 0,
321 #if PG_VERSION_NUM >= 90100
322 NULL,
323 #endif
324 NULL,
325 NULL);
326
327 #ifdef EXECUTION_TIMEOUT
328 DefineCustomIntVariable("plv8.execution_timeout",
329 gettext_noop("V8 execution timeout."),
330 gettext_noop("The default value is 300 seconds. "
331 "This allows you to override the default execution timeout."),
332 &plv8_execution_timeout,
333 300, 1, 65536,
334 PGC_USERSET, 0,
335 #if PG_VERSION_NUM >= 90100
336 NULL,
337 #endif
338 NULL,
339 NULL);
340 #endif
341
342 DefineCustomIntVariable("plv8.memory_limit",
343 gettext_noop("Per-isolate memory limit in MBytes"),
344 gettext_noop("The default value is 256 MB"),
345 (int*)&plv8_memory_limit,
346 256, 256, 3096, // hardcoded v8 limits for isolates
347 PGC_SUSET, 0,
348 #if PG_VERSION_NUM >= 90100
349 NULL,
350 #endif
351 NULL,
352 NULL);
353
354 RegisterXactCallback(plv8_xact_cb, NULL);
355
356 EmitWarningsOnPlaceholders("plv8");
357
358 if (plv8_icu_data == NULL) {
359 elog(DEBUG1, "no icu dir");
360 V8::InitializeICU();
361 } else {
362 elog(DEBUG1, "init icu data %s", plv8_icu_data);
363 V8::InitializeICU(plv8_icu_data);
364 }
365
366 #if (V8_MAJOR_VERSION == 4 && V8_MINOR_VERSION >= 6) || V8_MAJOR_VERSION >= 5
367 V8::InitializeExternalStartupData("plv8");
368 #endif
369 if (!v8_platform) {
370 v8_platform = platform::NewDefaultPlatform();
371 }
372 V8::InitializePlatform(v8_platform.get());
373 V8::Initialize();
374 if (plv8_v8_flags != NULL) {
375 V8::SetFlagsFromString(plv8_v8_flags, strlen(plv8_v8_flags));
376 }
377 }
378
379 static void
plv8_xact_cb(XactEvent event,void * arg)380 plv8_xact_cb(XactEvent event, void *arg)
381 {
382 plv8_exec_env *env = exec_env_head;
383
384 while (env)
385 {
386 if (!env->recv.IsEmpty())
387 {
388 env->recv.Reset();
389 }
390 env = env->next;
391 /*
392 * Each item was allocated in TopTransactionContext, so
393 * it will be freed eventually.
394 */
395 }
396 exec_env_head = NULL;
397 }
398
399 static inline plv8_exec_env *
plv8_new_exec_env(Isolate * isolate)400 plv8_new_exec_env(Isolate *isolate)
401 {
402 plv8_exec_env *xenv = (plv8_exec_env *)
403 MemoryContextAllocZero(TopTransactionContext, sizeof(plv8_exec_env));
404
405 new(&xenv->context) Persistent<Context>();
406 new(&xenv->recv) Persistent<Object>();
407 xenv->isolate = isolate;
408
409 /*
410 * Add it to the list, which will be freed in the end of top transaction.
411 */
412 xenv->next = exec_env_head;
413 exec_env_head = xenv;
414
415 return xenv;
416 }
417
418 static Datum
common_pl_call_handler(PG_FUNCTION_ARGS,Dialect dialect)419 common_pl_call_handler(PG_FUNCTION_ARGS, Dialect dialect) throw()
420 {
421 current_context = GetPlv8Context();
422 Oid fn_oid = fcinfo->flinfo->fn_oid;
423 bool is_trigger = CALLED_AS_TRIGGER(fcinfo);
424
425 try
426 {
427 #ifdef ENABLE_DEBUGGER_SUPPORT
428 Locker lock;
429 #endif // ENABLE_DEBUGGER_SUPPORT
430 Isolate::Scope scope(current_context->isolate);
431 HandleScope handle_scope(current_context->isolate);
432
433 if (!fcinfo->flinfo->fn_extra)
434 {
435 plv8_proc *proc = Compile(fn_oid, fcinfo,
436 false, is_trigger, dialect);
437 proc->xenv = CreateExecEnv(proc->cache->function, current_context);
438 fcinfo->flinfo->fn_extra = proc;
439 }
440
441 plv8_proc *proc = (plv8_proc *) fcinfo->flinfo->fn_extra;
442 plv8_proc_cache *cache = proc->cache;
443
444 if (is_trigger)
445 return CallTrigger(fcinfo, proc->xenv);
446 else if (cache->retset)
447 return CallSRFunction(fcinfo, proc->xenv,
448 cache->nargs, proc->argtypes, &proc->rettype);
449 else
450 return CallFunction(fcinfo, proc->xenv,
451 cache->nargs, proc->argtypes, &proc->rettype);
452 }
453 catch (js_error& e) { e.rethrow(); }
454 catch (pg_error& e) { e.rethrow(); }
455
456 return (Datum) 0; // keep compiler quiet
457 }
458
459 Datum
plv8_call_handler(PG_FUNCTION_ARGS)460 plv8_call_handler(PG_FUNCTION_ARGS)
461 {
462 return common_pl_call_handler(fcinfo, PLV8_DIALECT_NONE);
463 }
464
465 Datum
plcoffee_call_handler(PG_FUNCTION_ARGS)466 plcoffee_call_handler(PG_FUNCTION_ARGS)
467 {
468 return common_pl_call_handler(fcinfo, PLV8_DIALECT_COFFEE);
469 }
470
471 Datum
plls_call_handler(PG_FUNCTION_ARGS)472 plls_call_handler(PG_FUNCTION_ARGS)
473 {
474 return common_pl_call_handler(fcinfo, PLV8_DIALECT_LIVESCRIPT);
475 }
476
477 Datum
plv8_reset(PG_FUNCTION_ARGS)478 plv8_reset(PG_FUNCTION_ARGS)
479 {
480 Oid user_id = GetUserId();
481 unsigned long i;
482 HASH_SEQ_STATUS status;
483 plv8_proc_cache* cache;
484
485 for (i = 0; i < ContextVector.size(); i++)
486 {
487 if (ContextVector[i]->user_id == user_id)
488 {
489 plv8_context * context = ContextVector[i];
490 ContextVector.erase(ContextVector.begin() + i);
491 // need to search and reset all the user functions which were created in the old context
492 hash_seq_init(&status, plv8_proc_cache_hash);
493 cache = (plv8_proc_cache *) hash_seq_search(&status);
494 while (cache != nullptr) {
495 if (cache->user_id == user_id) {
496 if (cache->prosrc)
497 {
498 pfree(cache->prosrc);
499 cache->prosrc = NULL;
500 }
501 cache->function.Reset();
502 }
503 cache = (plv8_proc_cache *) hash_seq_search(&status);
504 }
505 context->context.Reset();
506 context->recv_templ.Reset();
507 context->compile_context.Reset();
508 context->plan_template.Reset();
509 context->cursor_template.Reset();
510 context->window_template.Reset();
511 delete context->array_buffer_allocator;
512 context->isolate->Dispose();
513 pfree(context);
514 break;
515 }
516 }
517 return (Datum) 0;
518 }
519
520 Datum
plv8_info(PG_FUNCTION_ARGS)521 plv8_info(PG_FUNCTION_ARGS)
522 {
523 unsigned long i;
524 unsigned long size = ContextVector.size();
525 char *infos[size];
526 size_t lengths[size];
527 size_t total_length = 3; // length of "[]\0"
528
529 for (i = 0; i < size; i++)
530 {
531 Isolate *isolate = ContextVector[i]->isolate;
532 Isolate::Scope scope(isolate);
533 HandleScope handle_scope(isolate);
534 Local<Context> context = ContextVector[i]->localContext();
535 Context::Scope context_scope(context);
536 JSONObject JSON;
537 Local<v8::Value> result;
538 Local<v8::Object> obj = v8::Object::New(isolate);
539
540 #if PG_VERSION_NUM >= 90500
541 char *username = GetUserNameFromId(ContextVector[i]->user_id, false);
542 #else
543 char *username = GetUserNameFromId(ContextVector[i]->user_id);
544 #endif
545 obj->Set(context, String::NewFromUtf8(isolate, "user").ToLocalChecked(), String::NewFromUtf8(isolate, username).ToLocalChecked());
546 GetMemoryInfo(obj);
547
548 result = JSON.Stringify(obj);
549 CString str(result);
550
551 infos[i] = pstrdup(str.str());
552 lengths[i] = strlen(infos[i]);
553 total_length += lengths[i] + 1; // add 1 byte for ','
554 }
555 char *out = (char *) palloc0(total_length);
556 out[0] = '[';
557 size_t current = 0;
558 for (i = 0; i < size; i++)
559 {
560 ++current;
561 strcpy(out + current, infos[i]);
562 pfree(infos[i]);
563 current += lengths[i];
564 out[current] = ',';
565 }
566 out[current] = ']';
567 return CStringGetTextDatum(out);
568 }
569
570 #if PG_VERSION_NUM >= 90000
571 static Datum
common_pl_inline_handler(PG_FUNCTION_ARGS,Dialect dialect)572 common_pl_inline_handler(PG_FUNCTION_ARGS, Dialect dialect) throw()
573 {
574 InlineCodeBlock *codeblock = (InlineCodeBlock *) DatumGetPointer(PG_GETARG_DATUM(0));
575
576 Assert(IsA(codeblock, InlineCodeBlock));
577
578 try
579 {
580 #ifdef ENABLE_DEBUGGER_SUPPORT
581 Locker lock;
582 #endif // ENABLE_DEBUGGER_SUPPORT
583 current_context = GetPlv8Context();
584 Isolate::Scope scope(current_context->isolate);
585 HandleScope handle_scope(current_context->isolate);
586 char *source_text = codeblock->source_text;
587 Persistent<Context> global_context;
588 global_context.Reset(current_context->isolate, current_context->context);
589
590 Local<Function> function = CompileFunction(current_context,
591 NULL, 0, NULL,
592 source_text, false, false, dialect);
593 plv8_exec_env *xenv = CreateExecEnv(function, current_context);
594 return CallFunction(fcinfo, xenv, 0, NULL, NULL);
595 }
596 catch (js_error& e) { e.rethrow(); }
597 catch (pg_error& e) { e.rethrow(); }
598
599 return (Datum) 0; // keep compiler quiet
600 }
601
602 Datum
plv8_inline_handler(PG_FUNCTION_ARGS)603 plv8_inline_handler(PG_FUNCTION_ARGS)
604 {
605 return common_pl_inline_handler(fcinfo, PLV8_DIALECT_NONE);
606 }
607
608 Datum
plcoffee_inline_handler(PG_FUNCTION_ARGS)609 plcoffee_inline_handler(PG_FUNCTION_ARGS)
610 {
611 return common_pl_inline_handler(fcinfo, PLV8_DIALECT_COFFEE);
612 }
613
614 Datum
plls_inline_handler(PG_FUNCTION_ARGS)615 plls_inline_handler(PG_FUNCTION_ARGS)
616 {
617 return common_pl_inline_handler(fcinfo, PLV8_DIALECT_LIVESCRIPT);
618 }
619 #endif
620
621 #ifdef EXECUTION_TIMEOUT
622 /*
623 * Breakout -- break out of a Call, with a thread
624 *
625 * This function breaks out of a javascript execution context.
626 */
627 #ifdef _MSC_VER // windows
628 DWORD WINAPI
Breakout(LPVOID lpParam)629 Breakout (LPVOID lpParam)
630 {
631 Isolate *isolate = (Isolate *) lpParam;
632 Sleep(plv8_execution_timeout * 1000);
633 isolate->TerminateExecution();
634
635 return 0;
636 }
637 #else // posix
638 void *
Breakout(void * d)639 Breakout (void *d)
640 {
641 auto *isolate = (Isolate *) d;
642 sleep(plv8_execution_timeout);
643 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
644 isolate->TerminateExecution();
645
646 return NULL;
647 }
648 #endif
649 #endif
650 void *int_handler = NULL;
651 void *term_handler = NULL;
652
653 /*
654 * signal handler
655 *
656 * This function kills the execution of the v8 process if a signal is called
657 */
658 void
signal_handler(int sig)659 signal_handler (int sig) {
660 elog(DEBUG1, "cancelling execution");
661 Isolate *isolate = Isolate::GetCurrent();
662 isolate->ThrowException(
663 String::NewFromUtf8(isolate, "Signal caught",
664 NewStringType::kNormal).ToLocalChecked());
665 }
666
667 /*
668 * DoCall -- Call a JS function with SPI support.
669 *
670 * This function could throw C++ exceptions, but must not throw PG exceptions.
671 */
672 static Local<v8::Value>
DoCall(Local<Context> ctx,Handle<Function> fn,Handle<Object> receiver,int nargs,Handle<v8::Value> args[],bool nonatomic)673 DoCall(Local<Context> ctx, Handle<Function> fn, Handle<Object> receiver,
674 int nargs, Handle<v8::Value> args[], bool nonatomic)
675 {
676 Isolate *isolate = ctx->GetIsolate();
677 TryCatch try_catch(isolate);
678 #ifdef EXECUTION_TIMEOUT
679 #ifdef _MSC_VER
680 HANDLE hThread;
681 DWORD dwThreadId;
682 #else
683 pthread_t breakout_thread;
684 void *thread_result;
685 #endif
686 #endif
687
688 #if PG_VERSION_NUM >= 110000
689 if (SPI_connect_ext(nonatomic ? SPI_OPT_NONATOMIC : 0) != SPI_OK_CONNECT)
690 throw js_error("could not connect to SPI manager");
691 #else
692 if (SPI_connect() != SPI_OK_CONNECT)
693 throw js_error("could not connect to SPI manager");
694 #endif
695
696 // set up the signal handlers
697 int_handler = (void *) signal(SIGINT, signal_handler);
698 term_handler = (void *) signal(SIGTERM, signal_handler);
699
700 #ifdef EXECUTION_TIMEOUT
701 #ifdef _MSC_VER // windows
702 hThread = CreateThread(NULL, 0, Breakout, isolate, 0, &dwThreadId);
703 #else
704 // set up the thread to break out the execution if needed
705 pthread_create(&breakout_thread, NULL, Breakout, isolate);
706 #endif
707 #endif
708
709 MaybeLocal<v8::Value> result = fn->Call(ctx, receiver, nargs, args);
710 int status = SPI_finish();
711
712 #ifdef EXECUTION_TIMEOUT
713 #ifdef _MSC_VER
714 BOOL cancel_state = TerminateThread(hThread, NULL);
715
716 if (cancel_state == 0) {
717 throw js_error("execution timeout exceeded");
718 }
719 #else
720 pthread_cancel(breakout_thread);
721 pthread_join(breakout_thread, &thread_result);
722
723 if (thread_result == NULL) {
724 throw js_error("execution timeout exceeded");
725 }
726 #endif
727 #endif
728
729 signal(SIGINT, (void (*)(int)) int_handler);
730 signal(SIGTERM, (void (*)(int)) term_handler);
731
732 if (result.IsEmpty()) {
733 if (isolate->IsExecutionTerminating())
734 throw js_error("Out of memory error");
735 throw js_error(try_catch);
736 }
737
738 if (status < 0)
739 throw js_error(FormatSPIStatus(status));
740
741 return result.ToLocalChecked();
742 }
743
744 static Datum
CallFunction(PG_FUNCTION_ARGS,plv8_exec_env * xenv,int nargs,plv8_type argtypes[],plv8_type * rettype)745 CallFunction(PG_FUNCTION_ARGS, plv8_exec_env *xenv,
746 int nargs, plv8_type argtypes[], plv8_type *rettype)
747 {
748 Local<Context> context = xenv->localContext();
749 Context::Scope context_scope(context);
750 Handle<v8::Value> args[FUNC_MAX_ARGS];
751 Handle<Object> plv8obj;
752
753 #if PG_VERSION_NUM >= 110000
754 bool nonatomic = fcinfo->context &&
755 IsA(fcinfo->context, CallContext) &&
756 !castNode(CallContext, fcinfo->context)->atomic;
757 #else
758 bool nonatomic = false;
759 #endif
760
761 WindowFunctionSupport support(context, fcinfo);
762
763 /*
764 * In window function case, we cannot see the argument datum
765 * in fcinfo. Instead, get them by WinGetFuncArgCurrent().
766 */
767 if (support.IsWindowCall())
768 {
769 WindowObject winobj = support.GetWindowObject();
770 for (int i = 0; i < nargs; i++)
771 {
772 bool isnull;
773 Datum arg = WinGetFuncArgCurrent(winobj, i, &isnull);
774 args[i] = ToValue(arg, isnull, &argtypes[i]);
775 }
776 }
777 else
778 {
779 for (int i = 0; i < nargs; i++) {
780 #if PG_VERSION_NUM < 120000
781 args[i] = ToValue(fcinfo->arg[i], fcinfo->argnull[i], &argtypes[i]);
782 #else
783 args[i] = ToValue(fcinfo->args[i].value, fcinfo->args[i].isnull, &argtypes[i]);
784 #endif
785 }
786 }
787
788 Local<Object> recv = Local<Object>::New(xenv->isolate, xenv->recv);
789 Local<Function> fn =
790 Local<Function>::Cast(recv->GetInternalField(0));
791 Local<v8::Value> result =
792 DoCall(context, fn, recv, nargs, args, nonatomic);
793
794 if (rettype)
795 return ToDatum(result, &fcinfo->isnull, rettype);
796 else
797 PG_RETURN_VOID();
798 }
799
800 static Tuplestorestate *
CreateTupleStore(PG_FUNCTION_ARGS,TupleDesc * tupdesc)801 CreateTupleStore(PG_FUNCTION_ARGS, TupleDesc *tupdesc)
802 {
803 Tuplestorestate *tupstore;
804
805 PG_TRY();
806 {
807 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
808 MemoryContext per_query_ctx;
809 MemoryContext oldcontext;
810 plv8_proc *proc = (plv8_proc *) fcinfo->flinfo->fn_extra;
811
812 /* check to see if caller supports us returning a tuplestore */
813 if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
814 ereport(ERROR,
815 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
816 errmsg("set-valued function called in context that cannot accept a set")));
817 if (!(rsinfo->allowedModes & SFRM_Materialize))
818 ereport(ERROR,
819 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
820 errmsg("materialize mode required, but it is not " \
821 "allowed in this context")));
822
823 if (!proc->functypclass)
824 proc->functypclass = get_call_result_type(fcinfo, NULL, NULL);
825
826 per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
827 oldcontext = MemoryContextSwitchTo(per_query_ctx);
828
829 tupstore = tuplestore_begin_heap(true, false, work_mem);
830 rsinfo->returnMode = SFRM_Materialize;
831 rsinfo->setResult = tupstore;
832 /* Build a tuple descriptor for our result type */
833 if (proc->rettype.typid == RECORDOID)
834 {
835 if (proc->functypclass != TYPEFUNC_COMPOSITE)
836 ereport(ERROR,
837 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
838 errmsg("function returning record called in context "
839 "that cannot accept type record")));
840 }
841 if (!rsinfo->setDesc)
842 {
843 *tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);
844 rsinfo->setDesc = *tupdesc;
845 }
846 else
847 *tupdesc = rsinfo->setDesc;
848
849 MemoryContextSwitchTo(oldcontext);
850 }
851 PG_CATCH();
852 {
853 throw pg_error();
854 }
855 PG_END_TRY();
856
857 return tupstore;
858 }
859
860 static Datum
CallSRFunction(PG_FUNCTION_ARGS,plv8_exec_env * xenv,int nargs,plv8_type argtypes[],plv8_type * rettype)861 CallSRFunction(PG_FUNCTION_ARGS, plv8_exec_env *xenv,
862 int nargs, plv8_type argtypes[], plv8_type *rettype)
863 {
864 plv8_proc *proc = (plv8_proc *) fcinfo->flinfo->fn_extra;
865 TupleDesc tupdesc;
866 Tuplestorestate *tupstore;
867
868 #if PG_VERSION_NUM >= 110000
869 bool nonatomic = fcinfo->context &&
870 IsA(fcinfo->context, CallContext) &&
871 !castNode(CallContext, fcinfo->context)->atomic;
872 #else
873 bool nonatomic = false;
874 #endif
875
876 tupstore = CreateTupleStore(fcinfo, &tupdesc);
877
878 Handle<Context> context = xenv->localContext();
879 Context::Scope context_scope(context);
880 Converter conv(tupdesc, proc->functypclass == TYPEFUNC_SCALAR);
881 Handle<v8::Value> args[FUNC_MAX_ARGS + 1];
882
883 /*
884 * In case this is nested via SPI, stash pre-registered converters
885 * for the previous SRF.
886 */
887 SRFSupport support(context, &conv, tupstore);
888
889 for (int i = 0; i < nargs; i++) {
890 #if PG_VERSION_NUM < 120000
891 args[i] = ToValue(fcinfo->arg[i], fcinfo->argnull[i], &argtypes[i]);
892 #else
893 args[i] = ToValue(fcinfo->args[i].value, fcinfo->args[i].isnull, &argtypes[i]);
894 #endif
895 }
896
897 Local<Object> recv = Local<Object>::New(xenv->isolate, xenv->recv);
898 Local<Function> fn =
899 Local<Function>::Cast(recv->GetInternalField(0));
900
901 Handle<v8::Value> result = DoCall(context, fn, recv, nargs, args, nonatomic);
902
903 if (result->IsUndefined())
904 {
905 // no additional values
906 }
907 else if (result->IsArray())
908 {
909 Handle<Array> array = Handle<Array>::Cast(result);
910 // return an array of records.
911 int length = array->Length();
912 for (int i = 0; i < length; i++)
913 conv.ToDatum(array->Get(context, i).ToLocalChecked(), tupstore);
914 }
915 else
916 {
917 // return a record or a scalar
918 conv.ToDatum(result, tupstore);
919 }
920
921 /* clean up and return the tuplestore */
922 tuplestore_donestoring(tupstore);
923
924 return (Datum) 0;
925 }
926
927 static Datum
CallTrigger(PG_FUNCTION_ARGS,plv8_exec_env * xenv)928 CallTrigger(PG_FUNCTION_ARGS, plv8_exec_env *xenv)
929 {
930 // trigger arguments are:
931 // 0: NEW
932 // 1: OLD
933 // 2: TG_NAME
934 // 3: TG_WHEN
935 // 4: TG_LEVEL
936 // 5: TG_OP
937 // 6: TG_RELID
938 // 7: TG_TABLE_NAME
939 // 8: TG_TABLE_SCHEMA
940 // 9: TG_ARGV
941 TriggerData *trig = (TriggerData *) fcinfo->context;
942 Relation rel = trig->tg_relation;
943 TriggerEvent event = trig->tg_event;
944 Handle<v8::Value> args[10];
945 Datum result = (Datum) 0;
946
947 #if PG_VERSION_NUM >= 110000
948 bool nonatomic = fcinfo->context &&
949 IsA(fcinfo->context, CallContext) &&
950 !castNode(CallContext, fcinfo->context)->atomic;
951 #else
952 bool nonatomic = false;
953 #endif
954
955 Handle<Context> context = xenv->localContext();
956 Context::Scope context_scope(context);
957
958 if (TRIGGER_FIRED_FOR_ROW(event))
959 {
960 TupleDesc tupdesc = RelationGetDescr(rel);
961 Converter conv(tupdesc);
962
963 if (TRIGGER_FIRED_BY_INSERT(event))
964 {
965 result = PointerGetDatum(trig->tg_trigtuple);
966 // NEW
967 args[0] = conv.ToValue(trig->tg_trigtuple);
968 // OLD
969 args[1] = Undefined(xenv->isolate);
970 }
971 else if (TRIGGER_FIRED_BY_DELETE(event))
972 {
973 result = PointerGetDatum(trig->tg_trigtuple);
974 // NEW
975 args[0] = Undefined(xenv->isolate);
976 // OLD
977 args[1] = conv.ToValue(trig->tg_trigtuple);
978 }
979 else if (TRIGGER_FIRED_BY_UPDATE(event))
980 {
981 result = PointerGetDatum(trig->tg_newtuple);
982 // NEW
983 args[0] = conv.ToValue(trig->tg_newtuple);
984 // OLD
985 args[1] = conv.ToValue(trig->tg_trigtuple);
986 }
987 }
988 else
989 {
990 args[0] = args[1] = Undefined(xenv->isolate);
991 }
992
993 // 2: TG_NAME
994 args[2] = ToString(trig->tg_trigger->tgname);
995
996 // 3: TG_WHEN
997 if (TRIGGER_FIRED_BEFORE(event))
998 args[3] = String::NewFromUtf8(xenv->isolate, "BEFORE").ToLocalChecked();
999 else
1000 args[3] = String::NewFromUtf8(xenv->isolate, "AFTER").ToLocalChecked();
1001
1002 // 4: TG_LEVEL
1003 if (TRIGGER_FIRED_FOR_ROW(event))
1004 args[4] = String::NewFromUtf8(xenv->isolate, "ROW").ToLocalChecked();
1005 else
1006 args[4] = String::NewFromUtf8(xenv->isolate, "STATEMENT").ToLocalChecked();
1007
1008 // 5: TG_OP
1009 if (TRIGGER_FIRED_BY_INSERT(event))
1010 args[5] = String::NewFromUtf8(xenv->isolate, "INSERT").ToLocalChecked();
1011 else if (TRIGGER_FIRED_BY_DELETE(event))
1012 args[5] = String::NewFromUtf8(xenv->isolate, "DELETE").ToLocalChecked();
1013 else if (TRIGGER_FIRED_BY_UPDATE(event))
1014 args[5] = String::NewFromUtf8(xenv->isolate, "UPDATE").ToLocalChecked();
1015 #ifdef TRIGGER_FIRED_BY_TRUNCATE
1016 else if (TRIGGER_FIRED_BY_TRUNCATE(event))
1017 args[5] = String::NewFromUtf8(xenv->isolate, "TRUNCATE").ToLocalChecked();
1018 #endif
1019 else
1020 args[5] = String::NewFromUtf8(xenv->isolate, "?").ToLocalChecked();
1021
1022 // 6: TG_RELID
1023 args[6] = Uint32::New(xenv->isolate, RelationGetRelid(rel));
1024
1025 // 7: TG_TABLE_NAME
1026 args[7] = ToString(RelationGetRelationName(rel));
1027
1028 // 8: TG_TABLE_SCHEMA
1029 args[8] = ToString(get_namespace_name(RelationGetNamespace(rel)));
1030
1031 // 9: TG_ARGV
1032 Handle<Array> tgargs = Array::New(xenv->isolate, trig->tg_trigger->tgnargs);
1033 for (int i = 0; i < trig->tg_trigger->tgnargs; i++)
1034 tgargs->Set(context, i, ToString(trig->tg_trigger->tgargs[i]));
1035 args[9] = tgargs;
1036
1037 TryCatch try_catch(xenv->isolate);
1038 Local<Object> recv = Local<Object>::New(xenv->isolate, xenv->recv);
1039 Local<Function> fn =
1040 Local<Function>::Cast(recv->GetInternalField(0));
1041 Handle<v8::Value> newtup =
1042 DoCall(context, fn, recv, lengthof(args), args, nonatomic);
1043
1044 if (newtup.IsEmpty())
1045 throw js_error(try_catch);
1046
1047 /*
1048 * If the function specifically returned null, return NULL to
1049 * tell executor to skip the operation. Otherwise, the function
1050 * result is the tuple to be returned.
1051 */
1052 if (newtup->IsNull() || !TRIGGER_FIRED_FOR_ROW(event))
1053 {
1054 result = PointerGetDatum(NULL);
1055 }
1056 else if (!newtup->IsUndefined())
1057 {
1058 TupleDesc tupdesc = RelationGetDescr(rel);
1059 Converter conv(tupdesc);
1060 HeapTupleHeader header;
1061
1062 header = DatumGetHeapTupleHeader(conv.ToDatum(newtup));
1063
1064 /* We know it's there; heap_form_tuple stores with this layout. */
1065 result = PointerGetDatum((char *) header - HEAPTUPLESIZE);
1066 }
1067
1068 return result;
1069 }
1070
1071 static Datum
common_pl_call_validator(PG_FUNCTION_ARGS,Dialect dialect)1072 common_pl_call_validator(PG_FUNCTION_ARGS, Dialect dialect) throw()
1073 {
1074 current_context = GetPlv8Context();
1075 Oid fn_oid = PG_GETARG_OID(0);
1076 HeapTuple tuple;
1077 Form_pg_proc proc;
1078 char functyptype;
1079 bool is_trigger = false;
1080 Isolate::Scope scope(current_context->isolate);
1081
1082 if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, fn_oid))
1083 PG_RETURN_VOID();
1084
1085 /* Get the new function's pg_proc entry */
1086 tuple = SearchSysCache(PROCOID, ObjectIdGetDatum(fn_oid), 0, 0, 0);
1087 if (!HeapTupleIsValid(tuple))
1088 elog(ERROR, "cache lookup failed for function %u", fn_oid);
1089 proc = (Form_pg_proc) GETSTRUCT(tuple);
1090
1091 functyptype = get_typtype(proc->prorettype);
1092
1093 /* Disallow pseudotype result */
1094 /* except for TRIGGER, RECORD, INTERNAL, VOID or polymorphic types */
1095 if (functyptype == TYPTYPE_PSEUDO)
1096 {
1097 #if PG_VERSION_NUM >= 130000
1098 if (proc->prorettype == TRIGGEROID)
1099 #else
1100 /* we assume OPAQUE with no arguments means a trigger */
1101 if (proc->prorettype == TRIGGEROID ||
1102 (proc->prorettype == OPAQUEOID && proc->pronargs == 0))
1103 #endif
1104 is_trigger = true;
1105 else if (proc->prorettype != RECORDOID &&
1106 proc->prorettype != VOIDOID &&
1107 proc->prorettype != INTERNALOID &&
1108 !IsPolymorphicType(proc->prorettype))
1109 ereport(ERROR,
1110 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1111 errmsg("PL/v8 functions cannot return type %s",
1112 format_type_be(proc->prorettype))));
1113 }
1114
1115 ReleaseSysCache(tuple);
1116
1117 try
1118 {
1119 #ifdef ENABLE_DEBUGGER_SUPPORT
1120 Locker lock;
1121 #endif // ENABLE_DEBUGGER_SUPPORT
1122 /* Don't use validator's fcinfo */
1123 plv8_proc *proc = Compile(fn_oid, NULL,
1124 true, is_trigger, dialect);
1125 (void) CreateExecEnv(proc->cache->function, current_context);
1126 /* the result of a validator is ignored */
1127 PG_RETURN_VOID();
1128 }
1129 catch (js_error& e) { e.rethrow(); }
1130 catch (pg_error& e) { e.rethrow(); }
1131
1132 return (Datum) 0; // keep compiler quiet
1133 }
1134
1135 Datum
plv8_call_validator(PG_FUNCTION_ARGS)1136 plv8_call_validator(PG_FUNCTION_ARGS)
1137 {
1138 return common_pl_call_validator(fcinfo, PLV8_DIALECT_NONE);
1139 }
1140
1141 Datum
plcoffee_call_validator(PG_FUNCTION_ARGS)1142 plcoffee_call_validator(PG_FUNCTION_ARGS)
1143 {
1144 return common_pl_call_validator(fcinfo, PLV8_DIALECT_COFFEE);
1145 }
1146
1147 Datum
plls_call_validator(PG_FUNCTION_ARGS)1148 plls_call_validator(PG_FUNCTION_ARGS)
1149 {
1150 return common_pl_call_validator(fcinfo, PLV8_DIALECT_LIVESCRIPT);
1151 }
1152
1153 static plv8_proc *
plv8_get_proc(Oid fn_oid,FunctionCallInfo fcinfo,bool validate,char *** argnames)1154 plv8_get_proc(Oid fn_oid, FunctionCallInfo fcinfo, bool validate, char ***argnames) throw()
1155 {
1156 HeapTuple procTup;
1157 plv8_proc_cache *cache;
1158 bool found;
1159 bool isnull;
1160 Datum prosrc;
1161 Oid *argtypes;
1162 char *argmodes;
1163 MemoryContext oldcontext;
1164
1165 procTup = SearchSysCache(PROCOID, ObjectIdGetDatum(fn_oid), 0, 0, 0);
1166 if (!HeapTupleIsValid(procTup))
1167 elog(ERROR, "cache lookup failed for function %u", fn_oid);
1168
1169 cache = (plv8_proc_cache *)
1170 hash_search(plv8_proc_cache_hash,&fn_oid, HASH_ENTER, &found);
1171
1172 if (found)
1173 {
1174 bool uptodate;
1175
1176 /*
1177 * We need to check user id and dispose it if it's different from
1178 * the previous cache user id, as the V8 function is associated
1179 * with the context where it was generated. In most cases,
1180 * we can expect this doesn't affect runtime performance.
1181 */
1182 uptodate = (!cache->function.IsEmpty() &&
1183 cache->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) &&
1184 ItemPointerEquals(&cache->fn_tid, &procTup->t_self) &&
1185 cache->user_id == GetUserId());
1186
1187 if (!uptodate)
1188 {
1189 if (cache->prosrc)
1190 {
1191 pfree(cache->prosrc);
1192 cache->prosrc = NULL;
1193 }
1194 cache->function.Reset();
1195 }
1196 else
1197 {
1198 ReleaseSysCache(procTup);
1199 }
1200 }
1201 else
1202 {
1203 new(&cache->function) Persistent<Function>();
1204 cache->prosrc = NULL;
1205 }
1206
1207 if (cache->function.IsEmpty())
1208 {
1209 Form_pg_proc procStruct;
1210
1211 procStruct = (Form_pg_proc) GETSTRUCT(procTup);
1212
1213 prosrc = SysCacheGetAttr(PROCOID, procTup, Anum_pg_proc_prosrc, &isnull);
1214 if (isnull)
1215 elog(ERROR, "null prosrc");
1216
1217 cache->retset = procStruct->proretset;
1218 cache->rettype = procStruct->prorettype;
1219
1220 strlcpy(cache->proname, NameStr(procStruct->proname), NAMEDATALEN);
1221 cache->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
1222 cache->fn_tid = procTup->t_self;
1223 cache->user_id = GetUserId();
1224
1225 int nargs = get_func_arg_info(procTup, &argtypes, argnames, &argmodes);
1226
1227 if (validate)
1228 {
1229 /*
1230 * Disallow non-polymorphic pseudotypes in arguments
1231 * (either IN or OUT). Internal type is used to declare
1232 * js functions for find_function().
1233 */
1234 for (int i = 0; i < nargs; i++)
1235 {
1236 if (get_typtype(argtypes[i]) == TYPTYPE_PSEUDO &&
1237 argtypes[i] != INTERNALOID &&
1238 !IsPolymorphicType(argtypes[i]))
1239 ereport(ERROR,
1240 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1241 errmsg("PL/v8 functions cannot accept type %s",
1242 format_type_be(argtypes[i]))));
1243 }
1244 }
1245
1246 oldcontext = MemoryContextSwitchTo(TopMemoryContext);
1247 cache->prosrc = TextDatumGetCString(prosrc);
1248 MemoryContextSwitchTo(oldcontext);
1249
1250 ReleaseSysCache(procTup);
1251
1252 int inargs = 0;
1253 for (int i = 0; i < nargs; i++)
1254 {
1255 Oid argtype = argtypes[i];
1256 char argmode = argmodes ? argmodes[i] : PROARGMODE_IN;
1257
1258 switch (argmode)
1259 {
1260 case PROARGMODE_IN:
1261 case PROARGMODE_INOUT:
1262 case PROARGMODE_VARIADIC:
1263 break;
1264 default:
1265 continue;
1266 }
1267
1268 if (*argnames)
1269 (*argnames)[inargs] = (*argnames)[i];
1270 cache->argtypes[inargs] = argtype;
1271 inargs++;
1272 }
1273 cache->nargs = inargs;
1274 }
1275
1276 MemoryContext mcxt = CurrentMemoryContext;
1277 if (fcinfo)
1278 mcxt = fcinfo->flinfo->fn_mcxt;
1279
1280 plv8_proc *proc = (plv8_proc *) MemoryContextAllocZero(mcxt,
1281 offsetof(plv8_proc, argtypes) + sizeof(plv8_type) * cache->nargs);
1282
1283 proc->cache = cache;
1284 for (int i = 0; i < cache->nargs; i++)
1285 {
1286 Oid argtype = cache->argtypes[i];
1287 /* Resolve polymorphic types, if this is an actual call context. */
1288 if (fcinfo && IsPolymorphicType(argtype))
1289 argtype = get_fn_expr_argtype(fcinfo->flinfo, i);
1290 plv8_fill_type(&proc->argtypes[i], argtype, mcxt);
1291 }
1292
1293 Oid rettype = cache->rettype;
1294 /* Resolve polymorphic return type if this is an actual call context. */
1295 if (fcinfo && IsPolymorphicType(rettype))
1296 rettype = get_fn_expr_rettype(fcinfo->flinfo);
1297 plv8_fill_type(&proc->rettype, rettype, mcxt);
1298
1299 return proc;
1300 }
1301
1302 static plv8_exec_env *
CreateExecEnv(Persistent<Function> & function,plv8_context * context)1303 CreateExecEnv(Persistent<Function>& function, plv8_context *context)
1304 {
1305 plv8_exec_env *xenv;
1306 HandleScope handle_scope(context->isolate);
1307
1308 PG_TRY();
1309 {
1310 xenv = plv8_new_exec_env(context->isolate);
1311 }
1312 PG_CATCH();
1313 {
1314 throw pg_error();
1315 }
1316 PG_END_TRY();
1317
1318 xenv->context.Reset(context->isolate, context->context);
1319 Local<Context> ctx = xenv->localContext();
1320 Context::Scope scope(ctx);
1321
1322 Local<ObjectTemplate> templ = Local<ObjectTemplate>::New(context->isolate, context->recv_templ);
1323 Local<Object> obj = templ->NewInstance(ctx).ToLocalChecked();
1324 Local<Function> f = Local<Function>::New(context->isolate, function);
1325 obj->SetInternalField(0, f);
1326 xenv->recv.Reset(context->isolate, obj);
1327
1328
1329 return xenv;
1330 }
1331
1332 static plv8_exec_env *
CreateExecEnv(Handle<Function> function,plv8_context * context)1333 CreateExecEnv(Handle<Function> function, plv8_context *context)
1334 {
1335 Isolate::Scope iscope(context->isolate);
1336 plv8_exec_env *xenv;
1337 HandleScope handle_scope(context->isolate);
1338
1339 PG_TRY();
1340 {
1341 xenv = plv8_new_exec_env(context->isolate);
1342 }
1343 PG_CATCH();
1344 {
1345 throw pg_error();
1346 }
1347 PG_END_TRY();
1348
1349 xenv->context.Reset(context->isolate, context->context);
1350 Local<Context> ctx = xenv->localContext();
1351 Context::Scope scope(ctx);
1352
1353 Local<ObjectTemplate> templ = Local<ObjectTemplate>::New(context->isolate, context->recv_templ);
1354 Local<Object> obj = templ->NewInstance(ctx).ToLocalChecked();
1355 Local<Function> f = Local<Function>::New(context->isolate, function);
1356 obj->SetInternalField(0, f);
1357 xenv->recv.Reset(context->isolate, obj);
1358
1359
1360 return xenv;
1361 }
1362
1363 /* Source transformation from a dialect (coffee or ls) to js */
1364 static char *
CompileDialect(const char * src,Dialect dialect,plv8_context * global_context)1365 CompileDialect(const char *src, Dialect dialect, plv8_context *global_context)
1366 {
1367 Isolate *isolate = Isolate::GetCurrent();
1368 HandleScope handle_scope(isolate);
1369 Local<Context> ctx = Local<Context>::New(isolate, global_context->compile_context);
1370 Context::Scope context_scope(ctx);
1371 TryCatch try_catch(isolate);
1372 Local<String> key;
1373 char *cresult;
1374 const char *dialect_binary_data;
1375
1376 switch (dialect)
1377 {
1378 case PLV8_DIALECT_COFFEE:
1379 if (coffee_script_binary_data[0] == '\0')
1380 throw js_error("CoffeeScript is not enabled");
1381 key = String::NewFromUtf8(isolate, "CoffeeScript").ToLocalChecked();
1382 dialect_binary_data = (const char *) coffee_script_binary_data;
1383 break;
1384 case PLV8_DIALECT_LIVESCRIPT:
1385 if (livescript_binary_data[0] == '\0')
1386 throw js_error("LiveScript is not enabled");
1387 key = String::NewFromUtf8(isolate, "LiveScript").ToLocalChecked();
1388 dialect_binary_data = (const char *) livescript_binary_data;
1389 break;
1390 default:
1391 throw js_error("Unknown Dialect");
1392 }
1393
1394 if (ctx->Global()->Get(ctx, key).ToLocalChecked()->IsUndefined())
1395 {
1396 HandleScope handle_scope(isolate);
1397 v8::ScriptOrigin origin(key);
1398 v8::Local<v8::Script> script;
1399 if (!Script::Compile(isolate->GetCurrentContext(), ToString(dialect_binary_data), &origin).ToLocal(&script))
1400 throw js_error(try_catch);
1401 if (script.IsEmpty())
1402 throw js_error(try_catch);
1403 v8::Local<v8::Value> result;
1404 if (!script->Run(isolate->GetCurrentContext()).ToLocal(&result))
1405 throw js_error(try_catch);
1406 if (result.IsEmpty()) {
1407 if (isolate->IsExecutionTerminating())
1408 throw js_error("Script is out of memory");
1409 throw js_error(try_catch);
1410 }
1411 }
1412
1413 Local<Object> compiler = Local<Object>::Cast(ctx->Global()->Get(ctx, key).ToLocalChecked());
1414 Local<Function> func = Local<Function>::Cast(
1415 compiler->Get(ctx, String::NewFromUtf8(isolate, "compile").ToLocalChecked()).ToLocalChecked());
1416 const int nargs = 1;
1417 Handle<v8::Value> args[nargs];
1418
1419 args[0] = ToString(src);
1420 MaybeLocal<v8::Value> value = func->Call(ctx, compiler, nargs, args);
1421
1422 if (value.IsEmpty()) {
1423 if (isolate->IsExecutionTerminating())
1424 throw js_error("Out of memory error");
1425 throw js_error(try_catch);
1426 }
1427 CString result(value.ToLocalChecked());
1428
1429 PG_TRY();
1430 {
1431 MemoryContext oldcontext = MemoryContextSwitchTo(TopMemoryContext);
1432 cresult = pstrdup(result.str());
1433 MemoryContextSwitchTo(oldcontext);
1434 }
1435 PG_CATCH();
1436 {
1437 throw pg_error();
1438 }
1439 PG_END_TRY();
1440
1441 return cresult;
1442 }
1443
1444 /*
1445 * fcinfo should be passed if this is an actual function call context, where
1446 * we can resolve polymorphic types and use function's memory context.
1447 */
1448 static plv8_proc *
Compile(Oid fn_oid,FunctionCallInfo fcinfo,bool validate,bool is_trigger,Dialect dialect)1449 Compile(Oid fn_oid, FunctionCallInfo fcinfo, bool validate, bool is_trigger,
1450 Dialect dialect)
1451 {
1452 plv8_proc *proc;
1453 char **argnames;
1454
1455 PG_TRY();
1456 {
1457 proc = plv8_get_proc(fn_oid, fcinfo, validate, &argnames);
1458 }
1459 PG_CATCH();
1460 {
1461 throw pg_error();
1462 }
1463 PG_END_TRY();
1464
1465 plv8_proc_cache *cache = proc->cache;
1466
1467 if (cache->function.IsEmpty())
1468 {
1469 /*
1470 * We need to create global context before entering CompileFunction
1471 * because GetPlv8Context could call startup procedure, which
1472 * could be this cache->function itself. In this scenario,
1473 * Compile is called recursively and plv8_get_proc tries to refresh
1474 * cache because cache->function is still not yet ready at this
1475 * point. Then some pointers of cache will become stale by pfree
1476 * and CompileFunction ends up compiling freed function source.
1477 */
1478 current_context = GetPlv8Context();
1479 Isolate::Scope scope(current_context->isolate);
1480 HandleScope handle_scope(current_context->isolate);
1481 Persistent<Context> global_context;
1482 global_context.Reset(current_context->isolate, current_context->context);
1483 cache->function.Reset(current_context->isolate, CompileFunction(
1484 current_context,
1485 cache->proname,
1486 cache->nargs,
1487 (const char **) argnames,
1488 cache->prosrc,
1489 is_trigger,
1490 cache->retset,
1491 dialect));
1492 }
1493
1494 return proc;
1495 }
1496
1497 static Local<Function>
CompileFunction(plv8_context * global_context,const char * proname,int proarglen,const char * proargs[],const char * prosrc,bool is_trigger,bool retset,Dialect dialect)1498 CompileFunction(
1499 plv8_context *global_context,
1500 const char *proname,
1501 int proarglen,
1502 const char *proargs[],
1503 const char *prosrc,
1504 bool is_trigger,
1505 bool retset,
1506 Dialect dialect)
1507 {
1508 Isolate *isolate = Isolate::GetCurrent();
1509 EscapableHandleScope handle_scope(isolate);
1510 StringInfoData src;
1511
1512 initStringInfo(&src);
1513
1514 if (dialect != PLV8_DIALECT_NONE)
1515 prosrc = CompileDialect(prosrc, dialect, global_context);
1516 /*
1517 * (function (<arg1, ...>){
1518 * <prosrc>
1519 * })
1520 */
1521 appendStringInfo(&src, "(function (");
1522 if (is_trigger)
1523 {
1524 if (proarglen != 0)
1525 throw js_error("trigger function cannot have arguments");
1526 // trigger function has special arguments.
1527 appendStringInfo(&src,
1528 "NEW, OLD, TG_NAME, TG_WHEN, TG_LEVEL, TG_OP, "
1529 "TG_RELID, TG_TABLE_NAME, TG_TABLE_SCHEMA, TG_ARGV");
1530 }
1531 else
1532 {
1533 for (int i = 0; i < proarglen; i++)
1534 {
1535 if (i > 0)
1536 appendStringInfoChar(&src, ',');
1537 if (proargs && proargs[i])
1538 appendStringInfoString(&src, proargs[i]);
1539 else
1540 appendStringInfo(&src, "$%d", i + 1); // unnamed argument to $N
1541 }
1542 }
1543 if (dialect)
1544 appendStringInfo(&src, "){\nreturn %s\n})", prosrc);
1545 else
1546 appendStringInfo(&src, "){\n%s\n})", prosrc);
1547
1548 Handle<v8::Value> name;
1549 if (proname)
1550 name = ToString(proname);
1551 else
1552 name = Undefined(isolate);
1553 Local<String> source = ToString(src.data, src.len);
1554 pfree(src.data);
1555
1556 Local<Context> context = Local<Context>::New(isolate, global_context->context);
1557 Context::Scope context_scope(context);
1558 TryCatch try_catch(isolate);
1559 v8::ScriptOrigin origin(name);
1560 v8::Local<v8::Script> script;
1561 if (!Script::Compile(isolate->GetCurrentContext(), source, &origin).ToLocal(&script))
1562 throw js_error(try_catch);
1563
1564 if (script.IsEmpty())
1565 throw js_error(try_catch);
1566
1567 v8::Local<v8::Value> result;
1568 if (!script->Run(isolate->GetCurrentContext()).ToLocal(&result))
1569 throw js_error(try_catch);
1570 if (result.IsEmpty()) {
1571 if (isolate->IsExecutionTerminating())
1572 throw js_error("Script is out of memory");
1573 throw js_error(try_catch);
1574 }
1575
1576 return handle_scope.Escape(Local<Function>::Cast(result));
1577 }
1578
1579 Local<Function>
find_js_function(Oid fn_oid)1580 find_js_function(Oid fn_oid)
1581 {
1582 HeapTuple tuple;
1583 Form_pg_proc proc;
1584 Oid prolang;
1585 NameData langnames[] = { {"plv8"}, {"plcoffee"}, {"plls"} };
1586 int langno;
1587 int langlen = sizeof(langnames) / sizeof(NameData);
1588 Local<Function> func;
1589 Isolate *isolate = Isolate::GetCurrent();
1590
1591 tuple = SearchSysCache(PROCOID, ObjectIdGetDatum(fn_oid), 0, 0, 0);
1592 if (!HeapTupleIsValid(tuple))
1593 elog(ERROR, "cache lookup failed for function %u", fn_oid);
1594 proc = (Form_pg_proc) GETSTRUCT(tuple);
1595 prolang = proc->prolang;
1596 ReleaseSysCache(tuple);
1597
1598 /* Should not happen? */
1599 if (!OidIsValid(prolang))
1600 return func;
1601
1602 /* See if the function language is a compatible one */
1603 for (langno = 0; langno < langlen; langno++)
1604 {
1605 tuple = SearchSysCache(LANGNAME, NameGetDatum(&langnames[langno]), 0, 0, 0);
1606 if (HeapTupleIsValid(tuple))
1607 {
1608 #if PG_VERSION_NUM < 120000
1609 Oid langtupoid = HeapTupleGetOid(tuple);
1610 #else
1611 Form_pg_database datForm = (Form_pg_database) GETSTRUCT(tuple);
1612 Oid langtupoid = datForm->oid;
1613 #endif
1614 ReleaseSysCache(tuple);
1615 if (langtupoid == prolang)
1616 break;
1617 }
1618 }
1619
1620 /* Not found or non-JS function */
1621 if (langno >= langlen)
1622 return func;
1623
1624 try
1625 {
1626 plv8_proc *proc = Compile(fn_oid, NULL,
1627 true, false,
1628 (Dialect) (PLV8_DIALECT_NONE + langno));
1629
1630 TryCatch try_catch(isolate);
1631
1632 func = Local<Function>::New(isolate, proc->cache->function);
1633 }
1634 catch (js_error& e) { e.rethrow(); }
1635 catch (pg_error& e) { e.rethrow(); }
1636
1637 return func;
1638 }
1639
1640 /*
1641 * NOTICE: the returned buffer could be an internal static buffer.
1642 */
1643 const char *
FormatSPIStatus(int status)1644 FormatSPIStatus(int status) throw()
1645 {
1646 static char private_buf[1024];
1647
1648 if (status > 0)
1649 return "OK";
1650
1651 switch (status)
1652 {
1653 case SPI_ERROR_CONNECT:
1654 return "SPI_ERROR_CONNECT";
1655 case SPI_ERROR_COPY:
1656 return "SPI_ERROR_COPY";
1657 case SPI_ERROR_OPUNKNOWN:
1658 return "SPI_ERROR_OPUNKNOWN";
1659 case SPI_ERROR_UNCONNECTED:
1660 case SPI_ERROR_TRANSACTION:
1661 return "current transaction is aborted, "
1662 "commands ignored until end of transaction block";
1663 case SPI_ERROR_CURSOR:
1664 return "SPI_ERROR_CURSOR";
1665 case SPI_ERROR_ARGUMENT:
1666 return "SPI_ERROR_ARGUMENT";
1667 case SPI_ERROR_PARAM:
1668 return "SPI_ERROR_PARAM";
1669 case SPI_ERROR_NOATTRIBUTE:
1670 return "SPI_ERROR_NOATTRIBUTE";
1671 case SPI_ERROR_NOOUTFUNC:
1672 return "SPI_ERROR_NOOUTFUNC";
1673 case SPI_ERROR_TYPUNKNOWN:
1674 return "SPI_ERROR_TYPUNKNOWN";
1675 default:
1676 snprintf(private_buf, sizeof(private_buf),
1677 "SPI_ERROR: %d", status);
1678 return private_buf;
1679 }
1680 }
1681
1682 static text *
charToText(char * string)1683 charToText(char *string)
1684 {
1685 int len = strlen(string);
1686 text *result = (text *) palloc(len + 1 + VARHDRSZ);
1687
1688 SET_VARSIZE(result, len + VARHDRSZ);
1689 memcpy(VARDATA(result), string, len + 1);
1690
1691 return result;
1692 }
1693
1694 static plv8_context*
GetPlv8Context()1695 GetPlv8Context() {
1696 Oid user_id = GetUserId();
1697 unsigned int i;
1698 plv8_context *my_context = nullptr;
1699
1700 for (i = 0; i < ContextVector.size(); i++)
1701 {
1702 if (ContextVector[i]->user_id == user_id)
1703 {
1704 my_context = ContextVector[i];
1705 break;
1706 }
1707 }
1708 if (!my_context)
1709 {
1710 my_context = (plv8_context *) MemoryContextAlloc(TopMemoryContext,
1711 sizeof(plv8_context));
1712 CreateIsolate(my_context);
1713 Isolate *isolate = my_context->isolate;
1714 Isolate::Scope scope(isolate);
1715 HandleScope handle_scope(isolate);
1716
1717 Local<ObjectTemplate> global = Local<ObjectTemplate>::New(isolate, GetGlobalObjectTemplate(isolate));
1718
1719 new(&my_context->context) Persistent<Context>();
1720 my_context->context.Reset(isolate, Context::New(isolate, NULL, global));
1721 my_context->user_id = user_id;
1722
1723 new(&my_context->recv_templ) Persistent<ObjectTemplate>();
1724 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
1725 templ->SetInternalFieldCount(1);
1726 my_context->recv_templ.Reset(isolate, templ);
1727
1728 new(&my_context->compile_context) Persistent<Context>();
1729 Local<Context> ctx = Context::New(isolate, (ExtensionConfiguration*)NULL);
1730 my_context->compile_context.Reset(isolate, ctx);
1731
1732 new(&my_context->plan_template) Persistent<ObjectTemplate>();
1733 Local<FunctionTemplate> base = FunctionTemplate::New(isolate);
1734 base->SetClassName(String::NewFromUtf8(isolate, "PreparedPlan").ToLocalChecked());
1735 templ = base->InstanceTemplate();
1736 SetupPrepFunctions(templ);
1737 my_context->plan_template.Reset(isolate, templ);
1738
1739 new(&my_context->cursor_template) Persistent<ObjectTemplate>();
1740 base = FunctionTemplate::New(isolate);
1741 base->SetClassName(String::NewFromUtf8(isolate, "Cursor").ToLocalChecked());
1742 templ = base->InstanceTemplate();
1743 SetupCursorFunctions(templ);
1744 my_context->cursor_template.Reset(isolate, templ);
1745
1746 new(&my_context->window_template) Persistent<ObjectTemplate>();
1747 base = FunctionTemplate::New(isolate);
1748 base->SetClassName(String::NewFromUtf8(isolate, "WindowObject").ToLocalChecked());
1749 templ = base->InstanceTemplate();
1750 SetupWindowFunctions(templ);
1751 my_context->window_template.Reset(isolate, templ);
1752 /*
1753 * Need to register it before running any code, as the code
1754 * recursively may want to the global context.
1755 */
1756 ContextVector.push_back(my_context);
1757
1758 /*
1759 * Run the start up procedure if configured.
1760 */
1761 if (plv8_start_proc != NULL)
1762 {
1763 Local<Function> func;
1764
1765 HandleScope handle_scope(isolate);
1766 Local<Context> context = my_context->localContext();
1767 Context::Scope context_scope(context);
1768 TryCatch try_catch(isolate);
1769 MemoryContext ctx = CurrentMemoryContext;
1770 text *arg;
1771 #if PG_VERSION_NUM < 120000
1772 FunctionCallInfoData fake_fcinfo;
1773 #else
1774 // Stack-allocate FunctionCallInfoBaseData with
1775 // space for 2 arguments:
1776 LOCAL_FCINFO(fake_fcinfo, 2);
1777 #endif
1778 FmgrInfo flinfo;
1779
1780 char perm[16];
1781 strcpy(perm, "EXECUTE");
1782 arg = charToText(perm);
1783
1784 PG_TRY();
1785 {
1786 Oid funcoid = DatumGetObjectId(DirectFunctionCall1(regprocin, CStringGetDatum(plv8_start_proc)));
1787 #if PG_VERSION_NUM < 120000
1788 MemSet(&fake_fcinfo, 0, sizeof(fake_fcinfo));
1789 MemSet(&flinfo, 0, sizeof(flinfo));
1790 fake_fcinfo.flinfo = &flinfo;
1791 flinfo.fn_oid = InvalidOid;
1792 flinfo.fn_mcxt = CurrentMemoryContext;
1793 fake_fcinfo.nargs = 2;
1794 fake_fcinfo.arg[0] = ObjectIdGetDatum(funcoid);
1795 fake_fcinfo.arg[1] = CStringGetDatum(arg);
1796 Datum ret = has_function_privilege_id(&fake_fcinfo);
1797 #else
1798 MemSet(&flinfo, 0, sizeof(flinfo));
1799 fake_fcinfo->flinfo = &flinfo;
1800 flinfo.fn_oid = InvalidOid;
1801 flinfo.fn_mcxt = CurrentMemoryContext;
1802 fake_fcinfo->nargs = 2;
1803 fake_fcinfo->args[0].value = ObjectIdGetDatum(funcoid);
1804 fake_fcinfo->args[1].value = CStringGetDatum(arg);
1805 Datum ret = has_function_privilege_id(fake_fcinfo);
1806 #endif
1807
1808 if (ret == 0) {
1809 elog(WARNING, "failed to find js function %s", plv8_start_proc);
1810 } else {
1811 if (DatumGetBool(ret)) {
1812 func = find_js_function(funcoid);
1813 } else {
1814 elog(WARNING, "no permission to execute js function %s", plv8_start_proc);
1815 }
1816 }
1817 }
1818 PG_CATCH();
1819 {
1820 ErrorData *edata;
1821
1822 MemoryContextSwitchTo(ctx);
1823 edata = CopyErrorData();
1824 elog(WARNING, "failed to find js function %s", edata->message);
1825 FlushErrorState();
1826 FreeErrorData(edata);
1827 }
1828 PG_END_TRY();
1829
1830 pfree(arg);
1831
1832 if (!func.IsEmpty())
1833 {
1834 Handle<v8::Value> result =
1835 DoCall(context, func, my_context->localContext()->Global(), 0, NULL, false);
1836 if (result.IsEmpty())
1837 throw js_error(try_catch);
1838 }
1839 }
1840
1841 #ifdef ENABLE_DEBUGGER_SUPPORT
1842 debug_message_context = v8::Persistent<v8::Context>::New(global_context);
1843
1844 v8::Locker locker;
1845
1846 v8::Debug::SetDebugMessageDispatchHandler(DispatchDebugMessages, true);
1847
1848 v8::Debug::EnableAgent("plv8", plv8_debugger_port, false);
1849 #endif // ENABLE_DEBUGGER_SUPPORT
1850 }
1851 return my_context;
1852 }
1853
1854 static Local<ObjectTemplate>
GetGlobalObjectTemplate(Isolate * isolate)1855 GetGlobalObjectTemplate(Isolate *isolate)
1856 {
1857 Persistent<ObjectTemplate> global;
1858
1859
1860 if (global.IsEmpty())
1861 {
1862 HandleScope handle_scope(isolate);
1863 Local<Context> context = isolate->GetCurrentContext();
1864
1865 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
1866 // ERROR levels for elog
1867 templ->Set(String::NewFromUtf8(isolate, "DEBUG5").ToLocalChecked(), Int32::New(isolate, DEBUG5));
1868 templ->Set(String::NewFromUtf8(isolate, "DEBUG4").ToLocalChecked(), Int32::New(isolate, DEBUG4));
1869 templ->Set(String::NewFromUtf8(isolate, "DEBUG3").ToLocalChecked(), Int32::New(isolate, DEBUG3));
1870 templ->Set(String::NewFromUtf8(isolate, "DEBUG2").ToLocalChecked(), Int32::New(isolate, DEBUG2));
1871 templ->Set(String::NewFromUtf8(isolate, "DEBUG1").ToLocalChecked(), Int32::New(isolate, DEBUG1));
1872 templ->Set(String::NewFromUtf8(isolate, "DEBUG").ToLocalChecked(), Int32::New(isolate, DEBUG5));
1873 templ->Set(String::NewFromUtf8(isolate, "LOG").ToLocalChecked(), Int32::New(isolate, LOG));
1874 templ->Set(String::NewFromUtf8(isolate, "INFO").ToLocalChecked(), Int32::New(isolate, INFO));
1875 templ->Set(String::NewFromUtf8(isolate, "NOTICE").ToLocalChecked(), Int32::New(isolate, NOTICE));
1876 templ->Set(String::NewFromUtf8(isolate, "WARNING").ToLocalChecked(), Int32::New(isolate, WARNING));
1877 templ->Set(String::NewFromUtf8(isolate, "ERROR").ToLocalChecked(), Int32::New(isolate, ERROR));
1878 global.Reset(isolate, templ);
1879
1880 Handle<ObjectTemplate> plv8 = ObjectTemplate::New(isolate);
1881
1882 SetupPlv8Functions(plv8);
1883 plv8->Set(isolate, "version", String::NewFromUtf8(isolate, PLV8_VERSION).ToLocalChecked());
1884 plv8->Set(isolate, "v8_version", String::NewFromUtf8(isolate, V8_VERSION_STRING).ToLocalChecked());
1885
1886 templ->Set(isolate, "plv8", plv8);
1887 }
1888 return Local<ObjectTemplate>::New(isolate, global);
1889 }
1890
1891 /*
1892 * Accessor to plv8_type stored in fcinfo.
1893 */
1894 plv8_type *
get_plv8_type(PG_FUNCTION_ARGS,int argno)1895 get_plv8_type(PG_FUNCTION_ARGS, int argno)
1896 {
1897 plv8_proc *proc = (plv8_proc *) fcinfo->flinfo->fn_extra;
1898 return &proc->argtypes[argno];
1899 }
1900
Converter(TupleDesc tupdesc)1901 Converter::Converter(TupleDesc tupdesc) :
1902 m_tupdesc(tupdesc),
1903 m_colnames(tupdesc->natts),
1904 m_coltypes(tupdesc->natts),
1905 m_is_scalar(false),
1906 m_memcontext(NULL)
1907 {
1908 Init();
1909 }
1910
Converter(TupleDesc tupdesc,bool is_scalar)1911 Converter::Converter(TupleDesc tupdesc, bool is_scalar) :
1912 m_tupdesc(tupdesc),
1913 m_colnames(tupdesc->natts),
1914 m_coltypes(tupdesc->natts),
1915 m_is_scalar(is_scalar),
1916 m_memcontext(NULL)
1917 {
1918 Init();
1919 }
1920
~Converter()1921 Converter::~Converter()
1922 {
1923 if (m_memcontext != NULL)
1924 {
1925 MemoryContext ctx = CurrentMemoryContext;
1926
1927 PG_TRY();
1928 {
1929 MemoryContextDelete(m_memcontext);
1930 }
1931 PG_CATCH();
1932 {
1933 ErrorData *edata;
1934
1935 MemoryContextSwitchTo(ctx);
1936 // don't throw out from deconstructor
1937 edata = CopyErrorData();
1938 elog(WARNING, "~Converter: %s", edata->message);
1939 FlushErrorState();
1940 FreeErrorData(edata);
1941 }
1942 PG_END_TRY();
1943 m_memcontext = NULL;
1944 }
1945 }
1946
1947 void
Init()1948 Converter::Init()
1949 {
1950 for (int c = 0; c < m_tupdesc->natts; c++)
1951 {
1952 if (TupleDescAttr(m_tupdesc, c)->attisdropped)
1953 continue;
1954
1955 m_colnames[c] = ToString(NameStr(TupleDescAttr(m_tupdesc, c)->attname));
1956
1957 PG_TRY();
1958 {
1959 if (m_memcontext == NULL)
1960 #if PG_VERSION_NUM < 110000
1961 m_memcontext = AllocSetContextCreate(
1962 CurrentMemoryContext,
1963 "ConverterContext",
1964 ALLOCSET_SMALL_MINSIZE,
1965 ALLOCSET_SMALL_INITSIZE,
1966 ALLOCSET_SMALL_MAXSIZE);
1967 plv8_fill_type(&m_coltypes[c],
1968 m_tupdesc->attrs[c]->atttypid,
1969 m_memcontext);
1970 #else
1971 m_memcontext = AllocSetContextCreate(
1972 CurrentMemoryContext,
1973 "ConverterContext",
1974 ALLOCSET_DEFAULT_SIZES);
1975 plv8_fill_type(&m_coltypes[c],
1976 m_tupdesc->attrs[c].atttypid,
1977 m_memcontext);
1978 #endif
1979 }
1980 PG_CATCH();
1981 {
1982 throw pg_error();
1983 }
1984 PG_END_TRY();
1985 }
1986 }
1987
1988 // TODO: use prototype instead of per tuple fields to reduce
1989 // memory consumption.
1990 Local<Object>
ToValue(HeapTuple tuple)1991 Converter::ToValue(HeapTuple tuple)
1992 {
1993 Isolate *isolate = Isolate::GetCurrent();
1994 Local<Object> obj = Object::New(isolate);
1995 Local<Context> context = isolate->GetCurrentContext();
1996
1997 for (int c = 0; c < m_tupdesc->natts; c++)
1998 {
1999 Datum datum;
2000 bool isnull;
2001
2002 if (TupleDescAttr(m_tupdesc, c)->attisdropped)
2003 continue;
2004
2005 #if PG_VERSION_NUM >= 90000
2006 datum = heap_getattr(tuple, c + 1, m_tupdesc, &isnull);
2007 #else
2008 /*
2009 * Due to the difference between C and C++ rules,
2010 * we cannot call heap_getattr from < 9.0 unfortunately.
2011 */
2012 datum = nocachegetattr(tuple, c + 1, m_tupdesc, &isnull);
2013 #endif
2014
2015 obj->Set(context, m_colnames[c], ::ToValue(datum, isnull, &m_coltypes[c]));
2016 }
2017
2018 return obj;
2019 }
2020
2021 Datum
ToDatum(Handle<v8::Value> value,Tuplestorestate * tupstore)2022 Converter::ToDatum(Handle<v8::Value> value, Tuplestorestate *tupstore)
2023 {
2024 Isolate *isolate = Isolate::GetCurrent();
2025 Local<Context> context = isolate->GetCurrentContext();
2026
2027 Datum result;
2028 TryCatch try_catch(isolate);
2029 Handle<Object> obj;
2030
2031 if (!m_is_scalar)
2032 {
2033 if (!value->IsObject())
2034 throw js_error("argument must be an object");
2035 obj = Handle<Object>::Cast(value);
2036 if (obj.IsEmpty())
2037 throw js_error(try_catch);
2038 }
2039
2040 /*
2041 * Use vector<char> instead of vector<bool> because <bool> version is
2042 * s specialized and different from bool[].
2043 */
2044 Datum *values = (Datum *) palloc(sizeof(Datum) * m_tupdesc->natts);
2045 bool *nulls = (bool *) palloc(sizeof(bool) * m_tupdesc->natts);
2046
2047 if (!m_is_scalar)
2048 {
2049 Handle<Array> names = obj->GetPropertyNames(isolate->GetCurrentContext()).ToLocalChecked();
2050
2051 for (int c = 0; c < m_tupdesc->natts; c++)
2052 {
2053 if (TupleDescAttr(m_tupdesc, c)->attisdropped)
2054 continue;
2055
2056 bool found = false;
2057 CString colname(m_colnames[c]);
2058 for (int d = 0; d < m_tupdesc->natts; d++)
2059 {
2060 CString fname(names->Get(context, d).ToLocalChecked());
2061 if (strcmp(colname, fname) == 0)
2062 {
2063 found = true;
2064 break;
2065 }
2066 }
2067 if (!found)
2068 throw js_error("field name / property name mismatch");
2069 }
2070 }
2071
2072 for (int c = 0; c < m_tupdesc->natts; c++)
2073 {
2074 /* Make sure dropped columns are skipped by backend code. */
2075 #if PG_VERSION_NUM < 110000
2076 if (m_tupdesc->attrs[c]->attisdropped)
2077 #else
2078 if (m_tupdesc->attrs[c].attisdropped)
2079 #endif
2080 {
2081 nulls[c] = true;
2082 continue;
2083 }
2084
2085 Handle<v8::Value> attr = m_is_scalar ? value : obj->Get(context, m_colnames[c]).ToLocalChecked();
2086 if (attr.IsEmpty() || attr->IsUndefined() || attr->IsNull())
2087 nulls[c] = true;
2088 else
2089 values[c] = ::ToDatum(attr, &nulls[c], &m_coltypes[c]);
2090 }
2091
2092 if (tupstore)
2093 {
2094 tuplestore_putvalues(tupstore, m_tupdesc, values, nulls);
2095 result = (Datum) 0;
2096 }
2097 else
2098 {
2099 result = HeapTupleGetDatum(heap_form_tuple(m_tupdesc, values, nulls));
2100 }
2101
2102 pfree(values);
2103 pfree(nulls);
2104
2105 return result;
2106 }
2107
js_error()2108 js_error::js_error() throw()
2109 : m_msg(NULL), m_code(0), m_detail(NULL), m_hint(NULL), m_context(NULL)
2110 {
2111 }
2112
js_error(const char * msg)2113 js_error::js_error(const char *msg) throw()
2114 {
2115 m_msg = pstrdup(msg);
2116 m_code = 0;
2117 m_detail = NULL;
2118 m_hint = NULL;
2119 m_context = NULL;
2120 }
2121
js_error(TryCatch & try_catch)2122 js_error::js_error(TryCatch &try_catch) throw()
2123 {
2124 Isolate *isolate = Isolate::GetCurrent();
2125 Local<Context> context = isolate->GetCurrentContext();
2126 HandleScope handle_scope(isolate);
2127 String::Utf8Value exception(isolate, try_catch.Exception());
2128 Handle<Message> message = try_catch.Message();
2129
2130 m_msg = NULL;
2131 m_code = 0;
2132 m_detail = NULL;
2133 m_hint = NULL;
2134 m_context = NULL;
2135
2136 try
2137 {
2138 m_msg = ToCStringCopy(exception);
2139 Handle<v8::Object> err = try_catch.Exception()->ToObject(context).ToLocalChecked();
2140 StringInfoData detailStr;
2141 StringInfoData hintStr;
2142 StringInfoData contextStr;
2143 initStringInfo(&detailStr);
2144 initStringInfo(&hintStr);
2145 initStringInfo(&contextStr);
2146
2147 if (!err.IsEmpty())
2148 {
2149 v8::Local<v8::Value> errCode = err->Get(context, String::NewFromUtf8(isolate, "code").ToLocalChecked()).ToLocalChecked();
2150 if (!errCode->IsUndefined() && !errCode->IsNull())
2151 {
2152 int32_t code = errCode->Int32Value(context).FromJust();
2153 m_code = code;
2154 }
2155
2156 v8::Local<v8::Value> errDetail = err->Get(context, String::NewFromUtf8(isolate, "detail").ToLocalChecked()).ToLocalChecked();
2157 if (!errDetail->IsUndefined() && !errDetail->IsNull())
2158 {
2159 CString detail(errDetail);
2160 appendStringInfo(&detailStr, "%s", detail.str("?"));
2161 m_detail = detailStr.data;
2162 }
2163
2164 v8::Local<v8::Value> errHint = err->Get(context, String::NewFromUtf8(isolate, "hint").ToLocalChecked()).ToLocalChecked();
2165 if (!errHint->IsUndefined() && !errHint->IsNull())
2166 {
2167 CString hint(errHint);
2168 appendStringInfo(&hintStr, "%s", hint.str("?"));
2169 m_hint = hintStr.data;
2170 }
2171
2172 v8::Local<v8::Value> errContext = err->Get(context, String::NewFromUtf8(isolate, "context").ToLocalChecked()).ToLocalChecked();
2173 if (!errContext->IsUndefined() && !errContext->IsNull())
2174 {
2175 CString context(errContext);
2176 appendStringInfo(&contextStr, "%s\n", context.str("?"));
2177 }
2178 }
2179
2180
2181 if (!message.IsEmpty())
2182 {
2183 CString script(message->GetScriptResourceName());
2184 int lineno = message->GetLineNumber(isolate->GetCurrentContext()).FromJust();
2185 CString source(message->GetSourceLine(isolate->GetCurrentContext()).ToLocalChecked());
2186 // TODO: Get stack trace?
2187 //Handle<StackTrace> stackTrace(message->GetStackTrace());
2188
2189 /*
2190 * Report lineno - 1 because "function _(...){" was added
2191 * at the first line to the javascript code.
2192 */
2193 if (strstr(m_msg, "Error: ") == m_msg)
2194 m_msg += 7;
2195
2196 appendStringInfo(&contextStr, "%s() LINE %d: %s",
2197 script.str("?"), lineno - 1, source.str("?"));
2198 }
2199
2200 m_context = contextStr.data;
2201 }
2202 catch (...)
2203 {
2204 // nested error, keep quiet.
2205 }
2206 }
2207
2208 Local<v8::Value>
error_object()2209 js_error::error_object()
2210 {
2211 char *msg = pstrdup(m_msg ? m_msg : "unknown exception");
2212 /*
2213 * Trim leading "Error: ", in case the message is generated from
2214 * another Error.
2215 */
2216 if (strstr(msg, "Error: ") == msg)
2217 msg += 7;
2218 Local<String> message = ToString(msg);
2219 return Exception::Error(message);
2220 }
2221
2222 __attribute__((noreturn))
2223 void
rethrow()2224 js_error::rethrow() throw()
2225 {
2226 ereport(ERROR,
2227 (
2228 m_code ? errcode(m_code): 0,
2229 m_msg ? errmsg("%s", m_msg) : 0,
2230 m_detail ? errdetail("%s", m_detail) : 0,
2231 m_hint ? errhint("%s", m_hint) : 0,
2232 m_context ? errcontext("%s", m_context) : 0
2233 ));
2234 exit(0); // keep compiler quiet
2235 }
2236
2237 __attribute__((noreturn))
2238 void
rethrow()2239 pg_error::rethrow() throw()
2240 {
2241 PG_RE_THROW();
2242 exit(0); // keep compiler quiet
2243 }
2244