/*
 * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0,
 * and the EPL 1.0 (http://h2database.com/html/license.html).
 * Initial Developer: H2 Group
 */
package org.h2.command;

import java.util.ArrayList;
import java.util.List;
import org.h2.api.DatabaseEventListener;
import org.h2.api.ErrorCode;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.expression.Parameter;
import org.h2.message.DbException;
import org.h2.message.Trace;
import org.h2.result.ResultInterface;
import org.h2.table.TableView;
import org.h2.util.MathUtils;
import org.h2.value.Value;

A prepared statement.
/** * A prepared statement. */
public abstract class Prepared {
The session.
/** * The session. */
protected Session session;
The SQL string.
/** * The SQL string. */
protected String sqlStatement;
Whether to create a new object (for indexes).
/** * Whether to create a new object (for indexes). */
protected boolean create = true;
The list of parameters.
/** * The list of parameters. */
protected ArrayList<Parameter> parameters;
If the query should be prepared before each execution. This is set for queries with LIKE ?, because the query plan depends on the parameter value.
/** * If the query should be prepared before each execution. This is set for * queries with LIKE ?, because the query plan depends on the parameter * value. */
protected boolean prepareAlways; private long modificationMetaId; private Command command;
Used to preserve object identities on database startup. 0 if object is not stored, -1 if object is stored and its ID is already read, >0 if object is stored and its id is not yet read.
/** * Used to preserve object identities on database startup. {@code 0} if * object is not stored, {@code -1} if object is stored and its ID is * already read, {@code >0} if object is stored and its id is not yet read. */
private int persistedObjectId; private long currentRowNumber; private int rowScanCount;
Common table expressions (CTE) in queries require us to create temporary views, which need to be cleaned up once a command is done executing.
/** * Common table expressions (CTE) in queries require us to create temporary views, * which need to be cleaned up once a command is done executing. */
private List<TableView> cteCleanups;
Create a new object.
Params:
  • session – the session
/** * Create a new object. * * @param session the session */
public Prepared(Session session) { this.session = session; modificationMetaId = session.getDatabase().getModificationMetaId(); }
Check if this command is transactional. If it is not, then it forces the current transaction to commit.
Returns:true if it is
/** * Check if this command is transactional. * If it is not, then it forces the current transaction to commit. * * @return true if it is */
public abstract boolean isTransactional();
Get an empty result set containing the meta data.
Returns:the result set
/** * Get an empty result set containing the meta data. * * @return the result set */
public abstract ResultInterface queryMeta();
Get the command type as defined in CommandInterface
Returns:the statement type
/** * Get the command type as defined in CommandInterface * * @return the statement type */
public abstract int getType();
Check if this command is read only.
Returns:true if it is
/** * Check if this command is read only. * * @return true if it is */
public boolean isReadOnly() { return false; }
Check if the statement needs to be re-compiled.
Returns:true if it must
/** * Check if the statement needs to be re-compiled. * * @return true if it must */
public boolean needRecompile() { Database db = session.getDatabase(); if (db == null) { throw DbException.get(ErrorCode.CONNECTION_BROKEN_1, "database closed"); } // parser: currently, compiling every create/drop/... twice // because needRecompile return true even for the first execution return prepareAlways || modificationMetaId < db.getModificationMetaId() || db.getSettings().recompileAlways; }
Get the meta data modification id of the database when this statement was compiled.
Returns:the meta data modification id
/** * Get the meta data modification id of the database when this statement was * compiled. * * @return the meta data modification id */
long getModificationMetaId() { return modificationMetaId; }
Set the meta data modification id of this statement.
Params:
  • id – the new id
/** * Set the meta data modification id of this statement. * * @param id the new id */
void setModificationMetaId(long id) { this.modificationMetaId = id; }
Set the parameter list of this statement.
Params:
  • parameters – the parameter list
/** * Set the parameter list of this statement. * * @param parameters the parameter list */
public void setParameterList(ArrayList<Parameter> parameters) { this.parameters = parameters; }
Get the parameter list.
Returns:the parameter list
/** * Get the parameter list. * * @return the parameter list */
public ArrayList<Parameter> getParameters() { return parameters; }
Check if all parameters have been set.
Throws:
  • DbException – if any parameter has not been set
/** * Check if all parameters have been set. * * @throws DbException if any parameter has not been set */
protected void checkParameters() { if (persistedObjectId < 0) { // restore original persistedObjectId on Command re-run // i.e. due to concurrent update persistedObjectId = -persistedObjectId - 1; } if (parameters != null) { for (Parameter param : parameters) { param.checkSet(); } } }
Set the command.
Params:
  • command – the new command
/** * Set the command. * * @param command the new command */
public void setCommand(Command command) { this.command = command; }
Check if this object is a query.
Returns:true if it is
/** * Check if this object is a query. * * @return true if it is */
public boolean isQuery() { return false; }
Prepare this statement.
/** * Prepare this statement. */
public void prepare() { // nothing to do }
Execute the statement.
Throws:
Returns:the update count
/** * Execute the statement. * * @return the update count * @throws DbException if it is a query */
public int update() { throw DbException.get(ErrorCode.METHOD_NOT_ALLOWED_FOR_QUERY); }
Execute the query.
Params:
  • maxrows – the maximum number of rows to return
Throws:
Returns:the result set
/** * Execute the query. * * @param maxrows the maximum number of rows to return * @return the result set * @throws DbException if it is not a query */
@SuppressWarnings("unused") public ResultInterface query(int maxrows) { throw DbException.get(ErrorCode.METHOD_ONLY_ALLOWED_FOR_QUERY); }
Set the SQL statement.
Params:
  • sql – the SQL statement
/** * Set the SQL statement. * * @param sql the SQL statement */
public void setSQL(String sql) { this.sqlStatement = sql; }
Get the SQL statement.
Returns:the SQL statement
/** * Get the SQL statement. * * @return the SQL statement */
public String getSQL() { return sqlStatement; }
Get the object id to use for the database object that is created in this statement. This id is only set when the object is already persisted. If not set, this method returns 0.
Returns:the object id or 0 if not set
/** * Get the object id to use for the database object that is created in this * statement. This id is only set when the object is already persisted. * If not set, this method returns 0. * * @return the object id or 0 if not set */
protected int getPersistedObjectId() { int id = persistedObjectId; return id >= 0 ? id : 0; }
Get the current object id, or get a new id from the database. The object id is used when creating new database object (CREATE statement). This method may be called only once.
Returns:the object id
/** * Get the current object id, or get a new id from the database. The object * id is used when creating new database object (CREATE statement). This * method may be called only once. * * @return the object id */
protected int getObjectId() { int id = persistedObjectId; if (id == 0) { id = session.getDatabase().allocateObjectId(); } else if (id < 0) { throw DbException.throwInternalError("Prepared.getObjectId() was called before"); } persistedObjectId = -persistedObjectId - 1; // while negative, it can be restored later return id; }
Get the SQL statement with the execution plan.
Params:
  • alwaysQuote – quote all identifiers
Returns:the execution plan
/** * Get the SQL statement with the execution plan. * * @param alwaysQuote quote all identifiers * @return the execution plan */
public String getPlanSQL(boolean alwaysQuote) { return null; }
Check if this statement was canceled.
Throws:
  • DbException – if it was canceled
/** * Check if this statement was canceled. * * @throws DbException if it was canceled */
public void checkCanceled() { session.checkCanceled(); Command c = command != null ? command : session.getCurrentCommand(); if (c != null) { c.checkCanceled(); } }
Set the persisted object id for this statement.
Params:
  • i – the object id
/** * Set the persisted object id for this statement. * * @param i the object id */
public void setPersistedObjectId(int i) { this.persistedObjectId = i; this.create = false; }
Set the session for this statement.
Params:
  • currentSession – the new session
/** * Set the session for this statement. * * @param currentSession the new session */
public void setSession(Session currentSession) { this.session = currentSession; }
Print information about the statement executed if info trace level is enabled.
Params:
  • startTimeNanos – when the statement was started
  • rowCount – the query or update row count
/** * Print information about the statement executed if info trace level is * enabled. * * @param startTimeNanos when the statement was started * @param rowCount the query or update row count */
void trace(long startTimeNanos, int rowCount) { if (session.getTrace().isInfoEnabled() && startTimeNanos > 0) { long deltaTimeNanos = System.nanoTime() - startTimeNanos; String params = Trace.formatParams(parameters); session.getTrace().infoSQL(sqlStatement, params, rowCount, deltaTimeNanos / 1000 / 1000); } // startTime_nanos can be zero for the command that actually turns on // statistics if (session.getDatabase().getQueryStatistics() && startTimeNanos != 0) { long deltaTimeNanos = System.nanoTime() - startTimeNanos; session.getDatabase().getQueryStatisticsData(). update(toString(), deltaTimeNanos, rowCount); } }
Set the prepare always flag. If set, the statement is re-compiled whenever it is executed.
Params:
  • prepareAlways – the new value
/** * Set the prepare always flag. * If set, the statement is re-compiled whenever it is executed. * * @param prepareAlways the new value */
public void setPrepareAlways(boolean prepareAlways) { this.prepareAlways = prepareAlways; }
Set the current row number.
Params:
  • rowNumber – the row number
/** * Set the current row number. * * @param rowNumber the row number */
public void setCurrentRowNumber(long rowNumber) { if ((++rowScanCount & 127) == 0) { checkCanceled(); } this.currentRowNumber = rowNumber; setProgress(); }
Get the current row number.
Returns:the row number
/** * Get the current row number. * * @return the row number */
public long getCurrentRowNumber() { return currentRowNumber; }
Notifies query progress via the DatabaseEventListener
/** * Notifies query progress via the DatabaseEventListener */
private void setProgress() { if ((currentRowNumber & 127) == 0) { session.getDatabase().setProgress( DatabaseEventListener.STATE_STATEMENT_PROGRESS, sqlStatement, // TODO update interface MathUtils.convertLongToInt(currentRowNumber), 0); } }
Convert the statement to a String.
Returns:the SQL statement
/** * Convert the statement to a String. * * @return the SQL statement */
@Override public String toString() { return sqlStatement; }
Get the SQL snippet of the value list.
Params:
  • values – the value list
Returns:the SQL snippet
/** * Get the SQL snippet of the value list. * * @param values the value list * @return the SQL snippet */
protected static String getSQL(Value[] values) { StringBuilder builder = new StringBuilder(); for (int i = 0, l = values.length; i < l; i++) { if (i > 0) { builder.append(", "); } Value v = values[i]; if (v != null) { v.getSQL(builder); } } return builder.toString(); }
Get the SQL snippet of the expression list.
Params:
  • list – the expression list
Returns:the SQL snippet
/** * Get the SQL snippet of the expression list. * * @param list the expression list * @return the SQL snippet */
protected static String getSimpleSQL(Expression[] list) { StringBuilder builder = new StringBuilder(); Expression.writeExpressions(builder, list, false); return builder.toString(); }
Set the SQL statement of the exception to the given row.
Params:
  • e – the exception
  • rowId – the row number
  • values – the values of the row
Returns:the exception
/** * Set the SQL statement of the exception to the given row. * * @param e the exception * @param rowId the row number * @param values the values of the row * @return the exception */
protected DbException setRow(DbException e, int rowId, String values) { StringBuilder buff = new StringBuilder(); if (sqlStatement != null) { buff.append(sqlStatement); } buff.append(" -- "); if (rowId > 0) { buff.append("row #").append(rowId + 1).append(' '); } buff.append('(').append(values).append(')'); return e.addSQL(buff.toString()); } public boolean isCacheable() { return false; }
Returns:the temporary views created for CTE's.
/** * @return the temporary views created for CTE's. */
public List<TableView> getCteCleanups() { return cteCleanups; }
Set the temporary views created for CTE's.
Params:
  • cteCleanups – the temporary views
/** * Set the temporary views created for CTE's. * * @param cteCleanups the temporary views */
public void setCteCleanups(List<TableView> cteCleanups) { this.cteCleanups = cteCleanups; } public Session getSession() { return session; } }