File D:\maven-qcr-plugin-testsunitaires\qcr4java\h2-1.3.170-sources\target\site\qc\lecode\command\Command.java.html

1 /*
2 * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
3 * Version 1.0, and under the Eclipse Public License, Version 1.0
4 * (http://h2database.com/html/license.html).
5 * Initial Developer: H2 Group
6 */
7 package org.h2.command;
8
9 import java.sql.SQLException;
10 import java.util.ArrayList;
11 import org.h2.constant.ErrorCode;
12 import org.h2.engine.Constants;
13 import org.h2.engine.Database;
14 import org.h2.engine.Session;
15 import org.h2.expression.ParameterInterface;
16 import org.h2.message.DbException;
17 import org.h2.message.Trace;
18 import org.h2.result.ResultInterface;
19 import org.h2.util.MathUtils;
20
21 /**
22 * Represents a SQL statement. This object is only used on the server side.
23 */
24 public abstract class Command implements CommandInterface {
25
26 /**
27 * The session.
28 */
29 protected final Session session;
30
31 /**
32 * The last start time.
33 */
34 protected long startTime;
35
36 /**
37 * The trace module.
38 */
39 private final Trace trace;
40
41 /**
42 * If this query was canceled.
43 */
44 private volatile boolean cancel;
45
46 private final String sql;
47
48 private boolean canReuse;
49
50 Command(Parser parser, String sql) {
51 this.session = parser.getSession();
52 this.sql = sql;
53 trace = session.getDatabase().getTrace(Trace.COMMAND);
54 }
55
56 /**
57 * Check if this command is transactional.
58 * If it is not, then it forces the current transaction to commit.
59 *
60 * @return true if it is
61 */
62 public abstract boolean isTransactional();
63
64 /**
65 * Check if this command is a query.
66 *
67 * @return true if it is
68 */
69 public abstract boolean isQuery();
70
71 /**
72 * Get the list of parameters.
73 *
74 * @return the list of parameters
75 */
76 public abstract ArrayList<? extends ParameterInterface> getParameters();
77
78 /**
79 * Check if this command is read only.
80 *
81 * @return true if it is
82 */
83 public abstract boolean isReadOnly();
84
85 /**
86 * Get an empty result set containing the meta data.
87 *
88 * @return an empty result set
89 */
90 public abstract ResultInterface queryMeta();
91
92 /**
93 * Execute an updating statement, if this is possible.
94 *
95 * @return the update count
96 * @throws DbException if the command is not an updating statement
97 */
98 public int update() {
99 throw DbException.get(ErrorCode.METHOD_NOT_ALLOWED_FOR_QUERY);
100 }
101
102 /**
103 * Execute a query statement, if this is possible.
104 *
105 * @param maxrows the maximum number of rows returned
106 * @return the local result set
107 * @throws DbException if the command is not a query
108 */
109 public ResultInterface query(int maxrows) {
110 throw DbException.get(ErrorCode.METHOD_ONLY_ALLOWED_FOR_QUERY);
111 }
112
113 public final ResultInterface getMetaData() {
114 return queryMeta();
115 }
116
117 /**
118 * Start the stopwatch.
119 */
120 void start() {
121 if (trace.isInfoEnabled()) {
122 startTime = System.currentTimeMillis();
123 }
124 }
125
126 void setProgress(int state) {
127 session.getDatabase().setProgress(state, sql, 0, 0);
128 }
129
130 /**
131 * Check if this command has been canceled, and throw an exception if yes.
132 *
133 * @throws DbException if the statement has been canceled
134 */
135 protected void checkCanceled() {
136 if (cancel) {
137 cancel = false;
138 throw DbException.get(ErrorCode.STATEMENT_WAS_CANCELED);
139 }
140 }
141
142 private void stop() {
143 session.closeTemporaryResults();
144 session.setCurrentCommand(null);
145 if (!isTransactional()) {
146 session.commit(true);
147 } else if (session.getAutoCommit()) {
148 session.commit(false);
149 } else if (session.getDatabase().isMultiThreaded()) {
150 Database db = session.getDatabase();
151 if (db != null) {
152 if (db.getLockMode() == Constants.LOCK_MODE_READ_COMMITTED) {
153 session.unlockReadLocks();
154 }
155 }
156 }
157 if (trace.isInfoEnabled() && startTime > 0) {
158 long time = System.currentTimeMillis() - startTime;
159 if (time > Constants.SLOW_QUERY_LIMIT_MS) {
160 trace.info("slow query: {0} ms", time);
161 }
162 }
163 }
164
165 /**
166 * Execute a query and return the result.
167 * This method prepares everything and calls {@link #query(int)} finally.
168 *
169 * @param maxrows the maximum number of rows to return
170 * @param scrollable if the result set must be scrollable (ignored)
171 * @return the result set
172 */
173 public ResultInterface executeQuery(int maxrows, boolean scrollable) {
174 startTime = 0;
175 long start = 0;
176 Database database = session.getDatabase();
177 Object sync = database.isMultiThreaded() ? (Object) session : (Object) database;
178 session.waitIfExclusiveModeEnabled();
179 boolean writing = !isReadOnly();
180 if (writing) {
181 while (!database.beforeWriting()) {
182 // wait
183 }
184 }
185 synchronized (sync) {
186 session.setCurrentCommand(this);
187 try {
188 while (true) {
189 database.checkPowerOff();
190 try {
191 return query(maxrows);
192 } catch (DbException e) {
193 start = filterConcurrentUpdate(e, start);
194 } catch (Throwable e) {
195 throw DbException.convert(e);
196 }
197 }
198 } catch (DbException e) {
199 e.addSQL(sql);
200 database.exceptionThrown(e.getSQLException(), sql);
201 throw e;
202 } finally {
203 stop();
204 if (writing) {
205 database.afterWriting();
206 }
207 }
208 }
209 }
210
211 public int executeUpdate() {
212 long start = 0;
213 Database database = session.getDatabase();
214 Object sync = database.isMultiThreaded() ? (Object) session : (Object) database;
215 session.waitIfExclusiveModeEnabled();
216 boolean callStop = true;
217 boolean writing = !isReadOnly();
218 if (writing) {
219 while (!database.beforeWriting()) {
220 // wait
221 }
222 }
223 synchronized (sync) {
224 int rollback = session.getUndoLogPos();
225 session.setCurrentCommand(this);
226 try {
227 while (true) {
228 database.checkPowerOff();
229 try {
230 return update();
231 } catch (DbException e) {
232 start = filterConcurrentUpdate(e, start);
233 } catch (Throwable e) {
234 throw DbException.convert(e);
235 }
236 }
237 } catch (DbException e) {
238 e = e.addSQL(sql);
239 SQLException s = e.getSQLException();
240 database.exceptionThrown(s, sql);
241 database.checkPowerOff();
242 if (s.getErrorCode() == ErrorCode.DEADLOCK_1) {
243 session.rollback();
244 } else if (s.getErrorCode() == ErrorCode.OUT_OF_MEMORY) {
245 // there is a serious problem:
246 // the transaction may be applied partially
247 // in this case we need to panic:
248 // close the database
249 callStop = false;
250 database.shutdownImmediately();
251 throw e;
252 } else {
253 session.rollbackTo(rollback, false);
254 }
255 throw e;
256 } finally {
257 try {
258 if (callStop) {
259 stop();
260 }
261 } finally {
262 if (writing) {
263 database.afterWriting();
264 }
265 }
266 }
267 }
268 }
269
270 private long filterConcurrentUpdate(DbException e, long start) {
271 if (e.getErrorCode() != ErrorCode.CONCURRENT_UPDATE_1) {
272 throw e;
273 }
274 long now = System.nanoTime() / 1000000;
275 if (start != 0 && now - start > session.getLockTimeout()) {
276 throw DbException.get(ErrorCode.LOCK_TIMEOUT_1, e.getCause(), "");
277 }
278 Database database = session.getDatabase();
279 int sleep = 1 + MathUtils.randomInt(10);
280 while (true) {
281 try {
282 if (database.isMultiThreaded()) {
283 Thread.sleep(sleep);
284 } else {
285 database.wait(sleep);
286 }
287 } catch (InterruptedException e1) {
288 // ignore
289 }
290 long slept = System.nanoTime() / 1000000 - now;
291 if (slept >= sleep) {
292 break;
293 }
294 }
295 return start == 0 ? now : start;
296 }
297
298 public void close() {
299 canReuse = true;
300 }
301
302 public void cancel() {
303 this.cancel = true;
304 }
305
306 public String toString() {
307 return sql + Trace.formatParams(getParameters());
308 }
309
310 public boolean isCacheable() {
311 return false;
312 }
313
314 /**
315 * Whether the command is already closed (in which case it can be re-used).
316 *
317 * @return true if it can be re-used
318 */
319 public boolean canReuse() {
320 return canReuse;
321 }
322
323 /**
324 * The command is now re-used, therefore reset the canReuse flag, and the
325 * parameter values.
326 */
327 public void reuse() {
328 canReuse = false;
329 ArrayList<? extends ParameterInterface> parameters = getParameters();
330 for (int i = 0, size = parameters.size(); i < size; i++) {
331 ParameterInterface param = parameters.get(i);
332 param.setValue(null, true);
333 }
334 }
335
336 }
337