OSDN Git Service

6e3b1227159cb74a7f8996068d11e4b239470845
[xerial/sqlite-jdbc.git] / src / main / java / org / sqlite / Stmt.java
1 /*
2  * Copyright (c) 2007 David Crawshaw <david@zentus.com>
3  * 
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  * 
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 package org.sqlite;
17
18 import java.sql.BatchUpdateException;
19 import java.sql.Connection;
20 import java.sql.ResultSet;
21 import java.sql.SQLException;
22 import java.sql.SQLWarning;
23 import java.sql.Statement;
24
25 class Stmt extends Unused implements Statement, Codes
26 {
27     final Conn conn;
28     final DB db;
29     final RS rs;
30
31     long pointer;
32     String sql = null;
33
34     int batchPos;
35     Object[] batch = null;
36     boolean resultsWaiting = false;
37
38     Stmt(Conn c)
39     {
40         conn = c;
41         db = conn.db();
42         rs = new RS(this);
43     }
44
45     protected final void checkOpen() throws SQLException
46     {
47         if (pointer == 0)
48             throw new SQLException("statement is not executing");
49     }
50
51     boolean isOpen() throws SQLException
52     {
53         return (pointer != 0);
54     }
55
56     /** Calls sqlite3_step() and sets up results. Expects a clean stmt. */
57     protected boolean exec() throws SQLException
58     {
59         if (sql == null)
60             throw new SQLException("SQLiteJDBC internal error: sql==null");
61         if (rs.isOpen())
62             throw new SQLException("SQLite JDBC internal error: rs.isOpen() on exec.");
63
64         boolean rc = false;
65         try
66         {
67             rc = db.execute(this, null);
68         }
69         finally
70         {
71             resultsWaiting = rc;
72         }
73
74         return db.column_count(pointer) != 0;
75     }
76
77     // PUBLIC INTERFACE /////////////////////////////////////////////
78
79     public void close() throws SQLException
80     {
81         if (pointer == 0)
82             return;
83         rs.close();
84         batch = null;
85         batchPos = 0;
86         int resp = db.finalize(this);
87         if (resp != SQLITE_OK && resp != SQLITE_MISUSE)
88             db.throwex();
89     }
90
91     protected void finalize() throws SQLException
92     {
93         close();
94     }
95
96     public boolean execute(String sql) throws SQLException
97     {
98         close();
99         this.sql = sql;
100         db.prepare(this);
101         return exec();
102     }
103
104     public ResultSet executeQuery(String sql) throws SQLException
105     {
106         close();
107         this.sql = sql;
108         db.prepare(this);
109         if (!exec())
110         {
111             close();
112             throw new SQLException("query does not return ResultSet");
113         }
114         return getResultSet();
115     }
116
117     public int executeUpdate(String sql) throws SQLException
118     {
119         close();
120         this.sql = sql;
121         int changes = 0;
122         try
123         {
124             //db.prepare(this);
125             //changes = db.executeUpdate(this, null);
126
127             int statusCode = db._exec(sql);
128             if (statusCode != SQLITE_OK)
129                 throw DB.newSQLException(statusCode, "");
130
131             changes = db.changes();
132         }
133         finally
134         {
135             close();
136         }
137         return changes;
138     }
139
140     public ResultSet getResultSet() throws SQLException
141     {
142         checkOpen();
143         if (rs.isOpen())
144             throw new SQLException("ResultSet already requested");
145         if (db.column_count(pointer) == 0)
146             throw new SQLException("no ResultSet available");
147         if (rs.colsMeta == null)
148             rs.colsMeta = db.column_names(pointer);
149         rs.cols = rs.colsMeta;
150
151         rs.open = resultsWaiting;
152         resultsWaiting = false;
153         return rs;
154     }
155
156     /*
157      * This function has a complex behaviour best understood by carefully
158      * reading the JavaDoc for getMoreResults() and considering the test
159      * StatementTest.execute().
160      */
161     public int getUpdateCount() throws SQLException
162     {
163         if (pointer != 0 && !rs.isOpen() && !resultsWaiting && db.column_count(pointer) == 0)
164             return db.changes();
165         return -1;
166     }
167
168     public void addBatch(String sql) throws SQLException
169     {
170         close();
171         if (batch == null || batchPos + 1 >= batch.length)
172         {
173             Object[] nb = new Object[Math.max(10, batchPos * 2)];
174             if (batch != null)
175                 System.arraycopy(batch, 0, nb, 0, batch.length);
176             batch = nb;
177         }
178         batch[batchPos++] = sql;
179     }
180
181     public void clearBatch() throws SQLException
182     {
183         batchPos = 0;
184         if (batch != null)
185             for (int i = 0; i < batch.length; i++)
186                 batch[i] = null;
187     }
188
189     public int[] executeBatch() throws SQLException
190     {
191         // TODO: optimize
192         close();
193         if (batch == null || batchPos == 0)
194             return new int[] {};
195
196         int[] changes = new int[batchPos];
197
198         synchronized (db)
199         {
200             try
201             {
202                 for (int i = 0; i < changes.length; i++)
203                 {
204                     try
205                     {
206                         this.sql = (String) batch[i];
207                         db.prepare(this);
208                         changes[i] = db.executeUpdate(this, null);
209                     }
210                     catch (SQLException e)
211                     {
212                         throw new BatchUpdateException("batch entry " + i + ": " + e.getMessage(), changes);
213                     }
214                     finally
215                     {
216                         db.finalize(this);
217                     }
218                 }
219             }
220             finally
221             {
222                 clearBatch();
223             }
224         }
225
226         return changes;
227     }
228
229     public void setCursorName(String name)
230     {}
231
232     public SQLWarning getWarnings() throws SQLException
233     {
234         return null;
235     }
236
237     public void clearWarnings() throws SQLException
238     {}
239
240     public Connection getConnection() throws SQLException
241     {
242         return conn;
243     }
244
245     public void cancel() throws SQLException
246     {
247         rs.checkOpen();
248         db.interrupt();
249     }
250
251     public int getQueryTimeout() throws SQLException
252     {
253         return conn.getTimeout();
254     }
255
256     public void setQueryTimeout(int seconds) throws SQLException
257     {
258         if (seconds < 0)
259             throw new SQLException("query timeout must be >= 0");
260         conn.setTimeout(1000 * seconds);
261     }
262
263     // TODO: write test
264     public int getMaxRows() throws SQLException
265     {
266         checkOpen();
267         return rs.maxRows;
268     }
269
270     public void setMaxRows(int max) throws SQLException
271     {
272         checkOpen();
273         if (max < 0)
274             throw new SQLException("max row count must be >= 0");
275         rs.maxRows = max;
276     }
277
278     public int getMaxFieldSize() throws SQLException
279     {
280         return 0;
281     }
282
283     public void setMaxFieldSize(int max) throws SQLException
284     {
285         if (max < 0)
286             throw new SQLException("max field size " + max + " cannot be negative");
287     }
288
289     public int getFetchSize() throws SQLException
290     {
291         return rs.getFetchSize();
292     }
293
294     public void setFetchSize(int r) throws SQLException
295     {
296         rs.setFetchSize(r);
297     }
298
299     public int getFetchDirection() throws SQLException
300     {
301         return rs.getFetchDirection();
302     }
303
304     public void setFetchDirection(int d) throws SQLException
305     {
306         rs.setFetchDirection(d);
307     }
308
309     /**
310      * As SQLite's last_insert_rowid() function is DB-specific not statement
311      * specific, this function introduces a race condition if the same
312      * connection is used by two threads and both insert.
313      */
314     public ResultSet getGeneratedKeys() throws SQLException
315     {
316         return ((MetaData) conn.getMetaData()).getGeneratedKeys();
317     }
318
319     /** SQLite does not support multiple results from execute(). */
320     public boolean getMoreResults() throws SQLException
321     {
322         return getMoreResults(0);
323     }
324
325     public boolean getMoreResults(int c) throws SQLException
326     {
327         checkOpen();
328         close(); // as we never have another result, clean up pointer
329         return false;
330     }
331
332     public int getResultSetConcurrency() throws SQLException
333     {
334         return ResultSet.CONCUR_READ_ONLY;
335     }
336
337     public int getResultSetHoldability() throws SQLException
338     {
339         return ResultSet.CLOSE_CURSORS_AT_COMMIT;
340     }
341
342     public int getResultSetType() throws SQLException
343     {
344         return ResultSet.TYPE_FORWARD_ONLY;
345     }
346 }