X-Git-Url: https://git.8kb.co.uk/?p=postgresql%2Fpg_jsonb_opx;a=blobdiff_plain;f=jsonb_opx.c;fp=jsonb_opx.c;h=5627d95d8271392e1a4bb22b2da3a2e3f9750edd;hp=29ea74a4b6ffa529f636f2ca76f67c7839cb3612;hb=e22272adc71704855818856546b134733d6334a1;hpb=c121fab7a779e85ecb31c42ba4788630370a2c75 diff --git a/jsonb_opx.c b/jsonb_opx.c index 29ea74a..5627d95 100755 --- a/jsonb_opx.c +++ b/jsonb_opx.c @@ -14,10 +14,12 @@ #include "postgres.h" #include "fmgr.h" #include "utils/array.h" +#include "utils/numeric.h" #include "utils/jsonb.h" #include "catalog/pg_type.h" #include "utils/builtins.h" #include "jsonb_opx.h" +#include "utils/lsyscache.h" #ifdef PG_MODULE_MAGIC PG_MODULE_MAGIC; @@ -38,23 +40,39 @@ Datum jsonb_delete_key(PG_FUNCTION_ARGS) { /* pointers to incoming jsonb and text data */ - Jsonb *input_jsonb = PG_GETARG_JSONB(0); - text *input_text = PG_GETARG_TEXT_P(1); - + Jsonb *input_jsonb = PG_GETARG_JSONB(0); + Oid input_element_type = get_fn_expr_argtype(fcinfo->flinfo, 1); + text *input_text = NULL; + Numeric input_numeric = NULL; + bool input_bool = false; + /* pointers to return jsonb value data and state to be converted to jsonb on return */ - JsonbParseState *state = NULL; - JsonbValue *return_jsonb_value = NULL; + JsonbParseState *state = NULL; + JsonbValue *return_jsonb_value = NULL; /* pointer to iterator for input_jsonb value data */ - JsonbIterator *jsonb_iterator; - JsonbValue jsonb_iterator_value; - int32 jsonb_iterator_token; + JsonbIterator *jsonb_iterator; + JsonbValue jsonb_iterator_value; + int32 jsonb_iterator_token; /* variables used for skip logic */ - int32 skip_level = 0; - int32 nest_level = 0; - int32 array_level = 0; - bool push = true; + int32 skip_level = 0; + int32 nest_level = 0; + int32 array_level = 0; + bool push = true; + + /* make function polymorphic for text/numeric/boolean */ + if (!OidIsValid(input_element_type)) + elog(ERROR, "could not determine data type of input"); + + if (input_element_type == TEXTOID) + input_text = PG_GETARG_TEXT_P(1); + else if (input_element_type == NUMERICOID) + input_numeric = PG_GETARG_NUMERIC(1); + else if (input_element_type == BOOLOID) + input_bool = PG_GETARG_BOOL(1); + else + elog(ERROR, "invalid data type input: %i", input_element_type); /* * If we've been supplied with an existing key iterate round json data and rebuild @@ -73,32 +91,48 @@ jsonb_delete_key(PG_FUNCTION_ARGS) push = true; switch (jsonb_iterator_token) { - case WJB_BEGIN_ARRAY: - array_level++; - break; - case WJB_BEGIN_OBJECT: - nest_level++; - break; - case WJB_ELEM: - case WJB_KEY: - if (skip_level == 0 && ((jsonb_iterator_token == WJB_KEY && nest_level == 1 && array_level == 0) || - (jsonb_iterator_token == WJB_ELEM && nest_level == 0 && array_level == 1))) - { - if (jsonb_iterator_value.type == jbvString) + case WJB_BEGIN_ARRAY: + array_level++; + break; + case WJB_BEGIN_OBJECT: + nest_level++; + break; + case WJB_ELEM: + if (skip_level == 0 && nest_level == 0 && array_level == 1) { - if ((jsonb_iterator_value.val.string.len == VARSIZE_ANY_EXHDR(input_text)) && - (memcmp(jsonb_iterator_value.val.string.val, - VARDATA_ANY(input_text), - jsonb_iterator_value.val.string.len) == 0)) + if ((jsonb_iterator_value.type == jbvNumeric) && (input_element_type == NUMERICOID)) { - + if (DatumGetBool(DirectFunctionCall2(numeric_eq, + PointerGetDatum(jsonb_iterator_value.val.numeric), + PointerGetDatum(input_numeric)))) + push = false; + } + else if ((jsonb_iterator_value.type == jbvBool) && (input_element_type == BOOLOID)) + { + if (jsonb_iterator_value.val.boolean == input_bool) + push = false; + } + /* sql null <> jsonb null */ + } + case WJB_KEY: + if (skip_level == 0 && ((jsonb_iterator_token == WJB_KEY && nest_level == 1 && array_level == 0) || + (jsonb_iterator_token == WJB_ELEM && nest_level == 0 && array_level == 1))) + { + if ((jsonb_iterator_value.type == jbvString) && (input_element_type == TEXTOID)) + { + if ((jsonb_iterator_value.val.string.len == VARSIZE_ANY_EXHDR(input_text)) && + (memcmp(jsonb_iterator_value.val.string.val, + VARDATA_ANY(input_text), + jsonb_iterator_value.val.string.len) == 0)) + { if (jsonb_iterator_token == WJB_ELEM) push = false; else skip_level = nest_level; + } } } - } + /* switch end */ } if (push && (skip_level == 0 || nest_level < skip_level)) @@ -108,19 +142,20 @@ jsonb_delete_key(PG_FUNCTION_ARGS) switch (jsonb_iterator_token) { - case WJB_END_OBJECT: - nest_level--; - if (skip_level == nest_level && array_level == 0) - skip_level = 0; - break; - case WJB_END_ARRAY: - array_level--; - if (skip_level == nest_level && array_level == 0) - skip_level = 0; + case WJB_END_OBJECT: + nest_level--; + if (skip_level == nest_level && array_level == 0) + skip_level = 0; + break; + case WJB_END_ARRAY: + array_level--; + if (skip_level == nest_level && array_level == 0) + skip_level = 0; break; - case WJB_VALUE: - if (skip_level == nest_level) - skip_level = 0; + case WJB_VALUE: + if (skip_level == nest_level) + skip_level = 0; + /* switch end */ } } @@ -128,7 +163,8 @@ jsonb_delete_key(PG_FUNCTION_ARGS) return_jsonb_value->val.array.rawScalar = true; PG_FREE_IF_COPY(input_jsonb, 0); - PG_FREE_IF_COPY(input_text, 1); + if (input_element_type == TEXTOID) + PG_FREE_IF_COPY(input_text, 1); PG_RETURN_JSONB(JsonbValueToJsonb(return_jsonb_value)); } @@ -148,37 +184,44 @@ Datum jsonb_delete_keys(PG_FUNCTION_ARGS) { /* general loops */ - int i; + int i; /* pointers to incoming jsonb and text[] data */ - Jsonb *input_jsonb = PG_GETARG_JSONB(0); - ArrayType *input_array = PG_GETARG_ARRAYTYPE_P(1); + Jsonb *input_jsonb = PG_GETARG_JSONB(0); + ArrayType *input_array = PG_GETARG_ARRAYTYPE_P(1); + int16 array_typlen; + bool array_typbyval; + char array_typalign; + Oid array_element_type = ARR_ELEMTYPE(input_array); /* pointers to return jsonb value data and state to be converted to jsonb on return */ - JsonbParseState *state = NULL; - JsonbValue *return_jsonb_value = NULL; + JsonbParseState *state = NULL; + JsonbValue *return_jsonb_value = NULL; /* pointer to iterator for input_jsonb value data */ - JsonbIterator *jsonb_iterator; - JsonbValue jsonb_iterator_value; - int32 jsonb_iterator_token; + JsonbIterator *jsonb_iterator; + JsonbValue jsonb_iterator_value; + int32 jsonb_iterator_token; /* variables used for skip logic */ - int32 skip_level = 0; - int32 nest_level = 0; - int32 array_level = 0; - bool push = true; + int32 skip_level = 0; + int32 nest_level = 0; + int32 array_level = 0; + bool push = true; /* array element variables for use during deconstruction */ - Datum *datums; - bool *nulls; - int32 count; + Datum *datums; + bool *nulls; + int32 count; /* individual array values values from incoming text[] */ - text *array_element_text; + text *array_element_text; + Numeric array_element_numeric = NULL; + bool array_element_bool = false; - /* assert input_array is a text array type */ - Assert(ARR_ELEMTYPE(input_array) == TEXTOID); + /* ? */ + if (array_element_type != TEXTOID && array_element_type != NUMERICOID && array_element_type != BOOLOID) + elog(ERROR, "invalid data type input"); /* check input_array is one-dimensional */ if (ARR_NDIM(input_array) > 1) @@ -187,7 +230,8 @@ jsonb_delete_keys(PG_FUNCTION_ARGS) errmsg("1 dimensional text array expected"))); /* deconstruct array elements */ - deconstruct_array(input_array, TEXTOID, -1, false, 'i', + get_typlenbyvalalign(array_element_type, &array_typlen, &array_typbyval, &array_typalign); + deconstruct_array(input_array, array_element_type, array_typlen, array_typbyval, array_typalign, &datums, &nulls, &count); /* if the array is empty there's no work to do so return the input value */ @@ -210,40 +254,80 @@ jsonb_delete_keys(PG_FUNCTION_ARGS) push = true; switch (jsonb_iterator_token) { - case WJB_BEGIN_ARRAY: - array_level++; - break; - case WJB_BEGIN_OBJECT: - nest_level++; - break; - case WJB_ELEM: - case WJB_KEY: - if (skip_level == 0 && ((jsonb_iterator_token == WJB_KEY && nest_level == 1 && array_level == 0) || + case WJB_BEGIN_ARRAY: + array_level++; + break; + case WJB_BEGIN_OBJECT: + nest_level++; + break; + case WJB_ELEM: + if (skip_level == 0 && nest_level == 0 && array_level == 1) + { + if ((jsonb_iterator_value.type == jbvNumeric) && (array_element_type == NUMERICOID)) + { + for (i=0; i jsonb null */ + } + case WJB_KEY: + if (skip_level == 0 && ((jsonb_iterator_token == WJB_KEY && nest_level == 1 && array_level == 0) || (jsonb_iterator_token == WJB_ELEM && nest_level == 0 && array_level == 1))) - { - if (jsonb_iterator_value.type == jbvString || jsonb_iterator_value.type == jbvNull) { - for (i=0; iroot); - while ((jsonb_iterator_token = JsonbIteratorNext(&jsonb_iterator, &jsonb_iterator_value, skip_nested)) != WJB_DONE) + while ((jsonb_iterator_token = JsonbIteratorNext(&jsonb_iterator, &jsonb_iterator_value, true)) != WJB_DONE) { - skip_nested = true; + //skip_nested = true; push = true; switch (jsonb_iterator_token) { - case WJB_BEGIN_ARRAY: - case WJB_BEGIN_OBJECT: - case WJB_END_ARRAY: - case WJB_END_OBJECT: - return_jsonb_value = pushJsonbValue(&state, jsonb_iterator_token, NULL); - break; - case WJB_ELEM: - /* - * findJsonbValueFromContainer only supports jsonb arrays containting scalar values? - * If container is something like '[[1]]' or '[{"a":1}]' will error with "invalid jsonb scalar type" - */ - jsonb_lookup_value = findJsonbValueFromContainer(&input_jsonb_b->root, JB_FOBJECT | JB_FARRAY, &jsonb_iterator_value); - if (jsonb_lookup_value == NULL) - { - if (jsonb_iterator_value.type == jbvBinary) + case WJB_BEGIN_ARRAY: + case WJB_BEGIN_OBJECT: + case WJB_END_ARRAY: + case WJB_END_OBJECT: + return_jsonb_value = pushJsonbValue(&state, jsonb_iterator_token, NULL); + break; + case WJB_ELEM: + switch (jsonb_iterator_value.type) { - return_jsonb_value = pushJsonbBinary(&state, jsonb_iterator_value.val.binary.data); + case jbvBinary: + jsonb_iterator2 = JsonbIteratorInit(&input_jsonb_b->root); + while ((jsonb_iterator_token2 = JsonbIteratorNext(&jsonb_iterator2, &jsonb_iterator_value2, true)) != WJB_DONE) + { + if (jsonb_iterator_value2.type == jbvBinary) + { + if ((jsonb_iterator_value2.val.binary.len == jsonb_iterator_value.val.binary.len) && + (memcmp(jsonb_iterator_value2.val.binary.data, + jsonb_iterator_value.val.binary.data, + jsonb_iterator_value2.val.binary.len) == 0)) + { + push = false; + break; + } + } + } + break; + default: + if (findJsonbValueFromContainer(&input_jsonb_b->root, + JB_FARRAY, &jsonb_iterator_value) != NULL) + push = false; + /* inner switch end */ } - else + + if (push) { - return_jsonb_value = pushJsonbValue(&state, WJB_ELEM, &jsonb_iterator_value); + if (jsonb_iterator_value.type == jbvBinary) + { + return_jsonb_value = pushJsonbBinary(&state, jsonb_iterator_value.val.binary.data); + } + else + { + return_jsonb_value = pushJsonbValue(&state, WJB_ELEM, &jsonb_iterator_value); + } } - } - break; - case WJB_KEY : - jsonb_lookup_value = findJsonbValueFromContainer(&input_jsonb_b->root, JB_FOBJECT | JB_FARRAY, &jsonb_iterator_value); - - jsonb_iterator_key = jsonb_iterator_value; - jsonb_iterator_token = JsonbIteratorNext(&jsonb_iterator, &jsonb_iterator_value, skip_nested); - if (jsonb_iterator_token != WJB_VALUE) - elog(ERROR, "invalid JsonbIteratorNext (expected WJB_VALUE) rc: %d", jsonb_iterator_token); + break; + case WJB_KEY: + jsonb_lookup_value = findJsonbValueFromContainer(&input_jsonb_b->root, JB_FOBJECT, &jsonb_iterator_value); - if (jsonb_lookup_value != NULL) - { + jsonb_iterator_key = jsonb_iterator_value; + jsonb_iterator_token = JsonbIteratorNext(&jsonb_iterator, &jsonb_iterator_value, true); + if (jsonb_iterator_token != WJB_VALUE) + elog(ERROR, "invalid JsonbIteratorNext (expected WJB_VALUE) rc: %d", jsonb_iterator_token); - if (jsonb_lookup_value->type == jsonb_iterator_value.type) + if (jsonb_lookup_value != NULL) { - switch (jsonb_lookup_value->type) + if (jsonb_lookup_value->type == jsonb_iterator_value.type) { - case jbvNull: - push = false; - break; - case jbvNumeric: - if (DatumGetBool(DirectFunctionCall2(numeric_eq, - PointerGetDatum(jsonb_lookup_value->val.numeric), - PointerGetDatum(jsonb_iterator_value.val.numeric)))) - push = false; - break; - case jbvString: - if ((jsonb_lookup_value->val.string.len == jsonb_iterator_value.val.string.len) && - (memcmp(jsonb_lookup_value->val.string.val, - jsonb_iterator_value.val.string.val, - jsonb_lookup_value->val.string.len) == 0)) - push = false; - break; - case jbvBinary: - if ((jsonb_lookup_value->val.binary.len == jsonb_iterator_value.val.binary.len) && - (memcmp(jsonb_lookup_value->val.binary.data, - jsonb_iterator_value.val.binary.data, - jsonb_lookup_value->val.binary.len) == 0)) - push = false; - break; - case jbvBool: - if (jsonb_lookup_value->val.boolean == jsonb_iterator_value.val.boolean) + switch (jsonb_lookup_value->type) + { + /* Nulls within json are equal, but should not be equal to SQL nulls */ + case jbvNull: push = false; - break; - case jbvArray: - /* should not be possible? */ - case jbvObject: - /* should not be possible? */ - default: - ereport(ERROR, (errcode(ERRCODE_SUCCESSFUL_COMPLETION), errmsg("unexpected lookup type %i", jsonb_iterator_token))); + break; + case jbvNumeric: + if (DatumGetBool(DirectFunctionCall2(numeric_eq, + PointerGetDatum(jsonb_lookup_value->val.numeric), + PointerGetDatum(jsonb_iterator_value.val.numeric)))) + push = false; + break; + case jbvString: + if ((jsonb_lookup_value->val.string.len == jsonb_iterator_value.val.string.len) && + (memcmp(jsonb_lookup_value->val.string.val, + jsonb_iterator_value.val.string.val, + jsonb_lookup_value->val.string.len) == 0)) + push = false; + break; + case jbvBinary: + if ((jsonb_lookup_value->val.binary.len == jsonb_iterator_value.val.binary.len) && + (memcmp(jsonb_lookup_value->val.binary.data, + jsonb_iterator_value.val.binary.data, + jsonb_lookup_value->val.binary.len) == 0)) + push = false; + break; + case jbvBool: + if (jsonb_lookup_value->val.boolean == jsonb_iterator_value.val.boolean) + push = false; + break; + case jbvArray: + case jbvObject: + /* should not be possible? */ + default: + ereport(ERROR, (errcode(ERRCODE_SUCCESSFUL_COMPLETION), errmsg("unexpected lookup type %i", jsonb_iterator_token))); + /* inner switch end */ + } } } - } - if (push) - { - return_jsonb_value = pushJsonbValue(&state, WJB_KEY, &jsonb_iterator_key); + if (push) + { + return_jsonb_value = pushJsonbValue(&state, WJB_KEY, &jsonb_iterator_key); - /* if our value is nested binary data, iterate separately pushing each val */ - if (jsonb_iterator_value.type == jbvBinary) - { - return_jsonb_value = pushJsonbBinary(&state, jsonb_iterator_value.val.binary.data); - } - else - { - return_jsonb_value = pushJsonbValue(&state, jsonb_iterator_token, &jsonb_iterator_value); + /* if our value is nested binary data, iterate separately pushing each val */ + if (jsonb_iterator_value.type == jbvBinary) + { + return_jsonb_value = pushJsonbBinary(&state, jsonb_iterator_value.val.binary.data); + } + else + { + return_jsonb_value = pushJsonbValue(&state, jsonb_iterator_token, &jsonb_iterator_value); + } } - } - break; - case WJB_VALUE: - /* should not be possible */ - default: - elog(ERROR, "invalid JsonbIteratorNext rc: %d", jsonb_iterator_token); + break; + case WJB_VALUE: + /* should not be possible */ + default: + elog(ERROR, "invalid JsonbIteratorNext rc: %d", jsonb_iterator_token); + /* switch end */ } } @@ -448,11 +557,11 @@ Datum jsonb_delete_path(PG_FUNCTION_ARGS) { /* pointers to incoming jsonb and text[] data */ - Jsonb *input_jsonb_a = PG_GETARG_JSONB(0); - ArrayType *input_array = PG_GETARG_ARRAYTYPE_P(1); + Jsonb *input_jsonb_a = PG_GETARG_JSONB(0); + ArrayType *input_array = PG_GETARG_ARRAYTYPE_P(1); /* pointer to return jsonb data */ - Jsonb *return_jsonb = NULL; + Jsonb *return_jsonb = NULL; return_jsonb = jsonbModifyPath(input_jsonb_a, input_array, NULL); @@ -477,22 +586,22 @@ Datum jsonb_concat_jsonb(PG_FUNCTION_ARGS) { /* incoming jsonb data */ - Jsonb *input_jsonb_a = PG_GETARG_JSONB(0); - Jsonb *input_jsonb_b = PG_GETARG_JSONB(1); + Jsonb *input_jsonb_a = PG_GETARG_JSONB(0); + Jsonb *input_jsonb_b = PG_GETARG_JSONB(1); /* return jsonb value data to be converted to jsonb on return */ - JsonbParseState *state = NULL; - JsonbValue *return_jsonb_value = NULL; + JsonbParseState *state = NULL; + JsonbValue *return_jsonb_value = NULL; /* iterator for input_jsonb_b */ - JsonbIterator *jsonb_iterator; - JsonbValue jsonb_iterator_value; - int32 jsonb_iterator_token; - int32 jsonb_root_open; - int32 jsonb_root_close; + JsonbIterator *jsonb_iterator; + JsonbValue jsonb_iterator_value; + int32 jsonb_iterator_token; + int32 jsonb_root_open; + int32 jsonb_root_close; - int32 nest_level = 0; - bool first = true; + int32 nest_level = 0; + bool first = true; /* * check if either supplied jsonb is empty and return the other if so @@ -599,22 +708,22 @@ Datum jsonb_replace_jsonb(PG_FUNCTION_ARGS) { /* incoming jsonb data */ - Jsonb *input_jsonb_a = PG_GETARG_JSONB(0); - Jsonb *input_jsonb_b = PG_GETARG_JSONB(1); + Jsonb *input_jsonb_a = PG_GETARG_JSONB(0); + Jsonb *input_jsonb_b = PG_GETARG_JSONB(1); /* return jsonb value data to be converted to jsonb on return */ - JsonbParseState *state = NULL; - JsonbValue *return_jsonb_value = NULL; + JsonbParseState *state = NULL; + JsonbValue *return_jsonb_value = NULL; /* lookup jsonb value data */ - JsonbValue jsonb_lookup_key; - JsonbValue *jsonb_lookup_value = NULL; - uint32 jsonb_lookup_flags; + JsonbValue jsonb_lookup_key; + JsonbValue *jsonb_lookup_value = NULL; + uint32 jsonb_lookup_flags; /* iterator for input_jsonb_b */ - JsonbIterator *jsonb_iterator; - JsonbValue jsonb_iterator_value; - int32 jsonb_iterator_token; + JsonbIterator *jsonb_iterator; + JsonbValue jsonb_iterator_value; + int32 jsonb_iterator_token; /* * check if supplied replacement jsonb is empty and return unchanged if so @@ -693,12 +802,42 @@ Datum jsonb_replace_path(PG_FUNCTION_ARGS) { /* pointers to incoming jsonb and text[] data */ - Jsonb *input_jsonb_a = PG_GETARG_JSONB(0); - ArrayType *input_array = PG_GETARG_ARRAYTYPE_P(1); - Jsonb *input_jsonb_b = PG_GETARG_JSONB(2); + Jsonb *input_jsonb_a = PG_GETARG_JSONB(0); + ArrayType *input_array = PG_GETARG_ARRAYTYPE_P(1); + Jsonb *input_jsonb_b = PG_GETARG_JSONB(2); + + /* pointer to return jsonb data */ + Jsonb *return_jsonb = NULL; + + return_jsonb = jsonbModifyPath(input_jsonb_a, input_array, input_jsonb_b); + + PG_FREE_IF_COPY(input_jsonb_a, 0); + PG_FREE_IF_COPY(input_array, 1); + PG_FREE_IF_COPY(input_jsonb_b, 2); + + PG_RETURN_JSONB(return_jsonb); +} + + +Datum jsonb_append_path(PG_FUNCTION_ARGS); + +PG_FUNCTION_INFO_V1(jsonb_append_path); + +/* + * Test + * jsonb, text[], jsonb -> jsonb + * + */ +Datum +jsonb_append_path(PG_FUNCTION_ARGS) +{ + /* pointers to incoming jsonb and text[] data */ + Jsonb *input_jsonb_a = PG_GETARG_JSONB(0); + ArrayType *input_array = PG_GETARG_ARRAYTYPE_P(1); + Jsonb *input_jsonb_b = PG_GETARG_JSONB(2); /* pointer to return jsonb data */ - Jsonb *return_jsonb = NULL; + Jsonb *return_jsonb = NULL; return_jsonb = jsonbModifyPath(input_jsonb_a, input_array, input_jsonb_b);