1 package SQLite.JDBC2z; 2 3 import java.sql.*; 4 import java.util.*; 5 6 public class JDBCConnection 7 implements java.sql.Connection, SQLite.BusyHandler { 8 9 /** 10 * Open database. 11 */ 12 protected DatabaseX db; 13 14 /** 15 * Database URL. 16 */ 17 protected String url; 18 19 /** 20 * Character encoding. 21 */ 22 protected String enc; 23 24 /** 25 * SQLite 3 VFS to use. 26 */ 27 protected String vfs; 28 29 /** 30 * Autocommit flag, true means autocommit. 31 */ 32 protected boolean autocommit = true; 33 34 /** 35 * In-transaction flag. 36 * Can be true only when autocommit false. 37 */ 38 protected boolean intrans = false; 39 40 /** 41 * Timeout for Database.exec() 42 */ 43 protected int timeout = 1000000; 44 45 /** 46 * Use double/julian date representation. 47 */ 48 protected boolean useJulian = false; 49 50 /** 51 * File name of database. 52 */ 53 private String dbfile = null; 54 55 /** 56 * Reference to meta data or null. 57 */ 58 private JDBCDatabaseMetaData meta = null; 59 60 /** 61 * Base time value for timeout handling. 62 */ 63 private long t0; 64 65 /** 66 * Database in readonly mode. 67 */ 68 private boolean readonly = false; 69 70 /** 71 * Transaction isolation mode. 72 */ 73 private int trmode = TRANSACTION_SERIALIZABLE; 74 75 private boolean busy0(DatabaseX db, int count) { 76 if (count <= 1) { 77 t0 = System.currentTimeMillis(); 78 } 79 if (db != null) { 80 long t1 = System.currentTimeMillis(); 81 if (t1 - t0 > timeout) { 82 return false; 83 } 84 db.wait(100); 85 return true; 86 } 87 return false; 88 } 89 90 public boolean busy(String table, int count) { 91 return busy0(db, count); 92 } 93 94 protected boolean busy3(DatabaseX db, int count) { 95 if (count <= 1) { 96 t0 = System.currentTimeMillis(); 97 } 98 if (db != null) { 99 long t1 = System.currentTimeMillis(); 100 if (t1 - t0 > timeout) { 101 return false; 102 } 103 return true; 104 } 105 return false; 106 } 107 108 private DatabaseX open(boolean readonly) throws SQLException { 109 DatabaseX dbx = null; 110 try { 111 dbx = new DatabaseX(); 112 dbx.open(dbfile, readonly ? SQLite.Constants.SQLITE_OPEN_READONLY : 113 (SQLite.Constants.SQLITE_OPEN_READWRITE | 114 SQLite.Constants.SQLITE_OPEN_CREATE), vfs); 115 dbx.set_encoding(enc); 116 } catch (SQLite.Exception e) { 117 throw new SQLException(e.toString()); 118 } 119 int loop = 0; 120 while (true) { 121 try { 122 dbx.exec("PRAGMA short_column_names = off;", null); 123 dbx.exec("PRAGMA full_column_names = on;", null); 124 dbx.exec("PRAGMA empty_result_callbacks = on;", null); 125 if (SQLite.Database.version().compareTo("2.6.0") >= 0) { 126 dbx.exec("PRAGMA show_datatypes = on;", null); 127 } 128 } catch (SQLite.Exception e) { 129 if (dbx.last_error() != SQLite.Constants.SQLITE_BUSY || 130 !busy0(dbx, ++loop)) { 131 try { 132 dbx.close(); 133 } catch (SQLite.Exception ee) { 134 } 135 throw new SQLException(e.toString()); 136 } 137 continue; 138 } 139 break; 140 } 141 return dbx; 142 } 143 144 public JDBCConnection(String url, String enc, String pwd, String drep, 145 String vfs) 146 throws SQLException { 147 if (url.startsWith("sqlite:/")) { 148 dbfile = url.substring(8); 149 } else if (url.startsWith("jdbc:sqlite:/")) { 150 dbfile = url.substring(13); 151 } else { 152 throw new SQLException("unsupported url"); 153 } 154 this.url = url; 155 this.enc = enc; 156 this.vfs = vfs; 157 try { 158 db = open(readonly); 159 try { 160 if (pwd != null && pwd.length() > 0) { 161 db.key(pwd); 162 } 163 } catch (SQLite.Exception se) { 164 throw new SQLException("error while setting key"); 165 } 166 db.busy_handler(this); 167 } catch (SQLException e) { 168 if (db != null) { 169 try { 170 db.close(); 171 } catch (SQLite.Exception ee) { 172 } 173 } 174 throw e; 175 } 176 useJulian = drep != null && 177 (drep.startsWith("j") || drep.startsWith("J")); 178 } 179 180 /* non-standard */ 181 public SQLite.Database getSQLiteDatabase() { 182 return (SQLite.Database) db; 183 } 184 185 public Statement createStatement() { 186 JDBCStatement s = new JDBCStatement(this); 187 return s; 188 } 189 190 public Statement createStatement(int resultSetType, 191 int resultSetConcurrency) 192 throws SQLException { 193 if (resultSetType != ResultSet.TYPE_FORWARD_ONLY && 194 resultSetType != ResultSet.TYPE_SCROLL_INSENSITIVE && 195 resultSetType != ResultSet.TYPE_SCROLL_SENSITIVE) { 196 throw new SQLFeatureNotSupportedException("unsupported result set type"); 197 } 198 if (resultSetConcurrency != ResultSet.CONCUR_READ_ONLY && 199 resultSetConcurrency != ResultSet.CONCUR_UPDATABLE) { 200 throw new SQLFeatureNotSupportedException("unsupported result set concurrency"); 201 } 202 JDBCStatement s = new JDBCStatement(this); 203 return s; 204 } 205 206 public DatabaseMetaData getMetaData() throws SQLException { 207 if (meta == null) { 208 meta = new JDBCDatabaseMetaData(this); 209 } 210 return meta; 211 } 212 213 public void close() throws SQLException { 214 try { 215 rollback(); 216 } catch (SQLException e) { 217 /* ignored */ 218 } 219 intrans = false; 220 if (db != null) { 221 try { 222 db.close(); 223 db = null; 224 } catch (SQLite.Exception e) { 225 throw new SQLException(e.toString()); 226 } 227 } 228 } 229 230 public boolean isClosed() throws SQLException { 231 return db == null; 232 } 233 234 public boolean isReadOnly() throws SQLException { 235 return readonly; 236 } 237 238 public void clearWarnings() throws SQLException { 239 } 240 241 public void commit() throws SQLException { 242 if (db == null) { 243 throw new SQLException("stale connection"); 244 } 245 if (!intrans) { 246 return; 247 } 248 try { 249 db.exec("COMMIT", null); 250 intrans = false; 251 } catch (SQLite.Exception e) { 252 throw new SQLException(e.toString()); 253 } 254 } 255 256 public boolean getAutoCommit() throws SQLException { 257 return autocommit; 258 } 259 260 public String getCatalog() throws SQLException { 261 return null; 262 } 263 264 public int getTransactionIsolation() throws SQLException { 265 return trmode; 266 } 267 268 public SQLWarning getWarnings() throws SQLException { 269 return null; 270 } 271 272 public String nativeSQL(String sql) throws SQLException { 273 throw new SQLException("not supported"); 274 } 275 276 public CallableStatement prepareCall(String sql) throws SQLException { 277 throw new SQLException("not supported"); 278 } 279 280 public CallableStatement prepareCall(String sql, int x, int y) 281 throws SQLException { 282 throw new SQLFeatureNotSupportedException(); 283 } 284 285 public PreparedStatement prepareStatement(String sql) throws SQLException { 286 JDBCPreparedStatement s = new JDBCPreparedStatement(this, sql); 287 return s; 288 } 289 290 public PreparedStatement prepareStatement(String sql, int resultSetType, 291 int resultSetConcurrency) 292 throws SQLException { 293 if (resultSetType != ResultSet.TYPE_FORWARD_ONLY && 294 resultSetType != ResultSet.TYPE_SCROLL_INSENSITIVE && 295 resultSetType != ResultSet.TYPE_SCROLL_SENSITIVE) { 296 throw new SQLFeatureNotSupportedException("unsupported result set type"); 297 } 298 if (resultSetConcurrency != ResultSet.CONCUR_READ_ONLY && 299 resultSetConcurrency != ResultSet.CONCUR_UPDATABLE) { 300 throw new SQLFeatureNotSupportedException("unsupported result set concurrency"); 301 } 302 JDBCPreparedStatement s = new JDBCPreparedStatement(this, sql); 303 return s; 304 } 305 306 public void rollback() throws SQLException { 307 if (db == null) { 308 throw new SQLException("stale connection"); 309 } 310 if (!intrans) { 311 return; 312 } 313 try { 314 db.exec("ROLLBACK", null); 315 intrans = false; 316 } catch (SQLite.Exception e) { 317 throw new SQLException(e.toString()); 318 } 319 } 320 321 public void setAutoCommit(boolean ac) throws SQLException { 322 if (ac && intrans && db != null) { 323 try { 324 db.exec("ROLLBACK", null); 325 } catch (SQLite.Exception e) { 326 throw new SQLException(e.toString()); 327 } finally { 328 intrans = false; 329 } 330 } 331 autocommit = ac; 332 } 333 334 public void setCatalog(String catalog) throws SQLException { 335 } 336 337 public void setReadOnly(boolean ro) throws SQLException { 338 if (intrans) { 339 throw new SQLException("incomplete transaction"); 340 } 341 if (ro != readonly) { 342 DatabaseX dbx = null; 343 try { 344 dbx = open(ro); 345 db.close(); 346 db = dbx; 347 dbx = null; 348 readonly = ro; 349 } catch (SQLException e) { 350 throw e; 351 } catch (SQLite.Exception ee) { 352 if (dbx != null) { 353 try { 354 dbx.close(); 355 } catch (SQLite.Exception eee) { 356 } 357 } 358 throw new SQLException(ee.toString()); 359 } 360 } 361 } 362 363 public void setTransactionIsolation(int level) throws SQLException { 364 if (db.is3() && SQLite.JDBCDriver.sharedCache) { 365 String flag = null; 366 if (level == TRANSACTION_READ_UNCOMMITTED && 367 trmode != TRANSACTION_READ_UNCOMMITTED) { 368 flag = "on"; 369 } else if (level == TRANSACTION_SERIALIZABLE && 370 trmode != TRANSACTION_SERIALIZABLE) { 371 flag = "off"; 372 } 373 if (flag != null) { 374 try { 375 db.exec("PRAGMA read_uncommitted = " + flag + ";", null); 376 trmode = level; 377 } catch (java.lang.Exception e) { 378 } 379 } 380 } 381 if (level != trmode) { 382 throw new SQLException("not supported"); 383 } 384 } 385 386 public java.util.Map<String, Class<?>> getTypeMap() throws SQLException { 387 throw new SQLFeatureNotSupportedException(); 388 } 389 390 public void setTypeMap(java.util.Map map) throws SQLException { 391 throw new SQLFeatureNotSupportedException(); 392 } 393 394 public int getHoldability() throws SQLException { 395 return ResultSet.HOLD_CURSORS_OVER_COMMIT; 396 } 397 398 public void setHoldability(int holdability) throws SQLException { 399 if (holdability == ResultSet.HOLD_CURSORS_OVER_COMMIT) { 400 return; 401 } 402 throw new SQLFeatureNotSupportedException("unsupported holdability"); 403 } 404 405 public Savepoint setSavepoint() throws SQLException { 406 throw new SQLFeatureNotSupportedException(); 407 } 408 409 public Savepoint setSavepoint(String name) throws SQLException { 410 throw new SQLFeatureNotSupportedException(); 411 } 412 413 public void rollback(Savepoint x) throws SQLException { 414 throw new SQLFeatureNotSupportedException(); 415 } 416 417 public void releaseSavepoint(Savepoint x) throws SQLException { 418 throw new SQLFeatureNotSupportedException(); 419 } 420 421 public Statement createStatement(int resultSetType, 422 int resultSetConcurrency, 423 int resultSetHoldability) 424 throws SQLException { 425 if (resultSetHoldability != ResultSet.HOLD_CURSORS_OVER_COMMIT) { 426 throw new SQLFeatureNotSupportedException("unsupported holdability"); 427 } 428 return createStatement(resultSetType, resultSetConcurrency); 429 } 430 431 public PreparedStatement prepareStatement(String sql, int resultSetType, 432 int resultSetConcurrency, 433 int resultSetHoldability) 434 throws SQLException { 435 if (resultSetHoldability != ResultSet.HOLD_CURSORS_OVER_COMMIT) { 436 throw new SQLFeatureNotSupportedException("unsupported holdability"); 437 } 438 return prepareStatement(sql, resultSetType, resultSetConcurrency); 439 } 440 441 public CallableStatement prepareCall(String sql, int x, int y, int z) 442 throws SQLException { 443 throw new SQLFeatureNotSupportedException(); 444 } 445 446 public PreparedStatement prepareStatement(String sql, int autokeys) 447 throws SQLException { 448 if (autokeys != Statement.NO_GENERATED_KEYS) { 449 throw new SQLFeatureNotSupportedException("generated keys not supported"); 450 } 451 return prepareStatement(sql); 452 } 453 454 public PreparedStatement prepareStatement(String sql, int colIndexes[]) 455 throws SQLException { 456 throw new SQLFeatureNotSupportedException(); 457 } 458 459 public PreparedStatement prepareStatement(String sql, String columns[]) 460 throws SQLException { 461 throw new SQLFeatureNotSupportedException(); 462 } 463 464 public Clob createClob() throws SQLException { 465 throw new SQLFeatureNotSupportedException(); 466 } 467 468 public Blob createBlob() throws SQLException { 469 throw new SQLFeatureNotSupportedException(); 470 } 471 472 public NClob createNClob() throws SQLException { 473 throw new SQLFeatureNotSupportedException(); 474 } 475 476 public SQLXML createSQLXML() throws SQLException { 477 throw new SQLFeatureNotSupportedException(); 478 } 479 480 public boolean isValid(int timeout) throws SQLException { 481 return true; 482 } 483 484 public void setClientInfo(String name, String value) 485 throws SQLClientInfoException { 486 throw new SQLClientInfoException(); 487 } 488 489 public void setClientInfo(Properties prop) throws SQLClientInfoException { 490 throw new SQLClientInfoException(); 491 } 492 493 public String getClientInfo(String name) throws SQLException { 494 throw new SQLException("unsupported"); 495 } 496 497 public Properties getClientInfo() throws SQLException { 498 return new Properties(); 499 } 500 501 public Array createArrayOf(String type, Object[] elems) 502 throws SQLException { 503 throw new SQLFeatureNotSupportedException(); 504 } 505 506 public Struct createStruct(String type, Object[] attrs) 507 throws SQLException { 508 throw new SQLFeatureNotSupportedException(); 509 } 510 511 public <T> T unwrap(java.lang.Class<T> iface) throws SQLException { 512 throw new SQLException("unsupported"); 513 } 514 515 public boolean isWrapperFor(java.lang.Class iface) throws SQLException { 516 return false; 517 } 518 519 } 520 521 class DatabaseX extends SQLite.Database { 522 523 static Object lock = new Object(); 524 525 public DatabaseX() { 526 super(); 527 } 528 529 void wait(int ms) { 530 try { 531 synchronized (lock) { 532 lock.wait(ms); 533 } 534 } catch (java.lang.Exception e) { 535 } 536 } 537 538 public void exec(String sql, SQLite.Callback cb) 539 throws SQLite.Exception { 540 super.exec(sql, cb); 541 synchronized (lock) { 542 lock.notifyAll(); 543 } 544 } 545 546 public void exec(String sql, SQLite.Callback cb, String args[]) 547 throws SQLite.Exception { 548 super.exec(sql, cb, args); 549 synchronized (lock) { 550 lock.notifyAll(); 551 } 552 } 553 554 public SQLite.TableResult get_table(String sql, String args[]) 555 throws SQLite.Exception { 556 SQLite.TableResult ret = super.get_table(sql, args); 557 synchronized (lock) { 558 lock.notifyAll(); 559 } 560 return ret; 561 } 562 563 public void get_table(String sql, String args[], SQLite.TableResult tbl) 564 throws SQLite.Exception { 565 super.get_table(sql, args, tbl); 566 synchronized (lock) { 567 lock.notifyAll(); 568 } 569 } 570 571 } 572