]> granicus.if.org Git - postgresql/blobdiff - src/backend/utils/adt/jsonb_util.c
Fix initialization of fake LSN for unlogged relations
[postgresql] / src / backend / utils / adt / jsonb_util.c
index 4d733159d06de0279d5c267b2c5c135c4e70d83d..8b739031609d223d40d15d25b17c5d23b7207646 100644 (file)
@@ -3,7 +3,7 @@
  * jsonb_util.c
  *       converting between Jsonb and JsonbValues, and iterating.
  *
- * Copyright (c) 2014-2015, PostgreSQL Global Development Group
+ * Copyright (c) 2014-2019, PostgreSQL Global Development Group
  *
  *
  * IDENTIFICATION
  */
 #include "postgres.h"
 
-#include "access/hash.h"
 #include "catalog/pg_collation.h"
+#include "catalog/pg_type.h"
 #include "miscadmin.h"
 #include "utils/builtins.h"
+#include "utils/datetime.h"
+#include "utils/hashutils.h"
+#include "utils/jsonapi.h"
 #include "utils/jsonb.h"
 #include "utils/memutils.h"
+#include "utils/varlena.h"
 
 /*
  * Maximum number of elements in an array (or key/value pairs in an object).
@@ -33,8 +37,8 @@
 #define JSONB_MAX_PAIRS (Min(MaxAllocSize / sizeof(JsonbPair), JB_CMASK))
 
 static void fillJsonbValue(JsonbContainer *container, int index,
-                          char *base_addr, uint32 offset,
-                          JsonbValue *result);
+                                                  char *base_addr, uint32 offset,
+                                                  JsonbValue *result);
 static bool equalsJsonbScalarValue(JsonbValue *a, JsonbValue *b);
 static int     compareJsonbScalarValue(JsonbValue *a, JsonbValue *b);
 static Jsonb *convertToJsonb(JsonbValue *val);
@@ -55,11 +59,13 @@ static void appendKey(JsonbParseState *pstate, JsonbValue *scalarVal);
 static void appendValue(JsonbParseState *pstate, JsonbValue *scalarVal);
 static void appendElement(JsonbParseState *pstate, JsonbValue *scalarVal);
 static int     lengthCompareJsonbStringValue(const void *a, const void *b);
+static int     lengthCompareJsonbString(const char *val1, int len1,
+                                                                        const char *val2, int len2);
 static int     lengthCompareJsonbPair(const void *a, const void *b, void *arg);
 static void uniqueifyJsonbObject(JsonbValue *object);
 static JsonbValue *pushJsonbValueScalar(JsonbParseState **pstate,
-                                        JsonbIteratorToken seq,
-                                        JsonbValue *scalarVal);
+                                                                               JsonbIteratorToken seq,
+                                                                               JsonbValue *scalarVal);
 
 /*
  * Turn an in-memory JsonbValue into a Jsonb for on-disk storage.
@@ -187,7 +193,7 @@ compareJsonbContainers(JsonbContainer *a, JsonbContainer *b)
        {
                JsonbValue      va,
                                        vb;
-               int                     ra,
+               JsonbIteratorToken ra,
                                        rb;
 
                ra = JsonbIteratorNext(&ita, &va, false);
@@ -241,6 +247,10 @@ compareJsonbContainers(JsonbContainer *a, JsonbContainer *b)
                                                break;
                                        case jbvBinary:
                                                elog(ERROR, "unexpected jbvBinary value");
+                                               break;
+                                       case jbvDatetime:
+                                               elog(ERROR, "unexpected jbvDatetime value");
+                                               break;
                                }
                        }
                        else
@@ -327,8 +337,7 @@ findJsonbValueFromContainer(JsonbContainer *container, uint32 flags,
                                                        JsonbValue *key)
 {
        JEntry     *children = container->children;
-       int                     count = (container->header & JB_CMASK);
-       JsonbValue *result;
+       int                     count = JsonContainerSize(container);
 
        Assert((flags & ~(JB_FARRAY | JB_FOBJECT)) == 0);
 
@@ -336,10 +345,9 @@ findJsonbValueFromContainer(JsonbContainer *container, uint32 flags,
        if (count <= 0)
                return NULL;
 
-       result = palloc(sizeof(JsonbValue));
-
-       if (flags & JB_FARRAY & container->header)
+       if ((flags & JB_FARRAY) && JsonContainerIsArray(container))
        {
+               JsonbValue *result = palloc(sizeof(JsonbValue));
                char       *base_addr = (char *) (children + count);
                uint32          offset = 0;
                int                     i;
@@ -356,56 +364,90 @@ findJsonbValueFromContainer(JsonbContainer *container, uint32 flags,
 
                        JBE_ADVANCE_OFFSET(offset, children[i]);
                }
+
+               pfree(result);
        }
-       else if (flags & JB_FOBJECT & container->header)
+       else if ((flags & JB_FOBJECT) && JsonContainerIsObject(container))
        {
-               /* Since this is an object, account for *Pairs* of Jentrys */
-               char       *base_addr = (char *) (children + count * 2);
-               uint32          stopLow = 0,
-                                       stopHigh = count;
-
                /* Object key passed by caller must be a string */
                Assert(key->type == jbvString);
 
