30
30
#include "utils/memutils.h"
31
31
#include "utils/varlena.h"
32
32
33
+ #define JSONB_SORTED_VALUES 1
34
+
33
35
/*
34
36
* Maximum number of elements in an array (or key/value pairs in an object).
35
37
* This is limited by two things: the size of the JEntry array must fit
@@ -82,6 +84,7 @@ typedef struct jsonbIterator
82
84
const JEntry * children ; /* JEntrys for child nodes */
83
85
/* Data proper. This points to the beginning of the variable-length data */
84
86
char * dataProper ;
87
+ uint32 * kvMap ;
85
88
86
89
/* Current item in buffer (up to nElems) */
87
90
int curIndex ;
@@ -556,6 +559,8 @@ jsonbFindKeyInObject(JsonContainer *jsc, const char *keyVal, int keyLen,
556
559
const JEntry * children = container -> children ;
557
560
int count = JsonContainerSize (jsc );
558
561
char * baseAddr ;
562
+ bool sorted_values = (container -> header & JBC_TMASK ) == JBC_TOBJECT_SORTED ;
563
+ const uint32 * kvmap ;
559
564
uint32 stopLow ,
560
565
stopHigh ;
561
566
@@ -569,7 +574,16 @@ jsonbFindKeyInObject(JsonContainer *jsc, const char *keyVal, int keyLen,
569
574
* Binary search the container. Since we know this is an object, account
570
575
* for *Pairs* of Jentrys
571
576
*/
572
- baseAddr = (char * ) (children + count * 2 );
577
+ if (sorted_values )
578
+ {
579
+ kvmap = & children [count * 2 ];
580
+ baseAddr = (char * ) & kvmap [count ];
581
+ }
582
+ else
583
+ {
584
+ kvmap = NULL ;
585
+ baseAddr = (char * ) (children + count * 2 );
586
+ }
573
587
stopLow = 0 ;
574
588
stopHigh = count ;
575
589
while (stopLow < stopHigh )
@@ -590,7 +604,7 @@ jsonbFindKeyInObject(JsonContainer *jsc, const char *keyVal, int keyLen,
590
604
if (difference == 0 )
591
605
{
592
606
/* Found our key, return corresponding value */
593
- int index = stopMiddle + count ;
607
+ int index = ( sorted_values ? kvmap [ stopMiddle ] : stopMiddle ) + count ;
594
608
595
609
if (!res )
596
610
res = palloc (sizeof (JsonbValue ));
@@ -1203,14 +1217,18 @@ jsonbIteratorNext(JsonIterator **jsit, JsonbValue *val, bool skipNested)
1203
1217
(* it )-> state = JBI_OBJECT_KEY ;
1204
1218
1205
1219
fillCompressedJsonbValue ((* it )-> compressed , (* it )-> container ,
1206
- (* it )-> curIndex + (* it )-> nElems ,
1207
- (* it )-> dataProper , (* it )-> curValueOffset ,
1220
+ ((* it )-> kvMap ? (* it )-> kvMap [(* it )-> curIndex ] : (* it )-> curIndex ) + (* it )-> nElems ,
1221
+ (* it )-> dataProper ,
1222
+ (* it )-> kvMap ?
1223
+ getJsonbOffset ((* it )-> container , (* it )-> kvMap [(* it )-> curIndex ] + (* it )-> nElems ) :
1224
+ (* it )-> curValueOffset ,
1208
1225
val );
1209
1226
1210
1227
JBE_ADVANCE_OFFSET ((* it )-> curDataOffset ,
1211
1228
(* it )-> children [(* it )-> curIndex ]);
1212
- JBE_ADVANCE_OFFSET ((* it )-> curValueOffset ,
1213
- (* it )-> children [(* it )-> curIndex + (* it )-> nElems ]);
1229
+ if (!(* it )-> kvMap )
1230
+ JBE_ADVANCE_OFFSET ((* it )-> curValueOffset ,
1231
+ (* it )-> children [(* it )-> curIndex + (* it )-> nElems ]);
1214
1232
(* it )-> curIndex ++ ;
1215
1233
1216
1234
/*
@@ -1263,24 +1281,34 @@ jsonbIteratorInitExt(JsonContainer *cont,
1263
1281
/* Array starts just after header */
1264
1282
it -> children = container -> children ;
1265
1283
1266
- switch (container -> header & ( JBC_FARRAY | JBC_FOBJECT ) )
1284
+ switch (container -> header & JBC_TMASK )
1267
1285
{
1268
- case JBC_FARRAY :
1286
+ case JBC_TSCALAR :
1287
+ it -> isScalar = true;
1288
+ /* FALLTHROUGH */
1289
+ case JBC_TARRAY :
1269
1290
it -> dataProper =
1270
1291
(char * ) it -> children + it -> nElems * sizeof (JEntry );
1271
- it -> isScalar = (container -> header & JBC_FSCALAR ) != 0 ;
1272
1292
/* This is either a "raw scalar", or an array */
1273
1293
Assert (!it -> isScalar || it -> nElems == 1 );
1274
1294
1275
1295
it -> state = JBI_ARRAY_START ;
1276
1296
break ;
1277
1297
1278
- case JBC_FOBJECT :
1298
+ case JBC_TOBJECT :
1299
+ it -> kvMap = NULL ;
1279
1300
it -> dataProper =
1280
1301
(char * ) it -> children + it -> nElems * sizeof (JEntry ) * 2 ;
1281
1302
it -> state = JBI_OBJECT_START ;
1282
1303
break ;
1283
1304
1305
+ case JBC_TOBJECT_SORTED :
1306
+ it -> kvMap = (uint32 * )
1307
+ ((char * ) it -> children + it -> nElems * sizeof (JEntry ) * 2 );
1308
+ it -> dataProper = (char * ) & it -> kvMap [it -> nElems ];
1309
+ it -> state = JBI_OBJECT_START ;
1310
+ break ;
1311
+
1284
1312
default :
1285
1313
elog (ERROR , "unknown type of jsonb container" );
1286
1314
}
@@ -1887,13 +1915,14 @@ convertJsonbArray(StringInfo buffer, JEntry *pheader, const JsonbValue *val, int
1887
1915
* Construct the header Jentry and store it in the beginning of the
1888
1916
* variable-length payload.
1889
1917
*/
1890
- header = nElems | JBC_FARRAY ;
1891
1918
if (val -> val .array .rawScalar )
1892
1919
{
1893
1920
Assert (nElems == 1 );
1894
1921
Assert (level == 0 );
1895
- header |= JBC_FSCALAR ;
1922
+ header = nElems | JBC_TSCALAR ;
1896
1923
}
1924
+ else
1925
+ header = nElems | JBC_TARRAY ;
1897
1926
1898
1927
appendToBuffer (buffer , (char * ) & header , sizeof (uint32 ));
1899
1928
@@ -1951,6 +1980,48 @@ convertJsonbArray(StringInfo buffer, JEntry *pheader, const JsonbValue *val, int
1951
1980
* pheader = JENTRY_ISCONTAINER | totallen ;
1952
1981
}
1953
1982
1983
+ static int
1984
+ int_cmp (const void * a , const void * b )
1985
+ {
1986
+ int x = * (const int * ) a ;
1987
+ int y = * (const int * ) b ;
1988
+
1989
+ return x == y ? 0 : x > y ? 1 : -1 ;
1990
+ }
1991
+
1992
+ static int
1993
+ estimateJsonbValueSize (const JsonbValue * jbv )
1994
+ {
1995
+ int size ;
1996
+
1997
+ switch (jbv -> type )
1998
+ {
1999
+ case jbvNull :
2000
+ case jbvBool :
2001
+ return 0 ;
2002
+ case jbvString :
2003
+ return jbv -> val .string .len ;
2004
+ case jbvNumeric :
2005
+ return VARSIZE_ANY (jbv -> val .numeric );
2006
+ case jbvArray :
2007
+ size = offsetof(JsonbContainerHeader , children [jbv -> val .array .nElems ]);
2008
+ for (int i = 0 ; i < jbv -> val .array .nElems ; i ++ )
2009
+ size += estimateJsonbValueSize (& jbv -> val .array .elems [i ]);
2010
+ return size ;
2011
+ case jbvObject :
2012
+ size = offsetof(JsonbContainerHeader , children [jbv -> val .object .nPairs * 2 ]);
2013
+ for (int i = 0 ; i < jbv -> val .object .nPairs ; i ++ )
2014
+ {
2015
+ size += estimateJsonbValueSize (& jbv -> val .object .pairs [i ].key );
2016
+ size += estimateJsonbValueSize (& jbv -> val .object .pairs [i ].value );
2017
+ }
2018
+ return size ;
2019
+ default :
2020
+ elog (ERROR , "invalid jsonb value type: %d" , jbv -> type );
2021
+ return 0 ;
2022
+ }
2023
+ }
2024
+
1954
2025
static void
1955
2026
convertJsonbObject (StringInfo buffer , JEntry * pheader , const JsonbValue * val , int level )
1956
2027
{
@@ -1960,9 +2031,39 @@ convertJsonbObject(StringInfo buffer, JEntry *pheader, const JsonbValue *val, in
1960
2031
int totallen ;
1961
2032
uint32 header ;
1962
2033
int nPairs = val -> val .object .nPairs ;
2034
+ int reserved_size ;
2035
+ bool sorted_values = JSONB_SORTED_VALUES && nPairs > 1 ;
2036
+ struct
2037
+ {
2038
+ int size ;
2039
+ int32 index ;
2040
+ } * values = sorted_values ? palloc (sizeof (* values ) * nPairs ) : NULL ;
1963
2041
1964
2042
Assert (nPairs >= 0 );
1965
2043
2044
+ if (sorted_values )
2045
+ {
2046
+ for (i = 0 ; i < nPairs ; i ++ )
2047
+ {
2048
+ values [i ].index = i ;
2049
+ values [i ].size = estimateJsonbValueSize (& val -> val .object .pairs [i ].value );
2050
+ }
2051
+
2052
+ qsort (values , nPairs , sizeof (* values ), int_cmp );
2053
+
2054
+ /* check if keys were really moved */
2055
+ sorted_values = false;
2056
+
2057
+ for (i = 0 ; i < nPairs ; i ++ )
2058
+ {
2059
+ if (values [i ].index != i )
2060
+ {
2061
+ sorted_values = true;
2062
+ break ;
2063
+ }
2064
+ }
2065
+ }
2066
+
1966
2067
/* Remember where in the buffer this object starts. */
1967
2068
base_offset = buffer -> len ;
1968
2069
@@ -1973,17 +2074,30 @@ convertJsonbObject(StringInfo buffer, JEntry *pheader, const JsonbValue *val, in
1973
2074
* Construct the header Jentry and store it in the beginning of the
1974
2075
* variable-length payload.
1975
2076
*/
1976
- header = nPairs | JBC_FOBJECT ;
2077
+ header = nPairs | ( sorted_values ? JBC_TOBJECT_SORTED : JBC_TOBJECT ) ;
1977
2078
appendToBuffer (buffer , (char * ) & header , sizeof (uint32 ));
1978
2079
1979
2080
/* Reserve space for the JEntries of the keys and values. */
1980
- jentry_offset = reserveFromBuffer (buffer , sizeof (JEntry ) * nPairs * 2 );
2081
+ reserved_size = sizeof (JEntry ) * nPairs * 2 ;
2082
+ if (sorted_values )
2083
+ reserved_size += sizeof (int32 ) * nPairs ;
2084
+
2085
+ jentry_offset = reserveFromBuffer (buffer , reserved_size );
2086
+
2087
+ /* Write key-value map */
2088
+ if (sorted_values )
2089
+ {
2090
+ for (i = 0 ; i < nPairs ; i ++ )
2091
+ copyToBuffer (buffer , jentry_offset + sizeof (JEntry ) * nPairs * 2 + values [i ].index * sizeof (int32 ),
2092
+ (void * ) & i , sizeof (int32 ));
2093
+ }
1981
2094
1982
2095
/*
1983
2096
* Iterate over the keys, then over the values, since that is the ordering
1984
2097
* we want in the on-disk representation.
1985
2098
*/
1986
2099
totallen = 0 ;
2100
+
1987
2101
for (i = 0 ; i < nPairs ; i ++ )
1988
2102
{
1989
2103
JsonbPair * pair = & val -> val .object .pairs [i ];
@@ -2019,9 +2133,11 @@ convertJsonbObject(StringInfo buffer, JEntry *pheader, const JsonbValue *val, in
2019
2133
copyToBuffer (buffer , jentry_offset , (char * ) & meta , sizeof (JEntry ));
2020
2134
jentry_offset += sizeof (JEntry );
2021
2135
}
2136
+
2022
2137
for (i = 0 ; i < nPairs ; i ++ )
2023
2138
{
2024
- JsonbPair * pair = & val -> val .object .pairs [i ];
2139
+ int val_index = sorted_values ? values [i ].index : i ;
2140
+ JsonbPair * pair = & val -> val .object .pairs [val_index ];
2025
2141
int len ;
2026
2142
JEntry meta ;
2027
2143
@@ -2055,6 +2171,9 @@ convertJsonbObject(StringInfo buffer, JEntry *pheader, const JsonbValue *val, in
2055
2171
jentry_offset += sizeof (JEntry );
2056
2172
}
2057
2173
2174
+ if (values )
2175
+ pfree (values );
2176
+
2058
2177
/* Total data size is everything we've appended to buffer */
2059
2178
totallen = buffer -> len - base_offset ;
2060
2179
@@ -2280,17 +2399,36 @@ uniqueifyJsonbObject(JsonbValue *object, bool unique_keys, bool skip_nulls)
2280
2399
}
2281
2400
}
2282
2401
2402
+ static void
2403
+ jsonbInitContainerFromHeader (JsonContainerData * jc , JsonbContainerHeader * jbc )
2404
+ {
2405
+ jc -> size = jbc -> header & JBC_CMASK ;
2406
+ switch (jbc -> header & JBC_TMASK )
2407
+ {
2408
+ case JBC_TOBJECT :
2409
+ case JBC_TOBJECT_SORTED :
2410
+ jc -> type = jbvObject ;
2411
+ break ;
2412
+ case JBC_TARRAY :
2413
+ jc -> type = jbvArray ;
2414
+ break ;
2415
+ case JBC_TSCALAR :
2416
+ jc -> type = jbvArray | jbvScalar ;
2417
+ break ;
2418
+ default :
2419
+ elog (ERROR , "invalid jsonb container type: %d" ,
2420
+ jbc -> header & JBC_TMASK );
2421
+ }
2422
+ }
2423
+
2283
2424
static void
2284
2425
jsonbInitContainer (JsonContainerData * jc , JsonbContainerHeader * jbc , int len )
2285
2426
{
2286
2427
jc -> ops = & jsonbContainerOps ;
2287
2428
JsonContainerDataPtr (jc ) = jbc ;
2288
2429
jc -> len = len ;
2289
2430
jc -> toasterid = InvalidOid ;
2290
- jc -> size = jbc -> header & JBC_CMASK ;
2291
- jc -> type = jbc -> header & JBC_FOBJECT ? jbvObject :
2292
- jbc -> header & JBC_FSCALAR ? jbvArray | jbvScalar :
2293
- jbvArray ;
2431
+ jsonbInitContainerFromHeader (jc , jbc );
2294
2432
}
2295
2433
2296
2434
static void
@@ -2406,10 +2544,7 @@ jsonbzInitContainer(JsonContainerData *jc, CompressedJsonb *cjb, int len)
2406
2544
2407
2545
jc -> ops = & jsonbzContainerOps ;
2408
2546
jc -> len = len ;
2409
- jc -> size = jbc -> header & JBC_CMASK ;
2410
- jc -> type = jbc -> header & JBC_FOBJECT ? jbvObject :
2411
- jbc -> header & JBC_FSCALAR ? jbvArray | jbvScalar :
2412
- jbvArray ;
2547
+ jsonbInitContainerFromHeader (jc , jbc );
2413
2548
}
2414
2549
2415
2550
static JsonbContainer *
@@ -2479,12 +2614,15 @@ findValueInCompressedJsonbObject(CompressedJsonb *cjb, const char *keystr, int k
2479
2614
JEntry * children = container -> children ;
2480
2615
int count = container -> header & JBC_CMASK ;
2481
2616
/* Since this is an object, account for *Pairs* of Jentrys */
2482
- char * base_addr = (char * ) (children + count * 2 );
2617
+ bool sorted_values = (container -> header & JBC_TMASK ) == JBC_TOBJECT_SORTED ;
2618
+ char * base_addr = (char * ) (children + count * 2 ) + (sorted_values ? sizeof (uint32 ) * count : 0 );
2619
+ uint32 * kvmap = sorted_values ? & container -> children [count * 2 ] : NULL ;
2483
2620
Size base_offset = base_addr - (char * ) jb ;
2484
2621
uint32 stopLow = 0 ,
2485
2622
stopHigh = count ;
2486
2623
2487
- Assert (jb -> root .header & JB_FOBJECT );
2624
+ Assert ((jb -> root .header & JBC_TMASK ) == JBC_TOBJECT ||
2625
+ (jb -> root .header & JBC_TMASK ) == JBC_TOBJECT_SORTED );
2488
2626
2489
2627
/* Quick out if object/array is empty */
2490
2628
if (count <= 0 )
@@ -2521,7 +2659,7 @@ findValueInCompressedJsonbObject(CompressedJsonb *cjb, const char *keystr, int k
2521
2659
if (difference == 0 )
2522
2660
{
2523
2661
/* Found our key, return corresponding value */
2524
- int index = stopMiddle + count ;
2662
+ int index = ( sorted_values ? kvmap [ stopMiddle ] : stopMiddle ) + count ;
2525
2663
2526
2664
if (!res )
2527
2665
res = palloc (sizeof (* res ));
0 commit comments