1 5 package org.h2.server; 6 7 import java.io.BufferedInputStream ; 8 import java.io.BufferedOutputStream ; 9 import java.io.DataInputStream ; 10 import java.io.DataOutputStream ; 11 import java.io.IOException ; 12 import java.io.InputStream ; 13 import java.io.OutputStream ; 14 import java.net.Socket ; 15 import java.sql.Connection ; 16 import java.sql.DatabaseMetaData ; 17 import java.sql.DriverManager ; 18 import java.sql.PreparedStatement ; 19 import java.sql.ResultSet ; 20 import java.sql.ResultSetMetaData ; 21 import java.sql.SQLException ; 22 import java.sql.Statement ; 23 import java.sql.Types ; 24 import java.util.HashMap ; 25 26 import org.h2.engine.ConnectionInfo; 27 import org.h2.message.Message; 28 import org.h2.util.JdbcUtils; 29 import org.h2.util.StringUtils; 30 import org.h2.value.DataType; 31 32 35 36 public class OdbcServerThread implements Runnable { 37 private OdbcServer server; 38 private Socket socket; 39 private Connection conn; 40 private DatabaseMetaData meta; 41 private boolean stop; 42 private int cacheId; 43 private Object cache; 44 private OdbcTransfer transfer; 45 private Thread thread; 46 private BufferedOutputStream outBuff; 47 private HashMap object = new HashMap (); 48 private int nextId; 49 50 OdbcServerThread(Socket socket, OdbcServer server) { 51 this.server = server; 52 this.socket = socket; 53 } 54 55 private int addObject(Object o) { 56 int id = nextId++; 57 server.log("addObj "+id+" "+o); 58 object.put(new Integer (id), o); 59 cacheId = id; 60 cache = o; 61 return id; 62 } 63 64 private void freeObject(int id) { 65 if (cacheId == id) { 66 cacheId = -1; 67 cache = null; 68 } 69 object.remove(new Integer (id)); 70 } 71 72 private Object getObject(int id) { 73 if (id == cacheId) { 74 server.log("getObj "+id+" "+cache); 75 return cache; 76 } 77 server.log("getObj "+id+" "+object.get(new Integer (id))); 78 return object.get(new Integer (id)); 79 } 80 81 public void run() { 82 try { 83 server.log("Connect"); 84 InputStream ins = socket.getInputStream(); 85 OutputStream outs = socket.getOutputStream(); 86 DataInputStream in; 87 in = new DataInputStream (new BufferedInputStream (ins, 88 OdbcTransfer.BUFFER_SIZE)); 89 outBuff = new BufferedOutputStream (outs, OdbcTransfer.BUFFER_SIZE); 90 DataOutputStream out = new DataOutputStream (outBuff); 91 transfer = new OdbcTransfer(in, out); 92 outBuff.flush(); 93 while (!stop) { 94 process(); 95 outBuff.flush(); 96 } 97 server.log("Disconnect"); 98 } catch (Exception e) { 99 server.logError(e); 100 } 101 } 102 103 public void close() { 104 try { 105 stop = true; 106 conn.close(); 107 socket.close(); 108 server.log("Close"); 109 } catch(Exception e) { 110 server.logError(e); 111 } 112 conn = null; 113 socket = null; 114 server.remove(this); 115 } 116 117 private void sendError(Throwable e) throws IOException { 118 SQLException s = Message.convert(e); 119 server.log("Exception "+s); 120 s.printStackTrace(); 121 transfer.writeByte((byte)'E'); 122 } 123 124 private void processResultSet(ResultSet rs) throws IOException , SQLException { 125 int id = addObject(rs); 126 transfer.writeInt(id); 127 ResultSetMetaData m = rs.getMetaData(); 128 int columnCount = m.getColumnCount(); 129 transfer.writeInt(columnCount); 130 for(int i=0; i<columnCount; i++) { 131 transfer.writeInt(mapType(m.getColumnType(i+1))); 132 transfer.writeString(m.getTableName(i+1)); 133 transfer.writeString(m.getColumnLabel(i+1)); 134 transfer.writeInt(m.getPrecision(i+1)); 135 transfer.writeInt(m.getScale(i+1)); 136 transfer.writeInt(m.getColumnDisplaySize(i+1)); 137 } 138 } 139 140 private void setParameter(PreparedStatement prep, int index, int type) throws SQLException , IOException { 141 switch(type) { 142 case Types.NULL: { 143 prep.setNull(index, Types.INTEGER); 145 break; 146 } 147 case Types.INTEGER: { 148 int value = transfer.readInt(); 149 server.log(" index="+index+" int="+value); 150 prep.setInt(index, value); 151 break; 152 } 153 case Types.VARCHAR: { 154 String value = transfer.readString(); 155 server.log(" index="+index+" string="+value); 156 prep.setString(index, value); 157 break; 158 } 159 default: 160 throw Message.getInternalError("unexpected data type "+type); 161 } 162 } 163 164 private void setParameters(PreparedStatement prep) throws SQLException , IOException { 165 while(true) { 166 int x = transfer.readByte(); 167 if(x == '0') { 168 break; 169 } else if(x=='1') { 170 int index = transfer.readInt(); 171 int type = transfer.readInt(); 172 setParameter(prep, index+1, type); 173 } else { 174 throw Message.getInternalError("unexpected "+x); 175 } 176 } 177 } 178 179 private void processMeta() throws IOException { 180 int operation = transfer.readByte(); 181 server.log("meta op="+(char)operation); 182 switch(operation) { 183 case 'B': { 184 String catalog = transfer.readString(); 185 String schema = transfer.readString(); 186 String table = transfer.readString(); 187 if(table ==null || table.length()==0) { 188 table = "%"; 189 } 190 int scope = transfer.readInt(); 191 boolean nullable = transfer.readBoolean(); 192 try { 193 ResultSet rs = meta.getBestRowIdentifier(catalog, schema, table, scope, nullable); 194 processResultSet(rs); 195 } catch(Throwable e) { 196 sendError(e); 197 } 198 break; 199 } 200 case 'C': { 201 String schemaPattern = transfer.readString(); 203 String tableNamePattern = transfer.readString(); 204 String columnNamePattern = transfer.readString(); 205 if(tableNamePattern ==null || tableNamePattern.length()==0) { 206 tableNamePattern = "%"; 207 } 208 if(columnNamePattern ==null || columnNamePattern.length()==0) { 209 columnNamePattern = "%"; 210 } 211 PreparedStatement prep = null; 212 try { 213 prep = conn.prepareStatement("SELECT " 214 + "TABLE_CATALOG TABLE_CAT, " 215 + "TABLE_SCHEMA TABLE_SCHEM, " 216 + "TABLE_NAME, " 217 + "COLUMN_NAME, " 218 + "DATA_TYPE, " 219 + "TYPE_NAME, " 220 + "CHARACTER_MAXIMUM_LENGTH COLUMN_SIZE, " 221 + "CHARACTER_MAXIMUM_LENGTH BUFFER_LENGTH, " 222 + "CAST(NUMERIC_SCALE AS SMALLINT) DECIMAL_DIGITS, " + "CAST(10 AS SMALLINT) NUM_PREC_RADIX, " + "CAST(NULLABLE AS SMALLINT) NULLABLE, " + "'' REMARKS, " 226 + "COLUMN_DEFAULT COLUMN_DEF, " 227 + "CAST(DATA_TYPE AS SMALLINT) SQL_DATA_TYPE, " + "CAST(0 AS SMALLINT) SQL_DATETIME_SUB, " + "CHARACTER_OCTET_LENGTH CHAR_OCTET_LENGTH, " 230 + "ORDINAL_POSITION ORDINAL_POSITION, " 231 + "NULLABLE IS_NULLABLE " 232 + "FROM INFORMATION_SCHEMA.COLUMNS " 233 + "WHERE TABLE_SCHEMA LIKE ? " 234 + "AND TABLE_NAME LIKE ? " 235 + "AND COLUMN_NAME LIKE ? " 236 + "ORDER BY TABLE_SCHEM, TABLE_NAME, ORDINAL_POSITION"); 237 prep.setString(1, schemaPattern == null ? "%" : schemaPattern); 238 prep.setString(2, tableNamePattern == null ? "%" : tableNamePattern); 239 prep.setString(3, columnNamePattern == null ? "%" : columnNamePattern); 240 ResultSet rs = prep.executeQuery(); 242 processResultSet(rs); 243 } catch(SQLException e) { 244 sendError(e); 245 } finally { 246 JdbcUtils.closeSilently(prep); 247 } 248 break; 249 } 250 case 'D': { 251 String where; 252 if(transfer.readByte()=='A') { 253 where= ""; 254 } else { 255 int type = transfer.readInt(); 256 where = " WHERE TYPE="+type+" "; 257 } 258 Statement stat = null; 259 try { 260 stat = conn.createStatement(); 261 ResultSet rs = stat.executeQuery("SELECT " 262 + "TYPE_NAME, " 263 + "DATA_TYPE, " 264 + "PRECISION COLUMN_SIZE, " 265 + "PREFIX LITERAL_PREFIX, " 266 + "PREFIX LITERAL_SUFFIX, " 267 + "PARAMS CREATE_PARAMS, " 268 + "CAST(" +DatabaseMetaData.typeNullable + " AS SMALLINT) NULLABLE, " 269 + "CAST(1 AS SMALLINT) CASE_SENSITIVE, " + "CAST(1 AS SMALLINT) SEARCHABLE, " + "CAST(0 AS SMALLINT) UNSIGNED_ATTRIBUTE, " + "CAST(0 AS SMALLINT) FIXED_PREC_SCALE, " + "CAST(0 AS SMALLINT) AUTO_UNIQUE_VALUE, " + "TYPE_NAME LOCAL_TYPE_NAME, " 275 + "MINIMUM_SCALE, " 276 + "MAXIMUM_SCALE, " 277 + "DATA_TYPE SQL_DATA_TYPE, " 278 + "CAST(1 AS SMALLINT) SQL_DATETIME_SUB, " + "RADIX NUM_PREC_RADIX, " 280 + "CAST(0 AS SMALLINT) INTERVAL_PRECISION " 281 + "FROM INFORMATION_SCHEMA.TYPE_INFO " 282 + where 283 + "ORDER BY DATA_TYPE, POS"); 284 processResultSet(rs); 285 } catch(SQLException e) { 286 sendError(e); 287 } finally { 288 JdbcUtils.closeSilently(stat); 289 } 290 break; 291 } 292 case 'I': { 293 String schemaPattern = transfer.readString(); 295 String tableNamePattern = transfer.readString(); 296 if(tableNamePattern==null || tableNamePattern.length()==0) { 297 tableNamePattern = "%"; 298 } 299 PreparedStatement prep = null; 302 try { 303 305 prep = conn.prepareStatement("SELECT " 306 + "TABLE_CATALOG TABLE_CAT, " 307 + "TABLE_SCHEMA TABLE_SCHEM, " 308 + "TABLE_NAME, " 309 + "CAST(NON_UNIQUE AS SMALLINT) NON_UNIQUE, " 310 + "TABLE_CATALOG INDEX_QUALIFIER, " 311 + "INDEX_NAME, " 312 + "CAST("+DatabaseMetaData.tableIndexOther + " AS SMALLINT) TYPE, " 313 + "ORDINAL_POSITION, " 314 + "COLUMN_NAME, " 315 + "'A' ASC_OR_DESC, " 316 + "CARDINALITY, " 317 + "0 PAGES, " 318 + "'' FILTER_CONDITION " 319 + "FROM INFORMATION_SCHEMA.INDEXES " 320 + "WHERE CATALOG_NAME LIKE ? " 321 + "AND TABLE_NAME LIKE ? " 322 + "ORDER BY NON_UNIQUE, TYPE, TABLE_SCHEM, INDEX_NAME, ORDINAL_POSITION"); 323 prep.setString(1, schemaPattern); 324 prep.setString(2, tableNamePattern); 325 ResultSet rs = prep.executeQuery(); 326 processResultSet(rs); 327 } catch(SQLException e) { 328 sendError(e); 329 } finally { 330 JdbcUtils.closeSilently(prep); 331 } 332 break; 333 } 334 case 'N': { 335 String sql = transfer.readString(); 336 try { 337 sql = conn.nativeSQL(sql); 338 } catch(SQLException e) { 339 sendError(e); 340 } 341 transfer.writeString(sql); 342 break; 343 } 344 case 'T': { 345 String catalog = transfer.readString(); 346 String schema = transfer.readString(); 347 String table = transfer.readString(); 348 String tableTypes = transfer.readString(); 349 server.log(" catalog="+catalog+" schema="+schema+" table="+table+" tableTypes="+tableTypes); 350 ResultSet rs; 351 String [] types = null; 352 PreparedStatement prep = null; 353 try { 354 if(catalog.equals("%") && schema.length()==0 && table.length()==0) { 355 server.log(" allCatalogs"); 356 prep = conn.prepareStatement("SELECT " 357 + "CATALOG_NAME TABLE_CAT, " 358 + "NULL TABLE_SCHEM, " 359 + "NULL TABLE_NAME, " 360 + "NULL TABLE_TYPE, " 361 + "'' REMARKS " 362 + "FROM INFORMATION_SCHEMA.CATALOGS"); 363 rs = prep.executeQuery(); 364 } else if(catalog.length()==0 && schema.equals("%") && table.length()==0) { 365 server.log(" allSchemas"); 366 prep = conn.prepareStatement("SELECT " 367 + "CATALOG_NAME TABLE_CAT, " 368 + "SCHEMA_NAME TABLE_SCHEM, " 369 + "NULL TABLE_NAME, " 370 + "NULL TABLE_TYPE, " 371 + "'' REMARKS " 372 + "FROM INFORMATION_SCHEMA.SCHEMATA"); 373 rs = prep.executeQuery(); 374 } else if(catalog.length()==0 && schema.length()==0 && table.length()==0 && tableTypes.equals("%")) { 375 server.log(" allTableTypes"); 376 prep = conn.prepareStatement("SELECT " 377 + "NULL TABLE_CAT, " 378 + "NULL TABLE_SCHEM, " 379 + "NULL TABLE_NAME, " 380 + "TYPE TABLE_TYPE, " 381 + "'' REMARKS " 382 + "FROM INFORMATION_SCHEMA.TABLE_TYPES"); 383 rs = prep.executeQuery(); 384 } else { 385 server.log(" getTables"); 386 if(tableTypes.equals("%") || tableTypes.length()==0) { 387 types = null; 388 } else { 389 types = StringUtils.arraySplit(tableTypes, ',', false); 390 for(int i=0; i<types.length; i++) { 391 String t = StringUtils.toUpperEnglish(types[i]); 392 if(t.startsWith("\'")) { 393 t = t.substring(1, t.length()-2); 394 } 395 types[i] = t; 396 } 397 } 398 server.log("getTables "+catalog+" "+schema+" "+table); 399 if(table.length() == 0) { 400 table = null; 401 } 402 rs = meta.getTables(catalog, schema, table, types); 403 } 404 processResultSet(rs); 405 } catch(SQLException e) { 406 sendError(e); 407 } finally { 408 JdbcUtils.closeSilently(prep); 409 } 410 break; 411 } 412 case 'V': { 413 String catalog = transfer.readString(); 414 String schema = transfer.readString(); 415 String table = transfer.readString(); 416 if(table ==null || table.length()==0) { 417 table = "%"; 418 } 419 try { 420 ResultSet rs = meta.getVersionColumns(catalog, schema, table); 421 433 processResultSet(rs); 435 } catch(SQLException e) { 436 sendError(e); 437 } 438 break; 439 } 440 default: 441 server.log("meta operation? " + (char)operation); 442 } 443 } 444 445 private void process() throws IOException { 446 int operation = transfer.readByte(); 447 if(operation == -1) { 448 stop = true; 449 return; 450 } 451 server.log("op="+(char)operation); 452 switch(operation) { 453 case 'A': { 454 try { 455 int op = transfer.readByte(); 456 switch(op) { 457 case '0': 458 server.log("autoCommit false"); 459 conn.setAutoCommit(false); 460 break; 461 case '1': 462 server.log("autoCommit true"); 463 conn.setAutoCommit(true); 464 break; 465 case 'C': 466 server.log("commit"); 467 conn.commit(); 468 break; 469 case 'R': 470 server.log("rollback"); 471 conn.rollback(); 472 break; 473 default: 474 server.log("operation? " + (char)operation); 475 } 476 } catch(SQLException e) { 477 sendError(e); 478 } 479 break; 480 } 481 case 'C': 482 server.log("connect"); 483 String db = transfer.readString(); 484 server.log(" db="+db); 485 String user = transfer.readString(); 486 server.log(" user="+user); 487 String password = transfer.readString(); 488 server.log(" password="+password); 489 String baseDir = server.getBaseDir(); 490 ConnectionInfo ci = new ConnectionInfo(db); 491 if(baseDir != null) { 492 ci.setBaseDir(baseDir); 493 } 494 if(server.getIfExists()) { 495 ci.setProperty("IFEXISTS", "TRUE"); 496 } 497 String dbName = ci.getDatabaseName(); 498 try { 499 conn = DriverManager.getConnection("jdbc:h2:" + dbName, user, password); 500 meta = conn.getMetaData(); 501 transfer.writeByte((byte)'O'); 502 } catch(SQLException e) { 503 sendError(e); 504 } 505 break; 506 case 'E': { 507 String sql = transfer.readString(); 508 server.log("<"+sql+">"); 509 try { 510 int params = getParametersCount(sql); 511 if(params > 0) { 512 PreparedStatement prep = conn.prepareStatement(sql); 514 int id = addObject(prep); 515 transfer.writeByte((byte)'O'); 516 transfer.writeInt(id); 517 transfer.writeInt(params); 518 } else { 519 Statement stat = null; 520 try { 521 stat = conn.createStatement(); 522 boolean isResultSet = stat.execute(sql); 523 if(isResultSet) { 524 transfer.writeByte((byte)'R'); 525 ResultSet rs = stat.getResultSet(); 526 processResultSet(rs); 527 } else { 528 transfer.writeByte((byte)'U'); 529 transfer.writeInt(stat.getUpdateCount()); 530 } 531 } finally { 532 JdbcUtils.closeSilently(stat); 533 } 534 } 535 } catch(SQLException e) { 536 sendError(e); 537 } 538 break; 539 } 540 case 'F': { 541 int id = transfer.readInt(); 542 server.log("free "+id); 543 freeObject(id); 544 break; 545 } 546 case 'G': { 547 int objectId = transfer.readInt(); 548 ResultSet rs = (ResultSet )getObject(objectId); 549 try { 550 boolean hasNext = rs.next(); 551 if(hasNext) { 552 transfer.writeByte((byte)'1'); 553 ResultSetMetaData m = rs.getMetaData(); 554 int columnCount = m.getColumnCount(); 555 for(int i=0; i<columnCount; i++) { 556 write(m, rs, i); 557 } 558 } else { 559 transfer.writeByte((byte)'0'); 560 } 561 } catch(SQLException e) { 562 sendError(e); 563 } 564 break; 565 } 566 case 'M': 567 processMeta(); 568 break; 569 case 'P': { 570 String sql = transfer.readString(); 571 server.log("<"+sql+">"); 572 try { 573 PreparedStatement prep = conn.prepareStatement(sql); 574 int id = addObject(prep); 575 transfer.writeByte((byte)'O'); 576 transfer.writeInt(id); 577 int params = getParametersCount(sql); 578 transfer.writeInt(params); 579 } catch(SQLException e) { 580 sendError(e); 581 } 582 break; 583 } 584 case 'Q': { 585 int id = transfer.readInt(); 587 PreparedStatement prep = (PreparedStatement )getObject(id); 588 try { 589 setParameters(prep); 590 boolean isResultSet = prep.execute(); 591 if(isResultSet) { 592 transfer.writeByte((byte)'R'); 593 ResultSet rs = prep.getResultSet(); 594 processResultSet(rs); 595 } else { 596 transfer.writeByte((byte)'U'); 597 transfer.writeInt(prep.getUpdateCount()); 598 } 599 } catch(SQLException e) { 600 sendError(e); 601 } 602 break; 603 } 604 case 'X': 605 stop = true; 606 break; 607 default: 608 server.log("operation? " + (char)operation); 609 } 610 } 611 612 private void write(ResultSetMetaData m, ResultSet rs, int i) throws IOException { 613 try { 614 int type = mapType(m.getColumnType(i+1)); 615 switch(type) { 616 case Types.SMALLINT: 617 case Types.INTEGER: { 618 int value = rs.getInt(i+1); 619 if(rs.wasNull()) { 620 transfer.writeBoolean(true); 621 } else { 622 transfer.writeBoolean(false); 623 transfer.writeInt(value); 624 } 625 break; 626 } 627 case Types.NULL: 628 break; 629 case Types.VARCHAR: 630 transfer.writeString(rs.getString(i+1)); 631 break; 632 default: 633 throw Message.getInternalError("unsupported data type "+type); 634 } 635 } catch(SQLException e) { 636 sendError(e); 637 } 638 } 639 640 int mapType(int sqlType) { 641 switch(sqlType) { 642 case Types.SMALLINT: 643 case Types.INTEGER: 644 case Types.NULL: 645 case Types.VARCHAR: 646 return sqlType; 647 case Types.TINYINT: 648 case Types.BIT: 649 case DataType.TYPE_BOOLEAN: 650 return Types.INTEGER; 651 case Types.BIGINT: 652 case Types.BINARY: 653 case Types.BLOB: 654 case Types.CHAR: 655 case Types.DATE: 656 case Types.TIME: 657 case Types.TIMESTAMP: 658 case Types.DECIMAL: 659 case Types.DOUBLE: 660 case Types.FLOAT: 661 case Types.JAVA_OBJECT: 662 case Types.LONGVARBINARY: 663 case Types.LONGVARCHAR: 664 case Types.NUMERIC: 665 case Types.OTHER: 666 case Types.REAL: 667 case Types.VARBINARY: 668 return Types.VARCHAR; 669 default: 670 throw Message.getInternalError("sqlType "+sqlType); 671 } 672 673 } 674 675 int getParametersCount(String sql) throws SQLException { 676 if (sql == null || sql.indexOf('?') < 0) { 677 return 0; 678 } 679 int len = sql.length(); 680 int param = 0; 681 for (int i = 0; i < len; i++) { 682 try { 683 char c = sql.charAt(i); 684 switch (c) { 685 case '\'': { 686 int j = sql.indexOf('\'', i + 1); 687 if (j < 0) { 688 throw Message.getSyntaxError(sql, i); 689 } 690 i = j; 691 break; 692 } 693 case '"': { 694 int j = sql.indexOf('"', i + 1); 695 if (j < 0) { 696 throw Message.getSyntaxError(sql, i); 697 } 698 i = j; 699 break; 700 } 701 case '/': { 702 if (sql.charAt(i + 1) == '*') { 703 int j = sql.indexOf("*/", i + 2); 705 if (j < 0) { 706 throw Message.getSyntaxError(sql, i); 707 } 708 i = j + 1; 709 } else if (sql.charAt(i + 1) == '/') { 710 i += 2; 712 while (i < len && (c = sql.charAt(i)) != '\r' 713 && c != '\n') { 714 i++; 715 } 716 } 717 break; 718 } 719 case '-': 720 if (sql.charAt(i + 1) == '-') { 721 i += 2; 723 while (i < len && (c = sql.charAt(i)) != '\r' 724 && c != '\n') { 725 i++; 726 } 727 } 728 break; 729 case '?': 730 param++; 731 break; 732 } 733 } catch (ArrayIndexOutOfBoundsException e) { 734 throw Message.getSyntaxError(sql, i); 735 } 736 } 737 return param; 738 } 739 740 public void setThread(Thread thread) { 741 this.thread = thread; 742 } 743 744 public Thread getThread() { 745 return thread; 746 } 747 748 } 749 | Popular Tags |