diff --git a/arrow-tools/arrow_pgsql.c b/arrow-tools/arrow_pgsql.c index 0e77c7669..e96bc1394 100644 --- a/arrow-tools/arrow_pgsql.c +++ b/arrow-tools/arrow_pgsql.c @@ -1813,7 +1813,8 @@ assignArrowTypePgSQL(SQLfield *column, if (extname != NULL) { /* contrib/cube (relocatable) */ - if (strcmp(extname, "cube") == 0 && + if (strcmp(typname, "cube") == 0 && + strcmp(extname, "cube") == 0 && strcmp(extschema, typnamespace) == 0) { __assignArrowTypeHint(column, typname, typnamespace); diff --git a/src/arrow_fdw.c b/src/arrow_fdw.c index cc3302a6b..7c3133c05 100644 --- a/src/arrow_fdw.c +++ b/src/arrow_fdw.c @@ -205,10 +205,13 @@ arrowFieldGetPGTypeHint(const ArrowField *field) for (int i=0; i < field->_num_custom_metadata; i++) { ArrowKeyValue *kv = &field->custom_metadata[i]; - char *namebuf, *pos; + Oid extension_oid = InvalidOid; Oid namespace_oid = PG_CATALOG_NAMESPACE; - HeapTuple tup; + Oid hint_oid; + bool namespace_specified = false; + char *namebuf, *pos; + /* pg_type = NAMESPACE.TYPENAME@EXTENSION */ if (strcmp(kv->key, "pg_type") != 0) continue; namebuf = alloca(kv->_value_len + 10); @@ -221,17 +224,50 @@ arrowFieldGetPGTypeHint(const ArrowField *field) if (!OidIsValid(namespace_oid)) continue; namebuf = pos; + namespace_specified = true; + } + pos = strchr(namebuf, '@'); + if (pos) + { + *pos++ = '\0'; + extension_oid = get_extension_oid(pos, true); + if (!OidIsValid(extension_oid)) + continue; } - tup = SearchSysCache2(TYPENAMENSP, - PointerGetDatum(namebuf), - ObjectIdGetDatum(namespace_oid)); - if (HeapTupleIsValid(tup)) + /* 1st try: user specified namespace or 'pg_catalog' */ + hint_oid = GetSysCacheOid2(TYPENAMENSP, + Anum_pg_type_oid, + CStringGetDatum(namebuf), + ObjectIdGetDatum(namespace_oid)); + if (OidIsValid(hint_oid)) { - Oid hint = ((Form_pg_type) GETSTRUCT(tup))->oid; + if (!OidIsValid(extension_oid) || + getExtensionOfObject(TypeRelationId, + hint_oid) == extension_oid) + return hint_oid; + } + /* 2nd try: any namespace (if not specified) */ + if (!namespace_specified) + { + CatCList *typelist; + HeapTuple htup; - ReleaseSysCache(tup); + typelist = SearchSysCacheList1(TYPENAMENSP, + CStringGetDatum(namebuf)); + for (int k=0; k < typelist->n_members; k++) + { + htup = &typelist->members[k]->tuple; + hint_oid = ((Form_pg_type) GETSTRUCT(htup))->oid; - return hint; + if (!OidIsValid(extension_oid) || + getExtensionOfObject(TypeRelationId, + hint_oid) == extension_oid) + { + ReleaseCatCacheList(typelist); + return hint_oid; + } + } + ReleaseCatCacheList(typelist); } } return InvalidOid; @@ -1301,11 +1337,7 @@ __arrowFieldTypeToPGType(const ArrowField *field, { case 8: attopts.unitsz = sizeof(int8_t); - type_oid = - GetSysCacheOid2(TYPENAMENSP, - Anum_pg_type_oid, - CStringGetDatum("int1"), - ObjectIdGetDatum(PG_CATALOG_NAMESPACE)); + type_oid = get_int1_type_oid(false); break; case 16: attopts.unitsz = sizeof(int16_t); @@ -1333,11 +1365,7 @@ __arrowFieldTypeToPGType(const ArrowField *field, { case ArrowPrecision__Half: attopts.unitsz = sizeof(float2_t); - type_oid = - GetSysCacheOid2(TYPENAMENSP, - Anum_pg_type_oid, - CStringGetDatum("float2"), - ObjectIdGetDatum(PG_CATALOG_NAMESPACE)); + type_oid = get_float2_type_oid(false); break; case ArrowPrecision__Single: attopts.unitsz = sizeof(float4_t); @@ -1490,13 +1518,21 @@ __arrowFieldTypeToPGType(const ArrowField *field, case ArrowNodeTag__Binary: attopts.tag = ArrowType__Binary; attopts.unitsz = sizeof(uint32_t); - type_oid = BYTEAOID; + if (OidIsValid(hint_oid) && + hint_oid == get_cube_type_oid(true)) + type_oid = hint_oid; + else + type_oid = BYTEAOID; break; case ArrowNodeTag__LargeBinary: attopts.tag = ArrowType__LargeBinary; attopts.unitsz = sizeof(uint64_t); - type_oid = BYTEAOID; + if (OidIsValid(hint_oid) && + hint_oid == get_cube_type_oid(true)) + type_oid = hint_oid; + else + type_oid = BYTEAOID; break; case ArrowNodeTag__List: @@ -2786,54 +2822,47 @@ static void pg_datum_arrow_ref(kern_data_store *kds, size_t index, Datum *p_datum, bool *p_isnull); - static Datum pg_varlena32_arrow_ref(kern_data_store *kds, - kern_colmeta *cmeta, size_t index) + kern_colmeta *cmeta, + size_t index, bool *p_isnull) { - uint32_t *offset = (uint32_t *)((char *)kds + - __kds_unpack(cmeta->values_offset)); - char *extra = (char *)kds + __kds_unpack(cmeta->extra_offset); - uint32_t len; - struct varlena *res; - - if (sizeof(uint32_t) * (index+2) > __kds_unpack(cmeta->values_length)) - elog(ERROR, "corruption? varlena index out of range"); - len = offset[index+1] - offset[index]; - if (offset[index] > offset[index+1] || - offset[index+1] > __kds_unpack(cmeta->extra_length)) - elog(ERROR, "corruption? varlena points out of extra buffer"); - if (len >= (1UL<vl_dat, addr, length); + SET_VARSIZE(res, VARHDRSZ + length); + } return PointerGetDatum(res); } static Datum pg_varlena64_arrow_ref(kern_data_store *kds, - kern_colmeta *cmeta, size_t index) + kern_colmeta *cmeta, + size_t index, bool *p_isnull) { - uint64_t *offset = (uint64_t *)((char *)kds + - __kds_unpack(cmeta->values_offset)); - char *extra = (char *)kds + __kds_unpack(cmeta->extra_offset); - uint64_t len; - struct varlena *res; - - if (sizeof(uint64_t) * (index+2) > __kds_unpack(cmeta->values_length)) - elog(ERROR, "corruption? varlena index out of range"); - len = offset[index+1] - offset[index]; - if (offset[index] > offset[index+1] || - offset[index+1] > __kds_unpack(cmeta->extra_length)) - elog(ERROR, "corruption? varlena points out of extra buffer"); - if (len >= (1UL<vl_dat, addr, length); + SET_VARSIZE(res, VARHDRSZ + length); + } return PointerGetDatum(res); } @@ -2863,11 +2892,12 @@ pg_bool_arrow_ref(kern_data_store *kds, { uint8_t *bitmap = (uint8_t *)kds + __kds_unpack(cmeta->values_offset); size_t length = __kds_unpack(cmeta->values_length); - uint8_t mask = (1 << (index & 7)); + bool rv; - if (sizeof(uint8_t) * index >= length) + if (sizeof(uint8_t) * (index>>3) >= length) elog(ERROR, "corruption? bool points out of range"); - return BoolGetDatum((bitmap[index>>3] & mask) != 0 ? true : false); + rv = ((bitmap[index>>3] & (1<<(index&7))) != 0); + return BoolGetDatum(rv); } static Datum @@ -3196,18 +3226,12 @@ pg_datum_arrow_ref(kern_data_store *kds, Datum datum = 0; bool isnull = false; - if (cmeta->nullmap_offset != 0) + if (KDS_ARROW_CHECK_ISNULL(kds, cmeta, index)) { - size_t nullmap_offset = __kds_unpack(cmeta->nullmap_offset); - uint8 *nullmap = (uint8 *)kds + nullmap_offset; - - if (att_isnull(index, nullmap)) - { - isnull = true; - goto out; - } + isnull = true; + goto out; } - + switch (cmeta->attopts.tag) { case ArrowType__Int: @@ -3234,11 +3258,11 @@ pg_datum_arrow_ref(kern_data_store *kds, break; case ArrowType__Utf8: case ArrowType__Binary: - datum = pg_varlena32_arrow_ref(kds, cmeta, index); + datum = pg_varlena32_arrow_ref(kds, cmeta, index, &isnull); break; case ArrowType__LargeUtf8: case ArrowType__LargeBinary: - datum = pg_varlena64_arrow_ref(kds, cmeta, index); + datum = pg_varlena64_arrow_ref(kds, cmeta, index, &isnull); break; case ArrowType__FixedSizeBinary: diff --git a/src/codegen.c b/src/codegen.c index 674dceb4c..e809effa4 100644 --- a/src/codegen.c +++ b/src/codegen.c @@ -68,6 +68,98 @@ get_extension_name_by_object(Oid class_id, Oid object_id) return NULL; } +/* + * type oid cache + */ +static Oid __type_oid_cache_int1 = UINT_MAX; +Oid +get_int1_type_oid(bool missing_ok) +{ + if (__type_oid_cache_int1 == UINT_MAX) + { + const char *ext_name; + Oid type_oid; + + type_oid = GetSysCacheOid2(TYPENAMENSP, + Anum_pg_type_oid, + CStringGetDatum("int1"), + ObjectIdGetDatum(PG_CATALOG_NAMESPACE)); + if (OidIsValid(type_oid)) + { + ext_name = get_extension_name_by_object(TypeRelationId, type_oid); + if (!ext_name || strcmp(ext_name, "pg_strom") != 0) + type_oid = InvalidOid; + } + __type_oid_cache_int1 = type_oid; + } + if (!missing_ok && !OidIsValid(__type_oid_cache_int1)) + elog(ERROR, "type 'int1' is not installed"); + return __type_oid_cache_int1; +} + +static Oid __type_oid_cache_float2 = UINT_MAX; +Oid +get_float2_type_oid(bool missing_ok) +{ + if (__type_oid_cache_float2 == UINT_MAX) + { + const char *ext_name; + Oid type_oid; + + type_oid = GetSysCacheOid2(TYPENAMENSP, + Anum_pg_type_oid, + CStringGetDatum("float2"), + ObjectIdGetDatum(PG_CATALOG_NAMESPACE)); + if (OidIsValid(type_oid)) + { + ext_name = get_extension_name_by_object(TypeRelationId, type_oid); + if (!ext_name || strcmp(ext_name, "pg_strom") != 0) + type_oid = InvalidOid; + } + __type_oid_cache_float2 = type_oid; + } + if (!missing_ok && !OidIsValid(__type_oid_cache_float2)) + elog(ERROR, "type 'float2' is not installed"); + return __type_oid_cache_float2; +} + +static Oid __type_oid_cache_cube = UINT_MAX; +Oid +get_cube_type_oid(bool missing_ok) +{ + if (__type_oid_cache_cube == UINT_MAX) + { + Oid type_oid = InvalidOid; + CatCList *typelist; + + typelist = SearchSysCacheList1(TYPENAMENSP, + CStringGetDatum("cube")); + for (int i=0; i < typelist->n_members; i++) + { + HeapTuple type_htup = &typelist->members[i]->tuple; + Form_pg_type type_form = (Form_pg_type)GETSTRUCT(type_htup); + const char *ext_name; + + ext_name = get_extension_name_by_object(TypeRelationId, + type_form->oid); + if (ext_name && strcmp(ext_name, "cube") == 0) + { + type_oid = type_form->oid; + break; + } + } + ReleaseCatCacheList(typelist); + + __type_oid_cache_cube = type_oid; + } + if (!missing_ok && !OidIsValid(__type_oid_cache_cube)) + elog(ERROR, "type 'cube' is not installed"); + return __type_oid_cache_cube; +} + +/* + * build_basic_devtype_info + */ static devtype_info * build_basic_devtype_info(TypeCacheEntry *tcache, const char *ext_name) { @@ -127,6 +219,9 @@ build_basic_devtype_info(TypeCacheEntry *tcache, const char *ext_name) return NULL; /* not found */ } +/* + * build_composite_devtype_info + */ static devtype_info * build_composite_devtype_info(TypeCacheEntry *tcache, const char *ext_name) { @@ -179,6 +274,9 @@ build_composite_devtype_info(TypeCacheEntry *tcache, const char *ext_name) return dtype; } +/* + * build_array_devtype_info + */ static devtype_info * build_array_devtype_info(TypeCacheEntry *tcache, const char *ext_name) { @@ -754,6 +852,14 @@ devtype_box2df_hash(bool isnull, Datum value) elog(ERROR, "box2df type has no device hash function"); } +static uint32_t +devtype_cube_hash(bool isnull, Datum value) +{ + if (isnull) + return 0; + return hash_any((unsigned char *)VARDATA_ANY(value), VARSIZE_ANY_EXHDR(value)); +} + /* * Built-in device functions/operators */ @@ -4325,6 +4431,10 @@ pgstrom_devcache_invalidator(Datum arg, int cacheid, uint32 hashvalue) memset(devtype_info_slot, 0, sizeof(List *) * DEVTYPE_INFO_NSLOTS); memset(devfunc_info_slot, 0, sizeof(List *) * DEVFUNC_INFO_NSLOTS); memset(devfunc_code_slot, 0, sizeof(List *) * DEVFUNC_INFO_NSLOTS); + + __type_oid_cache_int1 = UINT_MAX; + __type_oid_cache_float2 = UINT_MAX; + __type_oid_cache_cube = UINT_MAX; } void diff --git a/src/pg_strom.h b/src/pg_strom.h index 595ba44bb..54ed588a4 100644 --- a/src/pg_strom.h +++ b/src/pg_strom.h @@ -573,6 +573,9 @@ typedef struct } pd[1]; } codegen_context; +extern Oid get_int1_type_oid(bool missing_ok); +extern Oid get_float2_type_oid(bool missing_ok); +extern Oid get_cube_type_oid(bool missing_ok); extern devtype_info *pgstrom_devtype_lookup(Oid type_oid); extern devfunc_info *pgstrom_devfunc_lookup(Oid func_oid, List *func_args, diff --git a/src/xpu_misclib.cu b/src/xpu_misclib.cu index 74fc5b231..e4a2cab78 100644 --- a/src/xpu_misclib.cu +++ b/src/xpu_misclib.cu @@ -1485,3 +1485,493 @@ pgfn_network_overlap(XPU_PGFUNCTION_ARGS) } return true; } + +/* ---------------------------------------------------------------- + * + * cube (alias of earthdistance) data type and functions + * + * ---------------------------------------------------------------- + */ +INLINE_FUNCTION(bool) +IS_POINT(const __NDBOX *cube) +{ + return ((__Fetch(&cube->header) & POINT_BIT) != 0); +} + +INLINE_FUNCTION(int) +DIM(const __NDBOX *cube) +{ + return (__Fetch(&cube->header) & DIM_MASK); +} + +INLINE_FUNCTION(double) +LL_COORD(const __NDBOX *cube, int i) +{ + return __Fetch(cube->x + i); +} + +INLINE_FUNCTION(double) +UR_COORD(const __NDBOX *cube, int i) +{ + return __Fetch(cube->x + (IS_POINT(cube) ? i : DIM(cube) + i)); +} + +INLINE_FUNCTION(bool) +xpu_cube_is_valid(kern_context *kcxt, const xpu_cube_t *arg) +{ + int dim; + + if (arg->length < 0) + { + STROM_CPU_FALLBACK(kcxt, "cube datum is compressed or external"); + return false; + } + + dim = DIM((const __NDBOX *)arg->value); + if (arg->length < (IS_POINT((const __NDBOX *)arg->value) + ? offsetof(__NDBOX, x[dim]) + : offsetof(__NDBOX, x[2 * dim]))) + { + STROM_ELOG(kcxt, "cube datum is corrupted"); + return false; + } + return true; +} + +STATIC_FUNCTION(bool) +xpu_cube_datum_heap_read(kern_context *kcxt, + const void *addr, + xpu_datum_t *__result) +{ + xpu_cube_t *result = (xpu_cube_t *)__result; + + if (VARATT_IS_EXTERNAL(addr) || VARATT_IS_COMPRESSED(addr)) + { + result->value = (const char *)addr; + result->length = -1; + } + else + { + result->value = VARDATA_ANY(addr); + result->length = VARSIZE_ANY_EXHDR(addr); + } + result->expr_ops = &xpu_cube_ops; + return true; +} + +STATIC_FUNCTION(bool) +xpu_cube_datum_arrow_read(kern_context *kcxt, + const kern_data_store *kds, + const kern_colmeta *cmeta, + uint32_t kds_index, + xpu_datum_t *__result) +{ + xpu_cube_t *result = (xpu_cube_t *)__result; + int length; + + if (cmeta->attopts.tag == ArrowType__Binary) + { + result->value = (const char *) + KDS_ARROW_REF_VARLENA32_DATUM(kds, cmeta, kds_index, &length); + result->length = length; + result->expr_ops = &xpu_cube_ops; + } + else if (cmeta->attopts.tag == ArrowType__LargeBinary) + { + result->value = (const char *) + KDS_ARROW_REF_VARLENA64_DATUM(kds, cmeta, kds_index, &length); + result->length = length; + result->expr_ops = &xpu_cube_ops; + } + else + { + STROM_ELOG(kcxt, "not a mappable Arrow data type for cube"); + return false; + } + return true; +} + +STATIC_FUNCTION(bool) +xpu_cube_datum_kvec_load(kern_context *kcxt, + const kvec_datum_t *__kvecs, + uint32_t kvecs_id, + xpu_datum_t *__result) +{ + const kvec_cube_t *kvecs = (const kvec_cube_t *)__kvecs; + xpu_cube_t *result = (xpu_cube_t *)__result; + + result->expr_ops = &xpu_cube_ops; + result->length = kvecs->length[kvecs_id]; + result->value = kvecs->values[kvecs_id]; + return true; +} + +STATIC_FUNCTION(bool) +xpu_cube_datum_kvec_save(kern_context *kcxt, + const xpu_datum_t *__xdatum, + kvec_datum_t *__kvecs, + uint32_t kvecs_id) +{ + const xpu_cube_t *xdatum = (const xpu_cube_t *)__xdatum; + kvec_cube_t *kvecs = (kvec_cube_t *)__kvecs; + + kvecs->length[kvecs_id] = xdatum->length; + kvecs->values[kvecs_id] = xdatum->value; + return true; +} + +STATIC_FUNCTION(bool) +xpu_cube_datum_kvec_copy(kern_context *kcxt, + const kvec_datum_t *__kvecs_src, + uint32_t kvecs_src_id, + kvec_datum_t *__kvecs_dst, + uint32_t kvecs_dst_id) +{ + const kvec_cube_t *kvecs_src = (const kvec_cube_t *)__kvecs_src; + kvec_cube_t *kvecs_dst = (kvec_cube_t *)__kvecs_dst; + + kvecs_dst->length[kvecs_dst_id] = kvecs_src->length[kvecs_src_id]; + kvecs_dst->values[kvecs_dst_id] = kvecs_src->values[kvecs_src_id]; + return true; +} + +STATIC_FUNCTION(int) +xpu_cube_datum_write(kern_context *kcxt, + char *buffer, + const kern_colmeta *cmeta, + const xpu_datum_t *__arg) +{ + const xpu_cube_t *arg = (const xpu_cube_t *)__arg; + int nbytes; + + if (arg->length < 0) + { + nbytes = VARSIZE_ANY(arg->value); + if (buffer) + memcpy(buffer, arg->value, nbytes); + } + else + { + nbytes = VARHDRSZ + arg->length; + if (buffer) + { + memcpy(buffer+VARHDRSZ, arg->value, arg->length); + SET_VARSIZE(buffer, nbytes); + } + } + return nbytes; +} + +STATIC_FUNCTION(bool) +xpu_cube_datum_hash(kern_context *kcxt, + uint32_t *p_hash, + xpu_datum_t *__arg) +{ + xpu_cube_t *arg = (xpu_cube_t *)__arg; + + if (XPU_DATUM_ISNULL(arg)) + *p_hash = 0; + else if (xpu_cube_is_valid(kcxt, arg)) + *p_hash = pg_hash_any(arg->value, arg->length); + else + return false; + return true; +} + +STATIC_FUNCTION(int) +pg_cube_cmp_v0(const __NDBOX *a, const __NDBOX *b) +{ + int dim = Min(DIM(a), DIM(b)); + + /* compare the common dimensions */ + for (int i = 0; i < dim; i++) + { + if (Min(LL_COORD(a,i), UR_COORD(a,i)) > Min(LL_COORD(b,i), UR_COORD(b,i))) + return 1; + if (Min(LL_COORD(a,i), UR_COORD(a,i)) < Min(LL_COORD(b,i), UR_COORD(b,i))) + return -1; + } + for (int i = 0; i < dim; i++) + { + if (Max(LL_COORD(a,i), UR_COORD(a,i)) > Max(LL_COORD(b,i), UR_COORD(b,i))) + return 1; + if (Max(LL_COORD(a,i), UR_COORD(a,i)) < Max(LL_COORD(b,i), UR_COORD(b,i))) + return -1; + } + + /* compare extra dimensions to zero */ + if (DIM(a) > DIM(b)) + { + for (int i = dim; i < DIM(a); i++) + { + if (Min(LL_COORD(a,i), UR_COORD(a,i)) > 0) + return 1; + if (Min(LL_COORD(a,i), UR_COORD(a,i)) < 0) + return -1; + } + for (int i = dim; i < DIM(a); i++) + { + if (Max(LL_COORD(a, i), UR_COORD(a, i)) > 0) + return 1; + if (Max(LL_COORD(a, i), UR_COORD(a, i)) < 0) + return -1; + } + + /* + * if all common dimensions are equal, the cube with more dimensions + * wins + */ + return 1; + } + if (DIM(a) < DIM(b)) + { + for (int i = dim; i < DIM(b); i++) + { + if (Min(LL_COORD(b,i), UR_COORD(b,i)) > 0) + return -1; + if (Min(LL_COORD(b,i), UR_COORD(b,i)) < 0) + return 1; + } + for (int i = dim; i < DIM(b); i++) + { + if (Max(LL_COORD(b,i), UR_COORD(b,i)) > 0) + return -1; + if (Max(LL_COORD(b,i), UR_COORD(b,i)) < 0) + return 1; + } + + /* + * if all common dimensions are equal, the cube with more dimensions + * wins + */ + return -1; + } + /* They're really equal */ + return 0; +} + +STATIC_FUNCTION(bool) +xpu_cube_datum_comp(kern_context *kcxt, + int *p_comp, + xpu_datum_t *__a, + xpu_datum_t *__b) +{ + const xpu_cube_t *a = (const xpu_cube_t *)__a; + const xpu_cube_t *b = (const xpu_cube_t *)__b; + + if (!xpu_cube_is_valid(kcxt, a) || + !xpu_cube_is_valid(kcxt, b)) + return false; + + *p_comp = pg_cube_cmp_v0((const __NDBOX *)a->value, + (const __NDBOX *)b->value); + return true; +} +PGSTROM_SQLTYPE_OPERATORS(cube, false, 8, -1); + +PUBLIC_FUNCTION(bool) +pgfn_cube_eq(XPU_PGFUNCTION_ARGS) +{ + KEXP_PROCESS_ARGS2(bool, cube, arg1, cube, arg2); + + if (XPU_DATUM_ISNULL(&arg1) || XPU_DATUM_ISNULL(&arg2)) + result->expr_ops = NULL; + else if (!xpu_cube_is_valid(kcxt, &arg1) || + !xpu_cube_is_valid(kcxt, &arg2)) + return false; + else + { + result->expr_ops = &xpu_bool_ops; + result->value = (pg_cube_cmp_v0((const __NDBOX *)arg1.value, + (const __NDBOX *)arg2.value) == 0); + } + return true; +} + +PUBLIC_FUNCTION(bool) +pgfn_cube_ne(XPU_PGFUNCTION_ARGS) +{ + KEXP_PROCESS_ARGS2(bool, cube, arg1, cube, arg2); + + if (XPU_DATUM_ISNULL(&arg1) || XPU_DATUM_ISNULL(&arg2)) + result->expr_ops = NULL; + else if (!xpu_cube_is_valid(kcxt, &arg1) || + !xpu_cube_is_valid(kcxt, &arg2)) + return false; + else + { + result->expr_ops = &xpu_bool_ops; + result->value = (pg_cube_cmp_v0((const __NDBOX *)arg1.value, + (const __NDBOX *)arg2.value) != 0); + } + return true; +} + +PUBLIC_FUNCTION(bool) +pgfn_cube_lt(XPU_PGFUNCTION_ARGS) +{ + KEXP_PROCESS_ARGS2(bool, cube, arg1, cube, arg2); + + if (XPU_DATUM_ISNULL(&arg1) || XPU_DATUM_ISNULL(&arg2)) + result->expr_ops = NULL; + else if (!xpu_cube_is_valid(kcxt, &arg1) || + !xpu_cube_is_valid(kcxt, &arg2)) + return false; + else + { + result->expr_ops = &xpu_bool_ops; + result->value = (pg_cube_cmp_v0((const __NDBOX *)arg1.value, + (const __NDBOX *)arg2.value) < 0); + } + return true; +} + +PUBLIC_FUNCTION(bool) +pgfn_cube_gt(XPU_PGFUNCTION_ARGS) +{ + KEXP_PROCESS_ARGS2(bool, cube, arg1, cube, arg2); + + if (XPU_DATUM_ISNULL(&arg1) || XPU_DATUM_ISNULL(&arg2)) + result->expr_ops = NULL; + else if (!xpu_cube_is_valid(kcxt, &arg1) || + !xpu_cube_is_valid(kcxt, &arg2)) + return false; + else + { + result->expr_ops = &xpu_bool_ops; + result->value = (pg_cube_cmp_v0((const __NDBOX *)arg1.value, + (const __NDBOX *)arg2.value) > 0); + } + return true; +} + +PUBLIC_FUNCTION(bool) +pgfn_cube_le(XPU_PGFUNCTION_ARGS) +{ + KEXP_PROCESS_ARGS2(bool, cube, arg1, cube, arg2); + + if (XPU_DATUM_ISNULL(&arg1) || XPU_DATUM_ISNULL(&arg2)) + result->expr_ops = NULL; + else if (!xpu_cube_is_valid(kcxt, &arg1) || + !xpu_cube_is_valid(kcxt, &arg2)) + return false; + else + { + result->expr_ops = &xpu_bool_ops; + result->value = (pg_cube_cmp_v0((const __NDBOX *)arg1.value, + (const __NDBOX *)arg2.value) <= 0); + } + return true; +} + +PUBLIC_FUNCTION(bool) +pgfn_cube_ge(XPU_PGFUNCTION_ARGS) +{ + KEXP_PROCESS_ARGS2(bool, cube, arg1, cube, arg2); + + if (XPU_DATUM_ISNULL(&arg1) || XPU_DATUM_ISNULL(&arg2)) + result->expr_ops = NULL; + else if (!xpu_cube_is_valid(kcxt, &arg1) || + !xpu_cube_is_valid(kcxt, &arg2)) + return false; + else + { + result->expr_ops = &xpu_bool_ops; + result->value = (pg_cube_cmp_v0((const __NDBOX *)arg1.value, + (const __NDBOX *)arg2.value) >= 0); + } + return true; +} + +STATIC_FUNCTION(bool) +pg_cube_contains_v0(const __NDBOX *a, const __NDBOX *b) +{ + if (DIM(a) < DIM(b)) + { + /* + * the further comparisons will make sense if the excess dimensions of + * (b) were zeroes Since both UL and UR coordinates must be zero, we + * can check them all without worrying about which is which. + */ + for (int i = DIM(a); i < DIM(b); i++) + { + if (LL_COORD(b, i) != 0) + return false; + if (UR_COORD(b, i) != 0) + return false; + } + } + /* Can't care less about the excess dimensions of (a), if any */ + for (int i = 0; i < Min(DIM(a), DIM(b)); i++) + { + if (Min(LL_COORD(a, i), UR_COORD(a, i)) > + Min(LL_COORD(b, i), UR_COORD(b, i))) + return false; + if (Max(LL_COORD(a, i), UR_COORD(a, i)) < + Max(LL_COORD(b, i), UR_COORD(b, i))) + return false; + } + return true; +} + +PUBLIC_FUNCTION(bool) +pgfn_cube_contains(XPU_PGFUNCTION_ARGS) +{ + KEXP_PROCESS_ARGS2(bool, cube, arg1, cube, arg2); + + if (XPU_DATUM_ISNULL(&arg1) || XPU_DATUM_ISNULL(&arg2)) + result->expr_ops = NULL; + else if (!xpu_cube_is_valid(kcxt, &arg1) || + !xpu_cube_is_valid(kcxt, &arg2)) + return false; + else + { + result->expr_ops = &xpu_bool_ops; + result->value = pg_cube_contains_v0((const __NDBOX *)arg1.value, + (const __NDBOX *)arg2.value); + } + return true; +} + +PUBLIC_FUNCTION(bool) +pgfn_cube_contained(XPU_PGFUNCTION_ARGS) +{ + KEXP_PROCESS_ARGS2(bool, cube, arg1, cube, arg2); + + if (XPU_DATUM_ISNULL(&arg1) || XPU_DATUM_ISNULL(&arg2)) + result->expr_ops = NULL; + else if (!xpu_cube_is_valid(kcxt, &arg1) || + !xpu_cube_is_valid(kcxt, &arg2)) + return false; + else + { + result->expr_ops = &xpu_bool_ops; + result->value = pg_cube_contains_v0((const __NDBOX *)arg2.value, + (const __NDBOX *)arg1.value); + } + return true; +} + +PUBLIC_FUNCTION(bool) +pgfn_cube_ll_coord(XPU_PGFUNCTION_ARGS) +{ + KEXP_PROCESS_ARGS2(float8, cube, cval, int4, ival); + + if (XPU_DATUM_ISNULL(&cval) || XPU_DATUM_ISNULL(&ival)) + result->expr_ops = NULL; + else if (!xpu_cube_is_valid(kcxt, &cval)) + return false; + else + { + const __NDBOX *c = (const __NDBOX *)cval.value; + int n = ival.value; + + if (DIM(c) >= n && n > 0) + result->value = Max(LL_COORD(c, n-1), + UR_COORD(c, n-1)); + else + result->value = 0.0; + result->expr_ops = &xpu_float8_ops; + } + return true; +} diff --git a/src/xpu_misclib.h b/src/xpu_misclib.h index c3e9f9745..13b81b1c1 100644 --- a/src/xpu_misclib.h +++ b/src/xpu_misclib.h @@ -83,4 +83,31 @@ xpu_interval_write_heap(kern_context *kcxt, char *buffer, const xpu_datum_t *arg); +/* + * cube (contrib module) + */ +struct __NDBOX { + /*---------- + * Header contains info about NDBOX. For binary compatibility with old + * versions, it is defined as "unsigned int". + * + * Following information is stored: + * + * bits 0-7 : number of cube dimensions; + * bits 8-30 : unused, initialize to zero; + * bit 31 : point flag. If set, the upper right coordinates are not + * stored, and are implicitly the same as the lower left + * coordinates. + *---------- + */ + uint32_t header; + double x[1]; /* flexible length */ +} __attribute__ ((packed)); +typedef struct __NDBOX __NDBOX; + +#define POINT_BIT 0x80000000 +#define DIM_MASK 0x7fffffff + +PGSTROM_SQLTYPE_VARLENA_DECLARATION(cube); + #endif /* XPU_MISCLIB_H */ diff --git a/src/xpu_opcodes.h b/src/xpu_opcodes.h index 4ed18b988..e55a7f062 100644 --- a/src/xpu_opcodes.h +++ b/src/xpu_opcodes.h @@ -41,12 +41,14 @@ TYPE_OPCODE(inet, NULL, DEVTYPE__HAS_COMPARE) TYPE_OPCODE(jsonb, NULL, 0) TYPE_OPCODE(geometry, "postgis", DEVTYPE__USE_KVARS_SLOTBUF) TYPE_OPCODE(box2df, "postgis", 0) +TYPE_OPCODE(cube, "cube", 0) #ifndef TYPE_ALIAS #define TYPE_ALIAS(NAME,EXTENSION,BASE,BASE_EXTENSION) #endif TYPE_ALIAS(varchar, NULL, text, NULL) TYPE_ALIAS(cidr, NULL, inet, NULL) +TYPE_ALIAS(earth, "earthdistance", cube, "cube") /* * PostgreSQL Device Functions / Operators @@ -788,6 +790,17 @@ FUNC_OPCODE(is_contained_2d, box2df/geometry, DEVKIND__ANY, box2df_geometry_with FUNC_OPCODE(is_contained_2d, geometry/box2df, DEVKIND__ANY, geometry_box2df_within, 40, "postgis") FUNC_OPCODE(is_contained_2d, box2df/box2df, DEVKIND__ANY, box2df_within, 40, "postgis") +/* cube/earthdistance */ +__FUNC_OPCODE(cube_eq, cube/cube, 5, "cube") +__FUNC_OPCODE(cube_ne, cube/cube, 5, "cube") +__FUNC_OPCODE(cube_lt, cube/cube, 5, "cube") +__FUNC_OPCODE(cube_le, cube/cube, 5, "cube") +__FUNC_OPCODE(cube_gt, cube/cube, 5, "cube") +__FUNC_OPCODE(cube_ge, cube/cube, 5, "cube") +__FUNC_OPCODE(cube_contains, cube/cube, 10, "cube") +__FUNC_OPCODE(cube_contained, cube/cube, 10, "cube") +__FUNC_OPCODE(cube_ll_coord, cube/cube, 10, "cube") + #undef TYPE_OPCODE #undef TYPE_ALIAS #undef FUNC_OPCODE