Mercurial > hg > monetdb-java
comparison src/main/java/org/monetdb/jdbc/MonetConnection.java @ 665:8f7d51c478df
Improved implementation of methods ResultSetMetaData.getPrecision() and ResultSetMetaData.getScale().
They now use the Mapi header information as returned by the server when "sizeheader 1" is enabled.
This "typesizes" header returns more accurate precision and scale values for columns of types DECIMAL, NUMERIC, CHAR, VARCHAR, CLOB, JSON, URL and BLOB.
Also we no longer have to generate and execute a meta data query to retrieve this information, so it also is faster now.
author | Martin van Dinther <martin.van.dinther@monetdbsolutions.com> |
---|---|
date | Thu, 20 Oct 2022 18:09:07 +0200 (2022-10-20) |
parents | 7ec8d469fd0b |
children | 2448ce017593 |
comparison
equal
deleted
inserted
replaced
664:a6592430c8fc | 665:8f7d51c478df |
---|---|
419 if (offsetMinutes < 10) | 419 if (offsetMinutes < 10) |
420 tz.append('0'); | 420 tz.append('0'); |
421 tz.append(offsetMinutes).append("' HOUR TO MINUTE"); | 421 tz.append(offsetMinutes).append("' HOUR TO MINUTE"); |
422 sendIndependentCommand(tz.toString()); | 422 sendIndependentCommand(tz.toString()); |
423 } | 423 } |
424 | |
425 // set sizeheader to 1 to enable sending "typesizes" info by the server (see mapi_set_size_header() in mapi.c) | |
426 sendControlCommand("sizeheader 1"); | |
424 } | 427 } |
425 | 428 |
426 // we're absolutely not closed, since we're brand new | 429 // we're absolutely not closed, since we're brand new |
427 closed = false; | 430 closed = false; |
428 } | 431 } |
2197 * The ResultSetResponse represents a tabular result sent by the | 2200 * The ResultSetResponse represents a tabular result sent by the |
2198 * server. This is typically an SQL table. The MAPI headers of the | 2201 * server. This is typically an SQL table. The MAPI headers of the |
2199 * Response look like: | 2202 * Response look like: |
2200 * <pre> | 2203 * <pre> |
2201 * &1 1 28 2 10 | 2204 * &1 1 28 2 10 |
2202 * # name, value # name | 2205 * % sys.props, sys.props # table_name |
2203 * # varchar, varchar # type | 2206 * % name, value # name |
2207 * % varchar, int # type | |
2208 * % 15, 3 # length | |
2209 * % 60 0, 32 0 # typesizes | |
2204 * </pre> | 2210 * </pre> |
2205 * there the first line consists out of<br /> | 2211 * there the first line consists out of<br /> |
2206 * <tt>&"qt" "id" "tc" "cc" "rc"</tt>. | 2212 * <tt>&"qt" "id" "tc" "cc" "rc"</tt>. |
2207 */ | 2213 */ |
2208 // {{{ ResultSetResponse class implementation | 2214 // {{{ ResultSetResponse class implementation |
2213 public final long tuplecount; | 2219 public final long tuplecount; |
2214 /** The numbers of rows to retrieve per DataBlockResponse */ | 2220 /** The numbers of rows to retrieve per DataBlockResponse */ |
2215 private int cacheSize; | 2221 private int cacheSize; |
2216 /** The table ID of this result */ | 2222 /** The table ID of this result */ |
2217 public final int id; | 2223 public final int id; |
2224 | |
2225 /** arrays for the resultset columns metadata */ | |
2218 /** The names of the columns in this result */ | 2226 /** The names of the columns in this result */ |
2219 private String[] name; | 2227 private String[] name; |
2220 /** The types of the columns in this result */ | 2228 /** The types of the columns in this result */ |
2221 private String[] type; | 2229 private String[] type; |
2222 /** The max string length for each column in this result */ | 2230 /** The max string length for each column in this result */ |
2223 private int[] columnLengths; | 2231 private int[] columnLengths; |
2232 /** The precision for each column in this result */ | |
2233 private int[] colPrecisions; | |
2234 /** The scale for each column in this result */ | |
2235 private int[] colScales; | |
2224 /** The table for each column in this result */ | 2236 /** The table for each column in this result */ |
2225 private String[] tableNames; | 2237 private String[] tableNames; |
2226 /** The schema for each column in this result */ | 2238 /** The schema for each column in this result */ |
2227 private String[] schemaNames; | 2239 private String[] schemaNames; |
2240 | |
2228 /** The query sequence number */ | 2241 /** The query sequence number */ |
2229 private final int seqnr; | 2242 private final int seqnr; |
2230 /** A List of result blocks (chunks of size fetchSize/cacheSize) */ | 2243 /** A List of result blocks (chunks of size fetchSize/cacheSize) */ |
2231 private DataBlockResponse[] resultBlocks; | 2244 private DataBlockResponse[] resultBlocks; |
2232 | 2245 |
2250 private final boolean[] isSet; | 2263 private final boolean[] isSet; |
2251 private static final int NAMES = 0; | 2264 private static final int NAMES = 0; |
2252 private static final int TYPES = 1; | 2265 private static final int TYPES = 1; |
2253 private static final int TABLES = 2; | 2266 private static final int TABLES = 2; |
2254 private static final int LENS = 3; | 2267 private static final int LENS = 3; |
2255 | 2268 private static final int TYPESIZES = 4; |
2256 | 2269 |
2257 /** | 2270 /** |
2258 * Sole constructor, which requires a MonetConnection parent to | 2271 * Sole constructor, which requires a MonetConnection parent to |
2259 * be given. | 2272 * be given. |
2260 * | 2273 * |
2273 final int rowcount, | 2286 final int rowcount, |
2274 final MonetConnection.ResponseList parent, | 2287 final MonetConnection.ResponseList parent, |
2275 final int seq) | 2288 final int seq) |
2276 throws SQLException | 2289 throws SQLException |
2277 { | 2290 { |
2278 isSet = new boolean[4]; | 2291 isSet = new boolean[5]; |
2279 this.parent = parent; | 2292 this.parent = parent; |
2280 if (parent.cachesize == 0) { | 2293 if (parent.cachesize == 0) { |
2281 /* Below we have to calculate how many "chunks" we need | 2294 /* Below we have to calculate how many "chunks" we need |
2282 * to allocate to store the entire result. However, if | 2295 * to allocate to store the entire result. However, if |
2283 * the user didn't set a cache size, as in this case, we | 2296 * the user didn't set a cache size, as in this case, we |
2319 * | 2332 * |
2320 * @param tmpLine the string that contains the header | 2333 * @param tmpLine the string that contains the header |
2321 * @return a non-null String if the header cannot be parsed or | 2334 * @return a non-null String if the header cannot be parsed or |
2322 * is unknown | 2335 * is unknown |
2323 */ | 2336 */ |
2324 // {{{ addLine | |
2325 @Override | 2337 @Override |
2326 public String addLine(final String tmpLine, final LineType linetype) { | 2338 public String addLine(final String tmpLine, final LineType linetype) { |
2327 if (isSet[LENS] && isSet[TYPES] && isSet[TABLES] && isSet[NAMES]) { | 2339 // System.out.println("In ResultSetResponse.addLine(line, type: " + linetype + ") line: " + tmpLine); |
2340 if (linetype == LineType.RESULT || | |
2341 (isSet[LENS] && isSet[TYPES] && isSet[TABLES] && isSet[NAMES] && isSet[TYPESIZES])) { | |
2342 if (!isSet[TYPESIZES]) | |
2343 // this is needed to get proper output when processing a: DEBUG SQL-statement | |
2344 isSet[TYPESIZES] = true; | |
2328 return resultBlocks[0].addLine(tmpLine, linetype); | 2345 return resultBlocks[0].addLine(tmpLine, linetype); |
2329 } | 2346 } |
2330 | 2347 |
2331 if (linetype != LineType.HEADER) | 2348 if (linetype != LineType.HEADER) { |
2332 return "header expected, got: " + tmpLine; | 2349 if (!isSet[TYPESIZES]) |
2350 isSet[TYPESIZES] = true; | |
2351 return "Header expected, got: " + tmpLine; | |
2352 } | |
2333 | 2353 |
2334 // depending on the name of the header, we continue | 2354 // depending on the name of the header, we continue |
2335 try { | 2355 try { |
2336 switch (hlp.parse(tmpLine)) { | 2356 switch (hlp.parse(tmpLine)) { |
2337 case HeaderLineParser.NAME: | 2357 case HeaderLineParser.NAME: |
2338 name = hlp.values.clone(); | 2358 name = hlp.values.clone(); |
2339 isSet[NAMES] = true; | 2359 isSet[NAMES] = true; |
2340 break; | 2360 break; |
2341 case HeaderLineParser.LENGTH: | 2361 case HeaderLineParser.LENGTH: |
2342 columnLengths = hlp.intValues.clone(); | 2362 columnLengths = hlp.intValues.clone(); |
2343 isSet[LENS] = true; | 2363 isSet[LENS] = true; |
2344 break; | 2364 break; |
2345 case HeaderLineParser.TYPE: | 2365 case HeaderLineParser.TYPE: |
2346 type = hlp.values.clone(); | 2366 type = hlp.values.clone(); |
2347 isSet[TYPES] = true; | 2367 isSet[TYPES] = true; |
2348 break; | 2368 break; |
2349 case HeaderLineParser.TABLE: | 2369 case HeaderLineParser.TABLE: |
2350 { | 2370 { |
2351 tableNames = hlp.values.clone(); | 2371 tableNames = hlp.values.clone(); |
2352 final int array_size = tableNames.length; | 2372 final int array_size = tableNames.length; |
2353 schemaNames = new String[array_size]; | 2373 schemaNames = new String[array_size]; |
2366 schemaNames[i] = ""; | 2386 schemaNames[i] = ""; |
2367 tableNames[i] = ""; | 2387 tableNames[i] = ""; |
2368 } | 2388 } |
2369 } | 2389 } |
2370 isSet[TABLES] = true; | 2390 isSet[TABLES] = true; |
2391 break; | |
2371 } | 2392 } |
2372 break; | 2393 case HeaderLineParser.TYPESIZES: |
2394 { | |
2395 // System.out.println("In ResultSetResponse.addLine() case HeaderLineParser.TYPESIZES: values: " + hlp.values[0]); | |
2396 final int array_size = hlp.values.length; | |
2397 colPrecisions = new int[array_size]; | |
2398 colScales = new int[array_size]; | |
2399 // extract the precision and scale integer numbers from the string | |
2400 for (int i = 0; i < array_size; i++) { | |
2401 String ps = hlp.values[i]; | |
2402 if (ps != null) { | |
2403 try { | |
2404 int separator = ps.indexOf(' '); | |
2405 if (separator > 0) { | |
2406 colPrecisions[i] = Integer.parseInt(ps.substring(0, separator)); | |
2407 colScales[i] = Integer.parseInt(ps.substring(separator +1)); | |
2408 } else { | |
2409 colPrecisions[i] = Integer.parseInt(ps); | |
2410 colScales[i] = 0; | |
2411 } | |
2412 } catch (NumberFormatException nfe) { | |
2413 return nfe.getMessage(); | |
2414 } | |
2415 } else { | |
2416 colPrecisions[i] = 1; | |
2417 colScales[i] = 0; | |
2418 } | |
2419 } | |
2420 isSet[TYPESIZES] = true; | |
2421 break; | |
2422 } | |
2373 } | 2423 } |
2374 } catch (MCLParseException e) { | 2424 } catch (MCLParseException e) { |
2375 return e.getMessage(); | 2425 return e.getMessage(); |
2376 } | 2426 } |
2377 | 2427 |
2378 // all is well | 2428 // all is well |
2379 return null; | 2429 return null; |
2380 } | 2430 } |
2381 // }}} | |
2382 | 2431 |
2383 /** | 2432 /** |
2384 * Returns whether this ResultSetResponse needs more lines. | 2433 * Returns whether this ResultSetResponse needs more lines. |
2385 * This method returns true if not all headers are set, or the | 2434 * This method returns true if not all headers are set, or the |
2386 * first DataBlockResponse reports to want more. | 2435 * first DataBlockResponse reports to want more. |
2387 */ | 2436 */ |
2388 @Override | 2437 @Override |
2389 public boolean wantsMore() { | 2438 public boolean wantsMore() { |
2390 if (isSet[LENS] && isSet[TYPES] && isSet[TABLES] && isSet[NAMES]) { | 2439 if (isSet[LENS] && isSet[TYPES] && isSet[TABLES] && isSet[NAMES] && isSet[TYPESIZES]) { |
2391 return resultBlocks[0].wantsMore(); | 2440 return resultBlocks[0].wantsMore(); |
2392 } else { | 2441 } |
2393 return true; | 2442 return true; |
2394 } | |
2395 } | 2443 } |
2396 | 2444 |
2397 /** | 2445 /** |
2398 * Returns an array of Strings containing the values between | 2446 * Returns an array of Strings containing the values between |
2399 * ',\t' separators. | 2447 * ',\t' separators. |
2445 final StringBuilder err = new StringBuilder(99); | 2493 final StringBuilder err = new StringBuilder(99); |
2446 if (!isSet[NAMES]) err.append("name header missing\n"); | 2494 if (!isSet[NAMES]) err.append("name header missing\n"); |
2447 if (!isSet[TYPES]) err.append("type header missing\n"); | 2495 if (!isSet[TYPES]) err.append("type header missing\n"); |
2448 if (!isSet[TABLES]) err.append("table name header missing\n"); | 2496 if (!isSet[TABLES]) err.append("table name header missing\n"); |
2449 if (!isSet[LENS]) err.append("column width header missing\n"); | 2497 if (!isSet[LENS]) err.append("column width header missing\n"); |
2498 if (!isSet[TYPESIZES]) err.append("column precision and scale header missing\n"); | |
2450 if (err.length() > 0) | 2499 if (err.length() > 0) |
2451 throw new SQLException(err.toString(), "M0M10"); | 2500 throw new SQLException(err.toString(), "M0M10"); |
2452 } | 2501 } |
2453 */ | 2502 */ |
2454 | 2503 |
2469 String[] getTypes() { | 2518 String[] getTypes() { |
2470 return type; | 2519 return type; |
2471 } | 2520 } |
2472 | 2521 |
2473 /** | 2522 /** |
2474 * Returns the tables of the columns | 2523 * Returns the table names of the columns |
2475 * | 2524 * |
2476 * @return the tables of the columns | 2525 * @return the table names of the columns |
2477 */ | 2526 */ |
2478 String[] getTableNames() { | 2527 String[] getTableNames() { |
2479 return tableNames; | 2528 return tableNames; |
2480 } | 2529 } |
2481 | 2530 |
2482 /** | 2531 /** |
2483 * Returns the schemas of the columns | 2532 * Returns the schema names of the columns |
2484 * | 2533 * |
2485 * @return the schemas of the columns | 2534 * @return the schema names of the columns |
2486 */ | 2535 */ |
2487 String[] getSchemaNames() { | 2536 String[] getSchemaNames() { |
2488 return schemaNames; | 2537 return schemaNames; |
2489 } | 2538 } |
2490 | 2539 |
2491 /** | 2540 /** |
2492 * Returns the lengths of the columns | 2541 * Returns the display lengths of the columns |
2493 * | 2542 * |
2494 * @return the lengths of the columns | 2543 * @return the display lengths of the columns |
2495 */ | 2544 */ |
2496 int[] getColumnLengths() { | 2545 int[] getColumnLengths() { |
2497 return columnLengths; | 2546 return columnLengths; |
2547 } | |
2548 | |
2549 /** | |
2550 * Returns the precisions of the columns | |
2551 * | |
2552 * @return the precisions of the columns, it can return null | |
2553 */ | |
2554 int[] getColumnPrecisions() { | |
2555 return colPrecisions; | |
2556 } | |
2557 | |
2558 /** | |
2559 * Returns the scales of the columns (0 when scale is not applicable) | |
2560 * | |
2561 * @return the scales of the columns, it can return null | |
2562 */ | |
2563 int[] getColumnScales() { | |
2564 return colScales; | |
2498 } | 2565 } |
2499 | 2566 |
2500 /** | 2567 /** |
2501 * Returns the cache size used within this Response | 2568 * Returns the cache size used within this Response |
2502 * | 2569 * |
2635 r.close(); | 2702 r.close(); |
2636 } | 2703 } |
2637 name = null; | 2704 name = null; |
2638 type = null; | 2705 type = null; |
2639 columnLengths = null; | 2706 columnLengths = null; |
2707 colPrecisions = null; | |
2708 colScales = null; | |
2640 tableNames = null; | 2709 tableNames = null; |
2641 schemaNames = null; | 2710 schemaNames = null; |
2642 resultBlocks = null; | 2711 resultBlocks = null; |
2643 closed = true; | 2712 closed = true; |
2644 } | 2713 } |
3210 } else { | 3279 } else { |
3211 error = tmpLine.substring(1); | 3280 error = tmpLine.substring(1); |
3212 } | 3281 } |
3213 break; | 3282 break; |
3214 } // end of switch (linetype) | 3283 } // end of switch (linetype) |
3215 } // end of while (linetype != BufferedMCLReader.PROMPT) | 3284 } // end of while (linetype != LineType.PROMPT) |
3216 } // end of synchronized (server) | 3285 } // end of synchronized (server) |
3217 | 3286 |
3218 if (error != null) { | 3287 if (error != null) { |
3219 SQLException ret = null; | 3288 SQLException ret = null; |
3220 final String[] errors = error.split("\n"); | 3289 final String[] errors = error.split("\n"); |