Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Merge remote-tracking branch 'origin/master' into postgrest

Conflicts:
	README
  • Loading branch information...
commit 8327886564700ef5c21a22afcfe19d5538fe50a9 2 parents 862c16d + f919997
唐鳳 authored
14 Changes
... ... @@ -1,5 +1,19 @@
1 1 Revision history for plv8
2 2
  3 +1.3.0 2012-12-08
  4 + - Make two dialects (plcoffee, plls) official sub-extensions.
  5 + - Implement builtin json type conversion.
  6 + - Static build and automatic v8 build via 'static' target.
  7 + - Implement v8's remote debugger if enabled.
  8 + - Implement bytea type conversion and typed array.
  9 + - Allow polymorphic and internal types in argument and return types.
  10 + - Support user defined window functions.
  11 + - Potential bug fixes.
  12 +
  13 +1.2.1 2012-12-08
  14 + - Fix a crash in returning array value from array-return function.
  15 + - Fix trigger so that returned NULL can be handled correctly.
  16 +
3 17 1.2.0 2012-07-20
4 18 - Check the field names match for SRFs.
5 19 - Fix EpochToDate to handle non integer timestamp case.
14 META.json
@@ -2,7 +2,7 @@
2 2 "name": "plv8",
3 3 "abstract": "A procedural language in JavaScript powered by V8",
4 4 "description": "plv8 is a trusted procedural language that is safe to use, fast to run and easy to develop.",
5   - "version": "1.3.0devel",
  5 + "version": "1.3.0",
6 6 "maintainer": [
7 7 "Hitoshi Harada <umi.tanuki@gmail.com>",
8 8 "Andrew Dunstan <AMDunstan@gmail.com>",
@@ -26,21 +26,21 @@
26 26 },
27 27 "provides": {
28 28 "plv8": {
29   - "file": "plv8--1.3.0devel.sql",
  29 + "file": "plv8--1.3.0.sql",
30 30 "docfile": "doc/plv8.md",
31   - "version": "1.3.0devel",
  31 + "version": "1.3.0",
32 32 "abstract": "A procedural language in JavaScript"
33 33 },
34 34 "plcoffee": {
35   - "file": "plcoffee--1.3.0devel.sql",
  35 + "file": "plcoffee--1.3.0.sql",
36 36 "docfile": "doc/plv8.md",
37   - "version": "1.3.0devel",
  37 + "version": "1.3.0",
38 38 "abstract": "A procedural language in CoffeeScript"
39 39 },
40 40 "plls": {
41   - "file": "plls--1.3.0devel.sql",
  41 + "file": "plls--1.3.0.sql",
42 42 "docfile": "doc/plv8.md",
43   - "version": "1.3.0devel",
  43 + "version": "1.3.0",
44 44 "abstract": "A procedural language in LiveScript"
45 45 }
46 46 },
83 Makefile
@@ -2,21 +2,17 @@
2 2 #
3 3 # Makefile for plv8
4 4 #
5   -# @param V8_SRCDIR path to V8 source directory, used for include files
6   -# @param V8_OUTDIR path to V8 output directory, used for library files
7   -# @param V8_STATIC_SNAPSHOT if defined, statically link to v8 with snapshot
8   -# @param V8_STATIC_NOSNAPSHOT if defined, statically link to v8 w/o snapshot
9 5 # @param DISABLE_DIALECT if defined, not build dialects (i.e. plcoffee, etc)
  6 +# @param ENABLE_DEBUGGER_SUPPORT enables v8 deubbger agent
10 7 #
11   -# There are three ways to build plv8.
  8 +# There are two ways to build plv8.
12 9 # 1. Dynamic link to v8 (default)
13   -# 2. Static link to v8 with snapshot, if V8_STATIC_SNAPSHOT is defined
14   -# 3. Static link to v8 w/o snapshot, if V8_STATIC_NOSNAPSHOT is defined
15   -# In either case, V8_OUTDIR should point to the v8 output directory (such as
16   -# $(HOME)/v8/out/native) if linker doesn't find it automatically.
  10 +# You need to have libv8.so and header file installed.
  11 +# 2. Static link to v8 with snapshot
  12 +# 'make static' will download v8 and build, then statically link to it.
17 13 #
18 14 #-----------------------------------------------------------------------------#
19   -PLV8_VERSION = 1.3.0devel
  15 +PLV8_VERSION = 1.3.0
20 16
21 17 PG_CONFIG = pg_config
22 18 PGXS := $(shell $(PG_CONFIG) --pgxs)
@@ -40,47 +36,27 @@ DATA += plcoffee.control plcoffee--$(PLV8_VERSION).sql \
40 36 plls.control plls--$(PLV8_VERSION).sql
41 37 endif
42 38 DATA_built = plv8.sql
43   -REGRESS = init-extension plv8 inline json startup_pre startup varparam
  39 +REGRESS = init-extension plv8 inline json startup_pre startup varparam json_conv \
  40 + window
44 41 ifndef DISABLE_DIALECT
45 42 REGRESS += dialect
46 43 endif
47   -
48   -# V8 build options. See the top comment.
49   -V8_STATIC_SNAPSHOT_LIBS = libv8_base.a libv8_snapshot.a
50   -V8_STATIC_NOSNAPSHOT_LIBS = libv8_base.a libv8_nosnapshot.a
51   -ifdef V8_STATIC_SNAPSHOT
52   - ifdef V8_OUTDIR
53   -SHLIB_LINK += $(addprefix $(V8_OUTDIR)/, $(V8_STATIC_SNAPSHOT_LIBS))
54   - else
55   -SHLIB_LINK += $(V8_STATIC_SNAPSHOT_LIBS)
56   - endif
57   -else
58   - ifdef V8_STATIC_NOSNAPSHOT
59   - ifdef V8_OUTDIR
60   -SHLIB_LINK += $(addprefix $(V8_OUTDIR)/, $(V8_STATIC_NOSNAPSHOT_LIBS))
61   - else
62   -SHLIB_LINK += $(V8_STATIC_NOSNAPSHOT_LIBS)
63   - endif
64   - else
65 44 SHLIB_LINK += -lv8
66   - ifdef V8_OUTDIR
67   -SHLIB_LINK += -L$(V8_OUTDIR)
68   - endif
69   - endif
70   -endif
71 45
72   -OPTFLAGS = -O2
73   -CCFLAGS = -Wall $(OPTFLAGS)
74   -ifdef V8_SRCDIR
75   -override CPPFLAGS += -I$(V8_SRCDIR)/include
  46 +# v8's remote debugger is optional at the moment, since we don't know
  47 +# how much of the v8 installation is built with debugger enabled.
  48 +ifdef ENABLE_DEBUGGER_SUPPORT
  49 +OPT_ENABLE_DEBUGGER_SUPPORT = -DENABLE_DEBUGGER_SUPPORT
76 50 endif
  51 +OPTFLAGS = -O2
  52 +CCFLAGS = -Wall $(OPTFLAGS) $(OPT_ENABLE_DEBUGGER_SUPPORT)
77 53
78 54 all:
79 55
80 56 plv8_config.h: plv8_config.h.in Makefile
81 57 sed -e 's/^#undef PLV8_VERSION/#define PLV8_VERSION "$(PLV8_VERSION)"/' $< > $@
82 58
83   -%.o : %.cc plv8_config.h
  59 +%.o : %.cc plv8_config.h plv8.h
