8080 * <h2>Grammar</h2>
8181 * The grammar of the binary representation of JSON objects are defined in the MySQL codebase in the
8282 * <a href="https://github.com/mysql/mysql-server/blob/5.7/sql/json_binary.h">json_binary.h</a> file:
83- * <p>
84- *
8583 * <pre>
8684 * doc ::= type value
8785 * type ::=
@@ -178,12 +176,7 @@ public static String parseAsString(byte[] bytes) throws IOException {
178176 * @throws IOException if there is a problem reading or processing the binary representation
179177 */
180178 public static void parse (byte [] bytes , JsonFormatter formatter ) throws IOException {
181- if (bytes .length == 0 ) {
182- // When the top-level value is a JSON "null", the value in java shows up as a non-null, empty byte array
183- formatter .valueNull ();
184- } else {
185- new JsonBinary (bytes ).parse (formatter );
186- }
179+ new JsonBinary (bytes ).parse (formatter );
187180 }
188181
189182 private final ByteArrayInputStream reader ;
@@ -194,6 +187,7 @@ public JsonBinary(byte[] bytes) {
194187
195188 public JsonBinary (ByteArrayInputStream contents ) {
196189 this .reader = contents ;
190+ this .reader .mark (Integer .MAX_VALUE );
197191 }
198192
199193 public String getString () {
@@ -267,8 +261,6 @@ protected void parse(ValueType type, JsonFormatter formatter) throws IOException
267261 * <a href="https://github.com/mysql/mysql-server/blob/5.7/sql/json_binary.h">json_binary.h</a> file:
268262 * <h3>Grammar</h3>
269263 *
270- * <h3>Grammar</h3>
271- *
272264 * <pre>
273265 * value ::=
274266 * object |
@@ -323,17 +315,22 @@ protected void parse(ValueType type, JsonFormatter formatter) throws IOException
323315 * @throws IOException if there is a problem reading the JSON value
324316 */
325317 protected void parseObject (boolean small , JsonFormatter formatter )
326- throws IOException {
318+ throws IOException {
319+ // this is terrible, but without a decent seekable InputStream the other way seemed like
320+ // a full-on rewrite
321+ int objectOffset = this .reader .getPosition ();
322+
327323 // Read the header ...
328324 int numElements = readUnsignedIndex (Integer .MAX_VALUE , small , "number of elements in" );
329325 int numBytes = readUnsignedIndex (Integer .MAX_VALUE , small , "size of" );
330326 int valueSize = small ? 2 : 4 ;
331327
332328 // Read each key-entry, consisting of the offset and length of each key ...
333- int [] keyLengths = new int [numElements ];
329+ KeyEntry [] keys = new KeyEntry [numElements ];
334330 for (int i = 0 ; i != numElements ; ++i ) {
335- readUnsignedIndex (numBytes , small , "key offset in" ); // unused
336- keyLengths [i ] = readUInt16 ();
331+ keys [i ] = new KeyEntry (
332+ readUnsignedIndex (numBytes , small , "key offset in" ),
333+ readUInt16 ());
337334 }
338335
339336 // Read each key value value-entry
@@ -369,18 +366,23 @@ protected void parseObject(boolean small, JsonFormatter formatter)
369366 int offset = readUnsignedIndex (Integer .MAX_VALUE , small , "value offset in" );
370367 if (offset >= numBytes ) {
371368 throw new IOException ("The offset for the value in the JSON binary document is " +
372- offset +
373- ", which is larger than the binary form of the JSON document (" +
374- numBytes + " bytes)" );
369+ offset +
370+ ", which is larger than the binary form of the JSON document (" +
371+ numBytes + " bytes)" );
375372 }
376373 entries [i ] = new ValueEntry (type , offset );
377374 }
378375 }
379376
380377 // Read each key ...
381- String [] keys = new String [numElements ];
382378 for (int i = 0 ; i != numElements ; ++i ) {
383- keys [i ] = reader .readString (keyLengths [i ]);
379+ final int skipBytes = keys [i ].index + objectOffset - reader .getPosition ();
380+ // Skip to a start of a field name if the current position does not point to it
381+ // This can happen for MySQL 8
382+ if (skipBytes != 0 ) {
383+ reader .fastSkip (skipBytes );
384+ }
385+ keys [i ].name = reader .readString (keys [i ].length );
384386 }
385387
386388 // Now parse the values ...
@@ -389,7 +391,7 @@ protected void parseObject(boolean small, JsonFormatter formatter)
389391 if (i != 0 ) {
390392 formatter .nextEntry ();
391393 }
392- formatter .name (keys [i ]);
394+ formatter .name (keys [i ]. name );
393395 ValueEntry entry = entries [i ];
394396 if (entry .resolved ) {
395397 Object value = entry .value ;
@@ -402,6 +404,8 @@ protected void parseObject(boolean small, JsonFormatter formatter)
402404 }
403405 } else {
404406 // Parse the value ...
407+ this .reader .reset ();
408+ this .reader .fastSkip (objectOffset + entry .index );
405409 parse (entry .type , formatter );
406410 }
407411 }
@@ -467,7 +471,9 @@ protected void parseObject(boolean small, JsonFormatter formatter)
467471 */
468472 // checkstyle, please ignore MethodLength for the next line
469473 protected void parseArray (boolean small , JsonFormatter formatter )
470- throws IOException {
474+ throws IOException {
475+ int arrayOffset = this .reader .getPosition ();
476+
471477 // Read the header ...
472478 int numElements = readUnsignedIndex (Integer .MAX_VALUE , small , "number of elements in" );
473479 int numBytes = readUnsignedIndex (Integer .MAX_VALUE , small , "size of" );
@@ -506,9 +512,9 @@ protected void parseArray(boolean small, JsonFormatter formatter)
506512 int offset = readUnsignedIndex (Integer .MAX_VALUE , small , "value offset in" );
507513 if (offset >= numBytes ) {
508514 throw new IOException ("The offset for the value in the JSON binary document is " +
509- offset +
510- ", which is larger than the binary form of the JSON document (" +
511- numBytes + " bytes)" );
515+ offset +
516+ ", which is larger than the binary form of the JSON document (" +
517+ numBytes + " bytes)" );
512518 }
513519 entries [i ] = new ValueEntry (type , offset );
514520 }
@@ -532,6 +538,9 @@ protected void parseArray(boolean small, JsonFormatter formatter)
532538 }
533539 } else {
534540 // Parse the value ...
541+ this .reader .reset ();
542+ this .reader .fastSkip (arrayOffset + entry .index );
543+
535544 parse (entry .type , formatter );
536545 }
537546 }
@@ -655,7 +664,6 @@ protected void parseString(JsonFormatter formatter) throws IOException {
655664 * See the <a href=
656665 * "https://github.com/mysql/mysql-server/blob/e0e0ae2ea27c9bb76577664845507ef224d362e4/sql/json_binary.cc#L1034">
657666 * MySQL source code</a> for the logic used in this method.
658- * <p>
659667 * <h3>Grammar</h3>
660668 *
661669 * <pre>
@@ -680,7 +688,7 @@ protected void parseOpaque(JsonFormatter formatter) throws IOException {
680688 ColumnType type = ColumnType .byCode (customType );
681689 if (type == null ) {
682690 throw new IOException ("Unknown type '" + asHex (customType ) +
683- "' in first byte of a JSON opaque value" );
691+ "' in first byte of a JSON opaque value" );
684692 }
685693 // Read the data length ...
686694 int length = readVariableInt ();
@@ -857,7 +865,7 @@ protected void parseDecimal(int length, JsonFormatter formatter) throws IOExcept
857865 }
858866
859867 protected void parseOpaqueValue (ColumnType type , int length , JsonFormatter formatter )
860- throws IOException {
868+ throws IOException {
861869 formatter .valueOpaque (type , reader .read (length ));
862870 }
863871
@@ -879,7 +887,7 @@ protected int readUnsignedIndex(int maxValue, boolean isSmall, String desc) thro
879887 long result = isSmall ? readUInt16 () : readUInt32 ();
880888 if (result > maxValue ) {
881889 throw new IOException ("The " + desc + " the JSON document is " + result +
882- " and is too big for the binary form of the document (" + maxValue + ")" );
890+ " and is too big for the binary form of the document (" + maxValue + ")" );
883891 }
884892 if (result > Integer .MAX_VALUE ) {
885893 throw new IOException ("The " + desc + " the JSON document is " + result + " and is too big to be used" );
@@ -932,7 +940,7 @@ protected long readInt64() throws IOException {
932940 long b7 = reader .read () & 0xFF ;
933941 long b8 = reader .read ();
934942 return b8 << 56 | (b7 << 48 ) | (b6 << 40 ) | (b5 << 32 ) |
935- (b4 << 24 ) | (b3 << 16 ) | (b2 << 8 ) | b1 ;
943+ (b4 << 24 ) | (b3 << 16 ) | (b2 << 8 ) | b1 ;
936944 }
937945
938946 protected BigInteger readUInt64 () throws IOException {
@@ -951,6 +959,7 @@ protected BigInteger readUInt64() throws IOException {
951959 * to 16383, and so on...
952960 *
953961 * @return the integer value
962+ * @throws IOException if we don't encounter an end-of-int marker
954963 */
955964 protected int readVariableInt () throws IOException {
956965 int length = 0 ;
@@ -993,6 +1002,26 @@ protected static String asHex(int value) {
9931002 return Integer .toHexString (value );
9941003 }
9951004
1005+ /**
1006+ * Class used internally to hold key entry information.
1007+ */
1008+ protected static final class KeyEntry {
1009+
1010+ protected final int index ;
1011+ protected final int length ;
1012+ protected String name ;
1013+
1014+ public KeyEntry (int index , int length ) {
1015+ this .index = index ;
1016+ this .length = length ;
1017+ }
1018+
1019+ public KeyEntry setKey (String key ) {
1020+ this .name = key ;
1021+ return this ;
1022+ }
1023+ }
1024+
9961025 /**
9971026 * Class used internally to hold value entry information.
9981027 */
0 commit comments