comparison src/main/java/org/monetdb/jdbc/MonetDatabaseMetaData.java @ 427:e79bfbd0553e

Added support for escaped wildcards (\% en \_) in String arguments of DatabaseMetaData methods which return a ResultSet, such as getTables(), getColumns(), etc. When you do not want the characters % or _ to be interpreted as wildcards but as normal characters you can prefix them with a backslash (so \% and \_). Note: be sure all wildcards characters in the String argument are escaped else the search must still use a LIKE operator instead of an = comparison operator. This fixes: https://github.com/MonetDB/monetdb-java/issues/3
author Martin van Dinther <martin.van.dinther@monetdbsolutions.com>
date Wed, 03 Feb 2021 19:41:44 +0100 (2021-02-03)
parents 95d15f1d750d
children 3924a8516d7b
comparison
equal deleted inserted replaced
426:3da79315783b 427:e79bfbd0553e
4087 } 4087 }
4088 return rs; 4088 return rs;
4089 } 4089 }
4090 4090
4091 /** 4091 /**
4092 * Returns a SQL match part string where depending on the input value we 4092 * Returns a SQL match part string where depending on the input value we compose
4093 * compose an exact match (use =) or match with wildcards (use LIKE) or IS NULL 4093 * - an exact string match (using = comparator) or
4094 * - a match with wildcards (using LIKE comparator) or
4095 * - when input is missing (using IS NULL comparator)
4096 *
4097 * Note that wildcard characters can be escaped by prefix \ (a backslash)
4098 * When ALL wildcard characters are escaped then we remove the \'s and compose an = comparator string match.
4094 * 4099 *
4095 * @param in the string to match 4100 * @param in the string to match
4096 * @return the SQL match part string 4101 * @return the SQL match part string
4097 */ 4102 */
4098 private final String composeMatchPart(final String in) { 4103 private final String composeMatchPart(final String in) {
4099 if (in == null) 4104 if (in == null)
4100 return "IS NULL"; 4105 return "IS NULL";
4101 4106
4102 String cmp = "= "; 4107 // Scan input string for SQL wildcard characters: % and _
4103 // check if SQL wildcards are used in the input, if so use LIKE 4108 // When they are all prefixed by a backslash then the backslash is removed (to allow usage of = comparator)
4104 if (in.contains("%") || in.contains("_")) 4109 // else it needs to be interpreted as a wildcard and we need to use LIKE instead of = comparator.
4105 cmp = "LIKE "; 4110 // A backslash can be escaped by using two backslashes.
4106 4111 final int len = in.length();
4107 return cmp + MonetWrapper.sq(in); 4112 final StringBuilder sb = new StringBuilder(len);
4113 boolean removed_bs = false;
4114 boolean use_like = false;
4115 boolean escaped = false;
4116 try {
4117 // parse all characters in input to find if the wildcards are escaped by a \.
4118 // note: the escape character \ can also be escaped, so \\.
4119 for (int i = 0; i < len; i++) {
4120 char c = in.charAt(i);
4121 switch(c) {
4122 case '\\':
4123 if (escaped) {
4124 // copy the 2 backslash characters as in the original string
4125 sb.append(c).append(c);
4126 escaped = false;
4127 } else {
4128 escaped = true;
4129 }
4130 break;
4131 case '%':
4132 case '_':
4133 if (escaped) {
4134 removed_bs = true;
4135 escaped = false;
4136 } else {
4137 use_like = true;
4138 i = len; /* no need to scan further */
4139 }
4140 sb.append(c);
4141 break;
4142 default:
4143 if (escaped) {
4144 sb.append('\\'); // also copy the preceding escape found before this character
4145 escaped = false;
4146 }
4147 sb.append(c);
4148 break;
4149 }
4150 }
4151 } catch (IndexOutOfBoundsException iob) {
4152 /* ignore */
4153 }
4154
4155 if (use_like) {
4156 // for debug: System.out.println("input: " + in + " changed into: " + "LIKE " + MonetWrapper.sq(in));
4157 // we found a non-escaped wildcard character, use like and the original input
4158 return "LIKE " + MonetWrapper.sq(in);
4159 }
4160 if (removed_bs) {
4161 // for debug: System.out.println("input: " + in + " changed into: " + "= " + MonetWrapper.sq(sb.toString()));
4162 // we found only escaped wildcard character(s),
4163 // use the edited string without the ecapes before the wildcard character(s) so an equals match can be done (its is faster than LIKE)
4164 return "= " + MonetWrapper.sq(sb.toString());
4165 }
4166 // for debug: System.out.println("input: " + in + " changed into: " + "= " + MonetWrapper.sq(in));
4167 return "= " + MonetWrapper.sq(in);
4108 } 4168 }
4109 4169
4110 //== end helper methods 4170 //== end helper methods
4111 } 4171 }