84 60 $(CUSTOM_CC) $(CCFLAGS) $(CPPFLAGS) -fPIC -c -o $@ $<
85 61
86 62 # Convert .js to .cc
@@ -98,7 +74,7 @@ ifeq ($(shell test $(PG_VERSION_NUM) -ge 90100 && echo yes), yes)
98 74 DATA_built =
99 75 all: $(DATA)
100 76 %--$(PLV8_VERSION).sql: plv8.sql.common
101   - sed -e 's/@LANG_NAME@/$*/g' $< | $(CC) -E -P $(CPPFLAGS) - > $@
  77 + sed -e 's/@LANG_NAME@/$*/g' $< | $(CC) -E -P $(CPPFLAGS) -DLANG_$* - > $@
102 78 %.control: plv8.control.common
103 79 sed -e 's/@PLV8_VERSION@/$(PLV8_VERSION)/g' $< | $(CC) -E -P -DLANG_$* - > $@
104 80 modules:
@@ -109,20 +85,25 @@ modules:
109 85 subclean:
110 86 rm -f plv8_config.h $(DATA) $(JSCS)
111 87
  88 +ifeq ($(shell test $(PG_VERSION_NUM) -lt 90200 && echo yes), yes)
  89 +REGRESS := $(filter-out json_conv, $(REGRESS))
  90 +endif
  91 +
112 92 else # < 9.1
113 93
114 94 ifeq ($(shell test $(PG_VERSION_NUM) -ge 90000 && echo yes), yes)
115   -REGRESS := init $(filter-out init-extension dialect, $(REGRESS))
  95 +REGRESS := init $(filter-out init-extension dialect json_conv, $(REGRESS))
116 96
117 97 else # < 9.0
118 98
119   -REGRESS := init $(filter-out init-extension inline startup varparam dialect, $(REGRESS))
  99 +REGRESS := init $(filter-out init-extension inline startup \
  100 + varparam dialect json_conv window, $(REGRESS))
120 101
121 102 endif
122 103
123 104 DATA = uninstall_plv8.sql
124 105 %.sql.in: plv8.sql.common
125   - sed -e 's/@LANG_NAME@/$*/g' $< | $(CC) -E -P $(CPPFLAGS) - > $@
  106 + sed -e 's/@LANG_NAME@/$*/g' $< | $(CC) -E -P $(CPPFLAGS) -DLANG_$* - > $@
126 107 subclean:
127 108 rm -f plv8_config.h *.sql.in $(JSCS)
128 109
@@ -130,6 +111,13 @@ endif
130 111
131 112 clean: subclean
132 113
  114 +# build will be created by Makefile.v8
  115 +distclean:
  116 + rm -rf build
  117 +
  118 +static:
  119 + $(MAKE) -f Makefile.v8
  120 +
