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