Mercurial > hg > monetdb-java
diff src/main/java/org/monetdb/jdbc/MonetPreparedStatement.java @ 436:b32946647c77
Extended PreparedStatement.setString() json string validity test for json parameters to now also accept: numbers (with optional fractions and exponent), string literals between double quotes, and keywords: true, false and null.
Also extended and migrated test Bug_PrepStmtSetString_6382 to JDBC_API_Tester with no output on success.
author | Martin van Dinther <martin.van.dinther@monetdbsolutions.com> |
---|---|
date | Wed, 10 Feb 2021 17:51:12 +0100 (2021-02-10) |
parents | 3da79315783b |
children | b9f82064fe0c |
line wrap: on
line diff
--- a/src/main/java/org/monetdb/jdbc/MonetPreparedStatement.java +++ b/src/main/java/org/monetdb/jdbc/MonetPreparedStatement.java @@ -2251,26 +2251,120 @@ public class MonetPreparedStatement castprefix = "inet "; break; case "json": - // There is no support for JSON in standard java class libraries. - // Possibly we could use org.json.simple.JSONObject or other/faster libs - // javax.json.Json is not released yet (see https://json-processing-spec.java.net/) - // see also https://github.com/fabienrenaud/java-json-benchmark - // Note that it would make our JDBC driver dependent of an external jar - // and we don't want that. + { + // do a quick JSON string validity check to prevent failing exec #(..., ...) calls + // which destroy the prepared statement(s), see bug 6351 and 6832 + String conversionIssue = null; + if (x.isEmpty()) { + conversionIssue = "It may not be empty"; + } else { + // scan for first and last non-whitespace character + int start = 0; + int end = x.length() -1; + // find first non white space character + char firstc = x.charAt(start); + while ((firstc == ' ' || firstc == '\t' || firstc == '\n' || firstc == '\r') + && (start < end)) { + start++; + firstc = x.charAt(start); + } + // find last non white space character + char lastc = x.charAt(end); + while ((lastc == ' ' || lastc == '\t' || lastc == '\n' || lastc == '\r') + && (start < end)) { + end--; + lastc = x.charAt(end); + } - // do simplistic check if x represents a valid json string to prevent - // failing exec #(..., ...) calls which destroy the prepared statement, see bug 6351 - if (x.isEmpty() || - (x.startsWith("{") && !x.endsWith("}")) || - (x.startsWith("[") && !x.endsWith("]")) - // TODO check completely if x represents a valid json string - ) - throw new SQLDataException("Invalid json string. It does not start with { or [ and end with } or ]", "22M32"); - - // TODO check completely if x represents a valid json string + if (end - start <= 0) { + conversionIssue = "It may not be empty"; + } else { + switch (firstc) { + case '{': // start of an object + if (lastc != '}') + conversionIssue = "It does not end with }"; + break; + case '[': // start of an array + if (lastc != ']') + conversionIssue = "It does not end with ]"; + break; + case '"': // start of a string + if (lastc != '"') + conversionIssue = "It does not end with \""; + break; + case 'n': // start of literal: null + if (lastc != 'l' || (end - start != 3) || !"null".equals(x.trim())) + conversionIssue = "It does not match: null"; + break; + case 'f': // start of literal: false + if (lastc != 'e' || (end - start != 4) || !"false".equals(x.trim())) + conversionIssue = "It does not match: false"; + break; + case 't': // start of literal: true + if (lastc != 'e' || (end - start != 3) || !"true".equals(x.trim())) + conversionIssue = "It does not match: true"; + break; + case '-': // start of a number with a negative sign + case '+': // start of a number with a positive sign + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + // start of a number in format: [ - | + ] int [ frac ] [ exp ] + // check if it continues with more digits + if (start < end) + start++; + firstc = x.charAt(start); + while (firstc >= '0' && firstc <= '9' && start < end) { + start++; + firstc = x.charAt(start); + } + // check if it continues with optional fractions + if (firstc == '.' && start < end) { + // yes, consume the digits of the fraction + start++; + firstc = x.charAt(start); + while (firstc >= '0' && firstc <= '9' && start < end) { + start++; + firstc = x.charAt(start); + } + } + // check if it continues with optional exponent + if ((firstc == 'E' || firstc == 'e') && start < end) { + // yes, consume the optional sign + start++; + firstc = x.charAt(start); + if ((firstc == '+' || firstc == '-') && start < end) { + start++; + firstc = x.charAt(start); + } + // yes, consume the digits of the exponnent + while ((firstc >= '0' && firstc <= '9') && start < end) { + start++; + firstc = x.charAt(start); + } + } + if (start != end) + conversionIssue = "It does not represent a valid number"; + break; + default: + conversionIssue = "Invalid first character: " + firstc; + break; + } + } + } + if (conversionIssue != null) + throw new SQLDataException("Invalid json string. " + conversionIssue, "22M32"); castprefix = "json "; break; + } case "url": try { // also check if x represents a valid url string to prevent