comparison src/main/java/nl/cwi/monetdb/jdbc/MonetPreparedStatement.java @ 172:60063c67f9e7 embedded

Merged with default
author Pedro Ferreira <pedro.ferreira@monetdbsolutions.com>
date Tue, 19 Sep 2017 13:49:34 +0200 (2017-09-19)
parents 890dce2d5305 5c575fb21be0
children 89c285fc0a49
comparison
equal deleted inserted replaced
171:0f95fee3cf29 172:60063c67f9e7
44 * [ "int", 9, 0 ] 44 * [ "int", 9, 0 ]
45 * [ "int", 9, 0 ] 45 * [ "int", 9, 0 ]
46 * </pre> 46 * </pre>
47 * 47 *
48 * @author Fabian Groffen, Martin van Dinther, Pedro Ferreira 48 * @author Fabian Groffen, Martin van Dinther, Pedro Ferreira
49 * @version 0.4 49 * @version 0.5v
50 */ 50 */
51 public class MonetPreparedStatement extends MonetStatement implements PreparedStatement { 51 public class MonetPreparedStatement extends MonetStatement implements PreparedStatement, AutoCloseable {
52 52
53 private final MonetConnection connection; 53 private final MonetConnection connection;
54 private final String[] monetdbType; 54 private final String[] monetdbType;
55 private final int[] javaType; 55 private final int[] javaType;
56 private final int[] digits; 56 private final int[] digits;
104 104
105 this.connection = connection; 105 this.connection = connection;
106 106
107 // fill the arrays 107 // fill the arrays
108 ResultSet rs = super.getResultSet(); 108 ResultSet rs = super.getResultSet();
109 for (int i = 0; rs.next(); i++) { 109 if (rs != null) {
110 monetdbType[i] = rs.getString("type"); 110 // System.out.println("After super.getResultSet();");
111 javaType[i] = MonetDriver.getJavaType(monetdbType[i]); 111 int type_colnr = rs.findColumn("type");
112 digits[i] = rs.getInt("digits"); 112 int digits_colnr = rs.findColumn("digits");
113 scale[i] = rs.getInt("scale"); 113 int scale_colnr = rs.findColumn("scale");
114 if (rscolcnt == 3) 114 int schema_colnr = rs.findColumn("schema");
115 continue; 115 int table_colnr = rs.findColumn("table");
116 schema[i] = rs.getString("schema"); 116 int column_colnr = rs.findColumn("column");
117 table[i] = rs.getString("table"); 117 for (int i = 0; rs.next(); i++) {
118 column[i] = rs.getString("column"); 118 monetdbType[i] = rs.getString(type_colnr);
119 } 119 javaType[i] = MonetDriver.getJavaType(monetdbType[i]);
120 rs.close(); 120 digits[i] = rs.getInt(digits_colnr);
121 scale[i] = rs.getInt(scale_colnr);
122 if (rscolcnt == 3)
123 continue;
124 schema[i] = rs.getString(schema_colnr);
125 table[i] = rs.getString(table_colnr);
126 column[i] = rs.getString(column_colnr);
127 /* when column[i] != null it is a result column of the prepared query, see getColumnIdx(int),
128 when column[i] == null it is a parameter for the prepared statement, see getParamIdx(int). */
129 // System.out.println("column " + i + " has value: " + column[i]);
130 }
131 rs.close();
132 }
121 133
122 // PreparedStatements are by default poolable 134 // PreparedStatements are by default poolable
123 poolable = true; 135 poolable = true;
124 136
125 mTimestampZ = connection.getProtocol().getMonetTimestampTz(); 137 mTimestampZ = connection.getProtocol().getMonetTimestampTz();
236 * when not found 248 * when not found
237 */ 249 */
238 private int getColumnIdx(int colnr) throws SQLException { 250 private int getColumnIdx(int colnr) throws SQLException {
239 int curcol = 0; 251 int curcol = 0;
240 for (int i = 0; i < size; i++) { 252 for (int i = 0; i < size; i++) {
253 /* when column[i] == null it is a parameter, when column[i] != null it is a result column of the prepared query */
241 if (column[i] == null) 254 if (column[i] == null)
242 continue; 255 continue;
243 curcol++; 256 curcol++;
244 if (curcol == colnr) 257 if (curcol == colnr)
245 return i; 258 return i;
252 * parameter number or an SQLException when not found 265 * parameter number or an SQLException when not found
253 */ 266 */
254 private int getParamIdx(int paramnr) throws SQLException { 267 private int getParamIdx(int paramnr) throws SQLException {
255 int curparam = 0; 268 int curparam = 0;
256 for (int i = 0; i < size; i++) { 269 for (int i = 0; i < size; i++) {
270 /* when column[i] == null it is a parameter, when column[i] != null it is a result column of the prepared query */
257 if (column[i] != null) 271 if (column[i] != null)
258 continue; 272 continue;
259 curparam++; 273 curparam++;
260 if (curparam == paramnr) 274 if (curparam == paramnr)
261 return i; 275 return i;
798 812
799 /** 813 /**
800 * Sets the designated parameter to the given Array object. The 814 * Sets the designated parameter to the given Array object. The
801 * driver converts this to an SQL ARRAY value when it sends it to 815 * driver converts this to an SQL ARRAY value when it sends it to
802 * the database. 816 * the database.
803 * 817 *
804 * @param i the first parameter is 1, the second is 2, ... 818 * @param parameterIndex the first parameter is 1, the second is 2, ...
805 * @param x an Array object that maps an SQL ARRAY value 819 * @param x an Array object that maps an SQL ARRAY value
806 * @throws SQLException if a database access error occurs 820 * @throws SQLException if a database access error occurs
807 */ 821 */
808 @Override 822 @Override
809 public void setArray(int i, Array x) throws SQLException { 823 public void setArray(int parameterIndex, Array x) throws SQLException {
810 throw newSQLFeatureNotSupportedException("setArray"); 824 throw newSQLFeatureNotSupportedException("setArray");
811 } 825 }
812 826
813 /** 827 /**
814 * Sets the designated parameter to the given input stream, which will have 828 * Sets the designated parameter to the given input stream, which will have
880 /** 894 /**
881 * Sets the designated parameter to the given java.math.BigDecimal value. 895 * Sets the designated parameter to the given java.math.BigDecimal value.
882 * The driver converts this to an SQL NUMERIC value when it sends it to the 896 * The driver converts this to an SQL NUMERIC value when it sends it to the
883 * database. 897 * database.
884 * 898 *
885 * @param idx the first parameter is 1, the second is 2, ... 899 * @param parameterIndex the first parameter is 1, the second is 2, ...
886 * @param x the parameter value 900 * @param x the parameter value
887 * @throws SQLException if a database access error occurs 901 * @throws SQLException if a database access error occurs
888 */ 902 */
889 @Override 903 @Override
890 public void setBigDecimal(int idx, BigDecimal x) throws SQLException { 904 public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException {
891 // get array position 905 // get array position
892 int i = getParamIdx(idx); 906 int i = getParamIdx(parameterIndex);
893 907
894 // round to the scale of the DB: 908 // round to the scale of the DB:
895 x = x.setScale(scale[i], RoundingMode.HALF_UP); 909 x = x.setScale(scale[i], RoundingMode.HALF_UP);
896 910
897 // if precision is now greater than that of the db, throw an error: 911 // if precision is now greater than that of the db, throw an error:
908 int dot = xStr.indexOf('.'); 922 int dot = xStr.indexOf('.');
909 if (dot >= 0) 923 if (dot >= 0)
910 xStr = xStr.substring(0, Math.min(xStr.length(), dot + 1 + scale[i])); 924 xStr = xStr.substring(0, Math.min(xStr.length(), dot + 1 + scale[i]));
911 while (xStr.startsWith("0") && xStr.length() > 1) 925 while (xStr.startsWith("0") && xStr.length() > 1)
912 xStr = xStr.substring(1); 926 xStr = xStr.substring(1);
913 setValue(idx, xStr); 927 setValue(parameterIndex, xStr);
914 } 928 }
915 929
916 /** 930 /**
917 * Sets the designated parameter to the given input stream, which will have 931 * Sets the designated parameter to the given input stream, which will have
918 * the specified number of bytes. When a very large binary value is input 932 * the specified number of bytes. When a very large binary value is input
981 /** 995 /**
982 * Sets the designated parameter to the given Blob object. The driver 996 * Sets the designated parameter to the given Blob object. The driver
983 * converts this to an SQL BLOB value when it sends it to the database. 997 * converts this to an SQL BLOB value when it sends it to the database.
984 * 998 *
985 * @param parameterIndex the first parameter is 1, the second is 2, ... 999 * @param parameterIndex the first parameter is 1, the second is 2, ...
986 * @param stream an object that contains the data to set the parameter value to 1000 * @param x a Blob object that maps an SQL BLOB value
987 * @throws SQLException if a database access error occurs 1001 * @throws SQLException if a database access error occurs
988 */ 1002 */
989 @Override 1003 @Override
990 public void setBlob(int parameterIndex, InputStream stream) throws SQLException { 1004 public void setBlob(int parameterIndex, InputStream x) throws SQLException {
991 if (stream == null) { 1005 if (x == null) {
992 setNull(parameterIndex, -1); 1006 setNull(parameterIndex, -1);
993 return; 1007 return;
994 } 1008 }
995 // Some buffer. Size of 8192 is default for BufferedReader, so... 1009 // Some buffer. Size of 8192 is default for BufferedReader, so...
996 byte[] arr = new byte[8192]; 1010 byte[] arr = new byte[8192];
997 ByteArrayOutputStream buf = new ByteArrayOutputStream(); 1011 ByteArrayOutputStream buf = new ByteArrayOutputStream();
998 int numChars; 1012 int numChars;
999 try { 1013 try {
1000 while ((numChars = stream.read(arr, 0, arr.length)) > 0) { 1014 while ((numChars = x.read(arr, 0, arr.length)) > 0) {
1001 buf.write(arr, 0, numChars); 1015 buf.write(arr, 0, numChars);
1002 } 1016 }
1003 setBytes(parameterIndex, buf.toByteArray()); 1017 setBytes(parameterIndex, buf.toByteArray());
1004 } catch (IOException e) { 1018 } catch (IOException e) {
1005 throw new SQLException(e); 1019 throw new SQLException(e);
1033 * as a BLOB. When the setBinaryStream method is used, the driver 1047 * as a BLOB. When the setBinaryStream method is used, the driver
1034 * may have to do extra work to determine whether the parameter data 1048 * may have to do extra work to determine whether the parameter data
1035 * should be sent to the server as a LONGVARBINARY or a BLOB. 1049 * should be sent to the server as a LONGVARBINARY or a BLOB.
1036 * 1050 *
1037 * @param parameterIndex the first parameter is 1, the second is 2, ... 1051 * @param parameterIndex the first parameter is 1, the second is 2, ...
1038 * @param stream an object that contains the data to set the parameter value to 1052 * @param is an object that contains the data to set the parameter value to
1039 * @param length the number of bytes in the parameter data 1053 * @param length the number of bytes in the parameter data
1040 * @throws SQLException if a database access error occurs 1054 * @throws SQLException if a database access error occurs
1041 */ 1055 */
1042 @Override 1056 @Override
1043 public void setBlob(int parameterIndex, InputStream stream, long length) throws SQLException { 1057 public void setBlob(int parameterIndex, InputStream is, long length) throws SQLException {
1044 if (stream == null) { 1058 if (is == null) {
1045 setNull(parameterIndex, -1); 1059 setNull(parameterIndex, -1);
1046 return; 1060 return;
1047 } 1061 }
1048 try { 1062 try {
1049 byte[] arr = new byte[(int) length]; 1063 byte[] arr = new byte[(int) length];
1050 ByteArrayOutputStream buf = new ByteArrayOutputStream((int) length); 1064 ByteArrayOutputStream buf = new ByteArrayOutputStream((int) length);
1051 1065
1052 int numChars = stream.read(arr, 0, (int) length); 1066 int numChars = is.read(arr, 0, (int) length);
1053 buf.write(arr, 0, numChars); 1067 buf.write(arr, 0, numChars);
1054 setBytes(parameterIndex, buf.toByteArray()); 1068 setBytes(parameterIndex, buf.toByteArray());
1055 } catch (IOException e) { 1069 } catch (IOException e) {
1056 throw new SQLException(e); 1070 throw new SQLException(e);
1057 } 1071 }
1185 1199
1186 /** 1200 /**
1187 * Sets the designated parameter to the given Clob object. The driver 1201 * Sets the designated parameter to the given Clob object. The driver
1188 * converts this to an SQL CLOB value when it sends it to the database. 1202 * converts this to an SQL CLOB value when it sends it to the database.
1189 * 1203 *
1190 * @param i the first parameter is 1, the second is 2, ... 1204 * @param parameterIndex the first parameter is 1, the second is 2, ...
1191 * @param x a Clob object that maps an SQL CLOB value 1205 * @param x a Clob object that maps an SQL CLOB value
1192 * @throws SQLException if a database access error occurs 1206 * @throws SQLException if a database access error occurs
1193 */ 1207 */
1194 @Override 1208 @Override
1195 public void setClob(int i, Clob x) throws SQLException { 1209 public void setClob(int parameterIndex, Clob x) throws SQLException {
1196 if (x == null) { 1210 if (x == null) {
1197 setNull(i, -1); 1211 setNull(parameterIndex, -1);
1198 return; 1212 return;
1199 } 1213 }
1200 1214
1201 // simply serialise the CLOB into a variable for now... far from 1215 // simply serialise the CLOB into a variable for now... far from
1202 // efficient, but might work for a few cases... 1216 // efficient, but might work for a few cases...
1203 // be on your marks: we have to cast the length down! 1217 // be on your marks: we have to cast the length down!
1204 setString(i, x.getSubString(1L, (int)(x.length()))); 1218 setString(parameterIndex, x.getSubString(1L, (int)(x.length())));
1205 } 1219 }
1206 1220
1207 /** 1221 /**
1208 * Sets the designated parameter to the given Clob object. The driver 1222 * Sets the designated parameter to the given Clob object. The driver
1209 * converts this to an SQL CLOB value when it sends it to the database. 1223 * converts this to an SQL CLOB value when it sends it to the database.
1210 * 1224 *
1211 * @param i the first parameter is 1, the second is 2, ... 1225 * @param parameterIndex the first parameter is 1, the second is 2, ...
1212 * @param reader an object that contains the data to set the parameter 1226 * @param reader an object that contains the data to set the parameter
1213 * value to 1227 * value to
1214 * @throws SQLException if a database access error occurs 1228 * @throws SQLException if a database access error occurs
1215 */ 1229 */
1216 @Override 1230 @Override
1217 public void setClob(int i, Reader reader) throws SQLException { 1231 public void setClob(int parameterIndex, Reader reader) throws SQLException {
1218 if (reader == null) { 1232 if (reader == null) {
1219 setNull(i, -1); 1233 setNull(parameterIndex, -1);
1220 return; 1234 return;
1221 } 1235 }
1222 // Some buffer. Size of 8192 is default for BufferedReader, so... 1236 // Some buffer. Size of 8192 is default for BufferedReader, so...
1223 char[] arr = new char[8192]; 1237 char[] arr = new char[8192];
1224 StringBuilder buf = new StringBuilder(8192 * 8); 1238 StringBuilder buf = new StringBuilder(8192 * 8);
1225 int numChars; 1239 int numChars;
1226 try { 1240 try {
1227 while ((numChars = reader.read(arr, 0, arr.length)) > 0) { 1241 while ((numChars = reader.read(arr, 0, arr.length)) > 0) {
1228 buf.append(arr, 0, numChars); 1242 buf.append(arr, 0, numChars);
1229 } 1243 }
1230 setString(i, buf.toString()); 1244 setString(parameterIndex, buf.toString());
1231 } catch (IOException e) { 1245 } catch (IOException e) {
1232 throw new SQLException(e); 1246 throw new SQLException(e);
1233 } 1247 }
1234 } 1248 }
1235 1249
1242 * parameter value should be sent to the server as a CLOB. When the 1256 * parameter value should be sent to the server as a CLOB. When the
1243 * setCharacterStream method is used, the driver may have to do 1257 * setCharacterStream method is used, the driver may have to do
1244 * extra work to determine whether the parameter data should be sent 1258 * extra work to determine whether the parameter data should be sent
1245 * to the server as a LONGVARCHAR or a CLOB. 1259 * to the server as a LONGVARCHAR or a CLOB.
1246 * 1260 *
1247 * @param i the first parameter is 1, the second is 2, ... 1261 * @param parameterIndex the first parameter is 1, the second is 2, ...
1248 * @param reader An object that contains the data to set the 1262 * @param reader An object that contains the data to set the
1249 * parameter value to. 1263 * parameter value to.
1250 * @param length the number of characters in the parameter data. 1264 * @param length the number of characters in the parameter data.
1251 * @throws SQLException if a database access error occurs 1265 * @throws SQLException if a database access error occurs
1252 */ 1266 */
1253 @Override 1267 @Override
1254 public void setClob(int i, Reader reader, long length) throws SQLException { 1268 public void setClob(int parameterIndex, Reader reader, long length) throws SQLException {
1255 if (reader == null || length < 0) { 1269 if (reader == null || length < 0) {
1256 setNull(i, -1); 1270 setNull(parameterIndex, -1);
1257 return; 1271 return;
1258 } 1272 }
1259 // simply serialise the CLOB into a variable for now... far from 1273 // simply serialise the CLOB into a variable for now... far from
1260 // efficient, but might work for a few cases... 1274 // efficient, but might work for a few cases...
1261 CharBuffer buf = CharBuffer.allocate((int) length); // have to down cast :( 1275 CharBuffer buf = CharBuffer.allocate((int) length); // have to down cast :(
1265 throw new SQLException("failed to read from stream: " + 1279 throw new SQLException("failed to read from stream: " +
1266 e.getMessage(), "M1M25"); 1280 e.getMessage(), "M1M25");
1267 } 1281 }
1268 // We have to rewind the buffer, because otherwise toString() returns "". 1282 // We have to rewind the buffer, because otherwise toString() returns "".
1269 buf.rewind(); 1283 buf.rewind();
1270 setString(i, buf.toString()); 1284 setString(parameterIndex, buf.toString());
1271 } 1285 }
1272 1286
1273 /** 1287 /**
1274 * Sets the designated parameter to the given java.sql.Date value. The 1288 * Sets the designated parameter to the given java.sql.Date value. The
1275 * driver converts this to an SQL DATE value when it sends it to the 1289 * driver converts this to an SQL DATE value when it sends it to the
1369 * Sets the designated parameter to a Reader object. The Reader 1383 * Sets the designated parameter to a Reader object. The Reader
1370 * reads the data till end-of-file is reached. The driver does the 1384 * reads the data till end-of-file is reached. The driver does the
1371 * necessary conversion from Java character format to the national 1385 * necessary conversion from Java character format to the national
1372 * character set in the database. 1386 * character set in the database.
1373 * 1387 *
1374 * @param i the first parameter is 1, the second is 2, ... 1388 * @param parameterIndex the first parameter is 1, the second is 2, ...
1375 * @param value the parameter value 1389 * @param value the parameter value
1376 * @throws SQLException if a database access error occurs 1390 * @throws SQLException if a database access error occurs
1377 * @throws SQLFeatureNotSupportedException the JDBC driver does 1391 * @throws SQLFeatureNotSupportedException the JDBC driver does
1378 * not support this method 1392 * not support this method
1379 */ 1393 */
1380 @Override 1394 @Override
1381 public void setNCharacterStream(int i, Reader value) throws SQLException { 1395 public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException {
1382 throw newSQLFeatureNotSupportedException("setNCharacterStream"); 1396 setCharacterStream(parameterIndex, value, 0);
1383 } 1397 }
1384 1398
1385 /** 1399 /**
1386 * Sets the designated parameter to a Reader object. The Reader 1400 * Sets the designated parameter to a Reader object. The Reader
1387 * reads the data till end-of-file is reached. The driver does the 1401 * reads the data till end-of-file is reached. The driver does the
1388 * necessary conversion from Java character format to the national 1402 * necessary conversion from Java character format to the national
1389 * character set in the database. 1403 * character set in the database.
1390 * 1404 *
1391 * @param i the first parameter is 1, the second is 2, ... 1405 * @param parameterIndex the first parameter is 1, the second is 2, ...
1392 * @param value the parameter value 1406 * @param value the parameter value
1393 * @param length the number of characters in the parameter data. 1407 * @param length the number of characters in the parameter data.
1394 * @throws SQLException if a database access error occurs 1408 * @throws SQLException if a database access error occurs
1395 * @throws SQLFeatureNotSupportedException the JDBC driver does 1409 * @throws SQLFeatureNotSupportedException the JDBC driver does
1396 * not support this method 1410 * not support this method
1397 */ 1411 */
1398 @Override 1412 @Override
1399 public void setNCharacterStream(int i, Reader value, long length) throws SQLException { 1413 public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException {
1400 throw newSQLFeatureNotSupportedException("setNCharacterStream"); 1414 setCharacterStream(parameterIndex, value, length);
1401 } 1415 }
1402 1416
1403 /** 1417 /**
1404 * Sets the designated parameter to a java.sql.NClob object. The 1418 * Sets the designated parameter to a java.sql.NClob object. The
1405 * driver converts this to a SQL NCLOB value when it sends it to the 1419 * driver converts this to a SQL NCLOB value when it sends it to the
1406 * database. 1420 * database.
1407 * 1421 *
1408 * @param i the first parameter is 1, the second is 2, ... 1422 * @param parameterIndex the first parameter is 1, the second is 2, ...
1409 * @param value the parameter value 1423 * @param value the parameter value
1410 * @throws SQLException if a database access error occurs 1424 * @throws SQLException if a database access error occurs
1411 * @throws SQLFeatureNotSupportedException the JDBC driver does 1425 * @throws SQLFeatureNotSupportedException the JDBC driver does
1412 * not support this method 1426 * not support this method
1413 */ 1427 */
1414 @Override 1428 @Override
1415 public void setNClob(int i, Reader value) throws SQLException { 1429 public void setNClob(int parameterIndex, Reader value) throws SQLException {
1416 throw newSQLFeatureNotSupportedException("setNClob"); 1430 throw newSQLFeatureNotSupportedException("setNClob");
1417 } 1431 }
1418 1432
1419 /** 1433 /**
1420 * Sets the designated parameter to a java.sql.NClob object. The 1434 * Sets the designated parameter to a java.sql.NClob object. The
1421 * driver converts this to a SQL NCLOB value when it sends it to the 1435 * driver converts this to a SQL NCLOB value when it sends it to the
1422 * database. 1436 * database.
1423 * 1437 *
1424 * @param i the first parameter is 1, the second is 2, ... 1438 * @param parameterIndex the first parameter is 1, the second is 2, ...
1425 * @param value the parameter value 1439 * @param value the parameter value
1426 * @throws SQLException if a database access error occurs 1440 * @throws SQLException if a database access error occurs
1427 * @throws SQLFeatureNotSupportedException the JDBC driver does 1441 * @throws SQLFeatureNotSupportedException the JDBC driver does
1428 * not support this method 1442 * not support this method
1429 */ 1443 */
1430 @Override 1444 @Override
1431 public void setNClob(int i, NClob value) throws SQLException { 1445 public void setNClob(int parameterIndex, NClob value) throws SQLException {
1432 throw newSQLFeatureNotSupportedException("setNClob"); 1446 throw newSQLFeatureNotSupportedException("setNClob");
1433 } 1447 }
1434 1448
1435 /** 1449 /**
1436 * Sets the designated parameter to a Reader object. The reader must 1450 * Sets the designated parameter to a Reader object. The reader must
1441 * parameter value should be sent to the server as a NCLOB. When the 1455 * parameter value should be sent to the server as a NCLOB. When the
1442 * setCharacterStream method is used, the driver may have to do 1456 * setCharacterStream method is used, the driver may have to do
1443 * extra work to determine whether the parameter data should be sent 1457 * extra work to determine whether the parameter data should be sent
1444 * to the server as a LONGNVARCHAR or a NCLOB. 1458 * to the server as a LONGNVARCHAR or a NCLOB.
1445 * 1459 *
1446 * @param i the first parameter is 1, the second is 2, ... 1460 * @param parameterIndex the first parameter is 1, the second is 2, ...
1447 * @param r An object that contains the data to set the parameter 1461 * @param r An object that contains the data to set the parameter
1448 * value to 1462 * value to
1449 * @param length the number of characters in the parameter data 1463 * @param length the number of characters in the parameter data
1450 * @throws SQLException if a database access error occurs 1464 * @throws SQLException if a database access error occurs
1451 * @throws SQLFeatureNotSupportedException the JDBC driver does 1465 * @throws SQLFeatureNotSupportedException the JDBC driver does
1452 * not support this method 1466 * not support this method
1453 */ 1467 */
1454 @Override 1468 @Override
1455 public void setNClob(int i, Reader r, long length) throws SQLException { 1469 public void setNClob(int parameterIndex, Reader r, long length) throws SQLException {
1456 throw newSQLFeatureNotSupportedException("setNClob"); 1470 throw newSQLFeatureNotSupportedException("setNClob");
1457 } 1471 }
1458 1472
1459 /** 1473 /**
1460 * Sets the designated paramter to the given String object. The 1474 * Sets the designated paramter to the given String object. The
1461 * driver converts this to a SQL NCHAR or NVARCHAR or LONGNVARCHAR 1475 * driver converts this to a SQL NCHAR or NVARCHAR or LONGNVARCHAR
1462 * value (depending on the argument's size relative to the driver's 1476 * value (depending on the argument's size relative to the driver's
1463 * limits on NVARCHAR values) when it sends it to the database. 1477 * limits on NVARCHAR values) when it sends it to the database.
1464 * 1478 *
1465 * @param i the first parameter is 1, the second is 2, ... 1479 * @param parameterIndex the first parameter is 1, the second is 2, ...
1466 * @param value the parameter value 1480 * @param value the parameter value
1467 * @throws SQLException if a database access error occurs 1481 * @throws SQLException if a database access error occurs
1468 * @throws SQLFeatureNotSupportedException the JDBC driver does 1482 * @throws SQLFeatureNotSupportedException the JDBC driver does
1469 * not support this method 1483 * not support this method
1470 */ 1484 */
1471 @Override 1485 @Override
1472 public void setNString(int i, String value) throws SQLException { 1486 public void setNString(int parameterIndex, String value) throws SQLException {
1473 throw newSQLFeatureNotSupportedException("setNString"); 1487 setString(parameterIndex, value);
1474 } 1488 }
1475 1489
1476 /** 1490 /**
1477 * Sets the designated parameter to SQL NULL. 1491 * Sets the designated parameter to SQL NULL.
1478 * 1492 *
1503 * type name information, it may ignore it. Although it is intended for 1517 * type name information, it may ignore it. Although it is intended for
1504 * user-defined and Ref parameters, this method may be used to set a null 1518 * user-defined and Ref parameters, this method may be used to set a null
1505 * parameter of any JDBC type. If the parameter does not have a 1519 * parameter of any JDBC type. If the parameter does not have a
1506 * user-defined or REF type, the given typeName is ignored. 1520 * user-defined or REF type, the given typeName is ignored.
1507 * 1521 *
1508 * @param paramIndex the first parameter is 1, the second is 2, ... 1522 * @param parameterIndex the first parameter is 1, the second is 2, ...
1509 * @param sqlType a value from java.sql.Types 1523 * @param sqlType a value from java.sql.Types
1510 * @param typeName the fully-qualified name of an SQL user-defined type; 1524 * @param typeName the fully-qualified name of an SQL user-defined type;
1511 * ignored if the parameter is not a user-defined type or 1525 * ignored if the parameter is not a user-defined type or
1512 * REF 1526 * REF
1513 * @throws SQLException if a database access error occurs 1527 * @throws SQLException if a database access error occurs
1514 */ 1528 */
1515 @Override 1529 @Override
1516 public void setNull(int paramIndex, int sqlType, String typeName) throws SQLException { 1530 public void setNull(int parameterIndex, int sqlType, String typeName) throws SQLException {
1517 // MonetDB/SQL's NULL needs no type 1531 // MonetDB/SQL's NULL needs no type
1518 setNull(paramIndex, sqlType); 1532 setNull(parameterIndex, sqlType);
1519 } 1533 }
1520 1534
1521 /** 1535 /**
1522 * Sets the value of the designated parameter using the given 1536 * Sets the value of the designated parameter using the given
1523 * object. The second parameter must be of type Object; therefore, 1537 * object. The second parameter must be of type Object; therefore,
1538 * 1552 *
1539 * This method throws an exception if there is an ambiguity, for 1553 * This method throws an exception if there is an ambiguity, for
1540 * example, if the object is of a class implementing more than one 1554 * example, if the object is of a class implementing more than one
1541 * of the interfaces named above. 1555 * of the interfaces named above.
1542 * 1556 *
1543 * @param index the first parameter is 1, the second is 2, ... 1557 * @param parameterIndex the first parameter is 1, the second is 2, ...
1544 * @param x the object containing the input parameter value 1558 * @param x the object containing the input parameter value
1545 * @throws SQLException if a database access error occurs or the type of 1559 * @throws SQLException if a database access error occurs or the type of
1546 * the given object is ambiguous 1560 * the given object is ambiguous
1547 */ 1561 */
1548 @Override 1562 @Override
1549 public void setObject(int index, Object x) throws SQLException { 1563 public void setObject(int parameterIndex, Object x) throws SQLException {
1550 setObject(index, x, javaType[getParamIdx(index)]); 1564 setObject(parameterIndex, x, javaType[getParamIdx(parameterIndex)], 0);
1551 } 1565 }
1552 1566
1553 /** 1567 /**
1554 * Sets the value of the designated parameter with the given object. This 1568 * Sets the value of the designated parameter with the given object. This
1555 * method is like the method setObject below, except that it assumes a scale 1569 * method is like the method setObject below, except that it assumes a scale
1598 * ignored. 1612 * ignored.
1599 * @throws SQLException if a database access error occurs 1613 * @throws SQLException if a database access error occurs
1600 * @see Types 1614 * @see Types
1601 */ 1615 */
1602 @Override 1616 @Override
1603 public void setObject( 1617 public void setObject(int parameterIndex, Object x, int targetSqlType, int scale) throws SQLException {
1604 int parameterIndex, 1618 if (x == null) {
1605 Object x, 1619 setNull(parameterIndex, -1);
1606 int targetSqlType, 1620 return;
1607 int scale) 1621 }
1608 throws SQLException
1609 {
1610 // this is according to table B-5 1622 // this is according to table B-5
1611 if (x instanceof String) { 1623 if (x instanceof String) {
1612 switch (targetSqlType) { 1624 setString(parameterIndex, (String)x);
1613 case Types.CHAR:
1614 case Types.VARCHAR:
1615 case Types.LONGVARCHAR:
1616 case Types.CLOB:
1617 setString(parameterIndex, (String)x);
1618 break;
1619 case Types.TINYINT:
1620 case Types.SMALLINT:
1621 case Types.INTEGER:
1622 {
1623 int val;
1624 try {
1625 val = Integer.parseInt((String)x);
1626 } catch (NumberFormatException e) {
1627 val = 0;
1628 }
1629 setInt(parameterIndex, val);
1630 } break;
1631 case Types.BIGINT:
1632 {
1633 long val;
1634 try {
1635 val = Long.parseLong((String)x);
1636 } catch (NumberFormatException e) {
1637 val = 0;
1638 }
1639 setLong(parameterIndex, val);
1640 } break;
1641 case Types.REAL:
1642 {
1643 float val;
1644 try {
1645 val = Float.parseFloat((String)x);
1646 } catch (NumberFormatException e) {
1647 val = 0;
1648 }
1649 setFloat(parameterIndex, val);
1650 } break;
1651 case Types.FLOAT:
1652 case Types.DOUBLE:
1653 {
1654 double val;
1655 try {
1656 val = Double.parseDouble((String)x);
1657 } catch (NumberFormatException e) {
1658 val = 0;
1659 }
1660 setDouble(parameterIndex, val);
1661 } break;
1662 case Types.DECIMAL:
1663 case Types.NUMERIC:
1664 {
1665 BigDecimal val;
1666 try {
1667 val = new BigDecimal((String)x);
1668 } catch (NumberFormatException e) {
1669 try {
1670 val = new BigDecimal(0.0);
1671 } catch (NumberFormatException ex) {
1672 throw new SQLException("Internal error: unable to create template BigDecimal: " + ex.getMessage(), "M0M03");
1673 }
1674 }
1675 val = val.setScale(scale, BigDecimal.ROUND_HALF_UP);
1676 setBigDecimal(parameterIndex, val);
1677 } break;
1678 case Types.BIT:
1679 case Types.BOOLEAN:
1680 setBoolean(parameterIndex, (Boolean.valueOf((String)x)).booleanValue());
1681 break;
1682 case Types.BINARY:
1683 case Types.VARBINARY:
1684 case Types.LONGVARBINARY:
1685 setBytes(parameterIndex, ((String)x).getBytes());
1686 break;
1687 case Types.DATE:
1688 {
1689 java.sql.Date val;
1690 try {
1691 val = java.sql.Date.valueOf((String)x);
1692 } catch (IllegalArgumentException e) {
1693 val = new java.sql.Date(0L);
1694 }
1695 setDate(parameterIndex, val);
1696 } break;
1697 case Types.TIME:
1698 {
1699 Time val;
1700 try {
1701 val = Time.valueOf((String)x);
1702 } catch (IllegalArgumentException e) {
1703 val = new Time(0L);
1704 }
1705 setTime(parameterIndex, val);
1706 } break;
1707 case Types.TIMESTAMP:
1708 {
1709 Timestamp val;
1710 try {
1711 val = Timestamp.valueOf((String)x);
1712 } catch (IllegalArgumentException e) {
1713 val = new Timestamp(0L);
1714 }
1715 setTimestamp(parameterIndex, val);
1716 } break;
1717 case Types.NCHAR:
1718 case Types.NVARCHAR:
1719 case Types.LONGNVARCHAR:
1720 throw newSQLFeatureNotSupportedException("setObject() with targetType N[VAR]CHAR");
1721 default:
1722 throw new SQLException("Conversion not allowed", "M1M05");
1723 }
1724 } else if (x instanceof BigDecimal || 1625 } else if (x instanceof BigDecimal ||
1725 x instanceof Byte || 1626 x instanceof Byte ||
1726 x instanceof Short || 1627 x instanceof Short ||
1727 x instanceof Integer || 1628 x instanceof Integer ||
1728 x instanceof Long || 1629 x instanceof Long ||
1877 setDate(parameterIndex, new java.sql.Date(((Timestamp)x).getTime())); 1778 setDate(parameterIndex, new java.sql.Date(((Timestamp)x).getTime()));
1878 } else if (x instanceof java.util.Date) { 1779 } else if (x instanceof java.util.Date) {
1879 setDate(parameterIndex, new java.sql.Date( 1780 setDate(parameterIndex, new java.sql.Date(
1880 ((java.util.Date)x).getTime())); 1781 ((java.util.Date)x).getTime()));
1881 } else if (x instanceof Calendar) { 1782 } else if (x instanceof Calendar) {
1882 setDate(parameterIndex, new java.sql.Date( 1783 setDate(parameterIndex, new java.sql.Date(((Calendar)x).getTimeInMillis()));
1883 ((Calendar)x).getTimeInMillis()));
1884 } else { 1784 } else {
1885 throw new SQLException("Conversion not allowed", "M1M05"); 1785 throw new SQLException("Conversion not allowed", "M1M05");
1886 } 1786 }
1887 break; 1787 break;
1888 case Types.TIME: 1788 case Types.TIME:
1789 case 2013: //Types.TIME_WITH_TIMEZONE:
1889 if (x instanceof Time) { 1790 if (x instanceof Time) {
1890 setTime(parameterIndex, (Time)x); 1791 setTime(parameterIndex, (Time)x);
1891 } else if (x instanceof Timestamp) { 1792 } else if (x instanceof Timestamp) {
1892 setTime(parameterIndex, new Time(((Timestamp)x).getTime())); 1793 setTime(parameterIndex, new Time(((Timestamp)x).getTime()));
1893 } else if (x instanceof java.util.Date) { 1794 } else if (x instanceof java.util.Date) {
1894 setTime(parameterIndex, new java.sql.Time( 1795 setTime(parameterIndex, new java.sql.Time(
1895 ((java.util.Date)x).getTime())); 1796 ((java.util.Date)x).getTime()));
1896 } else if (x instanceof Calendar) { 1797 } else if (x instanceof Calendar) {
1897 setTime(parameterIndex, new java.sql.Time( 1798 setTime(parameterIndex, new java.sql.Time(((Calendar)x).getTimeInMillis()));
1898 ((Calendar)x).getTimeInMillis()));
1899 } else { 1799 } else {
1900 throw new SQLException("Conversion not allowed", "M1M05"); 1800 throw new SQLException("Conversion not allowed", "M1M05");
1901 } 1801 }
1902 break; 1802 break;
1903 case Types.TIMESTAMP: 1803 case Types.TIMESTAMP:
1804 case 2014: //Types.TIMESTAMP_WITH_TIMEZONE:
1904 if (x instanceof Timestamp) { 1805 if (x instanceof Timestamp) {
1905 setTimestamp(parameterIndex, (Timestamp)x); 1806 setTimestamp(parameterIndex, (Timestamp)x);
1906 } else if (x instanceof java.sql.Date) { 1807 } else if (x instanceof java.sql.Date) {
1907 setTimestamp(parameterIndex, new Timestamp(((java.sql.Date)x).getTime())); 1808 setTimestamp(parameterIndex, new Timestamp(((java.sql.Date)x).getTime()));
1908 } else if (x instanceof java.util.Date) { 1809 } else if (x instanceof java.util.Date) {
1909 setTimestamp(parameterIndex, new java.sql.Timestamp( 1810 setTimestamp(parameterIndex, new java.sql.Timestamp(
1910 ((java.util.Date)x).getTime())); 1811 ((java.util.Date)x).getTime()));
1911 } else if (x instanceof Calendar) { 1812 } else if (x instanceof Calendar) {
1912 setTimestamp(parameterIndex, new java.sql.Timestamp( 1813 setTimestamp(parameterIndex, new java.sql.Timestamp(((Calendar)x).getTimeInMillis()));
1913 ((Calendar)x).getTimeInMillis()));
1914 } else { 1814 } else {
1915 throw new SQLException("Conversion not allowed", "M1M05"); 1815 throw new SQLException("Conversion not allowed", "M1M05");
1916 } 1816 }
1917 break; 1817 break;
1918 case Types.CHAR: 1818 case Types.CHAR:
1924 default: 1824 default:
1925 throw new SQLException("Conversion not allowed", "M1M05"); 1825 throw new SQLException("Conversion not allowed", "M1M05");
1926 } 1826 }
1927 } else if (x instanceof Array) { 1827 } else if (x instanceof Array) {
1928 setArray(parameterIndex, (Array)x); 1828 setArray(parameterIndex, (Array)x);
1929 } else if (x instanceof Blob) { 1829 } else if (x instanceof Blob || x instanceof MonetBlob) {
1930 setBlob(parameterIndex, (Blob)x); 1830 setBlob(parameterIndex, (Blob)x);
1931 } else if (x instanceof Clob) { 1831 } else if (x instanceof Clob || x instanceof MonetClob) {
1932 setClob(parameterIndex, (Clob)x); 1832 setClob(parameterIndex, (Clob)x);
1933 } else if (x instanceof Struct) { 1833 } else if (x instanceof Struct) {
1934 // I have no idea how to do this... 1834 // I have no idea how to do this...
1935 throw newSQLFeatureNotSupportedException("setObject() with object of type Struct"); 1835 throw newSQLFeatureNotSupportedException("setObject() with object of type Struct");
1936 } else if (x instanceof Ref) { 1836 } else if (x instanceof Ref) {
1937 setRef(parameterIndex, (Ref)x); 1837 setRef(parameterIndex, (Ref)x);
1938 } else if (x instanceof java.net.URL) { 1838 } else if (x instanceof java.net.URL) {
1939 setURL(parameterIndex, (java.net.URL)x); 1839 setURL(parameterIndex, (java.net.URL)x);
1840 } else if (x instanceof java.util.UUID) {
1841 setString(parameterIndex, x.toString());
1940 } else if (x instanceof RowId) { 1842 } else if (x instanceof RowId) {
1941 setRowId(parameterIndex, (RowId)x); 1843 setRowId(parameterIndex, (RowId)x);
1942 } else if (x instanceof NClob) { 1844 } else if (x instanceof NClob) {
1943 throw newSQLFeatureNotSupportedException("setObject() with object of type NClob"); 1845 setNClob(parameterIndex, (NClob)x);
1944 } else if (x instanceof SQLXML) { 1846 } else if (x instanceof SQLXML) {
1945 throw newSQLFeatureNotSupportedException("setObject() with object of type SQLXML"); 1847 setSQLXML(parameterIndex, (SQLXML)x);
1946 } else if (x instanceof SQLData) { // not in JDBC4.1??? 1848 } else if (x instanceof SQLData) { // not in JDBC4.1???
1947 SQLData sx = (SQLData)x; 1849 SQLData sx = (SQLData)x;
1948 final int paramnr = parameterIndex; 1850 final int paramnr = parameterIndex;
1949 final String sqltype = sx.getSQLTypeName(); 1851 final String sqltype = sx.getSQLTypeName();
1950 SQLOutput out = new SQLOutput() { 1852 SQLOutput out = new SQLOutput() {
2020 setTimestamp(paramnr, x); 1922 setTimestamp(paramnr, x);
2021 } 1923 }
2022 1924
2023 @Override 1925 @Override
2024 public void writeCharacterStream(Reader x) throws SQLException { 1926 public void writeCharacterStream(Reader x) throws SQLException {
2025 setCharacterStream(paramnr, x); 1927 setCharacterStream(paramnr, x, 0);
2026 } 1928 }
2027 1929
2028 @Override 1930 @Override
2029 public void writeAsciiStream(InputStream x) throws SQLException { 1931 public void writeAsciiStream(InputStream x) throws SQLException {
2030 setAsciiStream(paramnr, x); 1932 setAsciiStream(paramnr, x);
2090 setSQLXML(paramnr, x); 1992 setSQLXML(paramnr, x);
2091 } 1993 }
2092 }; 1994 };
2093 sx.writeSQL(out); 1995 sx.writeSQL(out);
2094 } else { // java Class 1996 } else { // java Class
2095 throw newSQLFeatureNotSupportedException("setObject() with object of type Class"); 1997 throw newSQLFeatureNotSupportedException("setObject() with object of type Class " + x.getClass().getName());
2096 } 1998 }
2097 } 1999 }
2098 2000
2099 /** 2001 /**
2100 * Sets the designated parameter to the given REF(<structured-type>) value. 2002 * Sets the designated parameter to the given REF(<structured-type>) value.
2101 * The driver converts this to an SQL REF value when it sends it to the 2003 * The driver converts this to an SQL REF value when it sends it to the
2102 * database. 2004 * database.
2103 * 2005 *
2104 * @param i the first parameter is 1, the second is 2, ... 2006 * @param parameterIndex the first parameter is 1, the second is 2, ...
2105 * @param x an SQL REF value 2007 * @param x an SQL REF value
2106 * @throws SQLException if a database access error occurs 2008 * @throws SQLException if a database access error occurs
2107 * @throws SQLFeatureNotSupportedException the JDBC driver does 2009 * @throws SQLFeatureNotSupportedException the JDBC driver does
2108 * not support this method 2010 * not support this method
2109 */ 2011 */
2110 @Override 2012 @Override
2111 public void setRef(int i, Ref x) throws SQLException { 2013 public void setRef(int parameterIndex, Ref x) throws SQLException {
2112 throw newSQLFeatureNotSupportedException("setRef"); 2014 throw newSQLFeatureNotSupportedException("setRef");
2113 } 2015 }
2114 2016
2115 /** 2017 /**
2116 * Sets the designated parameter to the given java.sql.RowId object. 2018 * Sets the designated parameter to the given java.sql.RowId object.
2117 * The driver converts this to a SQL ROWID value when it sends it to 2019 * The driver converts this to a SQL ROWID value when it sends it to
2118 * the database. 2020 * the database.
2119 * 2021 *
2120 * @param i the first parameter is 1, the second is 2, ... 2022 * @param parameterIndex the first parameter is 1, the second is 2, ...
2121 * @param x the parameter value 2023 * @param x the parameter value
2122 * @throws SQLException if a database access error occurs 2024 * @throws SQLException if a database access error occurs
2123 * @throws SQLFeatureNotSupportedException the JDBC driver does 2025 * @throws SQLFeatureNotSupportedException the JDBC driver does
2124 * not support this method 2026 * not support this method
2125 */ 2027 */
2126 @Override 2028 @Override
2127 public void setRowId(int i, RowId x) throws SQLException { 2029 public void setRowId(int parameterIndex, RowId x) throws SQLException {
2128 throw newSQLFeatureNotSupportedException("setRowId"); 2030 throw newSQLFeatureNotSupportedException("setRowId");
2129 } 2031 }
2130 2032
2131 /** 2033 /**
2132 * Sets the designated parameter to the given Java short value. The driver 2034 * Sets the designated parameter to the given Java short value. The driver
2155 public void setString(int parameterIndex, String x) throws SQLException { 2057 public void setString(int parameterIndex, String x) throws SQLException {
2156 if (x == null) { 2058 if (x == null) {
2157 setNull(parameterIndex, -1); 2059 setNull(parameterIndex, -1);
2158 return; 2060 return;
2159 } 2061 }
2160 setValue(parameterIndex, "'" + x.replaceAll("\\\\", "\\\\\\\\").replaceAll("'", "\\\\'") + "'"); 2062 int paramIdx = getParamIdx(parameterIndex); // this will throw a SQLException if parameter can not be found
2063
2064 /* depending on the parameter data type (as expected by MonetDB) we
2065 may need to add the data type as cast prefix to the parameter value */
2066 int paramJdbcType = javaType[paramIdx];
2067 String paramMonetdbType = monetdbType[paramIdx];
2068
2069 switch (paramJdbcType) {
2070 case Types.CHAR:
2071 case Types.VARCHAR:
2072 case Types.LONGVARCHAR:
2073 case Types.CLOB:
2074 case Types.NCHAR:
2075 case Types.NVARCHAR:
2076 case Types.LONGNVARCHAR:
2077 {
2078 String castprefix = "";
2079 switch (paramMonetdbType) {
2080 // some MonetDB specific data types require a cast prefix
2081 case "inet":
2082 try {
2083 // check if x represents a valid inet string to prevent
2084 // failing exec #(..., ...) calls which destroy the prepared statement, see bug 6351
2085 nl.cwi.monetdb.jdbc.MonetINET inet_obj = new nl.cwi.monetdb.jdbc.MonetINET(x);
2086 } catch (Exception se) {
2087 throw new SQLDataException("Conversion of string: " + x + " to parameter data type " + paramMonetdbType + " failed. " + se.getMessage(), "22M29");
2088 }
2089 castprefix = "inet ";
2090 break;
2091 case "json":
2092 // There is no support for JSON in standard java class libraries.
2093 // Possibly we could use org.json.simple.JSONObject or other/faster libs
2094 // javax.json.Json is not released yet (see https://json-processing-spec.java.net/)
2095 // see also https://github.com/fabienrenaud/java-json-benchmark
2096 // Note that it would make our JDBC driver dependent of an external jar
2097 // and we don't want that.
2098
2099 // do simplistic check if x represents a valid json string to prevent
2100 // failing exec #(..., ...) calls which destroy the prepared statement, see bug 6351
2101 if (x.isEmpty() ||
2102 (x.startsWith("{") && !x.endsWith("}")) ||
2103 (x.startsWith("[") && !x.endsWith("]"))
2104 // TODO check completely if x represents a valid json string
2105 )
2106 throw new SQLDataException("Invalid json string. It does not start with { or [ and end with } or ]", "22M32");
2107
2108 // TODO check completely if x represents a valid json string
2109
2110 castprefix = "json ";
2111 break;
2112 case "url":
2113 try {
2114 // also check if x represents a valid url string to prevent
2115 // failing exec #(..., ...) calls which destroy the prepared statement, see bug 6351
2116 java.net.URL url_obj = new java.net.URL(x);
2117 } catch (java.net.MalformedURLException mue) {
2118 throw new SQLDataException("Conversion of string: " + x + " to parameter data type " + paramMonetdbType + " failed. " + mue.getMessage(), "22M30");
2119 }
2120 castprefix = "url ";
2121 break;
2122 case "uuid":
2123 try {
2124 // also check if x represents a valid uuid string to prevent
2125 // failing exec #(..., ...) calls which destroy the prepared statement, see bug 6351
2126 java.util.UUID uuid_obj = java.util.UUID.fromString(x);
2127 } catch (IllegalArgumentException iae) {
2128 throw new SQLDataException("Conversion of string: " + x + " to parameter data type " + paramMonetdbType + " failed. " + iae.getMessage(), "22M31");
2129 }
2130 castprefix = "uuid ";
2131 break;
2132 }
2133 /* in specific cases prefix the string with: inet or json or url or uuid */
2134 setValue(parameterIndex,
2135 castprefix + "'" + x.replaceAll("\\\\", "\\\\\\\\").replaceAll("'", "\\\\'") + "'");
2136 break;
2137 }
2138 case Types.TINYINT:
2139 case Types.SMALLINT:
2140 case Types.INTEGER:
2141 case Types.BIGINT:
2142 case Types.REAL:
2143 case Types.FLOAT:
2144 case Types.DOUBLE:
2145 case Types.DECIMAL:
2146 case Types.NUMERIC:
2147 try {
2148 // check (by calling parse) if the string represents a valid number to prevent
2149 // failing exec #(..., ...) calls which destroy the prepared statement, see bug 6351
2150 if (paramJdbcType == Types.TINYINT) {
2151 int number = Byte.parseByte(x);
2152 } else if (paramJdbcType == Types.SMALLINT ) {
2153 int number = Short.parseShort(x);
2154 } else if (paramJdbcType == Types.INTEGER) {
2155 int number = Integer.parseInt(x);
2156 } else if (paramJdbcType == Types.BIGINT) {
2157 long number = Long.parseLong(x);
2158 } else if (paramJdbcType == Types.REAL || paramJdbcType == Types.DOUBLE || paramJdbcType == Types.FLOAT) {
2159 double number = Double.parseDouble(x);
2160 } else {
2161 BigDecimal number = new BigDecimal(x);
2162 }
2163 } catch (NumberFormatException nfe) {
2164 throw new SQLDataException("Conversion of string: " + x + " to parameter data type " + paramMonetdbType + " failed. " + nfe.getMessage(), "22003");
2165 }
2166 setValue(parameterIndex, x);
2167 break;
2168 case Types.BIT:
2169 case Types.BOOLEAN:
2170 if (x.equalsIgnoreCase("false") || x.equalsIgnoreCase("true") || x.equals("0") || x.equals("1")) {
2171 setValue(parameterIndex, x);
2172 } else {
2173 throw new SQLDataException("Conversion of string: " + x + " to parameter data type " + paramMonetdbType + " failed", "22000");
2174 }
2175 break;
2176 case Types.DATE:
2177 case Types.TIME:
2178 case 2013: //Types.TIME_WITH_TIMEZONE:
2179 case Types.TIMESTAMP:
2180 case 2014: //Types.TIMESTAMP_WITH_TIMEZONE:
2181 try {
2182 // check if the string represents a valid calendar date or time or timestamp to prevent
2183 // failing exec #(..., ...) calls which destroy the prepared statement, see bug 6351
2184 if (paramJdbcType == Types.DATE) {
2185 java.sql.Date datum = java.sql.Date.valueOf(x);
2186 } else if (paramJdbcType == Types.TIME || paramJdbcType == 2013) {
2187 Time tijdstip = Time.valueOf(x);
2188 } else {
2189 Timestamp tijdstip = Timestamp.valueOf(x);
2190 }
2191 } catch (IllegalArgumentException iae) {
2192 throw new SQLDataException("Conversion of string: " + x + " to parameter data type " + paramMonetdbType + " failed. " + iae.getMessage(), "22007");
2193 }
2194 /* prefix the string with: date or time or timetz or timestamp or timestamptz */
2195 setValue(parameterIndex, paramMonetdbType + " '" + x + "'");
2196 break;
2197 case Types.BINARY:
2198 case Types.VARBINARY:
2199 case Types.LONGVARBINARY:
2200 case Types.BLOB:
2201 // check if the string x contains pairs of hex chars to prevent
2202 // failing exec #(..., ...) calls which destroy the prepared statement, see bug 6351
2203 int xlen = x.length();
2204 for (int i = 0; i < xlen; i++) {
2205 char c = x.charAt(i);
2206 if (c < '0' || c > '9') {
2207 if (c < 'A' || c > 'F') {
2208 if (c < 'a' || c > 'f') {
2209 throw new SQLDataException("Invalid string for parameter data type " + paramMonetdbType + ". The string may contain only hex chars", "22M28");
2210 }
2211 }
2212 }
2213 }
2214 /* prefix the string with: blob */
2215 setValue(parameterIndex, "blob '" + x + "'");
2216 break;
2217 default:
2218 throw new SQLException("Conversion of string to parameter data type " + paramMonetdbType + " is not (yet) supported", "M1M05");
2219 }
2161 } 2220 }
2162 2221
2163 /** 2222 /**
2164 * Sets the designated parameter to the given java.sql.SQLXML 2223 * Sets the designated parameter to the given java.sql.SQLXML
2165 * object. The driver converts this to an SQL XML value when it 2224 * object. The driver converts this to an SQL XML value when it
2179 /** 2238 /**
2180 * Sets the designated parameter to the given java.sql.Time value. 2239 * Sets the designated parameter to the given java.sql.Time value.
2181 * The driver converts this to an SQL TIME value when it sends it to 2240 * The driver converts this to an SQL TIME value when it sends it to
2182 * the database. 2241 * the database.
2183 * 2242 *
2184 * @param index the first parameter is 1, the second is 2, ... 2243 * @param parameterIndex the first parameter is 1, the second is 2, ...
2185 * @param x the parameter value 2244 * @param x the parameter value
2186 * @throws SQLException if a database access error occurs 2245 * @throws SQLException if a database access error occurs
2187 */ 2246 */
2188 @Override 2247 @Override
2189 public void setTime(int index, Time x) throws SQLException { 2248 public void setTime(int parameterIndex, Time x) throws SQLException {
2190 setTime(index, x, null); 2249 setTime(parameterIndex, x, null);
2191 } 2250 }
2192 2251
2193 /** 2252 /**
2194 * Sets the designated parameter to the given java.sql.Time value, 2253 * Sets the designated parameter to the given java.sql.Time value,
2195 * using the given Calendar object. The driver uses the Calendar 2254 * using the given Calendar object. The driver uses the Calendar
2198 * calculate the time taking into account a custom timezone. If no 2257 * calculate the time taking into account a custom timezone. If no
2199 * Calendar object is specified, the driver uses the default 2258 * Calendar object is specified, the driver uses the default
2200 * timezone, which is that of the virtual machine running the 2259 * timezone, which is that of the virtual machine running the
2201 * application. 2260 * application.
2202 * 2261 *
2203 * @param index the first parameter is 1, the second is 2, ... 2262 * @param parameterIndex the first parameter is 1, the second is 2, ...
2204 * @param x the parameter value 2263 * @param x the parameter value
2205 * @param cal the Calendar object the driver will use to construct the time 2264 * @param cal the Calendar object the driver will use to construct the time
2206 * @throws SQLException if a database access error occurs 2265 * @throws SQLException if a database access error occurs
2207 */ 2266 */
2208 @Override 2267 @Override
2209 public void setTime(int index, Time x, Calendar cal) throws SQLException { 2268 public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException {
2210 if (x == null) { 2269 if (x == null) {
2211 setNull(index, -1); 2270 setNull(parameterIndex, -1);
2212 return; 2271 return;
2213 } 2272 }
2214 2273
2215 boolean hasTimeZone = monetdbType[getParamIdx(index)].endsWith("tz"); 2274 String MonetDBType = monetdbType[getParamIdx(parameterIndex)];
2275 boolean hasTimeZone = ("timetz".equals(MonetDBType) || "timestamptz".equals(MonetDBType));
2216 if (hasTimeZone) { 2276 if (hasTimeZone) {
2217 // timezone shouldn't matter, since the server is timezone 2277 // timezone shouldn't matter, since the server is timezone
2218 // aware in this case 2278 // aware in this case
2219 String RFC822 = mTimeZ.format(x); 2279 String RFC822 = mTimeZ.format(x);
2220 setValue(index, "timetz '" + RFC822.substring(0, 15) + ":" + RFC822.substring(15) + "'"); 2280 setValue(parameterIndex, "timetz '" + RFC822.substring(0, 15) + ":" + RFC822.substring(15) + "'");
2221 } else { 2281 } else {
2222 // server is not timezone aware for this field, and no 2282 // server is not timezone aware for this field, and no
2223 // calendar given, since we told the server our timezone at 2283 // calendar given, since we told the server our timezone at
2224 // connection creation, we can just write a plain timestamp 2284 // connection creation, we can just write a plain timestamp
2225 // here 2285 // here
2226 if (cal == null) { 2286 if (cal == null) {
2227 setValue(index, "time '" + x.toString() + "'"); 2287 setValue(parameterIndex, "time '" + x.toString() + "'");
2228 } else { 2288 } else {
2229 mTime.setTimeZone(cal.getTimeZone()); 2289 mTime.setTimeZone(cal.getTimeZone());
2230 setValue(index, "time '" + mTime.format(x) + "'"); 2290 setValue(parameterIndex, "time '" + mTime.format(x) + "'");
2231 } 2291 }
2232 } 2292 }
2233 } 2293 }
2234 2294
2235 /** 2295 /**
2236 * Sets the designated parameter to the given java.sql.Timestamp 2296 * Sets the designated parameter to the given java.sql.Timestamp
2237 * value. The driver converts this to an SQL TIMESTAMP value when 2297 * value. The driver converts this to an SQL TIMESTAMP value when
2238 * it sends it to the database. 2298 * it sends it to the database.
2239 * 2299 *
2240 * @param index the first parameter is 1, the second is 2, ... 2300 * @param parameterIndex the first parameter is 1, the second is 2, ...
2241 * @param x the parameter value 2301 * @param x the parameter value
2242 * @throws SQLException if a database access error occurs 2302 * @throws SQLException if a database access error occurs
2243 */ 2303 */
2244 @Override 2304 @Override
2245 public void setTimestamp(int index, Timestamp x) throws SQLException { 2305 public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException {
2246 setTimestamp(index, x, null); 2306 setTimestamp(parameterIndex, x, null);
2247 } 2307 }
2248 2308
2249 /** 2309 /**
2250 * Sets the designated parameter to the given java.sql.Timestamp 2310 * Sets the designated parameter to the given java.sql.Timestamp
2251 * value, using the given Calendar object. The driver uses the 2311 * value, using the given Calendar object. The driver uses the
2254 * driver can calculate the timestamp taking into account a custom 2314 * driver can calculate the timestamp taking into account a custom
2255 * timezone. If no Calendar object is specified, the driver uses the 2315 * timezone. If no Calendar object is specified, the driver uses the
2256 * default timezone, which is that of the virtual machine running 2316 * default timezone, which is that of the virtual machine running
2257 * the application. 2317 * the application.
2258 * 2318 *
2259 * @param index the first parameter is 1, the second is 2, ... 2319 * @param parameterIndex the first parameter is 1, the second is 2, ...
2260 * @param x the parameter value 2320 * @param x the parameter value
2261 * @param cal the Calendar object the driver will use to construct the 2321 * @param cal the Calendar object the driver will use to construct the
2262 * timestamp 2322 * timestamp
2263 * @throws SQLException if a database access error occurs 2323 * @throws SQLException if a database access error occurs
2264 */ 2324 */
2265 @Override 2325 @Override
2266 public void setTimestamp(int index, Timestamp x, Calendar cal) throws SQLException { 2326 public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException {
2267 if (x == null) { 2327 if (x == null) {
2268 setNull(index, -1); 2328 setNull(parameterIndex, -1);
2269 return; 2329 return;
2270 } 2330 }
2271 2331
2272 boolean hasTimeZone = monetdbType[getParamIdx(index)].endsWith("tz"); 2332 String MonetDBType = monetdbType[getParamIdx(parameterIndex)];
2333 boolean hasTimeZone = ("timestamptz".equals(MonetDBType) || "timetz".equals(MonetDBType));
2273 if (hasTimeZone) { 2334 if (hasTimeZone) {
2274 // timezone shouldn't matter, since the server is timezone 2335 // timezone shouldn't matter, since the server is timezone
2275 // aware in this case 2336 // aware in this case
2276 String RFC822 = mTimestampZ.format(x); 2337 String RFC822 = mTimestampZ.format(x);
2277 setValue(index, "timestamptz '" + RFC822.substring(0, 26) + ":" + RFC822.substring(26) + "'"); 2338 setValue(parameterIndex, "timestamptz '" + RFC822.substring(0, 26) + ":" + RFC822.substring(26) + "'");
2278 } else { 2339 } else {
2279 // server is not timezone aware for this field, and no 2340 // server is not timezone aware for this field, and no
2280 // calendar given, since we told the server our timezone at 2341 // calendar given, since we told the server our timezone at
2281 // connection creation, we can just write a plain timestamp 2342 // connection creation, we can just write a plain timestamp here
2282 // here
2283 if (cal == null) { 2343 if (cal == null) {
2284 setValue(index, "timestamp '" + x.toString() + "'"); 2344 setValue(parameterIndex, "timestamp '" + x.toString() + "'");
2285 } else { 2345 } else {
2286 mTimestamp.setTimeZone(cal.getTimeZone()); 2346 mTimestamp.setTimeZone(cal.getTimeZone());
2287 setValue(index, "timestamp '" + mTimestamp.format(x) + "'"); 2347 setValue(parameterIndex, "timestamp '" + mTimestamp.format(x) + "'");
2288 } 2348 }
2289 } 2349 }
2290 } 2350 }
2291 2351
2292 /** 2352 /**
2324 * @param x the java.net.URL object to be set 2384 * @param x the java.net.URL object to be set
2325 * @throws SQLException if a database access error occurs 2385 * @throws SQLException if a database access error occurs
2326 */ 2386 */
2327 @Override 2387 @Override
2328 public void setURL(int parameterIndex, URL x) throws SQLException { 2388 public void setURL(int parameterIndex, URL x) throws SQLException {
2329 setString(parameterIndex, x.toString()); 2389 if (x == null) {
2330 values[getParamIdx(parameterIndex)] = "url " + values[getParamIdx(parameterIndex)]; 2390 setNull(parameterIndex, -1);
2391 return;
2392 }
2393
2394 String val = x.toString();
2395 setValue(parameterIndex, "url '" + val.replaceAll("\\\\", "\\\\\\\\")
2396 .replaceAll("'", "\\\\'") + "'");
2331 } 2397 }
2332 2398
2333 /** 2399 /**
2334 * Releases this PreparedStatement object's database and JDBC 2400 * Releases this PreparedStatement object's database and JDBC
2335 * resources immediately instead of waiting for this to happen when 2401 * resources immediately instead of waiting for this to happen when
2367 2433
2368 /** 2434 /**
2369 * Sets the given index with the supplied value. If the given index is out of bounds, and SQLException is thrown. 2435 * Sets the given index with the supplied value. If the given index is out of bounds, and SQLException is thrown.
2370 * The given value should never be null. 2436 * The given value should never be null.
2371 * 2437 *
2372 * @param index the parameter index 2438 * @param parameterIndex the parameter index
2373 * @param val the exact String representation to set 2439 * @param val the exact String representation to set
2374 * @throws SQLException if the given index is out of bounds 2440 * @throws SQLException if the given index is out of bounds
2375 */ 2441 */
2376 private void setValue(int index, String val) throws SQLException { 2442 private void setValue(int parameterIndex, String val) throws SQLException {
2377 values[getParamIdx(index)] = val; 2443 values[getParamIdx(parameterIndex)] = (val == null ? "NULL" : val);
2378 } 2444 }
2379 2445
2380 /** 2446 /**
2381 * Transforms the prepare query into a simple SQL query by replacing 2447 * Transforms the prepare query into a simple SQL query by replacing
2382 * the ?'s with the given column contents. 2448 * the ?'s with the given column contents.
2386 * @return the simple SQL string for the prepare query 2452 * @return the simple SQL string for the prepare query
2387 * @throws SQLException if not all columns are set 2453 * @throws SQLException if not all columns are set
2388 */ 2454 */
2389 private String transform() throws SQLException { 2455 private String transform() throws SQLException {
2390 StringBuilder buf = new StringBuilder(8 + 12 * size); 2456 StringBuilder buf = new StringBuilder(8 + 12 * size);
2391 buf.append("exec "); 2457 buf.append("exec ").append(id).append('(');
2392 buf.append(id);
2393 buf.append('(');
2394 // check if all columns are set and do a replace 2458 // check if all columns are set and do a replace
2395 int col = 0; 2459 int col = 0;
2396 for (int i = 0; i < size; i++) { 2460 for (int i = 0; i < size; i++) {
2397 if (column[i] != null) 2461 if (column[i] != null)
2398 continue; 2462 continue;
2399 col++; 2463 col++;
2400 if (col > 1) 2464 if (col > 1)
2401 buf.append(','); 2465 buf.append(',');
2402 if (values[i] == null) throw 2466 if (values[i] == null)
2403 new SQLException("Cannot execute, parameter " + col + " is missing.", "M1M05"); 2467 throw new SQLException("Cannot execute, parameter " + col + " is missing.", "M1M05");
2404 2468
2405 buf.append(values[i]); 2469 buf.append(values[i]);
2406 } 2470 }
2407 buf.append(')'); 2471 buf.append(')');
2408 2472
2409 return buf.toString(); 2473 return buf.toString();
2410 } 2474 }
2411 2475
2412 /** 2476 /**
2413 * Small helper method that formats the "Invalid Parameter Index number ..." message 2477 * Small helper method that formats the "Invalid Parameter Index number ..." message
2414 * and creates a new SQLException object whose SQLState is set to "M1M05". 2478 * and creates a new SQLDataException object whose SQLState is set
2479 * to "22010": invalid indicator parameter value.
2415 * 2480 *
2416 * @param paramIdx the parameter index number 2481 * @param paramIdx the parameter index number
2417 * @return a new created SQLException object with SQLState M1M05 2482 * @return a new created SQLDataException object with SQLState 22010
2418 */ 2483 */
2419 private static SQLException newSQLInvalidParameterIndexException(int paramIdx) { 2484 private static SQLDataException newSQLInvalidParameterIndexException(int paramIdx) {
2420 return new SQLException("Invalid Parameter Index number: " + paramIdx, "M1M05"); 2485 return new SQLDataException("Invalid Parameter Index number: " + paramIdx, "22010");
2421 } 2486 }
2422 2487
2423 /** 2488 /**
2424 * Small helper method that formats the "Method ... not implemented" message 2489 * Small helper method that formats the "Method ... not implemented" message
2425 * and creates a new SQLFeatureNotSupportedException object 2490 * and creates a new SQLFeatureNotSupportedException object
2426 * whose SQLState is set to "0A000". 2491 * whose SQLState is set to "0A000": feature not supported.
2427 * 2492 *
2428 * @param name the method name 2493 * @param name the method name
2429 * @return a new created SQLFeatureNotSupportedException object with SQLState 0A000 2494 * @return a new created SQLFeatureNotSupportedException object with SQLState 0A000
2430 */ 2495 */
2431 private static SQLFeatureNotSupportedException newSQLFeatureNotSupportedException(String name) { 2496 private static SQLFeatureNotSupportedException newSQLFeatureNotSupportedException(String name) {