-               /* Binary search on object/pair keys *only* */
-               while (stopLow < stopHigh)
-               {
-                       uint32          stopMiddle;
-                       int                     difference;
-                       JsonbValue      candidate;
+               return getKeyJsonValueFromContainer(container, key->val.string.val,
+                                                                                       key->val.string.len, NULL);
+       }
 
-                       stopMiddle = stopLow + (stopHigh - stopLow) / 2;
+       /* Not found */
+       return NULL;
+}
 
-                       candidate.type = jbvString;
-                       candidate.val.string.val =
-                               base_addr + getJsonbOffset(container, stopMiddle);
-                       candidate.val.string.len = getJsonbLength(container, stopMiddle);
+/*
+ * Find value by key in Jsonb object and fetch it into 'res', which is also
+ * returned.
+ *
+ * 'res' can be passed in as NULL, in which case it's newly palloc'ed here.
+ */
+JsonbValue *
+getKeyJsonValueFromContainer(JsonbContainer *container,
+                                                        const char *keyVal, int keyLen, JsonbValue *res)
+{
+       JEntry     *children = container->children;
+       int                     count = JsonContainerSize(container);
+       char       *baseAddr;
+       uint32          stopLow,
+                               stopHigh;
 
-                       difference = lengthCompareJsonbStringValue(&candidate, key);
+       Assert(JsonContainerIsObject(container));
 
-                       if (difference == 0)
-                       {
-                               /* Found our key, return corresponding value */
-                               int                     index = stopMiddle + count;
+       /* Quick out without a palloc cycle if object is empty */
+       if (count <= 0)
+               return NULL;
 
-                               fillJsonbValue(container, index, base_addr,
-                                                          getJsonbOffset(container, index),
-                                                          result);
+       /*
+        * Binary search the container. Since we know this is an object, account
+        * for *Pairs* of Jentrys
+        */
+       baseAddr = (char *) (children + count * 2);
+       stopLow = 0;
+       stopHigh = count;
+       while (stopLow < stopHigh)
+       {
+               uint32          stopMiddle;
+               int                     difference;
+               const char *candidateVal;
+               int                     candidateLen;
 
-                               return result;
-                       }
+               stopMiddle = stopLow + (stopHigh - stopLow) / 2;
+
+               candidateVal = baseAddr + getJsonbOffset(container, stopMiddle);
+               candidateLen = getJsonbLength(container, stopMiddle);
+
+               difference = lengthCompareJsonbString(candidateVal, candidateLen,
+                                                                                         keyVal, keyLen);
+
+               if (difference == 0)
+               {
+                       /* Found our key, return corresponding value */
+                       int                     index = stopMiddle + count;
+
+                       if (!res)
+                               res = palloc(sizeof(JsonbValue));
+
+                       fillJsonbValue(container, index, baseAddr,
+                                                  getJsonbOffset(container, index),
+                                                  res);
+
+                       return res;
+               }
+               else
+               {
+                       if (difference < 0)
+                               stopLow = stopMiddle + 1;
                        else
-                       {
-                               if (difference < 0)
-                                       stopLow = stopMiddle + 1;
-                               else
-                                       stopHigh = stopMiddle;
-                       }
+                               stopHigh = stopMiddle;
                }
        }
 
        /* Not found */
-       pfree(result);
        return NULL;
 }
 
