Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Introduce external array suport.

v8 has ever invented TypedArray, which maps JS array to fixed-length
element memory, originally for canvas pixel support.  We utilize it
for bytea conversion and int/float array.  In the array case, we had
to annotate the type by domain as it's an external array type to JS,
otherwise, we would loose the current flexibility around array conversion.
Those annotation type names may be changed in the future.  Also, it
might be reasonable to introduce TypedArray such like in d8, in order
for users to make their own fixed-length element array, but it will be
a different story.
  • Loading branch information...
commit 7b662505545f91c60eeac3fe4453e113c62ae9b5 1 parent a9c0580
@umitanuki umitanuki authored
Showing with 174 additions and 3 deletions.
  1. +2 −2 Makefile
  2. +1 −0  plv8.h
  3. +7 −0 plv8.sql.common
  4. +164 −1 plv8_type.cc
View
4 Makefile
@@ -73,7 +73,7 @@ ifeq ($(shell test $(PG_VERSION_NUM) -ge 90100 && echo yes), yes)
DATA_built =
all: $(DATA)
%--$(PLV8_VERSION).sql: plv8.sql.common
- sed -e 's/@LANG_NAME@/$*/g' $< | $(CC) -E -P $(CPPFLAGS) - > $@
+ sed -e 's/@LANG_NAME@/$*/g' $< | $(CC) -E -P $(CPPFLAGS) -DLANG_$* - > $@
%.control: plv8.control.common
sed -e 's/@PLV8_VERSION@/$(PLV8_VERSION)/g' $< | $(CC) -E -P -DLANG_$* - > $@
subclean:
@@ -97,7 +97,7 @@ endif
DATA = uninstall_plv8.sql
%.sql.in: plv8.sql.common
- sed -e 's/@LANG_NAME@/$*/g' $< | $(CC) -E -P $(CPPFLAGS) - > $@
+ sed -e 's/@LANG_NAME@/$*/g' $< | $(CC) -E -P $(CPPFLAGS) -DLANG_$* - > $@
subclean:
rm -f plv8_config.h *.sql.in $(JSCS)
View
1  plv8.h
@@ -77,6 +77,7 @@ typedef struct plv8_type
char category;
FmgrInfo fn_input;
FmgrInfo fn_output;
+ v8::ExternalArrayType ext_array;
} plv8_type;
/*
View
7 plv8.sql.common
@@ -24,6 +24,13 @@ CREATE TRUSTED LANGUAGE @LANG_NAME@
#endif
VALIDATOR @LANG_NAME@_call_validator;
+#ifdef LANG_plv8
+CREATE DOMAIN plv8_int2array AS int2[];
+CREATE DOMAIN plv8_int4array AS int4[];
+CREATE DOMAIN plv8_float4array AS float4[];
+CREATE DOMAIN plv8_float8array AS float8[];
+#endif
+
#if PG_VERSION_NUM < 90100
COMMIT;
#endif
View
165 plv8_type.cc
@@ -23,6 +23,7 @@ extern "C" {
#include "utils/datetime.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
+#include "utils/syscache.h"
#include "utils/typcache.h"
#undef delete
@@ -65,8 +66,54 @@ plv8_fill_type(plv8_type *type, Oid typid, MemoryContext mcxt)
Oid elemid = get_element_type(typid);
if (elemid == InvalidOid)
+ {
+ HeapTuple tp;
+ Form_pg_type typtup;
+
+#if PG_VERSION_NUM < 90100
+ tp = SearchSysCache(TYPEOID, ObjectIdGetDatum(typid), 0, 0, 0);
+#else
+ tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+#endif
+ if (HeapTupleIsValid(tp))
+ {
+ /*
+ * Check if the type is the external array types.
+ */
+ typtup = (Form_pg_type) GETSTRUCT(tp);
+ if (strcmp(NameStr(typtup->typname),
+ "plv8_int2array") == 0)
+ {
+ type->ext_array = kExternalShortArray;
+ }
+ else if (strcmp(NameStr(typtup->typname),
+ "plv8_int4array") == 0)
+ {
+ type->ext_array = kExternalIntArray;
+ }
+ else if (strcmp(NameStr(typtup->typname),
+ "plv8_float4array") == 0)
+ {
+ type->ext_array = kExternalFloatArray;
+ }
+ else if (strcmp(NameStr(typtup->typname),
+ "plv8_float8array") == 0)
+ {
+ type->ext_array = kExternalDoubleArray;
+ }
+
+ ReleaseSysCache(tp);
+ }
+
+ if (type->ext_array)
+ return;
+
+ /*
+ * Otherwise, we don't know how to handle it.
+ */
ereport(ERROR,
(errmsg("cannot determine element type of array: %u", typid)));
+ }
type->typid = elemid;
get_typlenbyvalalign(type->typid, &type->len, &type->byval, &type->align);
@@ -103,6 +150,70 @@ inferred_datum_type(Handle<v8::Value> value)
return InvalidOid;
}
+static Local<Object>
+CreateExternalArray(void *data, ExternalArrayType array_type, int byte_size,
+ Datum datum)
+{
+ static Persistent<ObjectTemplate> externalArray;
+
+ if (externalArray.IsEmpty())
+ {
+ externalArray = Persistent<ObjectTemplate>::New(ObjectTemplate::New());
+ externalArray->SetInternalFieldCount(1);
+ }
+
+ Local<Object> array = externalArray->NewInstance();
+ int length;
+
+ switch (array_type)
+ {
+ case kExternalByteArray:
+ case kExternalUnsignedByteArray:
+ length = byte_size;
+ break;
+ case kExternalShortArray:
+ case kExternalUnsignedShortArray:
+ length = byte_size / sizeof(int16);
+ break;
+ case kExternalIntArray:
+ case kExternalUnsignedIntArray:
+ length = byte_size / sizeof(int32);
+ break;
+ case kExternalFloatArray:
+ length = byte_size / sizeof(float4);
+ break;
+ case kExternalDoubleArray:
+ length = byte_size / sizeof(float8);
+ break;
+ default:
+ throw js_error("unexpected array type");
+ }
+ array->SetIndexedPropertiesToExternalArrayData(
+ data, array_type, length);
+ array->Set(String::New("length"), Int32::New(length), ReadOnly);
+ array->SetInternalField(0, External::Wrap(DatumGetPointer(datum)));
+
+ return array;
+}
+
+static void *
+ExtractExternalArrayDatum(Handle<v8::Value> value)
+{
+ if (value->IsUndefined() || value->IsNull())
+ return NULL;
+
+ if (value->IsObject())
+ {
+ Handle<Object> object = Handle<Object>::Cast(value);
+ if (object->GetIndexedPropertiesExternalArrayData())
+ {
+ return External::Unwrap(object->GetInternalField(0));
+ }
+ }
+
+ return NULL;
+}
+
Datum
ToDatum(Handle<v8::Value> value, bool *isnull, plv8_type *type)
{
@@ -179,6 +290,14 @@ ToScalarDatum(Handle<v8::Value> value, bool *isnull, plv8_type *type)
if (value->IsDate())
return EpochToTimestampTz(value->NumberValue());
break;
+ case BYTEAOID:
+ {
+ void *datum_p = ExtractExternalArrayDatum(value);
+ if (datum_p)
+ {
+ return PointerGetDatum(datum_p);
+ }
+ }
#if PG_VERSION_NUM >= 90200
case JSONOID:
if (value->IsObject() || value->IsArray())
@@ -241,6 +360,13 @@ ToArrayDatum(Handle<v8::Value> value, bool *isnull, plv8_type *type)
return (Datum) 0;
}
+ void *datum_p = ExtractExternalArrayDatum(value);
+ if (datum_p)
+ {
+ *isnull = false;
+ return PointerGetDatum(datum_p);
+ }
+
Handle<Array> array(Handle<Array>::Cast(value));
if (array.IsEmpty() || !array->IsArray())
throw js_error("value is not an Array");
@@ -348,6 +474,15 @@ ToScalarValue(Datum datum, bool isnull, plv8_type *type)
pfree(p); // free if detoasted
return result;
}
+ case BYTEAOID:
+ {
+ void *p = PG_DETOAST_DATUM_COPY(datum);
+
+ return CreateExternalArray(VARDATA_ANY(p),
+ kExternalUnsignedByteArray,
+ VARSIZE_ANY_EXHDR(p),
+ PointerGetDatum(p));
+ }
#if PG_VERSION_NUM >= 90200
case JSONOID:
{
@@ -367,7 +502,11 @@ ToScalarValue(Datum datum, bool isnull, plv8_type *type)
Handle<Function>::Cast(JSON->Get(String::NewSymbol("parse")));
// return JSON.parse.apply(JSON, jsonString);
- return scope.Close(JSON_parse->Call(JSON, 1, &jsonString));
+ Local<v8::Value> result = scope.Close(JSON_parse->Call(JSON, 1, &jsonString));
+
+ if (p != DatumGetPointer(datum))
+ pfree(p); // free if detoasted
+ return result;
}
#endif
default:
@@ -382,6 +521,30 @@ ToArrayValue(Datum datum, bool isnull, plv8_type *type)
bool *nulls;
int nelems;
+ /*
+ * If we can use an external array, do it instead.
+ */
+ if (type->ext_array)
+ {
+ ArrayType *array = DatumGetArrayTypePCopy(datum);
+
+ /*
+ * We allow only non-NULL, 1-dim array.
+ */
+ if (!ARR_HASNULL(array) && ARR_NDIM(array) <= 1)
+ {
+ int data_bytes = ARR_SIZE(array) -
+ ARR_OVERHEAD_NONULLS(1);
+ return CreateExternalArray(ARR_DATA_PTR(array),
+ type->ext_array,
+ data_bytes,
+ PointerGetDatum(array));
+ }
+
+ throw js_error("NULL element, or multi-dimension array not allowed"
+ " in external array type");
+ }
+
deconstruct_array(DatumGetArrayTypeP(datum),
type->typid, type->len, type->byval, type->align,
&values, &nulls, &nelems);
Please sign in to comment.
Something went wrong with that request. Please try again.