001: /*
002: * Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
003: * (http://h2database.com/html/license.html).
004: * Initial Developer: H2 Group
005: */
006: package org.h2.command;
007:
008: import java.sql.SQLException;
009:
010: import org.h2.constant.ErrorCode;
011: import org.h2.constant.SysProperties;
012: import org.h2.engine.Database;
013: import org.h2.engine.Session;
014: import org.h2.expression.Expression;
015: import org.h2.expression.Parameter;
016: import org.h2.message.Message;
017: import org.h2.result.LocalResult;
018: import org.h2.util.ObjectArray;
019:
020: /**
021: * A prepared statement.
022: */
023: public abstract class Prepared {
024:
025: /**
026: * The session.
027: */
028: protected Session session;
029:
030: /**
031: * The SQL string.
032: */
033: protected String sql;
034:
035: /**
036: * The position of the head record (used for indexes).
037: */
038: protected int headPos = -1;
039:
040: /**
041: * The list of parameters.
042: */
043: protected ObjectArray parameters;
044:
045: /**
046: * If the query should be prepared before each execution. This is set for
047: * queries with LIKE ?, because the query plan depends on the parameter
048: * value.
049: */
050: protected boolean prepareAlways;
051:
052: private long modificationMetaId;
053: private Command command;
054: private int objectId;
055: private int currentRowNumber;
056:
057: /**
058: * Check if this command is transactional.
059: * If it is not, then it forces the current transaction to commit.
060: *
061: * @return true if it is
062: */
063: public abstract boolean isTransactional();
064:
065: /**
066: * Get an empty result set containing the meta data.
067: *
068: * @return an empty result set
069: */
070: public abstract LocalResult queryMeta() throws SQLException;
071:
072: /**
073: * Check if this command is read only.
074: *
075: * @return true if it is
076: */
077: public boolean isReadOnly() {
078: return false;
079: }
080:
081: /**
082: * Create a new object.
083: *
084: * @param session the session
085: */
086: public Prepared(Session session) {
087: this .session = session;
088: modificationMetaId = session.getDatabase()
089: .getModificationMetaId();
090: }
091:
092: /**
093: * Check if the statement needs to be re-compiled.
094: *
095: * @return true if it must
096: */
097: public boolean needRecompile() throws SQLException {
098: Database db = session.getDatabase();
099: if (db == null) {
100: throw Message.getSQLException(ErrorCode.CONNECTION_BROKEN);
101: }
102: // TODO parser: currently, compiling every create/drop/... twice!
103: // because needRecompile return true even for the first execution
104: return SysProperties.RECOMPILE_ALWAYS || prepareAlways
105: || modificationMetaId < db.getModificationMetaId();
106: }
107:
108: /**
109: * Get the meta data modification id of the database when this statement was
110: * compiled.
111: *
112: * @return the meta data modification id
113: */
114: long getModificationMetaId() {
115: return modificationMetaId;
116: }
117:
118: /**
119: * Set the meta data modification id of this statement.
120: *
121: * @param id the new id
122: */
123: void setModificationMetaId(long id) {
124: this .modificationMetaId = id;
125: }
126:
127: /**
128: * Set the parameter list of this statement.
129: *
130: * @param parameters the parameter list
131: */
132: public void setParameterList(ObjectArray parameters) {
133: this .parameters = parameters;
134: }
135:
136: /**
137: * Get the parameter list.
138: *
139: * @return the parameter list
140: */
141: public ObjectArray getParameters() {
142: return parameters;
143: }
144:
145: protected void checkParameters() throws SQLException {
146: for (int i = 0; parameters != null && i < parameters.size(); i++) {
147: Parameter param = (Parameter) parameters.get(i);
148: param.checkSet();
149: }
150: }
151:
152: /**
153: * Set the command.
154: *
155: * @param command the new command
156: */
157: public void setCommand(Command command) {
158: this .command = command;
159: }
160:
161: /**
162: * Check if this object is a query.
163: *
164: * @return true if it is
165: */
166: public boolean isQuery() {
167: return false;
168: }
169:
170: /**
171: * Prepare this statement.
172: */
173: public void prepare() throws SQLException {
174: // nothing to do
175: }
176:
177: /**
178: * Execute the statement.
179: *
180: * @return the update count
181: * @throws SQLException if it is a query
182: */
183: public int update() throws SQLException {
184: throw Message
185: .getSQLException(ErrorCode.METHOD_NOT_ALLOWED_FOR_QUERY);
186: }
187:
188: /**
189: * Execute the query.
190: *
191: * @param maxrows the maximum number of rows to return
192: * @return the result set
193: * @throws SQLException if it is not a query
194: */
195: public LocalResult query(int maxrows) throws SQLException {
196: throw Message
197: .getSQLException(ErrorCode.METHOD_ONLY_ALLOWED_FOR_QUERY);
198: }
199:
200: /**
201: * Set the SQL statement.
202: *
203: * @param sql the SQL statement
204: */
205: public void setSQL(String sql) {
206: this .sql = sql;
207: }
208:
209: /**
210: * Get the SQL statement.
211: *
212: * @return the SQL statement
213: */
214: public String getSQL() {
215: return sql;
216: }
217:
218: protected int getObjectId(boolean needFresh, boolean dataFile) {
219: Database db = session.getDatabase();
220: int id = objectId;
221: if (id == 0) {
222: id = db.allocateObjectId(needFresh, dataFile);
223: }
224: objectId = 0;
225: return id;
226: }
227:
228: /**
229: * Get the SQL statement with the execution plan.
230: *
231: * @return the execution plan
232: */
233: public String getPlanSQL() {
234: return null;
235: }
236:
237: /**
238: * Check if this statement was cancelled.
239: *
240: * @throws SQLException if it was cancelled
241: */
242: public void checkCancelled() throws SQLException {
243: session.checkCancelled();
244: Command c = command != null ? command : session
245: .getCurrentCommand();
246: if (c != null) {
247: c.checkCancelled();
248: }
249: }
250:
251: /**
252: * Set the object id for this statement.
253: *
254: * @param i the object id
255: */
256: public void setObjectId(int i) {
257: this .objectId = i;
258: }
259:
260: /**
261: * Set the head position.
262: *
263: * @param headPos the head position
264: */
265: public void setHeadPos(int headPos) {
266: this .headPos = headPos;
267: }
268:
269: /**
270: * Set the session for this statement.
271: *
272: * @param currentSession the new session
273: */
274: public void setSession(Session currentSession) {
275: this .session = currentSession;
276: }
277:
278: void trace() throws SQLException {
279: if (session.getTrace().info()) {
280: StringBuffer buff = new StringBuffer();
281: buff.append(sql);
282: if (parameters.size() > 0) {
283: buff.append(" {");
284: for (int i = 0; i < parameters.size(); i++) {
285: if (i > 0) {
286: buff.append(", ");
287: }
288: buff.append(i + 1);
289: buff.append(": ");
290: Expression e = (Expression) parameters.get(i);
291: buff.append(e.getValue(session).getSQL());
292: }
293: buff.append("};");
294: } else {
295: buff.append(';');
296: }
297: session.getTrace().infoSQL(buff.toString());
298: }
299: }
300:
301: /**
302: * Set the prepare always flag.
303: * If set, the statement is re-compiled whenever it is executed.
304: *
305: * @param prepareAlways the new value
306: */
307: public void setPrepareAlways(boolean prepareAlways) {
308: this .prepareAlways = prepareAlways;
309: }
310:
311: /**
312: * Set the current row number.
313: *
314: * @param rowNumber the row number
315: */
316: protected void setCurrentRowNumber(int rowNumber) {
317: this .currentRowNumber = rowNumber;
318: }
319:
320: /**
321: * Get the current row number.
322: *
323: * @return the row number
324: */
325: public int getCurrentRowNumber() {
326: return currentRowNumber;
327: }
328:
329: /**
330: * Convert the statement to a String.
331: *
332: * @return the SQL statement
333: */
334: public String toString() {
335: return sql;
336: }
337:
338: }
|