@@ -421,10 +463,10 @@ getIthJsonbValueFromContainer(JsonbContainer *container, uint32 i)
        char       *base_addr;
        uint32          nelements;
 
-       if ((container->header & JB_FARRAY) == 0)
+       if (!JsonContainerIsArray(container))
                elog(ERROR, "not a jsonb array");
 
-       nelements = container->header & JB_CMASK;
+       nelements = JsonContainerSize(container);
        base_addr = (char *) &container->children[nelements];
 
        if (i >= nelements)
@@ -509,7 +551,7 @@ fillJsonbValue(JsonbContainer *container, int index,
  * "raw scalar" pseudo array to append it - the actual scalar should be passed
  * next and it will be added as the only member of the array.
  *
- * Values of type jvbBinary, which are rolled up arrays and objects,
+ * Values of type jbvBinary, which are rolled up arrays and objects,
  * are unpacked before being added to the result.
  */
 JsonbValue *
@@ -556,7 +598,7 @@ pushJsonbValueScalar(JsonbParseState **pstate, JsonbIteratorToken seq,
                        (*pstate)->contVal.type = jbvArray;
                        (*pstate)->contVal.val.array.nElems = 0;
                        (*pstate)->contVal.val.array.rawScalar = (scalarVal &&
-                                                                                        scalarVal->val.array.rawScalar);
+                                                                                                         scalarVal->val.array.rawScalar);
                        if (scalarVal && scalarVal->val.array.nElems > 0)
                        {
                                /* Assume that this array is still really a scalar */
@@ -871,7 +913,7 @@ recurse:
                        JBE_ADVANCE_OFFSET((*it)->curDataOffset,
                                                           (*it)->children[(*it)->curIndex]);
                        JBE_ADVANCE_OFFSET((*it)->curValueOffset,
-                                                  (*it)->children[(*it)->curIndex + (*it)->nElems]);
+                                                          (*it)->children[(*it)->curIndex + (*it)->nElems]);
                        (*it)->curIndex++;
 
                        /*
@@ -900,10 +942,10 @@ iteratorFromContainer(JsonbContainer *container, JsonbIterator *parent)
 {
        JsonbIterator *it;
 
-       it = palloc(sizeof(JsonbIterator));
+       it = palloc0(sizeof(JsonbIterator));
        it->container = container;
        it->parent = parent;
-       it->nElems = container->header & JB_CMASK;
+       it->nElems = JsonContainerSize(container);
 
        /* Array starts just after header */
        it->children = container->children;
@@ -913,7 +955,7 @@ iteratorFromContainer(JsonbContainer *container, JsonbIterator *parent)
                case JB_FARRAY:
                        it->dataProper =
                                (char *) it->children + it->nElems * sizeof(JEntry);
-                       it->isScalar = (container->header & JB_FSCALAR) != 0;
+                       it->isScalar = JsonContainerIsScalar(container);
                        /* This is either a "raw scalar", or an array */
                        Assert(!it->isScalar || it->nElems == 1);
 
@@ -961,10 +1003,10 @@ freeAndGetParent(JsonbIterator *it)
 bool
 JsonbDeepContains(JsonbIterator **val, JsonbIterator **mContained)
 {
-       uint32          rval,
-                               rcont;
        JsonbValue      vval,
                                vcontained;
+       JsonbIteratorToken rval,
+                               rcont;
 
        /*
         * Guard against stack overflow due to overly complex Jsonb.
@@ -1008,6 +1050,7 @@ JsonbDeepContains(JsonbIterator **val, JsonbIterator **mContained)
                for (;;)
                {
                        JsonbValue *lhsVal; /* lhsVal is from pair in lhs object */
+                       JsonbValue      lhsValBuf;
 
                        rcont = JsonbIteratorNext(mContained, &vcontained, false);
 
@@ -1020,12 +1063,14 @@ JsonbDeepContains(JsonbIterator **val, JsonbIterator **mContained)
                                return true;
 
                        Assert(rcont == WJB_KEY);
+                       Assert(vcontained.type == jbvString);
 
                        /* First, find value by key... */
-                       lhsVal = findJsonbValueFromContainer((*val)->container,
-                                                                                                JB_FOBJECT,
-                                                                                                &vcontained);
-
+                       lhsVal =
+                               getKeyJsonValueFromContainer((*val)->container,
+                                                                                        vcontained.val.string.val,
+                                                                                        vcontained.val.string.len,
+                                                                                        &lhsValBuf);
                        if (!lhsVal)
                                return false;
 
@@ -1227,7 +1272,7 @@ JsonbHashScalarValue(const JsonbValue *scalarVal, uint32 *hash)
                case jbvNumeric:
                        /* Must hash equal numerics to equal hash codes */
                        tmp = DatumGetUInt32(DirectFunctionCall1(hash_numeric,
-                                                                  NumericGetDatum(scalarVal->val.numeric)));
+                                                                                                        NumericGetDatum(scalarVal->val.numeric)));
                        break;
                case jbvBool:
                        tmp = scalarVal->val.boolean ? 0x02 : 0x04;
