summaryrefslogtreecommitdiffstats
path: root/src/backend/utils/adt/jsonb_op.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/adt/jsonb_op.c')
-rw-r--r--src/backend/utils/adt/jsonb_op.c338
1 files changed, 338 insertions, 0 deletions
diff --git a/src/backend/utils/adt/jsonb_op.c b/src/backend/utils/adt/jsonb_op.c
new file mode 100644
index 0000000..9270520
--- /dev/null
+++ b/src/backend/utils/adt/jsonb_op.c
@@ -0,0 +1,338 @@
+/*-------------------------------------------------------------------------
+ *
+ * jsonb_op.c
+ * Special operators for jsonb only, used by various index access methods
+ *
+ * Copyright (c) 2014-2022, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/adt/jsonb_op.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "catalog/pg_type.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/jsonb.h"
+
+Datum
+jsonb_exists(PG_FUNCTION_ARGS)
+{
+ Jsonb *jb = PG_GETARG_JSONB_P(0);
+ text *key = PG_GETARG_TEXT_PP(1);
+ JsonbValue kval;
+ JsonbValue *v = NULL;
+
+ /*
+ * We only match Object keys (which are naturally always Strings), or
+ * string elements in arrays. In particular, we do not match non-string
+ * scalar elements. Existence of a key/element is only considered at the
+ * top level. No recursion occurs.
+ */
+ kval.type = jbvString;
+ kval.val.string.val = VARDATA_ANY(key);
+ kval.val.string.len = VARSIZE_ANY_EXHDR(key);
+
+ v = findJsonbValueFromContainer(&jb->root,
+ JB_FOBJECT | JB_FARRAY,
+ &kval);
+
+ PG_RETURN_BOOL(v != NULL);
+}
+
+Datum
+jsonb_exists_any(PG_FUNCTION_ARGS)
+{
+ Jsonb *jb = PG_GETARG_JSONB_P(0);
+ ArrayType *keys = PG_GETARG_ARRAYTYPE_P(1);
+ int i;
+ Datum *key_datums;
+ bool *key_nulls;
+ int elem_count;
+
+ deconstruct_array(keys, TEXTOID, -1, false, TYPALIGN_INT,
+ &key_datums, &key_nulls, &elem_count);
+
+ for (i = 0; i < elem_count; i++)
+ {
+ JsonbValue strVal;
+
+ if (key_nulls[i])
+ continue;
+
+ strVal.type = jbvString;
+ /* We rely on the array elements not being toasted */
+ strVal.val.string.val = VARDATA_ANY(key_datums[i]);
+ strVal.val.string.len = VARSIZE_ANY_EXHDR(key_datums[i]);
+
+ if (findJsonbValueFromContainer(&jb->root,
+ JB_FOBJECT | JB_FARRAY,
+ &strVal) != NULL)
+ PG_RETURN_BOOL(true);
+ }
+
+ PG_RETURN_BOOL(false);
+}
+
+Datum
+jsonb_exists_all(PG_FUNCTION_ARGS)
+{
+ Jsonb *jb = PG_GETARG_JSONB_P(0);
+ ArrayType *keys = PG_GETARG_ARRAYTYPE_P(1);
+ int i;
+ Datum *key_datums;
+ bool *key_nulls;
+ int elem_count;
+
+ deconstruct_array(keys, TEXTOID, -1, false, TYPALIGN_INT,
+ &key_datums, &key_nulls, &elem_count);
+
+ for (i = 0; i < elem_count; i++)
+ {
+ JsonbValue strVal;
+
+ if (key_nulls[i])
+ continue;
+
+ strVal.type = jbvString;
+ /* We rely on the array elements not being toasted */
+ strVal.val.string.val = VARDATA_ANY(key_datums[i]);
+ strVal.val.string.len = VARSIZE_ANY_EXHDR(key_datums[i]);
+
+ if (findJsonbValueFromContainer(&jb->root,
+ JB_FOBJECT | JB_FARRAY,
+ &strVal) == NULL)
+ PG_RETURN_BOOL(false);
+ }
+
+ PG_RETURN_BOOL(true);
+}
+
+Datum
+jsonb_contains(PG_FUNCTION_ARGS)
+{
+ Jsonb *val = PG_GETARG_JSONB_P(0);
+ Jsonb *tmpl = PG_GETARG_JSONB_P(1);
+
+ JsonbIterator *it1,
+ *it2;
+
+ if (JB_ROOT_IS_OBJECT(val) != JB_ROOT_IS_OBJECT(tmpl))
+ PG_RETURN_BOOL(false);
+
+ it1 = JsonbIteratorInit(&val->root);
+ it2 = JsonbIteratorInit(&tmpl->root);
+
+ PG_RETURN_BOOL(JsonbDeepContains(&it1, &it2));
+}
+
+Datum
+jsonb_contained(PG_FUNCTION_ARGS)
+{
+ /* Commutator of "contains" */
+ Jsonb *tmpl = PG_GETARG_JSONB_P(0);
+ Jsonb *val = PG_GETARG_JSONB_P(1);
+
+ JsonbIterator *it1,
+ *it2;
+
+ if (JB_ROOT_IS_OBJECT(val) != JB_ROOT_IS_OBJECT(tmpl))
+ PG_RETURN_BOOL(false);
+
+ it1 = JsonbIteratorInit(&val->root);
+ it2 = JsonbIteratorInit(&tmpl->root);
+
+ PG_RETURN_BOOL(JsonbDeepContains(&it1, &it2));
+}
+
+Datum
+jsonb_ne(PG_FUNCTION_ARGS)
+{
+ Jsonb *jba = PG_GETARG_JSONB_P(0);
+ Jsonb *jbb = PG_GETARG_JSONB_P(1);
+ bool res;
+
+ res = (compareJsonbContainers(&jba->root, &jbb->root) != 0);
+
+ PG_FREE_IF_COPY(jba, 0);
+ PG_FREE_IF_COPY(jbb, 1);
+ PG_RETURN_BOOL(res);
+}
+
+/*
+ * B-Tree operator class operators, support function
+ */
+Datum
+jsonb_lt(PG_FUNCTION_ARGS)
+{
+ Jsonb *jba = PG_GETARG_JSONB_P(0);
+ Jsonb *jbb = PG_GETARG_JSONB_P(1);
+ bool res;
+
+ res = (compareJsonbContainers(&jba->root, &jbb->root) < 0);
+
+ PG_FREE_IF_COPY(jba, 0);
+ PG_FREE_IF_COPY(jbb, 1);
+ PG_RETURN_BOOL(res);
+}
+
+Datum
+jsonb_gt(PG_FUNCTION_ARGS)
+{
+ Jsonb *jba = PG_GETARG_JSONB_P(0);
+ Jsonb *jbb = PG_GETARG_JSONB_P(1);
+ bool res;
+
+ res = (compareJsonbContainers(&jba->root, &jbb->root) > 0);
+
+ PG_FREE_IF_COPY(jba, 0);
+ PG_FREE_IF_COPY(jbb, 1);
+ PG_RETURN_BOOL(res);
+}
+
+Datum
+jsonb_le(PG_FUNCTION_ARGS)
+{
+ Jsonb *jba = PG_GETARG_JSONB_P(0);
+ Jsonb *jbb = PG_GETARG_JSONB_P(1);
+ bool res;
+
+ res = (compareJsonbContainers(&jba->root, &jbb->root) <= 0);
+
+ PG_FREE_IF_COPY(jba, 0);
+ PG_FREE_IF_COPY(jbb, 1);
+ PG_RETURN_BOOL(res);
+}
+
+Datum
+jsonb_ge(PG_FUNCTION_ARGS)
+{
+ Jsonb *jba = PG_GETARG_JSONB_P(0);
+ Jsonb *jbb = PG_GETARG_JSONB_P(1);
+ bool res;
+
+ res = (compareJsonbContainers(&jba->root, &jbb->root) >= 0);
+
+ PG_FREE_IF_COPY(jba, 0);
+ PG_FREE_IF_COPY(jbb, 1);
+ PG_RETURN_BOOL(res);
+}
+
+Datum
+jsonb_eq(PG_FUNCTION_ARGS)
+{
+ Jsonb *jba = PG_GETARG_JSONB_P(0);
+ Jsonb *jbb = PG_GETARG_JSONB_P(1);
+ bool res;
+
+ res = (compareJsonbContainers(&jba->root, &jbb->root) == 0);
+
+ PG_FREE_IF_COPY(jba, 0);
+ PG_FREE_IF_COPY(jbb, 1);
+ PG_RETURN_BOOL(res);
+}
+
+Datum
+jsonb_cmp(PG_FUNCTION_ARGS)
+{
+ Jsonb *jba = PG_GETARG_JSONB_P(0);
+ Jsonb *jbb = PG_GETARG_JSONB_P(1);
+ int res;
+
+ res = compareJsonbContainers(&jba->root, &jbb->root);
+
+ PG_FREE_IF_COPY(jba, 0);
+ PG_FREE_IF_COPY(jbb, 1);
+ PG_RETURN_INT32(res);
+}
+
+/*
+ * Hash operator class jsonb hashing function
+ */
+Datum
+jsonb_hash(PG_FUNCTION_ARGS)
+{
+ Jsonb *jb = PG_GETARG_JSONB_P(0);
+ JsonbIterator *it;
+ JsonbValue v;
+ JsonbIteratorToken r;
+ uint32 hash = 0;
+
+ if (JB_ROOT_COUNT(jb) == 0)
+ PG_RETURN_INT32(0);
+
+ it = JsonbIteratorInit(&jb->root);
+
+ while ((r = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
+ {
+ switch (r)
+ {
+ /* Rotation is left to JsonbHashScalarValue() */
+ case WJB_BEGIN_ARRAY:
+ hash ^= JB_FARRAY;
+ break;
+ case WJB_BEGIN_OBJECT:
+ hash ^= JB_FOBJECT;
+ break;
+ case WJB_KEY:
+ case WJB_VALUE:
+ case WJB_ELEM:
+ JsonbHashScalarValue(&v, &hash);
+ break;
+ case WJB_END_ARRAY:
+ case WJB_END_OBJECT:
+ break;
+ default:
+ elog(ERROR, "invalid JsonbIteratorNext rc: %d", (int) r);
+ }
+ }
+
+ PG_FREE_IF_COPY(jb, 0);
+ PG_RETURN_INT32(hash);
+}
+
+Datum
+jsonb_hash_extended(PG_FUNCTION_ARGS)
+{
+ Jsonb *jb = PG_GETARG_JSONB_P(0);
+ uint64 seed = PG_GETARG_INT64(1);
+ JsonbIterator *it;
+ JsonbValue v;
+ JsonbIteratorToken r;
+ uint64 hash = 0;
+
+ if (JB_ROOT_COUNT(jb) == 0)
+ PG_RETURN_UINT64(seed);
+
+ it = JsonbIteratorInit(&jb->root);
+
+ while ((r = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
+ {
+ switch (r)
+ {
+ /* Rotation is left to JsonbHashScalarValueExtended() */
+ case WJB_BEGIN_ARRAY:
+ hash ^= ((uint64) JB_FARRAY) << 32 | JB_FARRAY;
+ break;
+ case WJB_BEGIN_OBJECT:
+ hash ^= ((uint64) JB_FOBJECT) << 32 | JB_FOBJECT;
+ break;
+ case WJB_KEY:
+ case WJB_VALUE:
+ case WJB_ELEM:
+ JsonbHashScalarValueExtended(&v, &hash, seed);
+ break;
+ case WJB_END_ARRAY:
+ case WJB_END_OBJECT:
+ break;
+ default:
+ elog(ERROR, "invalid JsonbIteratorNext rc: %d", (int) r);
+ }
+ }
+
+ PG_FREE_IF_COPY(jb, 0);
+ PG_RETURN_UINT64(hash);
+}