Home | History | Annotate | Download | only in JDBC2z
      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