@@ -1248,6 +1293,49 @@ JsonbHashScalarValue(const JsonbValue *scalarVal, uint32 *hash)
        *hash ^= tmp;
 }
 
+/*
+ * Hash a value to a 64-bit value, with a seed. Otherwise, similar to
+ * JsonbHashScalarValue.
+ */
+void
+JsonbHashScalarValueExtended(const JsonbValue *scalarVal, uint64 *hash,
+                                                        uint64 seed)
+{
+       uint64          tmp;
+
+       switch (scalarVal->type)
+       {
+               case jbvNull:
+                       tmp = seed + 0x01;
+                       break;
+               case jbvString:
+                       tmp = DatumGetUInt64(hash_any_extended((const unsigned char *) scalarVal->val.string.val,
+                                                                                                  scalarVal->val.string.len,
+                                                                                                  seed));
+                       break;
+               case jbvNumeric:
+                       tmp = DatumGetUInt64(DirectFunctionCall2(hash_numeric_extended,
+                                                                                                        NumericGetDatum(scalarVal->val.numeric),
+                                                                                                        UInt64GetDatum(seed)));
+                       break;
+               case jbvBool:
+                       if (seed)
+                               tmp = DatumGetUInt64(DirectFunctionCall2(hashcharextended,
+                                                                                                                BoolGetDatum(scalarVal->val.boolean),
+                                                                                                                UInt64GetDatum(seed)));
+                       else
+                               tmp = scalarVal->val.boolean ? 0x02 : 0x04;
+
+                       break;
+               default:
+                       elog(ERROR, "invalid jsonb scalar type");
+                       break;
+       }
+
+       *hash = ROTATE_HIGH_AND_LOW_32BITS(*hash);
+       *hash ^= tmp;
+}
+
 /*
  * Are two scalar JsonbValues of the same type a and b equal?
  */
@@ -1264,8 +1352,8 @@ equalsJsonbScalarValue(JsonbValue *aScalar, JsonbValue *bScalar)
                                return lengthCompareJsonbStringValue(aScalar, bScalar) == 0;
                        case jbvNumeric:
                                return DatumGetBool(DirectFunctionCall2(numeric_eq,
-                                                                          PointerGetDatum(aScalar->val.numeric),
-                                                                        PointerGetDatum(bScalar->val.numeric)));
+                                                                                                               PointerGetDatum(aScalar->val.numeric),
+                                                                                                               PointerGetDatum(bScalar->val.numeric)));
                        case jbvBool:
                                return aScalar->val.boolean == bScalar->val.boolean;
 
