1package SQLite.JDBC2z;
2
3import java.sql.*;
4import java.util.*;
5
6public class JDBCStatement implements java.sql.Statement {
7
8    protected JDBCConnection conn;
9    protected JDBCResultSet rs;
10    protected int updcnt;
11    protected int maxrows = 0;
12    private ArrayList<String> batch;
13
14    public JDBCStatement(JDBCConnection conn) {
15	this.conn = conn;
16	this.updcnt = 0;
17	this.rs = null;
18	this.batch = null;
19    }
20
21    public void setFetchSize(int fetchSize) throws SQLException {
22	if (fetchSize != 1) {
23	    throw new SQLException("fetch size not 1");
24	}
25    }
26
27    public int getFetchSize() throws SQLException {
28	return 1;
29    }
30
31    public int getMaxRows() throws SQLException {
32	return maxrows;
33    }
34
35    public void setMaxRows(int max) throws SQLException {
36	if (max < 0) {
37	    throw new SQLException("max must be >= 0 (was " + max + ")");
38	}
39	maxrows = max;
40    }
41
42    public void setFetchDirection(int fetchDirection) throws SQLException {
43	throw new SQLException("not supported");
44    }
45
46    public int getFetchDirection() throws SQLException {
47	return ResultSet.FETCH_UNKNOWN;
48    }
49
50    public int getResultSetConcurrency() throws SQLException {
51	return ResultSet.CONCUR_READ_ONLY;
52    }
53
54    public int getResultSetType() throws SQLException {
55	return ResultSet.TYPE_SCROLL_INSENSITIVE;
56    }
57
58    public void setQueryTimeout(int seconds) throws SQLException {
59	// BEGIN android-changed: more closely follow specification:
60	// "[throws SQLException if] this method is called on a closed Statement or the condition
61	// seconds >= 0 is not satisfied"
62	// (http://java.sun.com/javase/6/docs/api/java/sql/Statement.html#setQueryTimeout(int))
63	if (isClosed()) {
64	    throw new SQLException("can't set a query timeout on a closed statement");
65	} else if (seconds < 0) {
66	    throw new SQLException("can't set a query timeout of less than 0 seconds");
67	} else if (seconds == 0) {
68	    // An argument of 0 seconds should set an unlimited timeout. However, since this was not
69	    // done previously, I assume it isn't implemented and use the same implementation.
70	    conn.timeout = 5000;
71	} else {
72	    conn.timeout = seconds * 1000;
73	}
74	// END android-changed
75    }
76
77    public int getQueryTimeout() throws SQLException {
78	return conn.timeout / 1000; // android-changed: should return seconds
79    }
80
81    public ResultSet getResultSet() throws SQLException {
82	return rs;
83    }
84
85    ResultSet executeQuery(String sql, String args[], boolean updonly)
86	throws SQLException {
87	SQLite.TableResult tr = null;
88	if (rs != null) {
89	    rs.close();
90	    rs = null;
91	}
92	updcnt = -1;
93	if (conn == null || conn.db == null) {
94	    throw new SQLException("stale connection");
95	}
96	int busy = 0;
97	boolean starttrans = !conn.autocommit && !conn.intrans;
98	while (true) {
99	    try {
100		if (starttrans) {
101		    conn.db.exec("BEGIN TRANSACTION", null);
102		    conn.intrans = true;
103		}
104		if (args == null) {
105		    if (updonly) {
106			conn.db.exec(sql, null);
107		    } else {
108			tr = conn.db.get_table(sql, maxrows);
109		    }
110		} else {
111		    if (updonly) {
112			conn.db.exec(sql, null, args);
113		    } else {
114			tr = conn.db.get_table(sql, maxrows, args);
115		    }
116		}
117		updcnt = (int) conn.db.changes();
118	    } catch (SQLite.Exception e) {
119		if (conn.db.is3() &&
120		    conn.db.last_error() == SQLite.Constants.SQLITE_BUSY &&
121		    conn.busy3(conn.db, ++busy)) {
122		    try {
123			if (starttrans && conn.intrans) {
124			    conn.db.exec("ROLLBACK", null);
125			    conn.intrans = false;
126			}
127		    } catch (SQLite.Exception ee) {
128		    }
129		    try {
130			int ms = 20 + busy * 10;
131			if (ms > 1000) {
132			    ms = 1000;
133			}
134			synchronized (this) {
135			    this.wait(ms);
136			}
137		    } catch (java.lang.Exception eee) {
138		    }
139		    continue;
140		}
141		throw new SQLException(e.toString());
142	    }
143	    break;
144	}
145	if (!updonly && tr == null) {
146	    throw new SQLException("no result set produced");
147	}
148	if (!updonly && tr != null) {
149	    rs = new JDBCResultSet(new TableResultX(tr), this);
150	}
151	return rs;
152    }
153
154    public ResultSet executeQuery(String sql) throws SQLException {
155	return executeQuery(sql, null, false);
156    }
157
158    public boolean execute(String sql) throws SQLException {
159	return executeQuery(sql) != null;
160    }
161
162    public void cancel() throws SQLException {
163	if (conn == null || conn.db == null) {
164	    throw new SQLException("stale connection");
165	}
166	conn.db.interrupt();
167    }
168
169    public void clearWarnings() throws SQLException {
170    }
171
172    public Connection getConnection() throws SQLException {
173	return conn;
174    }
175
176    public void addBatch(String sql) throws SQLException {
177	if (batch == null) {
178	    batch = new ArrayList<String>(1);
179	}
180	batch.add(sql);
181    }
182
183    public int[] executeBatch() throws SQLException {
184	if (batch == null) {
185	    return new int[0];
186	}
187	int[] ret = new int[batch.size()];
188	for (int i = 0; i < ret.length; i++) {
189	    ret[i] = EXECUTE_FAILED;
190	}
191	int errs = 0;
192	for (int i = 0; i < ret.length; i++) {
193	    try {
194		execute((String) batch.get(i));
195		ret[i] = updcnt;
196	    } catch (SQLException e) {
197		++errs;
198	    }
199	}
200	if (errs > 0) {
201	    throw new BatchUpdateException("batch failed", ret);
202	}
203	return ret;
204    }
205
206    public void clearBatch() throws SQLException {
207	if (batch != null) {
208	    batch.clear();
209	    batch = null;
210	}
211    }
212
213    public void close() throws SQLException {
214	clearBatch();
215	conn = null;
216    }
217
218    public int executeUpdate(String sql) throws SQLException {
219	executeQuery(sql, null, true);
220	return updcnt;
221    }
222
223    public int getMaxFieldSize() throws SQLException {
224	return 0;
225    }
226
227    public boolean getMoreResults() throws SQLException {
228	if (rs != null) {
229	    rs.close();
230	    rs = null;
231	}
232	return false;
233    }
234
235    public int getUpdateCount() throws SQLException {
236	return updcnt;
237    }
238
239    public SQLWarning getWarnings() throws SQLException {
240	return null;
241    }
242
243    public void setCursorName(String name) throws SQLException {
244	throw new SQLFeatureNotSupportedException();
245    }
246
247    public void setEscapeProcessing(boolean enable) throws SQLException {
248	throw new SQLException("not supported");
249    }
250
251    public void setMaxFieldSize(int max) throws SQLException {
252	throw new SQLException("not supported");
253    }
254
255    public boolean getMoreResults(int x) throws SQLException {
256	throw new SQLFeatureNotSupportedException();
257    }
258
259    public ResultSet getGeneratedKeys() throws SQLException {
260	throw new SQLFeatureNotSupportedException();
261    }
262
263    public int executeUpdate(String sql, int autokeys)
264	throws SQLException {
265	if (autokeys != Statement.NO_GENERATED_KEYS) {
266	    throw new SQLFeatureNotSupportedException("generated keys not supported");
267	}
268	return executeUpdate(sql);
269    }
270
271    public int executeUpdate(String sql, int colIndexes[])
272	throws SQLException {
273	throw new SQLFeatureNotSupportedException();
274    }
275
276    public int executeUpdate(String sql, String colIndexes[])
277	throws SQLException {
278	throw new SQLFeatureNotSupportedException();
279    }
280
281    public boolean execute(String sql, int autokeys)
282	throws SQLException {
283	if (autokeys != Statement.NO_GENERATED_KEYS) {
284	    throw new SQLFeatureNotSupportedException("autogenerated keys not supported");
285	}
286	return execute(sql);
287    }
288
289    public boolean execute(String sql, int colIndexes[])
290	throws SQLException {
291	throw new SQLFeatureNotSupportedException();
292    }
293
294    public boolean execute(String sql, String colIndexes[])
295	throws SQLException {
296	throw new SQLFeatureNotSupportedException();
297    }
298
299    public int getResultSetHoldability() throws SQLException {
300	return ResultSet.HOLD_CURSORS_OVER_COMMIT;
301    }
302
303    public boolean isClosed() throws SQLException {
304	return conn == null; // android-changed: pretty sure this is correct, since it matches what's done in close()
305    }
306
307    public void setPoolable(boolean yes) throws SQLException {
308	if (yes) {
309	    throw new SQLException("poolable statements not supported");
310	}
311    }
312
313    public boolean isPoolable() throws SQLException {
314	return false;
315    }
316
317    public <T> T unwrap(java.lang.Class<T> iface) throws SQLException {
318	throw new SQLException("unsupported");
319    }
320
321    public boolean isWrapperFor(java.lang.Class iface) throws SQLException {
322	return false;
323    }
324
325}
326