public abstract class Database
- Object
- Database
Known subtypesThreadSafeDatabase
Allows access to SQLite specifically connecting to a database and executing sql queries on the data.
There is more thorough coverage of the Database API here.
The Database class abstracts the underlying SQLite of the device if available.
Notice that this might not be supported on all platforms in which case the Database will be null.
SQLite should be used for very large data handling, for small storage
refer to com.codename1.io.Storage which is more portable.
Example
Database db = null;
Cursor cur = null;
try {
db = Database.openOrCreate("MyDB.db");
db.execute("CREATE TABLE IF NOT EXISTS people (id INTEGER PRIMARY KEY, name TEXT)");
db.execute("INSERT INTO people (name) VALUES (?)", new Object[] {"Alice"});
cur = db.executeQuery("SELECT id, name FROM people ORDER BY id");
while (cur.next()) {
Row row = cur.getRow();
System.out.println(row.getInteger(0) + " " + row.getString(1));
}
} finally {
if (cur != null) {
cur.close();
}
if (db != null) {
db.close();
}
}
Encryption
Pass a DatabaseConfig to #openOrCreate(java.lang.String, com.codename1.db.DatabaseConfig)
to encrypt the database at rest. Check #isEncryptionSupported() first, and read the security
notes on DatabaseConfig before choosing how to key it.
Fields
protected boolean inTransaction | Tracks whether a transaction is open on this instance, so the flat-transaction rules in the package documentation are enforced identically on every port rather than being re-derived from each engine’s very different native semantics. |
Constructors
public Database() |
Methods
Inherited methods
Field details
inTransaction
protected boolean inTransactionConstructor details
Database
public Database()Method details
isCustomPathSupported
public static boolean isCustomPathSupported()#openOrCreate(java.lang.String), #exists(java.lang.String),
#delete(java.lang.String), and #getDatabasePath(java.lang.String).Returns
isLegacyBehavior
public static boolean isLegacyBehavior()Returns whether the database API is running in legacy compatibility mode.
The behaviour of this API used to differ substantially between platforms. Those
differences have been reconciled into the single contract documented in the
com.codename1.db package, but applications written against the old, divergent
behaviour may depend on it. Legacy mode restores each platform’s previous behaviour
exactly, and is intended as a transition aid rather than a permanent setting.
Enable it with the db.legacy build hint, or from code before the first database call:
Database.setLegacyBehavior(true);
The package documentation lists precisely which behaviours the flag covers. Fixes for outright defects, and capabilities that previously threw and now work, are not covered, because no application can depend on those.
Returns
setLegacyBehavior
public static void setLegacyBehavior(boolean legacy)Turns legacy compatibility mode on or off.
Call this before opening any database; cursors and connections capture the mode as they are created, so flipping it mid-session gives inconsistent results.
Parameters
legacyboolean- true to restore the pre-normalization behaviour
See also
openOrCreate
public static Database openOrCreate(String databaseName)
throws IOExceptionParameters
databaseNameString- the name of the database. Platforms that support custom database
paths (i.e.
#isCustomPathSupported()return true), will also accept a file path here.
Returns
Throws
IOException- if database cannot be created
exists
public static boolean exists(String databaseName)Indicates weather a database exists
NOTE: Not supported in the Javascript port. Will always return false.
Parameters
databaseNameString- the name of the database. Platforms that support custom database
paths (i.e.
#isCustomPathSupported()return true), will also accept a file path here.
Returns
delete
public static void delete(String databaseName)
throws IOExceptionDeletes database
NOTE: This method is not supported in the Javascript port. Will silently fail.
Parameters
databaseNameString- the name of the database. Platforms that support custom database
paths (i.e.
#isCustomPathSupported()return true), will also accept a file path here.
Throws
IOException- if database cannot be deleted
isDatabaseBeingDeleted
public static synchronized boolean isDatabaseBeingDeleted(String key)Parameters
keyString- the identity the port registers connections under
Returns
openDatabaseCount
protected static synchronized int openDatabaseCount(String key)Parameters
keyString- a normalized path, or null
Returns
getDatabasePath
public static String getDatabasePath(String databaseName)Parameters
databaseNameStringThe name of the database. Platforms that support custom database paths (i.e.
#isCustomPathSupported()return true), will also accept a file path here.NOTE: Where
#isCustomPathSupported()is false the databases are not filesystem backed, so what comes back identifies the database inside the platform’s storage but is not a pathcom.codename1.io.FileSystemStoragecan open.
Returns
openOrCreate
public static Database openOrCreate(String databaseName, DatabaseConfig config)
throws IOExceptionOpens an encrypted database, creating it if it does not exist.
The database is encrypted at rest using the key described by config. Every platform
that supports encryption writes the same on-disk format, so a database created on one
device can be opened on another and in the simulator.
If config is null or describes a plaintext database this behaves exactly like
#openOrCreate(java.lang.String).
Example
if (!Database.isEncryptionSupported()) {
throw new IOException("This build cannot store data securely");
}
DatabaseConfig config = DatabaseConfig.managed();
Database db = Database.openOrCreate("secure.db", config);
config.wipe();
Parameters
databaseNameString- the name of the database. Platforms that support custom
database paths (see
#isCustomPathSupported()) also accept a file path. configDatabaseConfig- how to key the database, or null for plaintext
Returns
Throws
DatabaseEncryptionException- with
DatabaseEncryptionException#NOT_SUPPORTEDif encryption was requested on a platform that cannot provide it, or withDatabaseEncryptionException#WRONG_KEYif the key does not decrypt an existing database IOException- if the database cannot be opened or created
isEncryptionSupported
public static boolean isEncryptionSupported()Returns
#openOrCreate(java.lang.String, com.codename1.db.DatabaseConfig) accepts an
encrypting configisEncrypted
public static boolean isEncrypted(String databaseName)Indicates whether a database file appears to be encrypted.
This inspects the file header: an unencrypted SQLite database begins with the ASCII bytes
SQLite format 3 followed by a zero byte, and an encrypted one does not. It is therefore a
header sniff, not a cryptographic assertion – a truncated or corrupt file also reports
true, and a false result only means the file is a readable plaintext SQLite database. An
empty file reports false: SQLite writes no header until the first change, so that is what a
database that has been created and not yet written to looks like.
Parameters
databaseNameString- the name of the database
Returns
encrypt
public static void encrypt(String databaseName, DatabaseConfig config)
throws IOExceptionEncrypts an existing plaintext database in place.
The conversion is performed by the database engine as a single transaction, so an
interruption leaves the original file intact rather than half-converted. Schema metadata
such as PRAGMA user_version is preserved.
Parameters
databaseNameString- the name of an existing plaintext database
configDatabaseConfig- how the encrypted database should be keyed
Throws
IOException- if the database cannot be converted
decrypt
public static void decrypt(String databaseName, DatabaseConfig config)
throws IOExceptionParameters
databaseNameString- the name of an existing encrypted database
configDatabaseConfig- the config that currently opens the database
Throws
IOException- if the database cannot be converted
forgetManagedKey
public static boolean forgetManagedKey(String keyAlias)Removes the stored managed key for an alias.
#delete(java.lang.String) deliberately leaves the managed key in place, because deleting
and recreating a database is a normal thing to do and should not discard the identity that
protects it. Call this explicitly when the key really should be forgotten – after which any
remaining database encrypted with it is permanently unreadable.
Parameters
keyAliasString- the alias passed to
DatabaseConfig#managed(java.lang.String), or the database name whenDatabaseConfig#managed()was used
Returns
beforeFirst
public static void beforeFirst(Cursor cursor)
throws IOExceptionRewinds a cursor to before its first row.
Uses CursorExt#beforeFirst() when the cursor provides it, and falls back to
Cursor#position(int) with -1 otherwise.
Parameters
cursorCursor- the cursor to rewind
Throws
IOException- if the cursor is closed or the rewind fails
count
public static int count(Cursor cursor)
throws IOExceptionReturns the number of rows a cursor holds, or -1 where the port cannot determine it.
This can be expensive. Only Android’s engine knows the count without looking; every
other port walks the result set to the end and rewinds, so this costs what the query costs
and should not be called on the EDT for a large one. See CursorExt#getCount().
Parameters
cursorCursor- the cursor to measure
Returns
Throws
IOException- if the cursor is closed
isBlobQueryParameterSupported
public static boolean isBlobQueryParameterSupported()Indicates whether #executeQuery(java.lang.String, java.lang.Object[]) accepts byte[]
parameters on this platform.
Blob values can always be written with #execute(java.lang.String, java.lang.Object[]).
Using one as a query parameter, for example in WHERE digest = ?, needs engine support
that not every port can provide.
Returns
changeKey
public void changeKey(DatabaseConfig config)
throws IOExceptionChanges the key of this open database, or removes it entirely.
Passing a plaintext config decrypts the database. The engine performs the conversion as a
single transaction and preserves schema metadata such as PRAGMA user_version.
Ports that support encryption override this. The default implementation reports that the
platform cannot do it; it is deliberately concrete rather than abstract, because Database
is public and is subclassed outside this repository.
Parameters
configDatabaseConfig- the new key, or
DatabaseConfig#plain()to decrypt
Throws
IOException- if the key cannot be changed
wasNull
public static boolean wasNull(Row row)
throws IOExceptionChecks if the last value accessed from a given row was null. Not all platforms support wasNull(). If the platform does not support it, this will just return false.
Check #supportsWasNull(com.codename1.db.Row) to see if the platform supports
wasNull().
Currently wasNull() is supported on UWP, iOS, Android, and JavaSE (Simulator).
Parameters
rowRow- The row to check.
Returns
Throws
supportsWasNull
public static boolean supportsWasNull(Row row)
throws IOException#wasNull(com.codename1.db.Row).Parameters
rowRow- The row to check.
Returns
Throws
isInTransaction
public boolean isInTransaction()Returns
#beginTransaction() and its commit or rollbacktoPragmaLiteral
protected static String toPragmaLiteral(String keyMaterial)Renders a key literal for use as a PRAGMA argument.
Raw keys are already the blob literal x'...', which has to reach the engine unquoted as a
literal rather than as a string. Passphrases are arbitrary text, so they are single quoted
with any embedded single quote doubled. Interpolating a passphrase directly would let one
containing a quote change the statement.
Parameters
keyMaterialString- the value from
DatabaseConfig#resolveKeyMaterial(java.lang.String)
Returns
PRAGMA key = or PRAGMA rekey =noteScriptTransactionControl
protected void noteScriptTransactionControl(String sql)Records what a script did to the transaction state.
#execute(java.lang.String) hands SQL straight to the engine, so execute("BEGIN") opens
a real transaction that #beginTransaction() never saw. Left untracked, the two ways of
saying the same thing disagree: a key change would be allowed inside a transaction opened
this way, and beginTransaction(); execute("COMMIT") would leave the flag set over a
transaction that has already ended, so the next commitTransaction() addresses one that is
not there.
Ports call this after #execute(java.lang.String), and after a parameterized call, with
the SQL they ran. A BEGIN opening a trigger body is not transaction control – the
splitter keeps a trigger together, so its body is never a statement here – and SAVEPOINT
is not tracked at all, because it nests and this API’s transactions do not.
A script that failed partway had already run everything before the statement that failed,
and the engine does not undo it. The control statements are read in order either way: they
are the least likely statement to be the one that failed, since BEGIN and COMMIT
reference nothing that can be missing. So BEGIN; INSERT INTO missing_table VALUES(1) is
left open, which it is, and BEGIN; COMMIT; INSERT INTO missing_table VALUES(1) is left
closed, which it also is – where treating any BEGIN as still open would hold the flag
over a committed transaction and block every key change until the connection was closed.
The one case left wrong is a script whose own BEGIN failed, reported as open when nothing
is. That is the recoverable direction – #rollbackTransaction() clears it – and the one
that refuses a key change rather than allowing one underneath a live transaction.
Parameters
sqlString- the SQL that was run, whether or not it finished
hasAttachments
protected boolean hasAttachments()Whether this connection currently holds any attached database.
For a port whose key change is not performed in place. Re-keying through PRAGMA rekey
keeps the connection, and its attachments with it; converting through an export does not –
the old connection closes, and SQLite drops every attachment when it does. A replacement
handle that restores only pragmas looks alive and answers “no such table” for every
attached schema, and the reservations taken for those files stay held, so the file nobody
is attached to any more still cannot be deleted.
Re-attaching is not an answer this layer can give: an encrypted attachment was opened with a key this connection did not keep, and there is nowhere honest to get it from. So a port in that position refuses the conversion and says what to detach.
Returns
reserveAttachments
protected void reserveAttachments(String sql)
throws IOExceptionReserves the databases a script is about to attach, before the engine attaches them.
Called by every port at the top of #execute(String). Reserving first is what makes this
safe rather than merely watchful: if the file is being deleted the reservation is refused
and this throws, so the ATTACH never runs and there is nothing to undo. Compensating
afterwards – attaching, then detaching again when the reservation lost – could itself
fail, on a locked database or inside a transaction, and left the attachment live with the
delete already under way.
Over-reserving is the deliberate direction. A statement that is reserved and then fails to execute leaves a reservation that is given back when the connection closes; the cost is a delete refused until then. The other direction loses data.
A relative name is reserved under this port’s database directory, which is not always
the file the engine opens: SQLite resolves a relative name against the process working
directory, and only the ports whose engine has no filesystem – the browser, where a name
is a pool entry – resolve it the same way this does. Predicting the other answer is not
possible from here; the working directory belongs to the process, differs per platform,
and is not something this API exposes or controls. So the reservation covers the file a
Codename One name means, which is what an application attaching 'data.db' almost
certainly intends, and the reconciliation afterwards is what covers the file the engine
really opened – including undoing an attachment that turns out to be unholdable. Attach
by an absolute path from #getDatabasePath(String) to be reserved exactly.
Parameters
sqlString- the script about to run
Throws
IOException- if a database it attaches is being deleted or converted, or if an earlier attachment had to be undone and nothing has reported that yet
reserveAttachments
protected void reserveAttachments(String sql, Object[] params)
throws IOExceptionReserves what a parameterized script is about to attach, including the bound values.
ATTACH DATABASE ? AS aux names its file in the parameters, so the statement text alone
cannot say what is about to be attached – and the reservation has to exist before the
engine attaches it, because a reservation refused afterwards cannot undo an attach.
Every parameter that resolves to a database identity is reserved, not just the one the placeholder stands for. Working out which parameter belongs to the ATTACH would mean counting placeholders through quoting and comments for no gain: an over-reservation costs a delete refused until the reconciliation gives it back, moments later.
Parameters
sqlString- the script about to run
paramsObject[]- the values bound to it, any of which may be the file
Throws
IOException- if a database it may attach is being deleted or converted
requireAttachmentsHeld
protected void requireAttachmentsHeld()
throws IOExceptionReports an attachment a reconciliation had to undo.
Thrown from the start of the next statement rather than from the one that attached,
because ports reconcile from a finally and a throw from there replaces whatever failure
the statement was already reporting – the one error the caller most needs. So the attach
is reversed as it is discovered, and the news waits for a place that can carry it.
Late, but not lost, and not misattributed either: the message names an ATTACH rather than “this statement”. The alternative is silence, and an application whose attachment quietly did not happen reads the absence of its tables as corruption.
Throws
IOException- if a reconciliation undid an attachment and nothing has reported it yet
noteConnectionClosed
protected void noteConnectionClosed()Releases what this connection held besides its own file.
Every port calls this as it closes. SQLite drops a connection’s attachments when it closes, so the registrations taken for them have to go at the same moment – otherwise a database that was attached once could never be deleted again for the life of the process.
noteFirstStatementTransactionControl
protected void noteFirstStatementTransactionControl(String sql)Records the transaction control in the first statement of a script, and only that one.
For the legacy hint, where a script runs as far as its first statement and the rest is
discarded. Reading the whole string there would credit statements that never ran: a
BEGIN; COMMIT would be read as opening and closing, when only the BEGIN was executed
and the transaction is still open – the direction that lets a key change run over it.
Parameters
sqlString- the script that was handed to the engine
noteEngineTransactionState
protected void noteEngineTransactionState(boolean open)Records the transaction state a port read back from its engine.
The reliable answer where a script runs as a whole. SQLite stops at the first statement
that fails and nothing outside can see which one that was, so reading the script cannot
tell an unexecuted trailing COMMIT from an executed one – and getting that wrong either
clears the flag over a live transaction, which lets a key change replace the database
underneath uncommitted work, or holds it over a finished one, which blocks every key change
until the connection closes. The engine knows; ports that can ask it should.
Parameters
openboolean- whether the engine reports a transaction in progress
transactionControlKeyword
protected static String transactionControlKeyword(String statement)The transaction-control keyword a statement starts with, or null if it is not one.
Shared so that a port which has to act on transaction control – the simulator routes it through JDBC, because there the transaction is the connection’s autocommit flag rather than something the driver reads back out of the SQL – classifies it exactly as the tracking here does. Two copies of this drifted apart once already.
Only a bare ROLLBACK counts: ROLLBACK TO <savepoint> unwinds within the transaction
rather than ending it, as SAVEPOINT and RELEASE do.
Parameters
statementString- a single statement
Returns
BEGIN, COMMIT, END, ROLLBACK, or nullbeginTransactionMode
protected static String beginTransactionMode(String statement)The locking mode a BEGIN asks for: IMMEDIATE, EXCLUSIVE or DEFERRED.
Reads the word after BEGIN rather than searching the statement for those names. The words
are ordinary text anywhere else, so /* IMMEDIATE migration */ BEGIN and
BEGIN /* EXCLUSIVE note */ TRANSACTION are both deferred – and a port that searched would
take a write lock on them that the same SQL does not take on any other platform.
Anything that is not one of the three, including a bare BEGIN and the optional
TRANSACTION keyword, is deferred, which is what SQLite does with it.
Parameters
statementString- a statement whose first keyword is
BEGIN
Returns
IMMEDIATE, EXCLUSIVE or DEFERREDrequireQueryStatement
protected void requireQueryStatement(String sql)
throws IOExceptionThe first word of a statement, upper cased, or an empty string.
Comments count as whitespace here, because they do to the engine: /* migration */ BEGIN
opens a transaction, and reading the keyword as empty would leave this believing none was
opened. The Android port relies on the same fact deliberately, prefixing a comment to a
ROLLBACK to get it past a statement classifier that reads the first three characters.
Refuses transaction control handed to executeQuery.
A cursor runs its statement when it is stepped, so executeQuery("BEGIN") opens a real
transaction that nothing here recorded: #isInTransaction() answers false over an open
one, a typed commit fails, and a key change is allowed across live work. Navigating the
cursor could run the control statement a second time on top of that.
The tracked ways in are #beginTransaction() and execute, both of which record what
they ran. This is not a capability being withdrawn: a transaction control statement
returns no rows, so asking for a cursor over one was never useful.
Skipped under the legacy hint, which restores what each port used to do with it.
Parameters
sqlString- the statement handed to executeQuery
Throws
IOException- if the statement is transaction control
normalizeDatabaseKey
public static String normalizeDatabaseKey(String path)A path reduced to one spelling, for use as an open-database registry key.
Two names for one file have to reach the registry as one entry, or the claim a key change
takes does not cover the other connection and the file is rewritten underneath it. The ports
with a real filesystem behind them (Android, the simulator) ask it to canonicalize, which
also resolves symlinks. The ports translated ahead of time have no such call to make, so
this collapses what can be collapsed without touching the disk: repeated separators, .
segments, and .. against the segment before it.
A symlink still reaches the registry under two names. That is a smaller hole than /a/./b
and /a/b counting as different databases, which is what an application writing a custom
path actually produces.
Parameters
pathString- a native filesystem path, or null
Returns
the reduced path, or null for a null input The shared path reduction, for a port that needs it outside a Database instance.
The implementations resolve a managed key’s implicit alias from this, so that two accepted spellings of one file derive one key rather than two.
normalizeDatabasePathKey
protected static String normalizeDatabasePathKey(String path)registerOpenDatabase
protected static synchronized void registerOpenDatabase(String key)
throws IOExceptionRecords that a connection to a database file has been opened.
Ports call this once they have a connection, and #releaseOpenDatabase(String) when they
let it go. A port whose engine cannot be given two connections to one file need not call
either.
Parameters
keyString- identifies the file, canonically enough that two spellings of one path agree, or null for a connection that cannot say which file it holds
Throws
IOException- if the file is being deleted or re-keyed, or – for a null key – if any database is, since such a connection cannot be ruled out as that one
releaseOpenDatabase
protected static synchronized void releaseOpenDatabase(String key)Parameters
keyString- the key the connection was registered under
requireSoleConnectionForKeyChange
protected static synchronized void requireSoleConnectionForKeyChange(String key)
throws IOExceptionRejects a key change while the same file is open more than once.
Ports call this from #changeKey(DatabaseConfig), after
#checkNoTransactionForKeyChange(). The count includes the connection asking, so more than
one means somebody else holds the file too.
Parameters
keyString- the key this connection was registered under
Throws
IOException- if another connection has the same file open
releaseKeyChangeClaim
protected static synchronized void releaseKeyChangeClaim(String key)Ends the exclusive claim #requireSoleConnectionForKeyChange(String) took.
Ports call this from a finally around the rewrite, so a key change that throws does not
leave the file barred from opening for the rest of the process.
Parameters
keyString- the key the claim was taken under
checkNoTransactionForKeyChange
protected void checkNoTransactionForKeyChange()
throws IOExceptionRejects a key change while a transaction is open.
Ports call this at the top of #changeKey(DatabaseConfig). Re-keying is not a statement
inside the transaction: depending on the engine it either rewrites the file in place or
exports into a new one and swaps it under the connection. Either way the open transaction
has nowhere to land – an export copies the uncommitted rows into the file that becomes the
database, and a following commit or rollback addresses a connection that has no transaction
to end. Refusing is the only outcome that keeps commit and rollback meaning what they
say, and the caller loses nothing: it can end the transaction and change the key after.
Throws
IOException- if a transaction is open
supportsNestedTransactions
protected boolean supportsNestedTransactions()Rejects a nested #beginTransaction(), then records that one is open.
Transactions are flat: only that model is expressible on all of the engines behind this
API. Ports call this at the top of #beginTransaction(). In legacy mode the check is
skipped, because a nested begin used to be accepted on Android.
Throws
IOExceptionif a transaction is already open Whether this engine counts nested transactions rather than rejecting the second one.
Only Android’s wrapper does, and only that port’s legacy behaviour allowed nesting. On the others a second BEGIN reaches SQLite and fails, and the port clears its flag on the way out – so allowing the call would report no transaction while the first one is still open, and a caller that caught the expected failure could then change the key across it.
checkBeginTransaction
protected void checkBeginTransaction()
throws IOExceptionThrows
checkEndTransaction
protected void checkEndTransaction()
throws IOExceptionRejects a commit or rollback with no open transaction.
Ports call this at the top of #commitTransaction() and #rollbackTransaction(), and
#markTransactionEnded() once the engine has ended it. The two are separate so that a
port can end the transaction on a path that does not commit it, which is what
#abandonFailedCommit(Throwable) does.
In legacy mode the check is skipped.
Throws
IOException- if no transaction is open
markTransactionEnded
protected void markTransactionEnded()abandonFailedCommit
protected IOException abandonFailedCommit(Throwable cause)Discards a transaction whose commit failed, and builds the exception to report it with.
A commit that fails cannot be retried, so the only remaining outcome is a rollback. The
engines disagree about what they leave behind: Android has already ended the transaction
by the time it reports the failure, while the SQLite C API and JDBC leave it open. Ports
call this from the failure path of #commitTransaction(), after making a best effort to
roll back, so that callers see one behavior everywhere – no transaction is open, and
#beginTransaction() works again.
Parameters
causeThrowable- the failure the engine reported
Returns
beginTransaction
public abstract void beginTransaction()
throws IOExceptionStarts a transaction.
Transactions are flat. Calling this while a transaction is already open throws, and committing or rolling back returns the connection to autocommit. Closing a database with an open transaction rolls it back.
Throws
IOException- if the database is not open, or a transaction is already in progress
commitTransaction
public abstract void commitTransaction()
throws IOExceptionCommits current transaction
NOTE: Not supported in Javascript port. This method will do nothing when running in Javascript.
Throws
IOException- if database is not opened or transaction was not started
rollbackTransaction
public abstract void rollbackTransaction()
throws IOExceptionRolls back current transaction
NOTE: Not supported in Javascript port. This method will do nothing when running in Javascript.
Throws
IOException- if database is not opened or transaction was not started
close
public abstract void close()
throws IOExceptionThrows
execute
public abstract void execute(String sql)
throws IOExceptionParameters
sqlString- the sql to execute
Throws
execute
public abstract void execute(String sql, String[] params)
throws IOExceptionParameters
sqlString- the sql to execute
paramsString[]- to bind to the query where the ‘?’ exists
Throws
execute
public void execute(String sql, Object... params)
throws IOExceptionParameters
sqlString- the sql to execute
paramsObject...- to bind to the query where the ‘?’ exists, supported object types are String, byte[], Double, Long and null
Throws
coerceToText
protected static String[] coerceToText(Object[] params, String operation)
throws IOExceptionRenders parameters as text for ports that have not implemented typed binding.
This is the fallback path only. Ports that can bind by type override the varargs methods
and never reach here, which is why hitting a byte[] is an error rather than something to
paper over: silently storing the result of byte[].toString() would write the array’s
identity hash into the database.
Parameters
paramsObject[]- the parameters supplied by the caller
operationString- the calling method name, used in the error message
Returns
Throws
IOException- if a parameter is a
byte[]and this port cannot bind blobs
executeQuery
public abstract Cursor executeQuery(String sql, String[] params)
throws IOExceptionParameters
sqlString- the sql to execute
paramsString[]- to bind to the query where the ‘?’ exists
Returns
Throws
executeQuery
public Cursor executeQuery(String sql, Object... params)
throws IOExceptionParameters
sqlString- the sql to execute
paramsObject...- to bind to the query where the ‘?’ exists, supported object types are String, byte[], Double, Long and null
Returns
Throws
executeQuery
public abstract Cursor executeQuery(String sql)
throws IOExceptionParameters
sqlString- the sql to execute