@@ -1274,7 +1362,7 @@ equalsJsonbScalarValue(JsonbValue *aScalar, JsonbValue *bScalar)
                }
        }
        elog(ERROR, "jsonb scalar type mismatch");
-       return -1;
+       return false;
 }
 
 /*
@@ -1300,12 +1388,12 @@ compareJsonbScalarValue(JsonbValue *aScalar, JsonbValue *bScalar)
                                                                  DEFAULT_COLLATION_OID);
                        case jbvNumeric:
                                return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
-                                                                          PointerGetDatum(aScalar->val.numeric),
-                                                                        PointerGetDatum(bScalar->val.numeric)));
+                                                                                                                PointerGetDatum(aScalar->val.numeric),
+                                                                                                                PointerGetDatum(bScalar->val.numeric)));
                        case jbvBool:
                                if (aScalar->val.boolean == bScalar->val.boolean)
                                        return 0;
-                               else if (aScalar->val.boolean >bScalar->val.boolean)
+                               else if (aScalar->val.boolean > bScalar->val.boolean)
                                        return 1;
                                else
                                        return -1;
@@ -1319,7 +1407,7 @@ compareJsonbScalarValue(JsonbValue *aScalar, JsonbValue *bScalar)
 
 
 /*
- * Functions for manipulating the resizeable buffer used by convertJsonb and
+ * Functions for manipulating the resizable buffer used by convertJsonb and
  * its subroutines.
  */
 
@@ -1684,6 +1772,14 @@ convertJsonbScalar(StringInfo buffer, JEntry *jentry, JsonbValue *scalarVal)
                        break;
 
                case jbvNumeric:
+                       /* replace numeric NaN with string "NaN" */
+                       if (numeric_is_nan(scalarVal->val.numeric))
+                       {
+                               appendToBuffer(buffer, "NaN", 3);
+                               *jentry = 3;
+                               break;
+                       }
+
                        numlen = VARSIZE_ANY(scalarVal->val.numeric);
                        padlen = padBufferToInt(buffer);
 
@@ -1697,6 +1793,22 @@ convertJsonbScalar(StringInfo buffer, JEntry *jentry, JsonbValue *scalarVal)
                                JENTRY_ISBOOL_TRUE : JENTRY_ISBOOL_FALSE;
                        break;
 
+               case jbvDatetime:
+                       {
+                               char            buf[MAXDATELEN + 1];
+                               size_t          len;
+
+                               JsonEncodeDateTime(buf,
+                                                                  scalarVal->val.datetime.value,
+                                                                  scalarVal->val.datetime.typid,
+                                                                  &scalarVal->val.datetime.tz);
+                               len = strlen(buf);
+                               appendToBuffer(buffer, buf, len);
+
+                               *jentry = len;
+                       }
+                       break;
+
                default:
                        elog(ERROR, "invalid jsonb scalar type");
        }
@@ -1719,21 +1831,27 @@ lengthCompareJsonbStringValue(const void *a, const void *b)
 {
        const JsonbValue *va = (const JsonbValue *) a;
        const JsonbValue *vb = (const JsonbValue *) b;
-       int                     res;
 
        Assert(va->type == jbvString);
        Assert(vb->type == jbvString);
 
-       if (va->val.string.len == vb->val.string.len)
-       {
-               res = memcmp(va->val.string.val, vb->val.string.val, va->val.string.len);
-       }
-       else
-       {
-               res = (va->val.string.len > vb->val.string.len) ? 1 : -1;
-       }
+       return lengthCompareJsonbString(va->val.string.val, va->val.string.len,
+                                                                       vb->val.string.val, vb->val.string.len);
+}
 
-       return res;
+/*
+ * Subroutine for lengthCompareJsonbStringValue
+ *
+ * This is also useful separately to implement binary search on
+ * JsonbContainers.
+ */
+static int
+lengthCompareJsonbString(const char *val1, int len1, const char *val2, int len2)
+{
+       if (len1 == len2)
+               return memcmp(val1, val2, len1);
+       else
+               return len1 > len2 ? 1 : -1;
 }
 
 /*