133 121 # Check if META.json.version and PLV8_VERSION is equal.
134 122 # Ideally we want to have only one place for this number, but parsing META.json
135 123 # is a bit challenging; at one time we had v8/d8 parsing META.json to pass
@@ -141,6 +129,15 @@ META_VER := $(shell v8 -e 'print(JSON.parse(read("META.json")).version)' 2>/dev/
141 129 ifndef META_VER
142 130 META_VER := $(shell d8 -e 'print(JSON.parse(read("META.json")).version)' 2>/dev/null)
143 131 endif
  132 +ifndef META_VER
  133 +META_VER := $(shell lsc -e 'console.log(JSON.parse(require("fs").readFileSync("META.json")).version)' 2>/dev/null)
  134 +endif
  135 +ifndef META_VER
  136 +META_VER := $(shell coffee -e 'console.log(JSON.parse(require("fs").readFileSync("META.json")).version)' 2>/dev/null)
  137 +endif
  138 +ifndef META_VER
  139 +META_VER := $(shell node -e 'console.log(JSON.parse(require("fs").readFileSync("META.json")).version)')
  140 +endif
144 141
145 142 integritycheck:
146 143 ifneq ($(META_VER),)
38 Makefile.v8
... ... @@ -0,0 +1,38 @@
  1 +#-----------------------------------------------------------------------------#
  2 +#
  3 +# Makefile for v8 static link
  4 +#
  5 +# 'make static' will download the v8 source and build it, then build plv8
  6 +# with statically link to v8 with snapshot. This assumes certain directory
  7 +# structure in v8 which may be different from version to another, but user
  8 +# can specify the v8 version by AUTOV8_VERSION, too.
  9 +#-----------------------------------------------------------------------------#
  10 +AUTOV8_VERSION = 3.14.5
  11 +AUTOV8_DIR = build/v8-$(AUTOV8_VERSION)
  12 +AUTOV8_OUT = build/v8-$(AUTOV8_VERSION)/out/native
  13 +AUTOV8_LIB = $(AUTOV8_OUT)/libv8_snapshot.a
  14 +AUTOV8_STATIC_LIBS = libv8_base.a libv8_snapshot.a
  15 +
  16 +SHLIB_LINK += $(addprefix $(AUTOV8_OUT)/, $(AUTOV8_STATIC_LIBS))
  17 +# We are sure the static buid can support debugger
  18 +ENABLE_DEBUGGER_SUPPORT=1
  19 +
  20 +all: $(AUTOV8_LIB)
  21 +
  22 +# For some reason, this solves parallel make dependency.
  23 +plv8_config.h: $(AUTOV8_LIB)
  24 +
  25 +$(AUTOV8_DIR):
  26 + mkdir -p build
  27 + curl -L 'https://github.com/v8/v8/archive/$(AUTOV8_VERSION).tar.gz' \
  28 + | tar zxf - -C build
  29 +
  30 +$(AUTOV8_LIB): $(AUTOV8_DIR)
  31 + $(MAKE) -C build/v8-$(AUTOV8_VERSION) dependencies
  32 + $(MAKE) -C build/v8-$(AUTOV8_VERSION) native
  33 +
  34 +include Makefile
  35 +
  36 +CCFLAGS += -I$(AUTOV8_DIR)/include
  37 +# We're gonna build static link. Rip it out after include Makefile
  38 +SHLIB_LINK := $(filter-out -lv8, $(SHLIB_LINK))
47 README
@@ -30,8 +30,8 @@ REQUIREMENT
30 30
31 31 plv8 is tested with:
32 32
33   -- PG: version 8.4, 9.0, 9.1 and 9.2dev (maybe older are allowed)
34   -- V8: version 3.6.2
  33 +- PG: version 8.4, 9.0, 9.1, 9.2 and 9.3dev (maybe older are allowed)
  34 +- V8: version 3.14.5
35 35 - g++: version 4.5.1
36 36
37 37 Also all tools that PostgreSQL and V8 require to be built are required.
@@ -39,17 +39,13 @@ Also all tools that PostgreSQL and V8 require to be built are required.
39 39 INSTALL
40 40 -------
41 41
42   -If you use pgxn client, you don't have to worry much. It does everything.
43   -If you are building plv8 from source, make sure V8 include files are in the
44   -system include directories, or you can pass V8 source path via V8_SRCDIR, and
45   -if so, you'll probably need V8_OUTDIR pointing where libv8 is located.
46   -Run make:
  42 +There are two ways to build plv8. The first is default `make`, which depends
  43 +on system-installed libv8 and plv8 will be dynamically link to it. The second
  44 +is `make static`, which will download v8 soure at a specific version and build
  45 +it, and statically link plv8 to it. PGXN install will use the former, while
  46 +you can do the latter manually if you have not installed v8 yet.
47 47
48   - $ make V8_SRCDIR=/path/to/v8 V8_OUTDIR=/path/to/v8/out/native install
49   - $ make modules
50   -
51   -If you prefer to build plv8 statically linking with v8, try V8_STATIC_SNAPSHOT
52   -or V8_STATIC_NOSNAPSHOT. Consult Makefile for more detail.
  48 +To install native FS connectivity, run `make modules` after `make install`.
53 49
54 50 Once you installed plv8 into your dabase, create language via
55 51
@@ -57,17 +53,25 @@ Once you installed plv8 into your dabase, create language via
57 53 $ psql -c 'CREATE EXTENSION plls'
58 54 $ psql -c 'CREATE EXTENSION plcoffee'
59 55
60   -in 9.1, or in the prior versions
  56 +in 9.1 or later, or in the prior versions
61 57
62 58 $ psql -f plv8.sql
63 59
64 60 to create database objects.
65 61
  62 +In mingw64, you may have difficulty in building plv8. If so, try to make
  63 +the following changes in Makefile.
  64 +
  65 + CUSTOM_CC = gcc
  66 + SHLIB_LINK := $(SHLIB_LINK) -lv8 -Wl,-Bstatic -lstdc++ -Wl,-Bdynamic -lm
  67 +
  68 +For more detail, please refer to http://code.google.com/p/plv8js/issues/detail?id=29
  69 +
66 70 TEST
67 71 ----
68 72
69 73 plv8 supports installcheck test. Make sure set custom_variable_classes = 'plv8'
70   -in your postgresql.conf and
  74 +in your postgresql.conf (before 9.2) and run
71 75
72 76 $ make installcheck
73 77
@@ -89,6 +93,21 @@ EXAMPLE (JAVASCRIPT)
89 93 {"name":"Tom","age":"29"}
90 94 (1 row)
91 95
  96 +EXAMPLE (COFFEESCRIPT)
  97 +----------------------
  98 +
  99 + CREATE OR REPLACE FUNCTION plcoffee_test(keys text[], vals text[])
  100 + RETURNS text AS $$
  101 + return JSON.stringify(keys.reduce(((o, key, idx) ->
  102 + o[key] = vals[idx]; return o), {}), {})
  103 + $$ LANGUAGE plcoffee IMMUTABLE STRICT;
  104 +
  105 + SELECT plcoffee_test(ARRAY['name', 'age'], ARRAY['Tom', '29']);
  106 + plcoffee_test
  107 + ---------------------------
  108 + {"name":"Tom","age":"29"}
  109 + (1 row)
  110 +
92 111 EXAMPLE (LIVESCRIPT)
93 112 --------------------
94 113
136 doc/plv8.md
Source Rendered
@@ -17,8 +17,12 @@ Implemented features are as follows.
17 17 - Database access via SPI including prepared statements and cursors
18 18 - Subtransaction
19 19 - Utility functions
  20 +- Window function API
  21 +- Typed array
  22 +- Remote debugger
20 23 - Runtime environment separation across users in the same session
21 24 - Start-up procedure
  25 +- Dialects
22 26
23 27 Scalar function calls
24 28 ---------------------
@@ -154,11 +158,15 @@ automatically. If the desired database type is one of
154 158 - date
155 159 - timestamp
156 160 - timestamptz
  161 +- bytea
  162 +- json (>= 9.2)
157 163
158 164 and the JS value looks compatible, then the conversion succeeds. Otherwise,
159 165 PL/v8 tries to convert them via cstring representation. An array type is
160 166 supported only if the dimention is one. A JS object will be mapped to
161   -a tuple when applicable.
  167 +a tuple when applicable. In addition to these types, PL/v8 supports
  168 +polymorphic types such like anyelement and anyarray. Conversion of bytea is
  169 +a little different story. See TypedArray section.
162 170
163 171 Database access via SPI including prepared statements and cursors
164 172 -----------------------------------------------------------------
@@ -291,11 +299,123 @@ been registered in the database.
291 299 With plv8.find_function(), you can look up other plv8 functions. If they are
292 300 not the plv8 function, it errors out. The function signature parameter to
293 301 plv8.find_function() is either of regproc (function name only) or regprocedure
294   -(function name with argument types).
  302 +(function name with argument types). You can make use of internal type for
  303 +arguments and void type for return type for the pure JavaScript function to
  304 +make sure any invocation from SQL statement should not happen.
295 305
296 306 The plv8 object provides version string as `plv8.version`. This string
297 307 corresponds to plv8 module version. Note this is not the extension version.
298 308
  309 +Window function API
  310 +-------------------
  311 +
  312 +You can define user-defined window functions with PL/v8. It wraps C-level
  313 +window function API to support full functionality. To create one, first
  314 +obtain window object by plv8.get_window_object(), which provides interfaces
  315 +as follows.
  316 +
  317 +### WindowObject.get_current_position() ###
  318 +
  319 +Returns the current position in the partition, starting from 0.
  320 +
  321 +### WindowObject.get_partition_row_count() ###
  322 +
  323 +Returns the number of rows in the partition.
  324 +
  325 +### WindowObject.set_mark_position( pos ) ###
  326 +
  327 +Set mark at the specified row. Rows above this position will be gone and
  328 +not be accessible later.
  329 +
  330 +### WindowObject.rows_are_peers( pos1, pos2 ) ###
  331 +
  332 +Returns true if the rows at `pos1` and `pos2` are peers.
  333 +
  334 +### WindowObject.get_func_arg_in_partition( argno, relpos, seektype, mark_pos ) ###
  335 +
  336 +### WindowObject.get_func_arg_in_frame( argno, relpos, seektype, mark_pos ) ###
  337 +
  338 +Returns the value of the argument in `argno` (starting from 0) to this
  339 +function at the `relpos` row from `seektype` in the current partition or
  340 +frame. `seektype` can be either of WindowObject.SEEK_HEAD,
  341 +WindowObject.SEEK_CURRENT, or WindowObject.SEEK_TAIL. If `mark_pos` is true,
  342 +the row the argument is fetched from is marked. If the specified row is
  343 +out of the partition/frame, the returned value will be `undefined`.
  344 +
  345 +### WindowObject.get_func_arg_in_current( argno ) ###
  346 +
  347 +Returns the value of the argument in `argno` (starting from 0) to this
  348 +function at the current row. Note that the returned value will be the
  349 +same as the argument variable of the function.
  350 +
  351 +### WindowObject.get_partition_local( [size] ) ###
  352 +
  353 +Returns partition-local value, which is released at the end of the current
  354 +partition. If nothing is stored, `undefined` is returned. `size` argument
  355 +(default 1000) is the byte size of the allocated memory in the first call.
  356 +Once the memory allocated, the size will not change.
  357 +
  358 +### WindowObject.set_partition_local( obj ) ###
  359 +
  360 +Stores the partition-local value, which you can retrieve later by
  361 +get_partition_local(). This function internally uses JSON.stringify to
  362 +serialize the object, so if you pass value that is not able to be serialized
  363 +may end up being unexpected value. If the size of serialized value is
  364 +more than the allocated memory, it will throw an exception.
  365 +
  366 +You can also learn more on how to use these API in sql/window.sql regression
  367 +test, which implements most of the native window functions. For the general
  368 +information of the user-defined window function, see the CREATE FUNCTION
  369 +page of the PostgreSQL manual.
  370 +
  371 +Typed array
  372 +-----------
  373 +
  374 +The typed array is something v8 provides to allow fast access to native memory,
  375 +mainly for the purpose of their canvas support in browsers. PL/v8 uses this
  376 +to map bytea and various array types to JavaScript array. In case of bytea,
  377 +you can access each byte as an array of unsigned bytes. For
  378 +int2/int4/float4/float8 array type, PL/v8 provides direct access to each
  379 +element by using PL/v8 domain types.
  380 +
  381 +- plv8_int2array maps int2[]
  382 +- plv8_int4array maps int4[]
  383 +- plv8_float4array maps float4[]
  384 +- plv8_float8array maps float8[]
  385 +
  386 +These are only annotations that tell PL/v8 to use fast access method instead of
  387 +regular one. For these typed arrays, only 1-dimensional array without NULL
  388 +element. Also, there is currently no way to create such typed array inside
  389 +PL/v8 functions. Only arguments can be typed array. You can modify the element
  390 +and return the value. An example for these types are as follows.
  391 +
  392 + CREATE FUNCTION int4sum(ary plv8_int4array) RETURNS int8 AS $$
  393 + var sum = 0;
  394 + for (var i = 0; i < ary.length; i++) {
  395 + sum += ary[i];
  396 + }
  397 + return sum;
  398 + $$ LANGUAGE plv8 IMMUTABLE STRICT;
  399 +
  400 + SELECT int4sum(ARRAY[1, 2, 3, 4, 5]);
  401 + int4sum
  402 + ---------
  403 + 15
  404 + (1 row)
  405 +
  406 +Remote debugger
  407 +---------------
  408 +
  409 +PL/v8 supports v8 remote debugger. You need to enable it at the compile time
  410 +to pass ENABLE_DEBUGGER_SUPPORT to `make`. `make static` will automatically
  411 +turns it on. If enabled, and once PL/v8 module is loaded (and the execution
  412 +engine is initialized, PL/v8 accepts a remote debugger connection. If you
  413 +have `d8` from v8 package, run with `--remote-debug --debug-port=35432` to
  414 +attach the functions. If you want to change the remote debugger port, there
  415 +is a GUC `plv8.debugger_port` to set the port number. You can also try
  416 +`debugger` statement inside functions to set a breakpoint. For more details
  417 +of v8 remote debugger, see v8 documentation.
  418 +
299 419 Runtime environment separation across users in the same session
300 420 ---------------------------------------------------------------
301 421
@@ -331,3 +451,15 @@ visible from any subsequent function as global variables.
331 451
332 452 Remember CREATE FUNCTION also starts the plv8 runtime environment, so make sure
333 453 to SET this GUC before any plv8 actions including CREATE FUNCTION.
  454 +
  455 +Dialects
  456 +--------
  457 +
  458 +This module also contains some dialect supports. Currently, we have two dialects
  459 +are supported.
  460 +
  461 +- CoffeeScript (plcoffee)
  462 +- LiveScript (plls)
  463 +
  464 +With PostgreSQL 9.1 or above, you are able to load tohse dialects via CREATE
  465 +EXTENSION command.
20 expected/json_conv.out
... ... @@ -0,0 +1,20 @@
  1 +CREATE FUNCTION conv(o json) RETURNS json AS $$
  2 +if (o instanceof Array) {
  3 + o[1] = 10;
  4 +} else if (typeof(o) == 'object') {
  5 + o.i = 10;
  6 +}
  7 +return o;
  8 +$$ LANGUAGE plv8;
  9 +SELECT conv('{"i": 3, "b": 20}');
  10 + conv
  11 +-----------------
  12 + {"i":10,"b":20}
  13 +(1 row)
  14 +
  15 +SELECT conv('[1, 2, 3]');
  16 + conv
  17 +----------
  18 + [1,10,3]
  19 +(1 row)
  20 +
88 expected/plv8.out
@@ -317,6 +317,36 @@ SELECT one_out(123);
317 317 ABC123
318 318 (1 row)
319 319
  320 +-- polymorphic types
  321 +CREATE FUNCTION polymorphic(poly anyarray) returns anyelement AS
  322 +$$
  323 + return poly[0];
  324 +$$
  325 +LANGUAGE plv8;
  326 +SELECT polymorphic(ARRAY[10, 11]), polymorphic(ARRAY['foo', 'bar']);
  327 + polymorphic | polymorphic
  328 +-------------+-------------
  329 + 10 | foo
  330 +(1 row)
  331 +
  332 +-- typed array
  333 +CREATE FUNCTION fastsum(ary plv8_int4array) RETURNS int8 AS
  334 +$$
  335 + sum = 0;
  336 + for (var i = 0; i < ary.length; i++) {
  337 + sum += ary[i];
  338 + }
  339 + return sum;
  340 +$$
  341 +LANGUAGE plv8 IMMUTABLE STRICT;
  342 +SELECT fastsum(ARRAY[1, 2, 3, 4, 5]);
  343 + fastsum
  344 +---------
  345 + 15
  346 +(1 row)
  347 +
  348 +SELECT fastsum(ARRAY[NULL, 2]);
  349 +ERROR: NULL element, or multi-dimension array not allowed in external array type
320 350 -- elog()
321 351 CREATE FUNCTION test_elog(arg text) RETURNS void AS
322 352 $$
@@ -521,6 +551,64 @@ SELECT * FROM test_tbl;
521 551 4 | s4
522 552 (3 rows)
523 553
  554 +-- One more trigger
  555 +CREATE FUNCTION test_trigger2() RETURNS trigger AS
  556 +$$
  557 + var tuple;
  558 + switch (TG_OP) {
  559 + case "INSERT":
  560 + tuple = NEW;
  561 + break;
  562 + case "UPDATE":
  563 + tuple = OLD;
  564 + break;
  565 + case "DELETE":
  566 + tuple = OLD;
  567 + break;
  568 + default:
  569 + return;
  570 + }
  571 + if (tuple.subject == "skip") {
  572 + return null;
  573 + }
  574 + if (tuple.subject == "modify" && NEW) {
  575 + NEW.val = tuple.val * 2;
  576 + return NEW;
  577 + }
  578 +$$
  579 +LANGUAGE "plv8";
  580 +CREATE TABLE trig_table (subject text, val int);
  581 +INSERT INTO trig_table VALUES('skip', 1);
  582 +CREATE TRIGGER test_trigger2
  583 + BEFORE INSERT OR UPDATE OR DELETE
  584 + ON trig_table FOR EACH ROW
  585 + EXECUTE PROCEDURE test_trigger2();
  586 +INSERT INTO trig_table VALUES
  587 + ('skip', 1), ('modify', 2), ('noop', 3);
  588 +SELECT * FROM trig_table;
  589 + subject | val
  590 +---------+-----
  591 + skip | 1
  592 + modify | 4
  593 + noop | 3
  594 +(3 rows)
  595 +
  596 +UPDATE trig_table SET val = 10;
  597 +SELECT * FROM trig_table;
  598 + subject | val
  599 +---------+-----
  600 + skip | 1
  601 + modify | 8
  602 + noop | 10
  603 +(3 rows)
  604 +
  605 +DELETE FROM trig_table;
  606 +SELECT * FROM trig_table;
  607 + subject | val
  608 +---------+-----
  609 + skip | 1
  610 +(1 row)
  611 +
524 612 -- ERRORS
525 613 CREATE FUNCTION syntax_error() RETURNS text AS '@' LANGUAGE plv8;
526 614 ERROR: SyntaxError: Unexpected token ILLEGAL
371 expected/window.out
... ... @@ -0,0 +1,371 @@
  1 +-- window functions
  2 +CREATE FUNCTION js_row_number() RETURNS int8 AS $$
  3 + var winobj = plv8.get_window_object();
  4 + return winobj.get_current_position() + 1;
  5 +$$ LANGUAGE plv8 WINDOW;
  6 +CREATE FUNCTION __js_rank_up(winobj internal, up_callback internal) RETURNS void AS $$
  7 + var context = winobj.get_partition_local() || {};
  8 + var pos = winobj.get_current_position();
  9 + context.up = false;
  10 + if (!context.rank) {
  11 + context.rank = 1;
  12 + } else {
  13 + if (!winobj.rows_are_peers(pos, pos - 1)) {
  14 + context.up = true;
  15 + if (up_callback) {
  16 + up_callback(context);
  17 + }
  18 + }
  19 + }
  20 + winobj.set_mark_position(pos);
  21 + winobj.set_partition_local(context);
  22 + return context;
  23 +$$ LANGUAGE plv8;
  24 +CREATE FUNCTION js_rank() RETURNS int8 AS $$
  25 + var winobj = plv8.get_window_object();
  26 + var context = plv8.find_function("__js_rank_up")(winobj, function(context){
  27 + context.rank = winobj.get_current_position() + 1;
  28 + });
  29 + return context.rank;
  30 +$$ LANGUAGE plv8 WINDOW;
  31 +CREATE FUNCTION js_dense_rank() RETURNS int8 AS $$
  32 + var winobj = plv8.get_window_object();
  33 + var context = plv8.find_function("__js_rank_up")(winobj, function(context){
  34 + context.rank++;
  35 + });
  36 + return context.rank;
  37 +$$ LANGUAGE plv8 WINDOW;
  38 +CREATE FUNCTION js_percent_rank() RETURNS float AS $$
  39 + var winobj = plv8.get_window_object();
  40 + var totalrows = winobj.get_partition_row_count();
  41 + if (totalrows <= 1)
  42 + return 0.0;
  43 + var context = plv8.find_function("__js_rank_up")(winobj, function(context){
  44 + context.rank = winobj.get_current_position() + 1;
  45 + });
  46 + return (context.rank - 1) / (totalrows - 1);
  47 +$$ LANGUAGE plv8 WINDOW;
  48 +CREATE FUNCTION js_cume_dist() RETURNS float AS $$
  49 + var winobj = plv8.get_window_object();
  50 + var totalrows = winobj.get_partition_row_count();
  51 + var context = plv8.find_function("__js_rank_up")(winobj);
  52 + if (context.up || context.rank == 1) {
  53 + context.rank = winobj.get_current_position() + 1;
  54 + for (var row = context.rank; row < totalrows; row++) {
  55 + if (!winobj.rows_are_peers(row - 1, row)) {
  56 + break;
  57 + }
  58 + context.rank++;
  59 + }
  60 + }
  61 + winobj.set_partition_local(context);
  62 + return context.rank / totalrows;
  63 +$$ LANGUAGE plv8 WINDOW;
  64 +CREATE FUNCTION js_ntile(nbuckets int8) RETURNS int AS $$
  65 + var winobj = plv8.get_window_object();
  66 + var context = winobj.get_partition_local() || {};
  67 +
  68 + if (!context.ntile) {
  69 + context.rows_per_bucket = 0;
  70 + var total = winobj.get_partition_row_count();
  71 + var nbuckets = winobj.get_func_arg_current(0);
  72 + if (nbuckets === null) {
  73 + return null;
  74 + }
  75 + if (nbuckets <= 0) {
  76 + plv8.elog(ERROR, "argument of ntile must be greater than zero");
  77 + }
  78 + context.ntile = 1;
  79 + context.rows_per_bucket = 0;
  80 + context.boundary = total / nbuckets;
  81 + if (context.boundary <= 0) {
  82 + context.boundary = 1;
  83 + } else {
  84 + context.remainder = total % nbuckets;
  85 + if (context.remainder != 0) {
  86 + context.boundary++;
  87 + }
  88 + }
  89 + }
  90 + context.rows_per_bucket++;
  91 + if (context.boundary < context.rows_per_bucket) {
  92 + if (context.remainder != 0 && context.ntile == context.remainder) {
  93 + context.remainder = 0;
  94 + context.boundary -= 1;
  95 + }
  96 + context.ntile += 1;
  97 + context.rows_per_bucket = 1;
  98 + }
  99 + winobj.set_partition_local(context);
  100 + return context.ntile;
  101 +$$ LANGUAGE plv8 WINDOW;
  102 +CREATE FUNCTION __js_lead_lag_common(forward internal, withoffset internal, withdefault internal) RETURNS void AS $$
  103 + var winobj = plv8.get_window_object();
  104 + var offset;
  105 + if (withoffset) {
  106 + offset = winobj.get_func_arg_current(1);
  107 + if (offset === null) {
  108 + return null;
  109 + }
  110 + } else {
  111 + offset = 1;
  112 + }
  113 + var result = winobj.get_func_arg_in_partition(0,
  114 + forward ? offset : -offset,
  115 + winobj.SEEK_CURRENT,
  116 + false);
  117 + if (result === undefined) {
  118 + if (withdefault) {
  119 + result = winobj.get_func_arg_current(2);
  120 + }
  121 + }
  122 + if (result === null) {
  123 + return null;
  124 + }
  125 + return result;
  126 +$$ LANGUAGE plv8;
  127 +CREATE FUNCTION js_lag(arg anyelement) RETURNS anyelement AS $$
  128 + return plv8.find_function("__js_lead_lag_common")(false, false, false);
  129 +$$ LANGUAGE plv8 WINDOW;
  130 +CREATE FUNCTION js_lag(arg anyelement, ofs int) RETURNS anyelement AS $$
  131 + return plv8.find_function("__js_lead_lag_common")(false, true, false);
  132 +$$ LANGUAGE plv8 WINDOW;
  133 +CREATE FUNCTION js_lag(arg anyelement, ofs int, deflt anyelement) RETURNS anyelement AS $$
  134 + return plv8.find_function("__js_lead_lag_common")(false, true, true);
  135 +$$ LANGUAGE plv8 WINDOW;
  136 +CREATE FUNCTION js_lead(arg anyelement) RETURNS anyelement AS $$
  137 + return plv8.find_function("__js_lead_lag_common")(true, false, false);
  138 +$$ LANGUAGE plv8 WINDOW;
  139 +CREATE FUNCTION js_lead(arg anyelement, ofs int) RETURNS anyelement AS $$
  140 + return plv8.find_function("__js_lead_lag_common")(true, true, false);
  141 +$$ LANGUAGE plv8 WINDOW;
  142 +CREATE FUNCTION js_lead(arg anyelement, ofs int, deflt anyelement) RETURNS anyelement AS $$
  143 + return plv8.find_function("__js_lead_lag_common")(true, true, true);
  144 +$$ LANGUAGE plv8 WINDOW;
  145 +CREATE FUNCTION js_first_value(arg anyelement) RETURNS anyelement AS $$
  146 + var winobj = plv8.get_window_object();
  147 + return winobj.get_func_arg_in_frame(0, 0, winobj.SEEK_HEAD, true);
  148 +$$ LANGUAGE plv8 WINDOW;
  149 +CREATE FUNCTION js_last_value(arg anyelement) RETURNS anyelement AS $$
  150 + var winobj = plv8.get_window_object();
  151 + return winobj.get_func_arg_in_frame(0, 0, winobj.SEEK_TAIL, true);
  152 +$$ LANGUAGE plv8 WINDOW;
  153 +CREATE FUNCTION js_nth_value(arg anyelement, nth int) RETURNS anyelement AS $$
  154 + var winobj = plv8.get_window_object();
  155 + nth = winobj.get_func_arg_current(1);
  156 + if (nth <= 0)
  157 + plv8.elog(ERROR, "argument of nth_value must be greater than zero");
  158 + return winobj.get_func_arg_in_frame(0, nth - 1, winobj.SEEK_HEAD, false);
  159 +$$ LANGUAGE plv8 WINDOW;
  160 +CREATE TABLE empsalary (
  161 + depname varchar,
  162 + empno bigint,
  163 + salary int,
  164 + enroll_date date
  165 +);
  166 +INSERT INTO empsalary VALUES
  167 +('develop', 10, 5200, '2007-08-01'),
  168 +('sales', 1, 5000, '2006-10-01'),
  169 +('personnel', 5, 3500, '2007-12-10'),
  170 +('sales', 4, 4800, '2007-08-08'),
  171 +('personnel', 2, 3900, '2006-12-23'),
  172 +('develop', 7, 4200, '2008-01-01'),
  173 +('develop', 9, 4500, '2008-01-01'),
  174 +('sales', 3, 4800, '2007-08-01'),
  175 +('develop', 8, 6000, '2006-10-01'),
  176 +('develop', 11, 5200, '2007-08-15');
  177 +SELECT row_number() OVER (w), js_row_number() OVER (w) FROM empsalary WINDOW w AS (ORDER BY salary);
  178 + row_number | js_row_number
  179 +------------+---------------
  180 + 1 | 1
  181 + 2 | 2
  182 + 3 | 3
  183 + 4 | 4
  184 + 5 | 5
  185 + 6 | 6
  186 + 7 | 7
  187 + 8 | 8
  188 + 9 | 9
  189 + 10 | 10
  190 +(10 rows)
  191 +
  192 +SELECT rank() OVER (w), js_rank() OVER (w) FROM empsalary WINDOW w AS (PARTITION BY depname ORDER BY salary);
  193 + rank | js_rank
  194 +------+---------
  195 + 1 | 1
  196 + 2 | 2
  197 + 3 | 3
  198 + 3 | 3
  199 + 5 | 5
  200 + 1 | 1
  201 + 2 | 2
  202 + 1 | 1
  203 + 1 | 1
  204 + 3 | 3
  205 +(10 rows)
  206 +
  207 +SELECT dense_rank() OVER (w), js_dense_rank() OVER (w) FROM empsalary WINDOW w AS (ORDER BY salary);
  208 + dense_rank | js_dense_rank
  209 +------------+---------------
  210 + 1 | 1
  211 + 2 | 2
  212 + 3 | 3
  213 + 4 | 4
  214 + 5 | 5
  215 + 5 | 5
  216 + 6 | 6
  217 + 7 | 7
  218 + 7 | 7
  219 + 8 | 8
  220 +(10 rows)
  221 +
  222 +SELECT percent_rank() OVER (w), js_percent_rank() OVER (w) FROM empsalary WINDOW w AS (ORDER BY salary);
  223 + percent_rank | js_percent_rank
  224 +-------------------+-------------------
  225 + 0 | 0
  226 + 0.111111111111111 | 0.111111111111111
  227 + 0.222222222222222 | 0.222222222222222
  228 + 0.333333333333333 | 0.333333333333333
  229 + 0.444444444444444 | 0.444444444444444
  230 + 0.444444444444444 | 0.444444444444444
  231 + 0.666666666666667 | 0.666666666666667
  232 + 0.777777777777778 | 0.777777777777778
  233 + 0.777777777777778 | 0.777777777777778
  234 + 1 | 1
  235 +(10 rows)
  236 +
  237 +SELECT cume_dist() OVER (w), js_cume_dist() OVER (w) FROM empsalary WINDOW w AS (ORDER BY salary);
  238 + cume_dist | js_cume_dist
  239 +-----------+--------------
  240 + 0.1 | 0.1
  241 + 0.2 | 0.2
  242 + 0.3 | 0.3
  243 + 0.4 | 0.4
  244 + 0.6 | 0.6
  245 + 0.6 | 0.6
  246 + 0.7 | 0.7
  247 + 0.9 | 0.9
  248 + 0.9 | 0.9
  249 + 1 | 1
  250 +(10 rows)
  251 +
  252 +SELECT ntile(3) OVER (w), js_ntile(3) OVER (w) FROM empsalary WINDOW w AS (ORDER BY salary);
  253 + ntile | js_ntile
  254 +-------+----------
  255 + 1 | 1
  256 + 1 | 1
  257 + 1 | 1
  258 + 1 | 1
  259 + 2 | 2
  260 + 2 | 2
  261 + 2 | 2
  262 + 3 | 3
  263 + 3 | 3
  264 + 3 | 3
  265 +(10 rows)
  266 +
  267 +SELECT lag(enroll_date) OVER (w), js_lag(enroll_date) OVER (w) FROM empsalary WINDOW w AS (ORDER BY salary);
  268 + lag | js_lag
  269 +------------+------------
  270 + |
  271 + 12-10-2007 | 12-10-2007
  272 + 12-23-2006 | 12-23-2006
  273 + 01-01-2008 | 01-01-2008
  274 + 01-01-2008 | 01-01-2008
  275 + 08-08-2007 | 08-08-2007
  276 + 08-01-2007 | 08-01-2007
  277 + 10-01-2006 | 10-01-2006
  278 + 08-15-2007 | 08-15-2007
  279 + 08-01-2007 | 08-01-2007
  280 +(10 rows)
  281 +
  282 +SELECT lead(enroll_date) OVER (w), js_lead(enroll_date) OVER (w) FROM empsalary WINDOW w AS (ORDER BY salary);
  283 + lead | js_lead
  284 +------------+------------
  285 + 12-23-2006 | 12-23-2006
  286 + 01-01-2008 | 01-01-2008
  287 + 01-01-2008 | 01-01-2008
  288 + 08-08-2007 | 08-08-2007
  289 + 08-01-2007 | 08-01-2007
  290 + 10-01-2006 | 10-01-2006
  291 + 08-15-2007 | 08-15-2007
  292 + 08-01-2007 | 08-01-2007
  293 + 10-01-2006 | 10-01-2006
  294 + |
  295 +(10 rows)
  296 +
  297 +SELECT first_value(empno) OVER (w ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING),
  298 + js_first_value(empno) OVER (w ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING)
  299 + FROM empsalary WINDOW w AS (ORDER BY salary);
  300 + first_value | js_first_value
  301 +-------------+----------------
  302 + 5 | 5
  303 + 5 | 5
  304 + 5 | 5
  305 + 2 | 2
  306 + 7 | 7
  307 + 9 | 9
  308 + 4 | 4
  309 + 3 | 3
  310 + 1 | 1
  311 + 11 | 11
  312 +(10 rows)
  313 +
  314 +SELECT last_value(empno) OVER (w ROWS BETWEEN 3 PRECEDING AND 1 PRECEDING),
  315 + js_last_value(empno) OVER (w ROWS BETWEEN 3 PRECEDING AND 1 PRECEDING)
  316 + FROM empsalary WINDOW w AS (ORDER BY salary);
  317 + last_value | js_last_value
  318 +------------+---------------
  319 + |
  320 + 5 | 5
  321 + 2 | 2
  322 + 7 | 7
  323 + 9 | 9
  324 + 4 | 4
  325 + 3 | 3
  326 + 1 | 1
  327 + 11 | 11
  328 + 10 | 10
  329 +(10 rows)
  330 +
  331 +SELECT nth_value(empno, 2) OVER (w ROWS BETWEEN 1 FOLLOWING AND 3 FOLLOWING),
  332 + js_nth_value(empno, 2) OVER (w ROWS BETWEEN 1 FOLLOWING AND 3 FOLLOWING)
  333 + FROM empsalary WINDOW w AS (ORDER BY salary);
  334 + nth_value | js_nth_value
  335 +-----------+--------------
  336 + 7 | 7
  337 + 9 | 9
  338 + 4 | 4
  339 + 3 | 3
  340 + 1 | 1
  341 + 11 | 11
  342 + 10 | 10
  343 + 8 | 8
  344 + |
  345 + |
  346 +(10 rows)
  347 +
  348 +CREATE FUNCTION bad_alloc(sz text) RETURNS void AS $$
  349 + var winobj = plv8.get_window_object();
  350 + var context = winobj.get_partition_local(sz - 0) || {};
  351 + context.long_text_key_and_value = "blablablablablablablablablablablablablablablabla";
  352 + winobj.set_partition_local(context);
  353 +$$ LANGUAGE plv8 WINDOW;
  354 +SELECT bad_alloc('5') OVER ();
  355 +ERROR: Error: window local memory overflow
  356 +DETAIL: bad_alloc() LINE 5: winobj.set_partition_local(context);
  357 +SELECT bad_alloc('not a number') OVER ();
  358 +ERROR: Error: window local memory overflow
  359 +DETAIL: bad_alloc() LINE 5: winobj.set_partition_local(context);
  360 +SELECT bad_alloc('1000') OVER (); -- not so bad
  361 + bad_alloc
  362 +-----------
  363 +
  364 +(1 row)
  365 +
  366 +CREATE FUNCTION non_window() RETURNS void AS $$
  367 + var winobj = plv8.get_window_object();
  368 +$$ LANGUAGE plv8;
  369 +SELECT non_window();
  370 +ERROR: Error: get_window_object called in wrong context
  371 +DETAIL: non_window() LINE 2: var winobj = plv8.get_window_object();
185 plv8.cc
@@ -132,7 +132,8 @@ extern const unsigned char livescript_binary_data[];
132 132 * lower_case_functions are postgres-like C functions.
133 133 * They could raise errors with elog/ereport(ERROR).
134 134 */
135   -static plv8_proc *plv8_get_proc(Oid fn_oid, MemoryContext fn_mcxt, bool validate, char ***argnames) throw();
  135 +static plv8_proc *plv8_get_proc(Oid fn_oid, FunctionCallInfo fcinfo,
  136 + bool validate, char ***argnames) throw();
136 137 static void plv8_xact_cb(XactEvent event, void *arg);
137 138
138 139 /*
@@ -140,7 +141,7 @@ static void plv8_xact_cb(XactEvent event, void *arg);
140 141 * They could raise errors with C++ throw statements, or never throw exceptions.
141 142 */
142 143 static plv8_exec_env *CreateExecEnv(Handle<Function> script);
143   -static plv8_proc *Compile(Oid fn_oid, MemoryContext fn_mcxt,
  144 +static plv8_proc *Compile(Oid fn_oid, FunctionCallInfo fcinfo,
144 145 bool validate, bool is_trigger, Dialect dialect);
145 146 static Local<Function> CompileFunction(const char *proname, int proarglen,
146 147 const char *proargs[], const char *prosrc,
@@ -155,12 +156,36 @@ static Persistent<ObjectTemplate> GetGlobalObjectTemplate();
155 156
156 157 /* A GUC to specify a custom start up function to call */
157 158 static char *plv8_start_proc = NULL;
  159 +
  160 +/* A GUC to specify the remote debugger port */
  161 +static int plv8_debugger_port;
158 162 /*
159 163 * We use vector instead of hash since the size of this array
160 164 * is expected to be short in most cases.
161 165 */
162 166 static std::vector<plv8_context *> ContextVector;
163 167
  168 +#ifdef ENABLE_DEBUGGER_SUPPORT
  169 +v8::Persistent<v8::Context> debug_message_context;
  170 +
  171 +void DispatchDebugMessages() {
  172 + // We are in some random thread. We should already have v8::Locker acquired
  173 + // (we requested this when registered this callback). We was called
  174 + // because new debug messages arrived; they may have already been processed,
  175 + // but we shouldn't worry about this.
  176 + //
  177 + // All we have to do is to set context and call ProcessDebugMessages.
  178 + //
  179 + // We should decide which V8 context to use here. This is important for
  180 + // "evaluate" command, because it must be executed some context.
  181 + // In our sample we have only one context, so there is nothing really to
  182 + // think about.
  183 + v8::Context::Scope scope(debug_message_context);
  184 +
  185 + v8::Debug::ProcessDebugMessages();
  186 +}
  187 +#endif // ENABLE_DEBUGGER_SUPPORT
  188 +
164 189 void
165 190 _PG_init(void)
166 191 {
@@ -184,6 +209,19 @@ _PG_init(void)
184 209 NULL,
185 210 NULL);
186 211
  212 + DefineCustomIntVariable("plv8.debugger_port",
  213 + gettext_noop("V8 remote debug port."),
  214 + gettext_noop("The default value is 35432. "
  215 + "This is effective only if PLV8 is built with ENABLE_DEBUGGER_SUPPORT."),
  216 + &plv8_debugger_port,
  217 + 35432, 0, 65536,
  218 + PGC_USERSET, 0,
  219 +#if PG_VERSION_NUM >= 90100
  220 + NULL,
  221 +#endif
  222 + NULL,
  223 + NULL);
  224 +
187 225 RegisterXactCallback(plv8_xact_cb, NULL);
188 226
189 227 EmitWarningsOnPlaceholders("plv8");
@@ -236,11 +274,14 @@ common_pl_call_handler(PG_FUNCTION_ARGS, Dialect dialect) throw()
236 274
237 275 try
238 276 {
  277 +#ifdef ENABLE_DEBUGGER_SUPPORT
  278 + Locker lock;
  279 +#endif // ENABLE_DEBUGGER_SUPPORT
239 280 HandleScope handle_scope;
240 281
241 282 if (!fcinfo->flinfo->fn_extra)
242 283 {
243   - plv8_proc *proc = Compile(fn_oid, fcinfo->flinfo->fn_mcxt,
  284 + plv8_proc *proc = Compile(fn_oid, fcinfo,
244 285 false, is_trigger, dialect);
245 286 proc->xenv = CreateExecEnv(proc->cache->function);
246 287 fcinfo->flinfo->fn_extra = proc;
@@ -292,6 +333,9 @@ common_pl_inline_handler(PG_FUNCTION_ARGS, Dialect dialect) throw()
292 333
293 334 try
294 335 {
  336 +#ifdef ENABLE_DEBUGGER_SUPPORT
  337 + Locker lock;
  338 +#endif // ENABLE_DEBUGGER_SUPPORT
295 339 HandleScope handle_scope;
296 340 char *source_text = codeblock->source_text;
297 341
@@ -357,9 +401,29 @@ CallFunction(PG_FUNCTION_ARGS, plv8_exec_env *xenv,
357 401 Handle<Context> context = xenv->context;
358 402 Context::Scope context_scope(context);
359 403 Handle<v8::Value> args[FUNC_MAX_ARGS];
  404 + Handle<Object> plv8obj;
360 405
361   - for (int i = 0; i < nargs; i++)
362   - args[i] = ToValue(fcinfo->arg[i], fcinfo->argnull[i], &argtypes[i]);
  406 + WindowFunctionSupport support(context, fcinfo);
  407 +
  408 + /*
  409 + * In window function case, we cannot see the argument datum
  410 + * in fcinfo. Instead, get them by WinGetFuncArgCurrent().
  411 + */
  412 + if (support.IsWindowCall())
  413 + {
  414 + WindowObject winobj = support.GetWindowObject();
  415 + for (int i = 0; i < nargs; i++)
  416 + {
  417 + bool isnull;
  418 + Datum arg = WinGetFuncArgCurrent(winobj, i, &isnull);
  419 + args[i] = ToValue(arg, isnull, &argtypes[i]);
  420 + }
  421 + }
  422 + else
  423 + {
  424 + for (int i = 0; i < nargs; i++)
  425 + args[i] = ToValue(fcinfo->arg[i], fcinfo->argnull[i], &argtypes[i]);
  426 + }
363 427
364 428 Local<Function> fn =
365 429 Local<Function>::Cast(xenv->recv->GetInternalField(0));
@@ -446,24 +510,12 @@ CallSRFunction(PG_FUNCTION_ARGS, plv8_exec_env *xenv,
446 510 Context::Scope context_scope(context);
447 511 Converter conv(tupdesc, proc->functypclass == TYPEFUNC_SCALAR);
448 512 Handle<v8::Value> args[FUNC_MAX_ARGS + 1];
449   - Handle<Object> plv8obj = Handle<Object>::Cast(
450   - context->Global()->Get(String::NewSymbol("plv8")));
451 513
452 514 /*
453 515 * In case this is nested via SPI, stash pre-registered converters
454 516 * for the previous SRF.
455 517 */
456   - Handle<v8::Value> conv_prev, tupstore_prev;
457   - if (!plv8obj->GetInternalField(PLV8_INTNL_CONV).IsEmpty())
458   - {
459   - conv_prev = plv8obj->GetInternalField(PLV8_INTNL_CONV);
460   - tupstore_prev = plv8obj->GetInternalField(PLV8_INTNL_TUPSTORE);
461   - }
462   - /*
463   - * Setup return_next information
464   - */
465   - plv8obj->SetInternalField(PLV8_INTNL_CONV, External::Wrap(&conv));
466   - plv8obj->SetInternalField(PLV8_INTNL_TUPSTORE, External::Wrap(tupstore));
  518 + SRFSupport support(context, &conv, tupstore);
467 519
468 520 for (int i = 0; i < nargs; i++)
469 521 args[i] = ToValue(fcinfo->arg[i], fcinfo->argnull[i], &argtypes[i]);
@@ -471,14 +523,7 @@ CallSRFunction(PG_FUNCTION_ARGS, plv8_exec_env *xenv,
471 523 Local<Function> fn =
472 524 Local<Function>::Cast(xenv->recv->GetInternalField(0));
473 525
474   - Handle<v8::Value> result =
475   - DoCall(fn, xenv->recv, nargs, args);
476   -
477   - /*
478   - * Restore old information.
479   - */
480   - plv8obj->SetInternalField(PLV8_INTNL_CONV, conv_prev);
481   - plv8obj->SetInternalField(PLV8_INTNL_TUPSTORE, tupstore_prev);
  526 + Handle<v8::Value> result = DoCall(fn, xenv->recv, nargs, args);
482 527
483 528 if (result->IsUndefined())
484 529 {
@@ -615,8 +660,16 @@ CallTrigger(PG_FUNCTION_ARGS, plv8_exec_env *xenv)
615 660 if (newtup.IsEmpty())
616 661 throw js_error(try_catch);
617 662
618   - if (TRIGGER_FIRED_BY_UPDATE(event) &&
619   - !(newtup->IsUndefined() || newtup->IsNull()))
  663 + /*
  664 + * If the function specifically returned null, return NULL to
  665 + * tell executor to skip the operation. Otherwise, the function
  666 + * result is the tuple to be returned.
  667 + */
  668 + if (newtup->IsNull() || !TRIGGER_FIRED_FOR_ROW(event))
  669 + {
  670 + result = PointerGetDatum(NULL);
  671 + }
  672 + else if (!newtup->IsUndefined())
620 673 {
621 674 TupleDesc tupdesc = RelationGetDescr(rel);
622 675 Converter conv(tupdesc);
@@ -649,7 +702,7 @@ common_pl_call_validator(PG_FUNCTION_ARGS, Dialect dialect) throw()
649 702 functyptype = get_typtype(proc->prorettype);
650 703
651 704 /* Disallow pseudotype result */
652   - /* except for TRIGGER, RECORD, or VOID */
  705 + /* except for TRIGGER, RECORD, INTERNAL, VOID or polymorphic types */
653 706 if (functyptype == TYPTYPE_PSEUDO)
654 707 {
655 708 /* we assume OPAQUE with no arguments means a trigger */
@@ -657,7 +710,9 @@ common_pl_call_validator(PG_FUNCTION_ARGS, Dialect dialect) throw()
657 710 (proc->prorettype == OPAQUEOID && proc->pronargs == 0))
658 711 is_trigger = true;
659 712 else if (proc->prorettype != RECORDOID &&
660   - proc->prorettype != VOIDOID)
  713 + proc->prorettype != VOIDOID &&
  714 + proc->prorettype != INTERNALOID &&
  715 + !IsPolymorphicType(proc->prorettype))
661 716 ereport(ERROR,
662 717 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
663 718 errmsg("PL/v8 functions cannot return type %s",
@@ -668,7 +723,11 @@ common_pl_call_validator(PG_FUNCTION_ARGS, Dialect dialect) throw()
668 723
669 724 try
670 725 {
671   - plv8_proc *proc = Compile(fn_oid, fcinfo->flinfo->fn_mcxt,
  726 +#ifdef ENABLE_DEBUGGER_SUPPORT
  727 + Locker lock;
  728 +#endif // ENABLE_DEBUGGER_SUPPORT
  729 + /* Don't use validator's fcinfo */