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 ;
@@ -562,6 +565,8 @@ getKeyJsonValueFromContainer(JsonContainer *jsc,
562
565
const JEntry * children = container -> children ;
563
566
int count = JsonContainerSize (jsc );
564
567
char * baseAddr ;
568
+ bool sorted_values = (container -> header & JB_TMASK ) == JB_TOBJECT_SORTED ;
569
+ const uint32 * kvmap ;
565
570
uint32 stopLow ,
566
571
stopHigh ;
567
572
@@ -575,7 +580,16 @@ getKeyJsonValueFromContainer(JsonContainer *jsc,
575
580
* Binary search the container. Since we know this is an object, account
576
581
* for *Pairs* of Jentrys
577
582
*/
578
- baseAddr = (char * ) (children + count * 2 );
583
+ if (sorted_values )
584
+ {
585
+ kvmap = & children [count * 2 ];
586
+ baseAddr = (char * ) & kvmap [count ];
587
+ }
588
+ else
589
+ {
590
+ kvmap = NULL ;
591
+ baseAddr = (char * ) (children + count * 2 );
592
+ }
579
593
stopLow = 0 ;
580
594
stopHigh = count ;
581
595
while (stopLow < stopHigh )
@@ -596,7 +610,7 @@ getKeyJsonValueFromContainer(JsonContainer *jsc,
596
610
if (difference == 0 )
597
611
{
598
612
/* Found our key, return corresponding value */
599
- int index = stopMiddle + count ;
613
+ int index = ( sorted_values ? kvmap [ stopMiddle ] : stopMiddle ) + count ;
600
614
601
615
if (!res )
602
616
res = palloc (sizeof (JsonbValue ));
@@ -1134,14 +1148,18 @@ JsonbIteratorNext(JsonIterator **jsit, JsonbValue *val, bool skipNested)
1134
1148
(* it )-> state = JBI_OBJECT_KEY ;
1135
1149
1136
1150
fillCompressedJsonbValue ((* it )-> compressed , (* it )-> container ,
1137
- (* it )-> curIndex + (* it )-> nElems ,
1138
- (* it )-> dataProper , (* it )-> curValueOffset ,
1151
+ ((* it )-> kvMap ? (* it )-> kvMap [(* it )-> curIndex ] : (* it )-> curIndex ) + (* it )-> nElems ,
1152
+ (* it )-> dataProper ,
1153
+ (* it )-> kvMap ?
1154
+ getJsonbOffset ((* it )-> container , (* it )-> kvMap [(* it )-> curIndex ] + (* it )-> nElems ) :
1155
+ (* it )-> curValueOffset ,
1139
1156
val );
1140
1157
1141
1158
JBE_ADVANCE_OFFSET ((* it )-> curDataOffset ,
1142
1159
(* it )-> children [(* it )-> curIndex ]);
1143
- JBE_ADVANCE_OFFSET ((* it )-> curValueOffset ,
1144
- (* it )-> children [(* it )-> curIndex + (* it )-> nElems ]);
1160
+ if (!(* it )-> kvMap )
1161
+ JBE_ADVANCE_OFFSET ((* it )-> curValueOffset ,
1162
+ (* it )-> children [(* it )-> curIndex + (* it )-> nElems ]);
1145
1163
(* it )-> curIndex ++ ;
1146
1164
1147
1165
/*
@@ -1193,24 +1211,34 @@ jsonbIteratorInit(JsonContainer *cont, const JsonbContainer *container,
1193
1211
/* Array starts just after header */
1194
1212
it -> children = container -> children ;
1195
1213
1196
- switch (container -> header & ( JB_FARRAY | JB_FOBJECT ) )
1214
+ switch (container -> header & JB_TMASK )
1197
1215
{
1198
- case JB_FARRAY :
1216
+ case JB_TSCALAR :
1217
+ it -> isScalar = true;
1218
+ /* FALLTHROUGH */
1219
+ case JB_TARRAY :
1199
1220
it -> dataProper =
1200
1221
(char * ) it -> children + it -> nElems * sizeof (JEntry );
1201
- it -> isScalar = (container -> header & JB_FSCALAR ) != 0 ;
1202
1222
/* This is either a "raw scalar", or an array */
1203
1223
Assert (!it -> isScalar || it -> nElems == 1 );
1204
1224
1205
1225
it -> state = JBI_ARRAY_START ;
1206
1226
break ;
1207
1227
1208
- case JB_FOBJECT :
1228
+ case JB_TOBJECT :
1229
+ it -> kvMap = NULL ;
1209
1230
it -> dataProper =
1210
1231
(char * ) it -> children + it -> nElems * sizeof (JEntry ) * 2 ;
1211
1232
it -> state = JBI_OBJECT_START ;
1212
1233
break ;
1213
1234
1235
+ case JB_TOBJECT_SORTED :
1236
+ it -> kvMap = (uint32 * )
1237
+ ((char * ) it -> children + it -> nElems * sizeof (JEntry ) * 2 );
1238
+ it -> dataProper = (char * ) & it -> kvMap [it -> nElems ];
1239
+ it -> state = JBI_OBJECT_START ;
1240
+ break ;
1241
+
1214
1242
default :
1215
1243
elog (ERROR , "unknown type of jsonb container" );
1216
1244
}
@@ -1814,13 +1842,14 @@ convertJsonbArray(StringInfo buffer, JEntry *pheader, const JsonbValue *val, int
1814
1842
* Construct the header Jentry and store it in the beginning of the
1815
1843
* variable-length payload.
1816
1844
*/
1817
- header = nElems | JB_FARRAY ;
1818
1845
if (val -> val .array .rawScalar )
1819
1846
{
1820
1847
Assert (nElems == 1 );
1821
1848
Assert (level == 0 );
1822
- header |= JB_FSCALAR ;
1849
+ header = nElems | JB_TSCALAR ;
1823
1850
}
1851
+ else
1852
+ header = nElems | JB_TARRAY ;
1824
1853
1825
1854
appendToBuffer (buffer , (char * ) & header , sizeof (uint32 ));
1826
1855
@@ -1878,6 +1907,48 @@ convertJsonbArray(StringInfo buffer, JEntry *pheader, const JsonbValue *val, int
1878
1907
* pheader = JENTRY_ISCONTAINER | totallen ;
1879
1908
}
1880
1909
1910
+ static int
1911
+ int_cmp (const void * a , const void * b )
1912
+ {
1913
+ int x = * (const int * ) a ;
1914
+ int y = * (const int * ) b ;
1915
+
1916
+ return x == y ? 0 : x > y ? 1 : -1 ;
1917
+ }
1918
+
1919
+ static int
1920
+ estimateJsonbValueSize (const JsonbValue * jbv )
1921
+ {
1922
+ int size ;
1923
+
1924
+ switch (jbv -> type )
1925
+ {
1926
+ case jbvNull :
1927
+ case jbvBool :
1928
+ return 0 ;
1929
+ case jbvString :
1930
+ return jbv -> val .string .len ;
1931
+ case jbvNumeric :
1932
+ return VARSIZE_ANY (jbv -> val .numeric );
1933
+ case jbvArray :
1934
+ size = offsetof(JsonbContainer , children [jbv -> val .array .nElems ]);
1935
+ for (int i = 0 ; i < jbv -> val .array .nElems ; i ++ )
1936
+ size += estimateJsonbValueSize (& jbv -> val .array .elems [i ]);
1937
+ return size ;
1938
+ case jbvObject :
1939
+ size = offsetof(JsonbContainer , children [jbv -> val .object .nPairs * 2 ]);
1940
+ for (int i = 0 ; i < jbv -> val .object .nPairs ; i ++ )
1941
+ {
1942
+ size += estimateJsonbValueSize (& jbv -> val .object .pairs [i ].key );
1943
+ size += estimateJsonbValueSize (& jbv -> val .object .pairs [i ].value );
1944
+ }
1945
+ return size ;
1946
+ default :
1947
+ elog (ERROR , "invalid jsonb value type: %d" , jbv -> type );
1948
+ return 0 ;
1949
+ }
1950
+ }
1951
+
1881
1952
static void
1882
1953
convertJsonbObject (StringInfo buffer , JEntry * pheader , const JsonbValue * val , int level )
1883
1954
{
@@ -1887,9 +1958,39 @@ convertJsonbObject(StringInfo buffer, JEntry *pheader, const JsonbValue *val, in
1887
1958
int totallen ;
1888
1959
uint32 header ;
1889
1960
int nPairs = val -> val .object .nPairs ;
1961
+ int reserved_size ;
1962
+ bool sorted_values = JSONB_SORTED_VALUES && nPairs > 1 ;
1963
+ struct
1964
+ {
1965
+ int size ;
1966
+ int32 index ;
1967
+ } * values = sorted_values ? palloc (sizeof (* values ) * nPairs ) : NULL ;
1890
1968
1891
1969
Assert (nPairs >= 0 );
1892
1970
1971
+ if (sorted_values )
1972
+ {
1973
+ for (i = 0 ; i < nPairs ; i ++ )
1974
+ {
1975
+ values [i ].index = i ;
1976
+ values [i ].size = estimateJsonbValueSize (& val -> val .object .pairs [i ].value );
1977
+ }
1978
+
1979
+ qsort (values , nPairs , sizeof (* values ), int_cmp );
1980
+
1981
+ /* check if keys were really moved */
1982
+ sorted_values = false;
1983
+
1984
+ for (i = 0 ; i < nPairs ; i ++ )
1985
+ {
1986
+ if (values [i ].index != i )
1987
+ {
1988
+ sorted_values = true;
1989
+ break ;
1990
+ }
1991
+ }
1992
+ }
1993
+
1893
1994
/* Remember where in the buffer this object starts. */
1894
1995
base_offset = buffer -> len ;
1895
1996
@@ -1900,17 +2001,30 @@ convertJsonbObject(StringInfo buffer, JEntry *pheader, const JsonbValue *val, in
1900
2001
* Construct the header Jentry and store it in the beginning of the
1901
2002
* variable-length payload.
1902
2003
*/
1903
- header = nPairs | JB_FOBJECT ;
2004
+ header = nPairs | ( sorted_values ? JB_TOBJECT_SORTED : JB_TOBJECT ) ;
1904
2005
appendToBuffer (buffer , (char * ) & header , sizeof (uint32 ));
1905
2006
1906
2007
/* Reserve space for the JEntries of the keys and values. */
1907
- jentry_offset = reserveFromBuffer (buffer , sizeof (JEntry ) * nPairs * 2 );
2008
+ reserved_size = sizeof (JEntry ) * nPairs * 2 ;
2009
+ if (sorted_values )
2010
+ reserved_size += sizeof (int32 ) * nPairs ;
2011
+
2012
+ jentry_offset = reserveFromBuffer (buffer , reserved_size );
2013
+
2014
+ /* Write key-value map */
2015
+ if (sorted_values )
2016
+ {
2017
+ for (i = 0 ; i < nPairs ; i ++ )
2018
+ copyToBuffer (buffer , jentry_offset + sizeof (JEntry ) * nPairs * 2 + values [i ].index * sizeof (int32 ),
2019
+ & i , sizeof (int32 ));
2020
+ }
1908
2021
1909
2022
/*
1910
2023
* Iterate over the keys, then over the values, since that is the ordering
1911
2024
* we want in the on-disk representation.
1912
2025
*/
1913
2026
totallen = 0 ;
2027
+
1914
2028
for (i = 0 ; i < nPairs ; i ++ )
1915
2029
{
1916
2030
JsonbPair * pair = & val -> val .object .pairs [i ];
@@ -1946,9 +2060,11 @@ convertJsonbObject(StringInfo buffer, JEntry *pheader, const JsonbValue *val, in
1946
2060
copyToBuffer (buffer , jentry_offset , (char * ) & meta , sizeof (JEntry ));
1947
2061
jentry_offset += sizeof (JEntry );
1948
2062
}
2063
+
1949
2064
for (i = 0 ; i < nPairs ; i ++ )
1950
2065
{
1951
- JsonbPair * pair = & val -> val .object .pairs [i ];
2066
+ int val_index = sorted_values ? values [i ].index : i ;
2067
+ JsonbPair * pair = & val -> val .object .pairs [val_index ];
1952
2068
int len ;
1953
2069
JEntry meta ;
1954
2070
@@ -1982,6 +2098,9 @@ convertJsonbObject(StringInfo buffer, JEntry *pheader, const JsonbValue *val, in
1982
2098
jentry_offset += sizeof (JEntry );
1983
2099
}
1984
2100
2101
+ if (values )
2102
+ pfree (values );
2103
+
1985
2104
/* Total data size is everything we've appended to buffer */
1986
2105
totallen = buffer -> len - base_offset ;
1987
2106
@@ -2276,16 +2395,35 @@ JsonUniquify(Json *json)
2276
2395
return json ;
2277
2396
}
2278
2397
2398
+ static void
2399
+ jsonbInitContainerFromHeader (JsonContainerData * jc , JsonbContainer * jbc )
2400
+ {
2401
+ jc -> size = jbc -> header & JB_CMASK ;
2402
+ switch (jbc -> header & JB_TMASK )
2403
+ {
2404
+ case JB_TOBJECT :
2405
+ case JB_TOBJECT_SORTED :
2406
+ jc -> type = jbvObject ;
2407
+ break ;
2408
+ case JB_TARRAY :
2409
+ jc -> type = jbvArray ;
2410
+ break ;
2411
+ case JB_TSCALAR :
2412
+ jc -> type = jbvArray | jbvScalar ;
2413
+ break ;
2414
+ default :
2415
+ elog (ERROR , "invalid jsonb container type: %d" ,
2416
+ jbc -> header & JB_TMASK );
2417
+ }
2418
+ }
2419
+
2279
2420
static void
2280
2421
jsonbInitContainer (JsonContainerData * jc , JsonbContainer * jbc , int len )
2281
2422
{
2282
2423
jc -> ops = & jsonbContainerOps ;
2283
2424
JsonContainerDataPtr (jc ) = jbc ;
2284
2425
jc -> len = len ;
2285
- jc -> size = jbc -> header & JB_CMASK ;
2286
- jc -> type = jbc -> header & JB_FOBJECT ? jbvObject :
2287
- jbc -> header & JB_FSCALAR ? jbvArray | jbvScalar :
2288
- jbvArray ;
2426
+ jsonbInitContainerFromHeader (jc , jbc );
2289
2427
}
2290
2428
2291
2429
static void
@@ -2399,10 +2537,7 @@ jsonbzInitContainer(JsonContainerData *jc, CompressedJsonb *cjb, int len)
2399
2537
2400
2538
jc -> ops = & jsonbzContainerOps ;
2401
2539
jc -> len = len ;
2402
- jc -> size = jbc -> header & JB_CMASK ;
2403
- jc -> type = jbc -> header & JB_FOBJECT ? jbvObject :
2404
- jbc -> header & JB_FSCALAR ? jbvArray | jbvScalar :
2405
- jbvArray ;
2540
+ jsonbInitContainerFromHeader (jc , jbc );
2406
2541
}
2407
2542
2408
2543
static JsonbContainer *
@@ -2470,7 +2605,9 @@ findValueInCompressedJsonbObject(CompressedJsonb *cjb, const char *keystr, int k
2470
2605
JEntry * children = container -> children ;
2471
2606
int count = container -> header & JB_CMASK ;
2472
2607
/* Since this is an object, account for *Pairs* of Jentrys */
2473
- char * base_addr = (char * ) (children + count * 2 );
2608
+ bool sorted_values = (container -> header & JB_TMASK ) == JB_TOBJECT_SORTED ;
2609
+ char * base_addr = (char * ) (children + count * 2 ) + (sorted_values ? sizeof (uint32 ) * count : 0 );
2610
+ uint32 * kvmap = sorted_values ? & container -> children [count * 2 ] : NULL ;
2474
2611
Size base_offset = base_addr - (char * ) jb ;
2475
2612
uint32 stopLow = 0 ,
2476
2613
stopHigh = count ;
@@ -2512,7 +2649,7 @@ findValueInCompressedJsonbObject(CompressedJsonb *cjb, const char *keystr, int k
2512
2649
if (difference == 0 )
2513
2650
{
2514
2651
/* Found our key, return corresponding value */
2515
- int index = stopMiddle + count ;
2652
+ int index = ( sorted_values ? kvmap [ stopMiddle ] : stopMiddle ) + count ;
2516
2653
2517
2654
return fillCompressedJsonbValue (cjb , container , index , base_addr ,
2518
2655
getJsonbOffset (container , index ),
0 commit comments