31
31
#include "utils/memutils.h"
32
32
#include "utils/varlena.h"
33
33
34
+ #define JSONB_SORTED_VALUES 1
35
+
34
36
/*
35
37
* Maximum number of elements in an array (or key/value pairs in an object).
36
38
* This is limited by two things: the size of the JEntry array must fit
@@ -81,6 +83,7 @@ struct JsonbIterator
81
83
const JEntry * children ;/* JEntrys for child nodes */
82
84
/* Data proper. This points to the beginning of the variable-length data */
83
85
char * dataProper ;
86
+ uint32 * kvMap ;
84
87
85
88
/* Current item in buffer (up to nElems) */
86
89
int curIndex ;
@@ -561,6 +564,8 @@ getKeyJsonValueFromContainer(JsonContainer *jsc,
561
564
const JEntry * children = container -> children ;
562
565
int count = JsonContainerSize (jsc );
563
566
char * baseAddr ;
567
+ bool sorted_values = (container -> header & JB_TMASK )== JB_TOBJECT_SORTED ;
568
+ const uint32 * kvmap ;
564
569
uint32 stopLow ,
565
570
stopHigh ;
566
571
@@ -574,7 +579,16 @@ getKeyJsonValueFromContainer(JsonContainer *jsc,
574
579
* Binary search the container. Since we know this is an object, account
575
580
* for *Pairs* of Jentrys
576
581
*/
577
- baseAddr = (char * ) (children + count * 2 );
582
+ if (sorted_values )
583
+ {
584
+ kvmap = & children [count * 2 ];
585
+ baseAddr = (char * )& kvmap [count ];
586
+ }
587
+ else
588
+ {
589
+ kvmap = NULL ;
590
+ baseAddr = (char * ) (children + count * 2 );
591
+ }
578
592
stopLow = 0 ;
579
593
stopHigh = count ;
580
594
while (stopLow < stopHigh )
@@ -595,7 +609,7 @@ getKeyJsonValueFromContainer(JsonContainer *jsc,
595
609
if (difference == 0 )
596
610
{
597
611
/* Found our key, return corresponding value */
598
- int index = stopMiddle + count ;
612
+ int index = ( sorted_values ? kvmap [ stopMiddle ] : stopMiddle ) + count ;
599
613
600
614
if (!res )
601
615
res = palloc (sizeof (JsonbValue ));
@@ -1197,14 +1211,18 @@ JsonbIteratorNext(JsonIterator **jsit, JsonbValue *val, bool skipNested)
1197
1211
(* it )-> state = JBI_OBJECT_KEY ;
1198
1212
1199
1213
fillCompressedJsonbValue ((* it )-> compressed , (* it )-> container ,
1200
- (* it )-> curIndex + (* it )-> nElems ,
1201
- (* it )-> dataProper , (* it )-> curValueOffset ,
1214
+ ((* it )-> kvMap ? (* it )-> kvMap [(* it )-> curIndex ] : (* it )-> curIndex )+ (* it )-> nElems ,
1215
+ (* it )-> dataProper ,
1216
+ (* it )-> kvMap ?
1217
+ getJsonbOffset ((* it )-> container , (* it )-> kvMap [(* it )-> curIndex ]+ (* it )-> nElems ) :
1218
+ (* it )-> curValueOffset ,
1202
1219
val );
1203
1220
1204
1221
JBE_ADVANCE_OFFSET ((* it )-> curDataOffset ,
1205
1222
(* it )-> children [(* it )-> curIndex ]);
1206
- JBE_ADVANCE_OFFSET ((* it )-> curValueOffset ,
1207
- (* it )-> children [(* it )-> curIndex + (* it )-> nElems ]);
1223
+ if (!(* it )-> kvMap )
1224
+ JBE_ADVANCE_OFFSET ((* it )-> curValueOffset ,
1225
+ (* it )-> children [(* it )-> curIndex + (* it )-> nElems ]);
1208
1226
(* it )-> curIndex ++ ;
1209
1227
1210
1228
/*
@@ -1256,24 +1274,34 @@ jsonbIteratorInit(JsonContainer *cont, const JsonbContainer *container,
1256
1274
/* Array starts just after header */
1257
1275
it -> children = container -> children ;
1258
1276
1259
- switch (container -> header & ( JB_FARRAY | JB_FOBJECT ) )
1277
+ switch (container -> header & JB_TMASK )
1260
1278
{
1261
- case JB_FARRAY :
1279
+ case JB_TSCALAR :
1280
+ it -> isScalar = true;
1281
+ /* FALLTHROUGH */
1282
+ case JB_TARRAY :
1262
1283
it -> dataProper =
1263
1284
(char * )it -> children + it -> nElems * sizeof (JEntry );
1264
- it -> isScalar = (container -> header & JB_FSCALAR )!= 0 ;
1265
1285
/* This is either a "raw scalar", or an array */
1266
1286
Assert (!it -> isScalar || it -> nElems == 1 );
1267
1287
1268
1288
it -> state = JBI_ARRAY_START ;
1269
1289
break ;
1270
1290
1271
- case JB_FOBJECT :
1291
+ case JB_TOBJECT :
1292
+ it -> kvMap = NULL ;
1272
1293
it -> dataProper =
1273
1294
(char * )it -> children + it -> nElems * sizeof (JEntry )* 2 ;
1274
1295
it -> state = JBI_OBJECT_START ;
1275
1296
break ;
1276
1297
1298
+ case JB_TOBJECT_SORTED :
1299
+ it -> kvMap = (uint32 * )
1300
+ ((char * )it -> children + it -> nElems * sizeof (JEntry )* 2 );
1301
+ it -> dataProper = (char * )& it -> kvMap [it -> nElems ];
1302
+ it -> state = JBI_OBJECT_START ;
1303
+ break ;
1304
+
1277
1305
default :
1278
1306
elog (ERROR ,"unknown type of jsonb container" );
1279
1307
}
@@ -1877,13 +1905,14 @@ convertJsonbArray(StringInfo buffer, JEntry *pheader, const JsonbValue *val, int
1877
1905
* Construct the header Jentry and store it in the beginning of the
1878
1906
* variable-length payload.
1879
1907
*/
1880
- header = nElems |JB_FARRAY ;
1881
1908
if (val -> val .array .rawScalar )
1882
1909
{
1883
1910
Assert (nElems == 1 );
1884
1911
Assert (level == 0 );
1885
- header |= JB_FSCALAR ;
1912
+ header = nElems | JB_TSCALAR ;
1886
1913
}
1914
+ else
1915
+ header = nElems |JB_TARRAY ;
1887
1916
1888
1917
appendToBuffer (buffer , (char * )& header ,sizeof (uint32 ));
1889
1918
@@ -1941,6 +1970,48 @@ convertJsonbArray(StringInfo buffer, JEntry *pheader, const JsonbValue *val, int
1941
1970
* pheader = JENTRY_ISCONTAINER |totallen ;
1942
1971
}
1943
1972
1973
+ static int
1974
+ int_cmp (const void * a ,const void * b )
1975
+ {
1976
+ int x = * (const int * )a ;
1977
+ int y = * (const int * )b ;
1978
+
1979
+ return x == y ?0 :x > y ?1 :-1 ;
1980
+ }
1981
+
1982
+ static int
1983
+ estimateJsonbValueSize (const JsonbValue * jbv )
1984
+ {
1985
+ int size ;
1986
+
1987
+ switch (jbv -> type )
1988
+ {
1989
+ case jbvNull :
1990
+ case jbvBool :
1991
+ return 0 ;
1992
+ case jbvString :
1993
+ return jbv -> val .string .len ;
1994
+ case jbvNumeric :
1995
+ return VARSIZE_ANY (jbv -> val .numeric );
1996
+ case jbvArray :
1997
+ size = offsetof(JsonbContainer ,children [jbv -> val .array .nElems ]);
1998
+ for (int i = 0 ;i < jbv -> val .array .nElems ;i ++ )
1999
+ size += estimateJsonbValueSize (& jbv -> val .array .elems [i ]);
2000
+ return size ;
2001
+ case jbvObject :
2002
+ size = offsetof(JsonbContainer ,children [jbv -> val .object .nPairs * 2 ]);
2003
+ for (int i = 0 ;i < jbv -> val .object .nPairs ;i ++ )
2004
+ {
2005
+ size += estimateJsonbValueSize (& jbv -> val .object .pairs [i ].key );
2006
+ size += estimateJsonbValueSize (& jbv -> val .object .pairs [i ].value );
2007
+ }
2008
+ return size ;
2009
+ default :
2010
+ elog (ERROR ,"invalid jsonb value type: %d" ,jbv -> type );
2011
+ return 0 ;
2012
+ }
2013
+ }
2014
+
1944
2015
static void
1945
2016
convertJsonbObject (StringInfo buffer ,JEntry * pheader ,const JsonbValue * val ,int level )
1946
2017
{
@@ -1950,9 +2021,39 @@ convertJsonbObject(StringInfo buffer, JEntry *pheader, const JsonbValue *val, in
1950
2021
int totallen ;
1951
2022
uint32 header ;
1952
2023
int nPairs = val -> val .object .nPairs ;
2024
+ int reserved_size ;
2025
+ bool sorted_values = JSONB_SORTED_VALUES && nPairs > 1 ;
2026
+ struct
2027
+ {
2028
+ int size ;
2029
+ int32 index ;
2030
+ }* values = sorted_values ?palloc (sizeof (* values )* nPairs ) :NULL ;
1953
2031
1954
2032
Assert (nPairs >=0 );
1955
2033
2034
+ if (sorted_values )
2035
+ {
2036
+ for (i = 0 ;i < nPairs ;i ++ )
2037
+ {
2038
+ values [i ].index = i ;
2039
+ values [i ].size = estimateJsonbValueSize (& val -> val .object .pairs [i ].value );
2040
+ }
2041
+
2042
+ qsort (values ,nPairs ,sizeof (* values ),int_cmp );
2043
+
2044
+ /* check if keys were really moved */
2045
+ sorted_values = false;
2046
+
2047
+ for (i = 0 ;i < nPairs ;i ++ )
2048
+ {
2049
+ if (values [i ].index != i )
2050
+ {
2051
+ sorted_values = true;
2052
+ break ;
2053
+ }
2054
+ }
2055
+ }
2056
+
1956
2057
/* Remember where in the buffer this object starts. */
1957
2058
base_offset = buffer -> len ;
1958
2059
@@ -1963,17 +2064,30 @@ convertJsonbObject(StringInfo buffer, JEntry *pheader, const JsonbValue *val, in
1963
2064
* Construct the header Jentry and store it in the beginning of the
1964
2065
* variable-length payload.
1965
2066
*/
1966
- header = nPairs |JB_FOBJECT ;
2067
+ header = nPairs |( sorted_values ? JB_TOBJECT_SORTED : JB_TOBJECT ) ;
1967
2068
appendToBuffer (buffer , (char * )& header ,sizeof (uint32 ));
1968
2069
1969
2070
/* Reserve space for the JEntries of the keys and values. */
1970
- jentry_offset = reserveFromBuffer (buffer ,sizeof (JEntry )* nPairs * 2 );
2071
+ reserved_size = sizeof (JEntry )* nPairs * 2 ;
2072
+ if (sorted_values )
2073
+ reserved_size += sizeof (int32 )* nPairs ;
2074
+
2075
+ jentry_offset = reserveFromBuffer (buffer ,reserved_size );
2076
+
2077
+ /* Write key-value map */
2078
+ if (sorted_values )
2079
+ {
2080
+ for (i = 0 ;i < nPairs ;i ++ )
2081
+ copyToBuffer (buffer ,jentry_offset + sizeof (JEntry )* nPairs * 2 + values [i ].index * sizeof (int32 ),
2082
+ & i ,sizeof (int32 ));
2083
+ }
1971
2084
1972
2085
/*
1973
2086
* Iterate over the keys, then over the values, since that is the ordering
1974
2087
* we want in the on-disk representation.
1975
2088
*/
1976
2089
totallen = 0 ;
2090
+
1977
2091
for (i = 0 ;i < nPairs ;i ++ )
1978
2092
{
1979
2093
JsonbPair * pair = & val -> val .object .pairs [i ];
@@ -2009,9 +2123,11 @@ convertJsonbObject(StringInfo buffer, JEntry *pheader, const JsonbValue *val, in
2009
2123
copyToBuffer (buffer ,jentry_offset , (char * )& meta ,sizeof (JEntry ));
2010
2124
jentry_offset += sizeof (JEntry );
2011
2125
}
2126
+
2012
2127
for (i = 0 ;i < nPairs ;i ++ )
2013
2128
{
2014
- JsonbPair * pair = & val -> val .object .pairs [i ];
2129
+ int val_index = sorted_values ?values [i ].index :i ;
2130
+ JsonbPair * pair = & val -> val .object .pairs [val_index ];
2015
2131
int len ;
2016
2132
JEntry meta ;
2017
2133
@@ -2045,6 +2161,9 @@ convertJsonbObject(StringInfo buffer, JEntry *pheader, const JsonbValue *val, in
2045
2161
jentry_offset += sizeof (JEntry );
2046
2162
}
2047
2163
2164
+ if (values )
2165
+ pfree (values );
2166
+
2048
2167
/* Total data size is everything we've appended to buffer */
2049
2168
totallen = buffer -> len - base_offset ;
2050
2169
@@ -2339,16 +2458,35 @@ JsonUniquify(Json *json)
2339
2458
return json ;
2340
2459
}
2341
2460
2461
+ static void
2462
+ jsonbInitContainerFromHeader (JsonContainerData * jc ,JsonbContainer * jbc )
2463
+ {
2464
+ jc -> size = jbc -> header & JB_CMASK ;
2465
+ switch (jbc -> header & JB_TMASK )
2466
+ {
2467
+ case JB_TOBJECT :
2468
+ case JB_TOBJECT_SORTED :
2469
+ jc -> type = jbvObject ;
2470
+ break ;
2471
+ case JB_TARRAY :
2472
+ jc -> type = jbvArray ;
2473
+ break ;
2474
+ case JB_TSCALAR :
2475
+ jc -> type = jbvArray |jbvScalar ;
2476
+ break ;
2477
+ default :
2478
+ elog (ERROR ,"invalid jsonb container type: %d" ,
2479
+ jbc -> header & JB_TMASK );
2480
+ }
2481
+ }
2482
+
2342
2483
static void
2343
2484
jsonbInitContainer (JsonContainerData * jc ,JsonbContainer * jbc ,int len )
2344
2485
{
2345
2486
jc -> ops = & jsonbContainerOps ;
2346
2487
JsonContainerDataPtr (jc )= jbc ;
2347
2488
jc -> len = len ;
2348
- jc -> size = jbc -> header & JB_CMASK ;
2349
- jc -> type = jbc -> header & JB_FOBJECT ?jbvObject :
2350
- jbc -> header & JB_FSCALAR ?jbvArray |jbvScalar :
2351
- jbvArray ;
2489
+ jsonbInitContainerFromHeader (jc ,jbc );
2352
2490
}
2353
2491
2354
2492
static void
@@ -2462,10 +2600,7 @@ jsonbzInitContainer(JsonContainerData *jc, CompressedJsonb *cjb, int len)
2462
2600
2463
2601
jc -> ops = & jsonbzContainerOps ;
2464
2602
jc -> len = len ;
2465
- jc -> size = jbc -> header & JB_CMASK ;
2466
- jc -> type = jbc -> header & JB_FOBJECT ?jbvObject :
2467
- jbc -> header & JB_FSCALAR ?jbvArray |jbvScalar :
2468
- jbvArray ;
2603
+ jsonbInitContainerFromHeader (jc ,jbc );
2469
2604
}
2470
2605
2471
2606
static JsonbContainer *
@@ -2533,7 +2668,9 @@ findValueInCompressedJsonbObject(CompressedJsonb *cjb, const char *keystr, int k
2533
2668
JEntry * children = container -> children ;
2534
2669
int count = container -> header & JB_CMASK ;
2535
2670
/* Since this is an object, account for *Pairs* of Jentrys */
2536
- char * base_addr = (char * ) (children + count * 2 );
2671
+ bool sorted_values = (container -> header & JB_TMASK )== JB_TOBJECT_SORTED ;
2672
+ char * base_addr = (char * ) (children + count * 2 )+ (sorted_values ?sizeof (uint32 )* count :0 );
2673
+ uint32 * kvmap = sorted_values ?& container -> children [count * 2 ] :NULL ;
2537
2674
Size base_offset = base_addr - (char * )jb ;
2538
2675
uint32 stopLow = 0 ,
2539
2676
stopHigh = count ;
@@ -2575,7 +2712,7 @@ findValueInCompressedJsonbObject(CompressedJsonb *cjb, const char *keystr, int k
2575
2712
if (difference == 0 )
2576
2713
{
2577
2714
/* Found our key, return corresponding value */
2578
- int index = stopMiddle + count ;
2715
+ int index = ( sorted_values ? kvmap [ stopMiddle ] : stopMiddle ) + count ;
2579
2716
2580
2717
return fillCompressedJsonbValue (cjb ,container ,index ,base_addr ,
2581
2718
getJsonbOffset (container ,index ),