// Can't throw. private void reconfigureAllConnectionsLocked() { if (mAvailablePrimaryConnection != null) { try { mAvailablePrimaryConnection.reconfigure(mConfiguration); // might throw } catch (RuntimeException ex) { Log.e( TAG, "Failed to reconfigure available primary connection, closing it: " + mAvailablePrimaryConnection, ex); closeConnectionAndLogExceptionsLocked(mAvailablePrimaryConnection); mAvailablePrimaryConnection = null; } } int count = mAvailableNonPrimaryConnections.size(); for (int i = 0; i < count; i++) { final SQLiteConnection connection = mAvailableNonPrimaryConnections.get(i); try { connection.reconfigure(mConfiguration); // might throw } catch (RuntimeException ex) { Log.e( TAG, "Failed to reconfigure available non-primary connection, closing it: " + connection, ex); closeConnectionAndLogExceptionsLocked(connection); mAvailableNonPrimaryConnections.remove(i--); count -= 1; } } markAcquiredConnectionsLocked(AcquiredConnectionStatus.RECONFIGURE); }
// Might throw. private SQLiteConnection tryAcquireNonPrimaryConnectionLocked(String sql, int connectionFlags) { // Try to acquire the next connection in the queue. SQLiteConnection connection; final int availableCount = mAvailableNonPrimaryConnections.size(); if (availableCount > 1 && sql != null) { // If we have a choice, then prefer a connection that has the // prepared statement in its cache. for (int i = 0; i < availableCount; i++) { connection = mAvailableNonPrimaryConnections.get(i); if (connection.isPreparedStatementInCache(sql)) { mAvailableNonPrimaryConnections.remove(i); finishAcquireConnectionLocked(connection, connectionFlags); // might throw return connection; } } } if (availableCount > 0) { // Otherwise, just grab the next one. connection = mAvailableNonPrimaryConnections.remove(availableCount - 1); finishAcquireConnectionLocked(connection, connectionFlags); // might throw return connection; } // Expand the pool if needed. int openConnections = mAcquiredConnections.size(); if (mAvailablePrimaryConnection != null) { openConnections += 1; } if (openConnections >= mMaxConnectionPoolSize) { return null; } connection = openConnectionLocked(mConfiguration, false /*primaryConnection*/); // might throw finishAcquireConnectionLocked(connection, connectionFlags); // might throw return connection; }
public void testOpenV2() throws SQLiteException { SQLiteConnection db = fileDb(); db.openV2( SQLiteConstants.SQLITE_OPEN_CREATE | SQLiteConstants.SQLITE_OPEN_READWRITE | SQLiteConstants.SQLITE_OPEN_NOMUTEX); db.exec("create table x(x)"); db.dispose(); }
/** * Collects statistics about database connection memory usage. * * @param dbStatsList The list to populate. */ public void collectDbStats(ArrayList<DbStats> dbStatsList) { synchronized (mLock) { if (mAvailablePrimaryConnection != null) { mAvailablePrimaryConnection.collectDbStats(dbStatsList); } for (SQLiteConnection connection : mAvailableNonPrimaryConnections) { connection.collectDbStats(dbStatsList); } for (SQLiteConnection connection : mAcquiredConnections.keySet()) { connection.collectDbStatsUnsafe(dbStatsList); } } }
/** * Releases a connection back to the pool. * * <p>It is ok to call this method after the pool has closed, to release connections that were * still in use at the time of closure. * * @param connection The connection to release. Must not be null. * @throws IllegalStateException if the connection was not acquired from this pool or if it has * already been released. */ public void releaseConnection(SQLiteConnection connection) { synchronized (mLock) { AcquiredConnectionStatus status = mAcquiredConnections.remove(connection); if (status == null) { throw new IllegalStateException( "Cannot perform this operation " + "because the specified connection was not acquired " + "from this pool or has already been released."); } if (!mIsOpen) { closeConnectionAndLogExceptionsLocked(connection); } else if (connection.isPrimaryConnection()) { if (recycleConnectionLocked(connection, status)) { assert mAvailablePrimaryConnection == null; mAvailablePrimaryConnection = connection; } wakeConnectionWaitersLocked(); } else if (mAvailableNonPrimaryConnections.size() >= mMaxConnectionPoolSize - 1) { closeConnectionAndLogExceptionsLocked(connection); } else { if (recycleConnectionLocked(connection, status)) { mAvailableNonPrimaryConnections.add(connection); } wakeConnectionWaitersLocked(); } } }
/** Close a database (in the current thread). */ @Override void closeDatabaseNow() { try { if (mydb != null) mydb.dispose(); } catch (Exception e) { Log.e(SQLitePlugin.class.getSimpleName(), "couldn't close database, ignoring", e); } }
public void enableLocalizedCollators() { synchronized (mLock) { if (!mAcquiredConnections.isEmpty() || mAvailablePrimaryConnection == null) { throw new IllegalStateException( "Cannot enable localized collators while database is in use"); } mAvailablePrimaryConnection.enableLocalizedCollators(); } }
// Can't throw. private void logConnectionPoolBusyLocked(long waitMillis, int connectionFlags) { final Thread thread = Thread.currentThread(); StringBuilder msg = new StringBuilder(); msg.append("The connection pool for database '").append(mConfiguration.label); msg.append("' has been unable to grant a connection to thread "); msg.append(thread.getId()).append(" (").append(thread.getName()).append(") "); msg.append("with flags 0x").append(Integer.toHexString(connectionFlags)); msg.append(" for ").append(waitMillis * 0.001f).append(" seconds.\n"); ArrayList<String> requests = new ArrayList<String>(); int activeConnections = 0; int idleConnections = 0; if (!mAcquiredConnections.isEmpty()) { for (SQLiteConnection connection : mAcquiredConnections.keySet()) { String description = connection.describeCurrentOperationUnsafe(); if (description != null) { requests.add(description); activeConnections += 1; } else { idleConnections += 1; } } } int availableConnections = mAvailableNonPrimaryConnections.size(); if (mAvailablePrimaryConnection != null) { availableConnections += 1; } msg.append("Connections: ").append(activeConnections).append(" active, "); msg.append(idleConnections).append(" idle, "); msg.append(availableConnections).append(" available.\n"); if (!requests.isEmpty()) { msg.append("\nRequests in progress:\n"); for (String request : requests) { msg.append(" ").append(request).append("\n"); } } Log.w(TAG, msg.toString()); }
// Can't throw. private void closeConnectionAndLogExceptionsLocked(SQLiteConnection connection) { try { connection.close(); // might throw } catch (RuntimeException ex) { Log.e( TAG, "Failed to close connection, its fate is now in the hands " + "of the merciful GC: " + connection, ex); } }
// Might throw. private SQLiteConnection tryAcquirePrimaryConnectionLocked(int connectionFlags) { // If the primary connection is available, acquire it now. SQLiteConnection connection = mAvailablePrimaryConnection; if (connection != null) { mAvailablePrimaryConnection = null; finishAcquireConnectionLocked(connection, connectionFlags); // might throw return connection; } // Make sure that the primary connection actually exists and has just been acquired. for (SQLiteConnection acquiredConnection : mAcquiredConnections.keySet()) { if (acquiredConnection.isPrimaryConnection()) { return null; } } // Uhoh. No primary connection! Either this is the first time we asked // for it, or maybe it leaked? connection = openConnectionLocked(mConfiguration, true /*primaryConnection*/); // might throw finishAcquireConnectionLocked(connection, connectionFlags); // might throw return connection; }
// Can't throw. private boolean recycleConnectionLocked( SQLiteConnection connection, AcquiredConnectionStatus status) { if (status == AcquiredConnectionStatus.RECONFIGURE) { try { connection.reconfigure(mConfiguration); // might throw } catch (RuntimeException ex) { Log.e(TAG, "Failed to reconfigure released connection, closing it: " + connection, ex); status = AcquiredConnectionStatus.DISCARD; } } if (status == AcquiredConnectionStatus.DISCARD) { closeConnectionAndLogExceptionsLocked(connection); return false; } return true; }
/** * Returns true if the session should yield the connection due to contention over available * database connections. * * @param connection The connection owned by the session. * @param connectionFlags The connection request flags. * @return True if the session should yield its connection. * @throws IllegalStateException if the connection was not acquired from this pool or if it has * already been released. */ public boolean shouldYieldConnection(SQLiteConnection connection, int connectionFlags) { synchronized (mLock) { if (!mAcquiredConnections.containsKey(connection)) { throw new IllegalStateException( "Cannot perform this operation " + "because the specified connection was not acquired " + "from this pool or has already been released."); } if (!mIsOpen) { return false; } return isSessionBlockingImportantConnectionWaitersLocked( connection.isPrimaryConnection(), connectionFlags); } }
// Might throw. private void finishAcquireConnectionLocked(SQLiteConnection connection, int connectionFlags) { try { final boolean readOnly = (connectionFlags & CONNECTION_FLAG_READ_ONLY) != 0; connection.setOnlyAllowReadOnlyOperations(readOnly); mAcquiredConnections.put(connection, AcquiredConnectionStatus.NORMAL); } catch (RuntimeException ex) { Log.e( TAG, "Failed to prepare acquired connection for session, closing it: " + connection + ", connectionFlags=" + connectionFlags); closeConnectionAndLogExceptionsLocked(connection); throw ex; // rethrow! } }
public void testExec() throws SQLiteException { SQLiteConnection db = fileDb(); try { db.exec("create table xxx (x)"); fail("exec unopened"); } catch (SQLiteException e) { // ok } db.open(); db.exec("pragma encoding=\"UTF-8\";"); db.exec("create table x (x)"); db.exec("insert into x values (1)"); try { db.exec("blablabla"); fail("execed bad sql"); } catch (SQLiteException e) { // ok } }
/** * Get rows results from query cursor. * * @param cur Cursor into query results * @return results in string form */ private JSONObject executeSqlStatementNDK( String query, JSONArray paramsAsJson, CallbackContext cbc) throws Exception { JSONObject rowsResult = new JSONObject(); boolean hasRows = false; SQLiteStatement myStatement = mydb.prepareStatement(query); try { String[] params = null; params = new String[paramsAsJson.length()]; for (int i = 0; i < paramsAsJson.length(); ++i) { if (paramsAsJson.isNull(i)) { myStatement.bindNull(i + 1); } else { Object p = paramsAsJson.get(i); if (p instanceof Float || p instanceof Double) myStatement.bindDouble(i + 1, paramsAsJson.getDouble(i)); else if (p instanceof Number) myStatement.bindLong(i + 1, paramsAsJson.getLong(i)); else myStatement.bindTextNativeString(i + 1, paramsAsJson.getString(i)); } } hasRows = myStatement.step(); } catch (Exception ex) { ex.printStackTrace(); String errorMessage = ex.getMessage(); Log.v("executeSqlBatch", "SQLitePlugin.executeSql[Batch](): Error=" + errorMessage); // cleanup statement and throw the exception: myStatement.dispose(); throw ex; } // If query result has rows if (hasRows) { JSONArray rowsArrayResult = new JSONArray(); String key = ""; int colCount = myStatement.getColumnCount(); // Build up JSON result object for each row do { JSONObject row = new JSONObject(); try { for (int i = 0; i < colCount; ++i) { key = myStatement.getColumnName(i); switch (myStatement.getColumnType(i)) { case SQLColumnType.NULL: row.put(key, JSONObject.NULL); break; case SQLColumnType.REAL: row.put(key, myStatement.getColumnDouble(i)); break; case SQLColumnType.INTEGER: row.put(key, myStatement.getColumnLong(i)); break; case SQLColumnType.BLOB: case SQLColumnType.TEXT: default: // (just in case) row.put(key, myStatement.getColumnTextNativeString(i)); } } rowsArrayResult.put(row); } catch (JSONException e) { e.printStackTrace(); } } while (myStatement.step()); try { rowsResult.put("rows", rowsArrayResult); } catch (JSONException e) { e.printStackTrace(); } } myStatement.dispose(); return rowsResult; }
/** * Executes a batch request and sends the results via cbc. * * @param dbname The name of the database. * @param queryarr Array of query strings * @param jsonparams Array of JSON query parameters * @param queryIDs Array of query ids * @param cbc Callback context from Cordova API */ @Override void executeSqlBatch( String[] queryarr, JSONArray[] jsonparams, String[] queryIDs, CallbackContext cbc) { if (mydb == null) { // not allowed - can only happen if someone has closed (and possibly deleted) a database and // then re-used the database cbc.error("database has been closed"); return; } int len = queryarr.length; JSONArray batchResults = new JSONArray(); for (int i = 0; i < len; i++) { int rowsAffectedCompat = 0; boolean needRowsAffectedCompat = false; String query_id = queryIDs[i]; JSONObject queryResult = null; String errorMessage = "unknown"; try { String query = queryarr[i]; long lastTotal = mydb.getTotalChanges(); queryResult = this.executeSqlStatementNDK(query, jsonparams[i], cbc); long newTotal = mydb.getTotalChanges(); long rowsAffected = newTotal - lastTotal; queryResult.put("rowsAffected", rowsAffected); if (rowsAffected > 0) { long insertId = mydb.getLastInsertRowid(); if (insertId > 0) { queryResult.put("insertId", insertId); } } } catch (Exception ex) { ex.printStackTrace(); errorMessage = ex.getMessage(); Log.v("executeSqlBatch", "SQLitePlugin.executeSql[Batch](): Error=" + errorMessage); } try { if (queryResult != null) { JSONObject r = new JSONObject(); r.put("qid", query_id); r.put("type", "success"); r.put("result", queryResult); batchResults.put(r); } else { JSONObject r = new JSONObject(); r.put("qid", query_id); r.put("type", "error"); JSONObject er = new JSONObject(); er.put("message", errorMessage); r.put("result", er); batchResults.put(r); } } catch (JSONException ex) { ex.printStackTrace(); Log.v("executeSqlBatch", "SQLitePlugin.executeSql[Batch](): Error=" + ex.getMessage()); // TODO what to do? } } cbc.success(batchResults); }
public void testCannotReopen() throws SQLiteException { SQLiteConnection connection = fileDb(); connection.open(); assertTrue(connection.isOpen()); try { connection.open(); } catch (AssertionError e) { // ok } assertTrue(connection.isOpen()); connection.dispose(); assertFalse(connection.isOpen()); connection.dispose(); assertFalse(connection.isOpen()); try { connection.open(); fail("reopened connection"); } catch (SQLiteException e) { // ok } assertFalse(connection.isOpen()); }
public void testOpenMemory() throws SQLiteException { SQLiteConnection connection = memDb(); assertFalse(connection.isOpen()); try { connection.openReadonly(); fail("successfully opened"); } catch (SQLiteException e) { // norm } assertFalse(connection.isOpen()); connection.dispose(); assertFalse(connection.isOpen()); connection.dispose(); assertFalse(connection.isOpen()); connection = memDb(); try { connection.open(false); fail("successfully opened"); } catch (SQLiteException e) { // norm } connection.open(); assertTrue(connection.isOpen()); assertNull(connection.getDatabaseFile()); assertTrue(connection.isMemoryDatabase()); connection.dispose(); assertFalse(connection.isOpen()); }
public void testOpenFile() throws SQLiteException { SQLiteConnection connection = fileDb(); assertFalse(connection.isOpen()); try { connection.openReadonly(); fail("successfully opened"); } catch (SQLiteException e) { // norm } assertFalse(connection.isOpen()); connection.dispose(); assertFalse(connection.isOpen()); connection.dispose(); assertFalse(connection.isOpen()); connection = fileDb(); boolean allowCreate = false; try { connection.open(allowCreate); fail("successfully opened"); } catch (SQLiteException e) { // norm } connection.open(true); assertTrue(connection.isOpen()); assertEquals(dbFile(), connection.getDatabaseFile()); connection.dispose(); assertFalse(connection.isOpen()); }
// Might throw. private SQLiteConnection openConnectionLocked( SQLiteDatabaseConfiguration configuration, boolean primaryConnection) { final int connectionId = mNextConnectionId++; return SQLiteConnection.open( this, configuration, connectionId, primaryConnection); // might throw }