1package SQLite.JDBC2z;
2
3import java.sql.*;
4import java.util.*;
5
6public 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
521class 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