Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BABEL: Added support for ISJSON(), JSON_VALUE(), and JSON_QUERY() #25

Merged
merged 2 commits into from
Nov 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 122 additions & 4 deletions src/backend/utils/adt/jsonb_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "common/hashfn.h"
#include "common/jsonapi.h"
#include "miscadmin.h"
#include "parser/parser.h"
#include "port/pg_bitutils.h"
#include "utils/builtins.h"
#include "utils/datetime.h"
Expand Down Expand Up @@ -381,8 +382,11 @@ findJsonbValueFromContainer(JsonbContainer *container, uint32 flags,
{
/* Object key passed by caller must be a string */
Assert(key->type == jbvString);

return getKeyJsonValueFromContainer(container, key->val.string.val,
if (sql_dialect == SQL_DIALECT_TSQL)
return tsqlGetKeyJsonValueFromContainer(container, key->val.string.val,
key->val.string.len, NULL);
else
return getKeyJsonValueFromContainer(container, key->val.string.val,
key->val.string.len, NULL);
}

Expand Down Expand Up @@ -461,6 +465,86 @@ getKeyJsonValueFromContainer(JsonbContainer *container,
return NULL;
}

/*
* Find leftmost value by key in Jsonb object and fetch it into 'res', which is also
* returned.
*
* Since SQL Server json expressions can contain duplicate key/value pairs, we must
* return the first occurrence
*
* 'res' can be passed in as NULL, in which case it's newly palloc'ed here.
*/
JsonbValue *
tsqlGetKeyJsonValueFromContainer(JsonbContainer *container,
const char *keyVal, int keyLen, JsonbValue *res)
{
JEntry *children = container->children;
int count = JsonContainerSize(container);
int difference,
candidateLen,
index;
const char *candidateVal;
char *baseAddr;
uint32 stopLow,
stopHigh,
stopMiddle,
firstIndex;

Assert(JsonContainerIsObject(container));

/* Quick out without a palloc cycle if object is empty */
if (count <= 0)
return NULL;

/*
* Binary search the container. Since we know this is an object, account
* for *Pairs* of Jentrys
*/
baseAddr = (char *) (children + count * 2);
stopLow = 0;
stopHigh = firstIndex = count;
while (stopLow < stopHigh)
{
stopMiddle = stopLow + (stopHigh - stopLow) / 2;

candidateVal = baseAddr + getJsonbOffset(container, stopMiddle);
candidateLen = getJsonbLength(container, stopMiddle);

difference = lengthCompareJsonbString(candidateVal, candidateLen,
keyVal, keyLen);

if (difference > 0)
{
stopHigh = stopMiddle;
}
else if (difference == 0)
{
firstIndex = stopMiddle;
stopHigh = stopMiddle;
}
else
{
stopLow = stopMiddle + 1;
}
}

/* If we found our key, return corresponding value */
if (firstIndex < count)
{
index = firstIndex + count;
if (!res)
res = palloc(sizeof(JsonbValue));

fillJsonbValue(container, index, baseAddr,
getJsonbOffset(container, index),
res);
return res;
}

/* Not found */
return NULL;
}

/*
* Get i-th value of a Jsonb array.
*
Expand Down Expand Up @@ -1938,6 +2022,34 @@ lengthCompareJsonbPair(const void *a, const void *b, void *binequal)
return res;
}

/*
* qsort_arg() comparator to compare JsonbPair values when
* sql_dialect is set to SQL_DIALECT_TSQL
*
* Pairs with equal keys are ordered such that last elements
* are preferred, in opposite order of the lengthCompareJsonbPair
*/
static int
tsqlLengthCompareJsonbPair(const void *a, const void *b, void *binequal)
{
const JsonbPair *pa = (const JsonbPair *) a;
const JsonbPair *pb = (const JsonbPair *) b;
int res;

res = lengthCompareJsonbStringValue(&pa->key, &pb->key);
if (res == 0 && binequal)
*((bool *) binequal) = true;

/*
* Guarantee keeping order of equal pair. Unique algorithm will prefer
* first element as value.
*/
if (res == 0)
res = (pa->order > pb->order) ? 1 : -1;

return res;
}

/*
* Sort and unique-ify pairs in JsonbValue object
*/
Expand All @@ -1949,15 +2061,21 @@ uniqueifyJsonbObject(JsonbValue *object, bool unique_keys, bool skip_nulls)
Assert(object->type == jbvObject);

if (object->val.object.nPairs > 1)
qsort_arg(object->val.object.pairs, object->val.object.nPairs, sizeof(JsonbPair),
{
if (sql_dialect == SQL_DIALECT_TSQL)
qsort_arg(object->val.object.pairs, object->val.object.nPairs, sizeof(JsonbPair),
tsqlLengthCompareJsonbPair, &hasNonUniq);
else
qsort_arg(object->val.object.pairs, object->val.object.nPairs, sizeof(JsonbPair),
lengthCompareJsonbPair, &hasNonUniq);
}

if (hasNonUniq && unique_keys)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE),
errmsg("duplicate JSON object key value")));

if (hasNonUniq || skip_nulls)
if ((hasNonUniq || skip_nulls) && sql_dialect != SQL_DIALECT_TSQL)
{
JsonbPair *ptr,
*res;
Expand Down
11 changes: 11 additions & 0 deletions src/backend/utils/adt/jsonpath_exec.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
#include "lib/stringinfo.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "parser/parser.h"
#include "regex/regex.h"
#include "utils/builtins.h"
#include "utils/date.h"
Expand Down Expand Up @@ -796,6 +797,16 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
bool singleton = size < 0;
bool hasNext = jspGetNext(jsp, &elem);

if (JsonbType(jb) != jbvArray && sql_dialect == SQL_DIALECT_TSQL)
{
if (jspIgnoreStructuralErrors(cxt))
break;
else
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
errmsg("Property cannot be found on the specified JSON path"))));
}

if (singleton)
size = 1;

Expand Down
3 changes: 3 additions & 0 deletions src/include/utils/jsonb.h
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,9 @@ extern JsonbValue *findJsonbValueFromContainer(JsonbContainer *sheader,
extern JsonbValue *getKeyJsonValueFromContainer(JsonbContainer *container,
const char *keyVal, int keyLen,
JsonbValue *res);
extern JsonbValue *tsqlGetKeyJsonValueFromContainer(JsonbContainer *container,
const char *keyVal, int keyLen,
JsonbValue *res);
extern JsonbValue *getIthJsonbValueFromContainer(JsonbContainer *sheader,
uint32 i);
extern JsonbValue *pushJsonbValue(JsonbParseState **pstate,
Expand Down