org.sqlite.Conn Maven / Gradle / Ivy
Show all versions of sqlite-javacpp Show documentation
/*
* The author disclaims copyright to this source code. In place of
* a legal notice, here is a blessing:
*
* May you do good and not evil.
* May you find forgiveness for yourself and forgive others.
* May you share freely, never taking more than you give.
*/
package org.sqlite;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import org.bytedeco.javacpp.BytePointer;
import org.bytedeco.javacpp.IntPointer;
import org.bytedeco.javacpp.Pointer;
import org.sqlite.parser.ast.LiteralExpr;
import org.sqlite.parser.ast.Pragma;
import org.sqlite.parser.ast.QualifiedName;
import static org.sqlite.SQLite.*;
/**
* Database Connection Handle
* @see sqlite3
*/
public final class Conn implements AutoCloseable {
/** If the filename is ":memory:", then a private, temporary in-memory database is created for the connection. */
public static final String MEMORY = ":memory:";
/** If the filename is an empty string, then a private, temporary on-disk database will be created. */
public static final String TEMP_FILE = "";
private sqlite3 pDb;
private final boolean sharedCacheMode;
private TimeoutProgressCallback timeoutProgressCallback;
private final Map cache = new LinkedHashMap() {
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
if (size() <= maxCacheSize) {
return false;
}
Iterator it = values().iterator();
while (it.hasNext()) {
if (size() <= maxCacheSize) {
return false;
}
it.next().close(true);
it.remove();
}
return false;
}
};
private int maxCacheSize = 100; // TODO parameterize
// Make sure a stmt is not finalized while current conn is being closed
final Object lock = new Object();
/**
* Open a new database connection.
*
* When the `filename` is an URI, extra query parameters can be used ({@link OpenQueryParameter})
* @param filename ":memory:" for memory db, "" for temp file db
* @param flags {@link org.sqlite.OpenFlags}.* (TODO EnumSet or BitSet, default flags)
* @param vfs may be null
* @return Opened Connection
*/
public static Conn open(String filename, int flags, String vfs) throws SQLiteException {
if (!sqlite3_threadsafe()) {
throw new SQLiteException("sqlite library was not compiled for thread-safe operation", ErrCodes.WRAPPER_SPECIFIC);
}
final sqlite3 pDb = new sqlite3();
final int res = sqlite3_open_v2(nativeString(filename), pDb, flags, nativeString(vfs));
if (res != SQLITE_OK) {
if (pDb.isNull()) {
sqlite3_close(new sqlite3(pDb));
}
throw new SQLiteException(String.format("error while opening a database connection to '%s'", filename), res);
}
final boolean uri = (flags & OpenFlags.SQLITE_OPEN_URI) != 0;
final Map queryParams = uri ? OpenQueryParameter.getQueryParams(filename) : Collections.emptyMap();
// TODO not reliable (and may depend on sqlite3_enable_shared_cache global status)
final boolean sharedCacheMode = "shared".equals(queryParams.get("cache")) || (flags & OpenFlags.SQLITE_OPEN_SHAREDCACHE) != 0;
final sqlite3 sqlite3 = pDb.isNull() ? null: pDb;
final Conn conn = new Conn(sqlite3, sharedCacheMode);
if (uri && !queryParams.isEmpty()) {
for (OpenQueryParameter parameter : OpenQueryParameter.values()) {
parameter.config(queryParams, conn);
}
}
return conn;
}
@Override
protected void finalize() throws Throwable {
if (pDb != null) {
sqlite3_log(-1, nativeString("dangling SQLite connection."));
closeNoCheck();
}
super.finalize();
}
/**
* Close a database connection.
* @return result code (No exception is thrown).
*/
public int closeNoCheck() {
if (pDb == null) {
return SQLITE_OK;
}
synchronized (lock) {
flush();
// Dangling statements
sqlite3_stmt stmt = sqlite3_next_stmt(pDb, null);
while (stmt != null && !stmt.isNull()) {
if (sqlite3_stmt_busy(stmt)) {
sqlite3_log(ErrCodes.SQLITE_MISUSE, nativeString("Dangling statement (not reset): \"" + sqlite3_sql(stmt) + "\""));
} else {
sqlite3_log(ErrCodes.SQLITE_MISUSE, nativeString("Dangling statement (not finalize): \"" + sqlite3_sql(stmt) + "\""));
}
stmt = sqlite3_next_stmt(pDb, stmt);
}
final int res = sqlite3_close_v2(pDb); // must be called only once...
pDb = null;
return res;
}
}
/**
* Close a database connection and throw an exception if an error occurred.
*/
public void close() throws ConnException {
final int res = closeNoCheck();
if (res != ErrCodes.SQLITE_OK) {
throw new ConnException(this, "error while closing connection", res);
}
}
private Conn(sqlite3 pDb, boolean sharedCacheMode) {
assert pDb != null;
this.pDb = pDb;
this.sharedCacheMode = sharedCacheMode;
}
/**
* Determine if a database is read-only.
* @param dbName "main" or "temp" or attached database name
* @return true
if specified database is in read only mode.
* @throws ConnException if current connection is closed or dbName
is not valid.
* @see sqlite3_db_readonly
*/
public boolean isReadOnly(String dbName) throws ConnException {
checkOpen();
final int res = sqlite3_db_readonly(pDb, nativeString(dbName)); // ko if pDb is null
if (res < 0) {
throw new ConnException(this, String.format("'%s' is not the name of a database", dbName), ErrCodes.WRAPPER_SPECIFIC);
}
return res == 1;
}
/**
* Determine if a database is query-only.
* @param dbName "main" or "temp" or attached database name
* @return true
if specified database is in query only mode.
* @throws SQLiteException if current connection is closed or dbName
is not valid.
* @see pragma query_only
*/
public boolean isQueryOnly(String dbName) throws SQLiteException { // since 3.8.0
return pragma(dbName, "query_only");
}
/**
* Set a database is in query-only mode.
* @param dbName "main" or "temp" or attached database name
* @param queryOnly true
to activate query-only mode
* @throws ConnException if current connection is closed or dbName
is not valid.
*/
public void setQueryOnly(String dbName, boolean queryOnly) throws ConnException { // since 3.8.0
pragma(dbName, "query_only", queryOnly);
}
/**
* Determine if a connection is in shared-cache mode.
* not reliable (and may depend on sqlite3_enable_shared_cache global status)
* @return true
if shared-cache mode is active.
* @see SQLite Shared-Cache Mode
*/
public boolean isSharedCacheMode() {
return sharedCacheMode;
}
/**
* Query READ UNCOMMITTED isolation.
* @param dbName "main" or "temp" or attached database name
* @return true
if READ UNCOMMITTED isolation is active.
* @throws SQLiteException if current connection is closed or dbName
is not valid.
*/
public boolean getReadUncommitted(String dbName) throws SQLiteException {
return pragma(dbName, "read_uncommitted");
}
/**
* Query READ UNCOMMITTED isolation.
* @param dbName "main" or "temp" or attached database name
* @param flag true
to activate READ UNCOMMITTED isolation
* @throws ConnException if current connection is closed or dbName
is not valid.
*/
public void setReadUncommitted(String dbName, boolean flag) throws ConnException {
pragma(dbName, "read_uncommitted", flag);
}
/**
* Test for auto-commit mode.
* @return true
if auto-commit is active.
* @throws ConnException if current connection is closed or dbName
is not valid.
* @see sqlite3_get_autocommit
*/
public boolean getAutoCommit() throws ConnException {
checkOpen();
return sqlite3_get_autocommit(pDb); // ko if pDb is null
}
/**
* Compile an SQL statement.
* @param sql query
* @return Prepared Statement
* @throws ConnException if current connection is closed or an error occurred during statement compilation.
* @see sqlite3_prepare_v2
*/
public Stmt prepare(String sql, boolean cacheable) throws ConnException {
checkOpen();
if (cacheable) {
final Stmt stmt = find(sql);
if (stmt != null) {
return stmt;
}
}
final BytePointer pSql = nativeString(sql);
sqlite3_stmt stmt = new sqlite3_stmt();
final BytePointer tail = new BytePointer();
final int res = blockingPrepare(null, pSql, stmt, tail);
check(res, "error while preparing statement '%s'", sql);
stmt = stmt.isNull() ? null: stmt;
return new Stmt(this, sql, stmt, tail, cacheable);
}
// http://sqlite.org/unlock_notify.html
//JCP! if mvn.project.property.sqlite.enable.unlock.notify == "true"
//JCP> private int blockingPrepare(Conn unused, BytePointer pSql, sqlite3_stmt ppStmt, BytePointer ppTail) throws ConnException {
//JCP> int rc;
//JCP> while (ErrCodes.SQLITE_LOCKED == (rc = sqlite3_prepare_v2(pDb, pSql, -1, ppStmt, ppTail))) {
//JCP> rc = waitForUnlockNotify(null);
//JCP> if (rc != SQLITE_OK) {
//JCP> break;
//JCP> }
//JCP> }
//JCP> return rc;
//JCP> }
//JCP! else
private int blockingPrepare(Object unused, BytePointer pSql, sqlite3_stmt ppStmt, BytePointer ppTail) {
return sqlite3_prepare_v2(pDb, pSql, -1, ppStmt, ppTail); // FIXME nbytes + 1
}
//JCP! endif
// http://sqlite.org/unlock_notify.html
//JCP! if mvn.project.property.sqlite.enable.unlock.notify == "true"
//JCP> int waitForUnlockNotify(Conn unused) throws ConnException {
//JCP> UnlockNotification notif = UnlockNotificationCallback.INSTANCE.add(pDb);
//JCP> int rc = sqlite3_unlock_notify(pDb, UnlockNotificationCallback.INSTANCE, pDb);
//JCP> assert rc == ErrCodes.SQLITE_LOCKED || rc == ExtErrCodes.SQLITE_LOCKED_SHAREDCACHE || rc == SQLITE_OK;
//JCP> if (rc == SQLITE_OK) {
//JCP> notif.await(this);
//JCP> }
//JCP> return rc;
//JCP> }
//JCP! else
int waitForUnlockNotify(Object unused) throws ConnException {
return ErrCodes.SQLITE_LOCKED;
}
//JCP! endif
/**
* @return Run-time library version number
* @see sqlite3_libversion
*/
public static String libversion() {
return getString(sqlite3_libversion());
}
/**
* @return Run-time library version number
* @see sqlite3_libversion_number
*/
public static int libversionNumber() {
return sqlite3_libversion_number();
}
public Stmt prepareAndBind(String sql, boolean cacheable, Object... params) throws SQLiteException {
Stmt s = null;
try {
s = prepare(sql, cacheable);
s.bind(params);
return s;
} catch (SQLiteException e) {
if (s!= null) {
s.closeNoCheck();
}
throw e;
}
}
/**
* Execute an INSERT and return the ROWID.
* @param sql INSERT statement
* @param params SQL statement parameters
* @return the rowid inserted.
* @throws SQLiteException if no row is inserted or many rows are inserted.
*/
public long insert(String sql, boolean cacheable, Object... params) throws SQLiteException {
try (Stmt s = prepare(sql, cacheable)) {
return s.insert(params);
}
}
/**
* Execute a DML and return the number of rows impacted.
* @param sql DML statement
* @param params SQL statement parameters
* @return the number of database rows that were changed or inserted or deleted.
*/
public int execDml(String sql, boolean cacheable, Object... params) throws SQLiteException {
try (Stmt s = prepare(sql, cacheable)) {
return s.execDml(params);
}
}
/**
* @param sql SELECT statement
* @param params Statement parameters
* @return returns true
if a query in the SQL statement returns one or more rows and
* false
if the SQL returns an empty set.
*/
public boolean exists(String sql, boolean cacheable, Object... params) throws SQLiteException {
try (Stmt s = prepare(sql, cacheable)) {
return s.exists(params);
}
}
/**
* Run multiple statements of SQL.
* @param sql statements
* @throws SQLiteException if current connection is closed or an error occurred during SQL execution.
*/
public void exec(String sql) throws SQLiteException {
while (sql != null && !sql.isEmpty()) {
try (Stmt s = prepare(sql, false)) {
sql = s.getTail();
if (!s.isDumb()) { // this happens for a comment or white-space
s.exec();
}
}
}
}
/**
* Executes one or many non-parameterized statement(s) (separated by semi-colon) with no control and no stmt cache.
* @param sql statements
* @throws ConnException if current connection is closed or an error occurred during SQL execution.
* @see sqlite3_exec
*/
public void fastExec(String sql) throws ConnException {
checkOpen();
check(sqlite3_exec(pDb, nativeString(sql), null, null, null), "error while executing '%s'", sql);
}
/**
* Open A BLOB For Incremental I/O.
* @param dbName "main" or "temp" or attached database name
* @param tblName table name
* @param colName column name
* @param iRow row id
* @param rw true
for read-write mode, false
for read-only mode.
* @return BLOB
* @throws SQLiteException if current connection is closed or an error occurred during BLOB open.
* @see sqlite3_blob_open
*/
public Blob open(String dbName, String tblName, String colName, long iRow, boolean rw) throws SQLiteException {
checkOpen();
final sqlite3_blob pBlob = new sqlite3_blob();
final int res = sqlite3_blob_open(pDb, nativeString(dbName), nativeString(tblName), nativeString(colName), iRow, rw, pBlob); // ko if pDb is null
final sqlite3_blob blob = pBlob.isNull() ? null : new sqlite3_blob(pBlob);
if (res != SQLITE_OK) {
sqlite3_blob_close(blob);
throw new SQLiteException(this, String.format("error while opening a blob to (db: '%s', table: '%s', col: '%s', row: %d)",
dbName, tblName, colName, iRow), res);
}
return new Blob(this, blob);
}
/**
* @return the number of database rows that were changed or inserted or deleted by the most recently completed SQL statement
* on the current database connection.
* @throws ConnException if current connection is closed
* @see sqlite3_changes
*/
public int getChanges() throws ConnException {
checkOpen();
return sqlite3_changes(pDb);
}
/**
* @return Total number of rows modified
* @throws ConnException if current connection is closed
* @see sqlite3_total_changes
*/
public int getTotalChanges() throws ConnException {
checkOpen();
return sqlite3_total_changes(pDb);
}
/**
* @return the rowid of the most recent successful INSERT into the database.
* @throws ConnException if current connection is closed
* @see sqlite3_last_insert_rowid
*/
public long getLastInsertRowid() throws ConnException {
checkOpen();
return sqlite3_last_insert_rowid(pDb);
}
/**
* Interrupt a long-running query
* @throws ConnException if current connection is closed
* @see sqlite3_interrupt
*/
public void interrupt() throws ConnException {
checkOpen();
sqlite3_interrupt(pDb);
}
/**
* Set a busy timeout
* @param ms timeout in millis
* @throws ConnException if current connection is closed
* @see sqlite3_busy_timeout
*/
public void setBusyTimeout(int ms) throws ConnException {
checkOpen();
check(sqlite3_busy_timeout(pDb, ms), "error while setting busy timeout on '%s'", getFilename());
}
/**
* Register a callback to handle SQLITE_BUSY errors
* @param bh Busy handler
* @return result code
* @see sqlite3_busy_handler
*/
public int setBusyHandler(BusyHandler bh) throws ConnException {
checkOpen();
return sqlite3_busy_handler(pDb, bh, null);
}
/**
* @return the filename for the current "main" database connection
* @see sqlite3_db_filename
*/
public String getFilename() {
if (pDb == null) {
return null;
}
return getString(sqlite3_db_filename(pDb, nativeString("main"))); // ko if pDb is null
}
/**
* @return english-language text that describes the last error
* @see sqlite3_errmsg
*/
public String getErrMsg() {
return getString(sqlite3_errmsg(pDb)); // ok if pDb is null => SQLITE_NOMEM
}
/**
* @return the numeric result code or extended result code of the last API call
* @see ErrCodes
* @see sqlite3_errmsg
*/
public int getErrCode() {
return sqlite3_errcode(pDb); // ok if pDb is null => SQLITE_NOMEM
}
/**
* @param onoff enable or disable extended result codes
*/
public void setExtendedResultCodes(boolean onoff) throws ConnException {
checkOpen();
check(sqlite3_extended_result_codes(pDb, onoff), "error while enabling extended result codes on '%s'", getFilename()); // ko if pDb is null
}
/**
* @return the extended result code even when extended result codes are disabled.
* @see ExtErrCodes
* @see sqlite3_errmsg
*/
public int getExtendedErrcode() {
return sqlite3_extended_errcode(pDb); // ok if pDb is null => SQLITE_NOMEM
}
/**
* @param onoff enable or disable foreign keys constraints
*/
public boolean enableForeignKeys(boolean onoff) throws ConnException {
checkOpen();
final IntPointer pOk = new IntPointer(1);
check(sqlite3_db_config(pDb, 1002, onoff ? 1 : 0, pOk), "error while setting db config on '%s'", getFilename());
return toBool(pOk);
}
/**
* @return whether or not foreign keys constraints enforcement is enabled
*/
public boolean areForeignKeysEnabled() throws ConnException {
checkOpen();
final IntPointer pOk = new IntPointer(1);
check(sqlite3_db_config(pDb, 1002, -1, pOk), "error while querying db config on '%s'", getFilename());
return toBool(pOk);
}
/**
* @param onoff enable or disable triggers
*/
public boolean enableTriggers(boolean onoff) throws ConnException {
checkOpen();
final IntPointer pOk = new IntPointer(1);
check(sqlite3_db_config(pDb, 1003, onoff ? 1 : 0, pOk), "error while setting db config on '%s'", getFilename());
return toBool(pOk);
}
/**
* @return whether or not triggers are enabled
*/
public boolean areTriggersEnabled() throws ConnException {
checkOpen();
final IntPointer pOk = new IntPointer(1);
check(sqlite3_db_config(pDb, 1003, -1, pOk), "error while querying db config on '%s'", getFilename());
return toBool(pOk);
}
/**
* @param onoff enable or disable loading extension
* @see sqlite3_enable_load_extension
*/
public void enableLoadExtension(boolean onoff) throws ConnException {
checkOpen();
check(sqlite3_enable_load_extension(pDb, onoff), "error while enabling load extension on '%s'", getFilename());
}
/**
* Load an extension.
* @param file path to the extension
* @param proc entry point (may be null)
* @return error message or null
* @see sqlite3_load_extension
*/
public String loadExtension(String file, String proc) throws ConnException {
checkOpen();
final BytePointer pErrMsg = new BytePointer();
final int res = sqlite3_load_extension(pDb, nativeString(file), nativeString(proc), pErrMsg);
if (res != SQLITE_OK) {
final String errMsg = getString(pErrMsg);
sqlite3_free(pErrMsg);
return errMsg;
}
return null;
}
/**
* Find the current value of a limit.
* @param id one of the limit categories
* @return current limit value
* @see sqlite3_limit
*/
public int getLimit(int id) throws ConnException {
checkOpen();
return sqlite3_limit(pDb, id, -1);
}
/**
* Change the value of a limit.
* @param id one of the limit categories
* @param newVal new limit value
* @return previous limit value
* @see sqlite3_limit
*/
public int setLimit(int id, int newVal) throws ConnException {
checkOpen();
return sqlite3_limit(pDb, id, newVal);
}
boolean[] getTableColumnMetadata(String dbName, String tblName, String colName) throws ConnException {
checkOpen();
final IntPointer pNotNull = new IntPointer(1);
final IntPointer pPrimaryKey = new IntPointer(1);
final IntPointer pAutoinc = new IntPointer(1);
check(sqlite3_table_column_metadata(pDb,
nativeString(dbName),
nativeString(tblName),
nativeString(colName),
null, null,
pNotNull, pPrimaryKey, pAutoinc), "error while accessing table column metatada of '%s'", tblName);
return new boolean[]{toBool(pNotNull), toBool(pPrimaryKey), toBool(pAutoinc)};
}
private static boolean toBool(IntPointer p) {
return p.get() != 0;
}
/**
* Initialize the backup.
* @param dst destination database
* @param dstName destination database name
* @param src source database
* @param srcName source database name
* @return Backup
* @throws ConnException if backup init failed.
* @see sqlite3_backup_init
*/
public static Backup open(Conn dst, String dstName, Conn src, String srcName) throws ConnException {
dst.checkOpen();
src.checkOpen();
final sqlite3_backup pBackup = sqlite3_backup_init(dst.pDb, nativeString(dstName), src.pDb, nativeString(srcName));
if (pBackup == null || pBackup.isNull()) {
throw new ConnException(dst, "backup init failed", dst.getErrCode());
}
return new Backup(pBackup, dst, src);
}
/**
* Sets the number of seconds the driver will wait for a statement to execute to the given number of seconds.
* @param timeout in seconds
*/
public void setQueryTimeout(int timeout) throws ConnException {
if (timeout == 0) {
if (timeoutProgressCallback == null) {
return; // nothing to do
}
}
if (timeoutProgressCallback == null) {
checkOpen();
timeoutProgressCallback = new TimeoutProgressCallback();
sqlite3_progress_handler(pDb, 100, timeoutProgressCallback, null);
}
timeoutProgressCallback.setTimeout(timeout * 1000);
}
/**
* @param tc Tracing callback
* @see sqlite3_trace
*/
public void trace(TraceCallback tc) throws ConnException {
checkOpen();
sqlite3_trace(pDb, tc, null);
}
/**
* @param pc Profiling callback
* @see sqlite3_profile
*/
public void profile(ProfileCallback pc) throws ConnException {
checkOpen();
sqlite3_profile(pDb, pc, null);
}
/**
* @param uh Data change notification callback.
* @see sqlite3_update_hook
*/
public Pointer updateHook(UpdateHook uh) throws ConnException {
checkOpen();
return sqlite3_update_hook(pDb, uh, null);
}
/**
* Register an authorizer callback.
* @param auth Compile-time authorization callback (may be null)
* @return result code
* @see sqlite3_set_authorizer
*/
public int setAuhtorizer(Authorizer auth) throws ConnException {
checkOpen();
return sqlite3_set_authorizer(pDb, auth, null);
}
/**
* Create a user defined SQL scalar function.
* @param name function name
* @param nArg number of arguments expected
* @param flags {@link org.sqlite.FunctionFlags}.*
* @param xFunc function implementation
* @see sqlite3_create_function_v2
*/
public void createScalarFunction(String name, int nArg, int flags, ScalarCallback xFunc) throws ConnException {
checkOpen();
check(sqlite3_create_function_v2(pDb, nativeString(name), nArg, flags, null, xFunc, null, null, null),
"error while registering function %s", name);
}
/**
* Create a user defined SQL aggregate function.
* @param name function name
* @param nArg number of arguments expected
* @param flags {@link org.sqlite.FunctionFlags}.*
* @param xStep function implementation
* @param xFinal function implementation
* @see sqlite3_create_function_v2
*/
public void createAggregateFunction(String name, int nArg, int flags, AggregateStepCallback xStep,
AggregateFinalCallback xFinal) throws ConnException {
checkOpen();
check(sqlite3_create_function_v2(pDb, nativeString(name), nArg, flags, null, null, xStep, xFinal, null),
"error while registering function %s", name);
}
/**
* @param dbName "main" or "temp" or attached database name
* @return the text encoding used by the dbName
database
* @see pragma encoding
*/
public String encoding(String dbName) throws SQLiteException {
Pragma pragma = new Pragma(new QualifiedName(dbName, "encoding"), null);
try (Stmt s = prepare(pragma.toSql(), false)) {
if (!s.step(0)) {
throw new StmtException(s, "No result", ErrCodes.WRAPPER_SPECIFIC);
}
return s.getColumnText(0);
}
}
/**
* @return whether or not this database connection is closed
*/
public boolean isClosed() {
return pDb == null;
}
/**
* @throws ConnException when this database connection is closed
*/
public void checkOpen() throws ConnException {
if (isClosed()) {
throw new ConnException(this, "connection closed", ErrCodes.WRAPPER_SPECIFIC);
}
}
private void check(int res, String format, String param) throws ConnException {
if (res != SQLITE_OK) {
throw new ConnException(this, String.format(format, param), res);
}
}
boolean pragma(String dbName, String name) throws SQLiteException {
Pragma pragma = new Pragma(new QualifiedName(dbName, name), null);
try (Stmt s = prepare(pragma.toSql(), false)) {
if (!s.step(0)) {
throw new StmtException(s, "No result", ErrCodes.WRAPPER_SPECIFIC);
}
return s.getColumnInt(0) == 1;
}
}
void pragma(String dbName, String name, boolean value) throws ConnException {
Pragma pragma = new Pragma(new QualifiedName(dbName, name), LiteralExpr.integer(value ? 1 : 0));
fastExec(pragma.toSql());
}
// To be called in Conn.prepare
Stmt find(String sql) {
if (maxCacheSize <= 0) {
return null;
}
synchronized (cache) {
return cache.remove(sql);
}
}
// To be called in Stmt.close
boolean release(Stmt stmt) {
if (maxCacheSize <= 0) {
return false;
}
synchronized (cache) {
cache.put(stmt.sql,stmt);
}
return true;
}
/**
* Prepared statements cache is turned off when max size is 0
*/
public int getMaxCacheSize() {
return maxCacheSize;
}
/**
* Prepared statements cache size
*/
public int getCacheSize() {
return cache.size();
}
/**
* sets the size of prepared statements cache.
* Cache is turned off (and flushed) when size <= 0
*/
public void setMaxCacheSize(int maxCacheSize) {
if (maxCacheSize <= 0) {
flush();
}
this.maxCacheSize = maxCacheSize;
}
/**
* Finalize and free the cached prepared statements
* To be called in Conn.close
*/
private void flush() {
if (maxCacheSize <= 0) {
return;
}
synchronized (cache) {
final Iterator it = cache.values().iterator();
while (it.hasNext()) {
final Stmt stmt = it.next();
stmt.close(true);
it.remove();
}
}
}
}