// 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();
      }
    }
  }
示例#6
0
 /** 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
    }
  }
示例#15
0
    /**
     * 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;
    }
示例#16
0
    /**
     * 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
 }