I have a Blob in an SQLite-Database.
When I type:
Select length(blobblob) FROM Database WHERE id=9387134648;
I get a size of 20519 bytes (quite big), but when I fetch the Blob with android:
Cursor cursor = database.rawQuery("Select ? FROM Database WHERE id=?;",
new String[]{"blobblob", "9387134648"});
if (cursor.moveToNext()) {
byte[] bytes = cursor.getBlob(0);
Log.v("Bloblength", String.valueOf(bytes.length));
}
I just get a size of 9 bytes.
What do I do wrong?
I'd guess that you're getting the string "blobblob" (as an array of bytes) out of your query, those eight characters plus a null terminator would be nine bytes. When you say this:
select ? from database where id = ?
Both placeholders will be replaced by values from your arguments and the arguments are Strings so the ? will be replaced with SQL strings. The result will be a query that is effectively this:
select 'blobblob' from database where id = '9387134648'
and all that query does is select the SQL string literal 'blobblob' from your table.
The underlying problem is that you can't use placeholders for SQL identifiers (table names, column names, ...) in a query, placeholders are only for values like strings and numbers. Your code should look more like this:
Cursor cursor = database.rawQuery("Select blobblob FROM Database WHERE id=?;",
new String[]{"9387134648"});
I'm not much of a Java or Android guy so hopefully I haven't butchered the code.
Related
How can I find the tables having column Blob type in Sqlite. I need to get the table names from which I get the column blob type and then want to see the total no. of records where the blob is not empty.
If you wanted tables that have a column defined as a blob then you could use
SELECT * FROM sqlite_master WHERE sql LIKE '%blob%';
as the basis for determining the tables. e.g. this could return results such as :-
However, this does not necessarily find all values that are stored as blobs. This is because with the exception of the rowid column or an alias thereof, any type of value (blob included) can be stored in any column.
e.g. consider the following :-
DROP TABLE IF EXISTS not_a_blob_table;
CREATE TABLE IF NOT EXISTS not_a_blob_table (col1 TEXT, col2 INTEGER, col3 REAL, col4 something_or_other);
INSERT INTO not_a_blob_table VALUES
('test text',123,123.4567,'anything'), -- Insert using types as defined
(x'00',x'12',x'34',x'1234567890abcdefff00') -- Insert with all columns as blobs
;
SELECT typeof(col1),typeof(col2),typeof(col3),typeof(col4) FROM not_a_blob_table;
This results in :-
If you want to find all blobs then you would need to process all columns from all rows of all tables based upon a check for the column type. This could perhaps be based upon :-
SELECT typeof(col1),typeof(col2),typeof(col3),typeof(col4),* FROM not_a_blob_table
WHERE typeof(col1) = 'blob' OR typeof(col2) = 'blob' OR typeof(col3) = 'blob' OR typeof(col4) = 'blob';
Using the table above this would result (only the 2nd row has blobs) in :-
A further complication is what you mean by not empty, null obviously. However what about x'00'? or if you used a default of zeroblob(0) ?.
zeroblob(N)
The zeroblob(N) function returns a BLOB consisting of N bytes of 0x00. SQLite manages these zeroblobs very efficiently. Zeroblobs can
be used to reserve space for a BLOB that is later written using
incremental BLOB I/O. This SQL function is implemented using the
sqlite3_result_zeroblob() routine from the C/C++ interface.
If null though then this wouldn't have a type of blob, instead it's type would be null, which could complicate matters if checking for all values stored as blobs.
You may wish to consider having a look at the code from Are there any methods that assist with resolving common SQLite issues?
as this could well be the basis for what you want.
You also wish to have a look at typeof(X) and zeroblob(N).
I fetch records in my SQLite database like this.
spellId = extra.getString("spellId");
DBase db = new DBase(this);
db.open();
String[] data = db.getRecord(Integer.parseInt(spellId));
db.close();
Can I get random data like this without using raw queries and cursor?
try like this:
db.rawQuery("SELECT * FROM mainTable ORDER BY RANDOM() LIMIT 1", null);
You can use Random#nextInt() like
String[] data = db.getRecord(new Random().nextInt(num));
where num falls in the range of your record IDs. You would need to adapt this solution in case your Ids are fragmented and do not form a consecutive range.
One of the ways to do that would be to first create a query to fetch all the primary keys and store the values in a set somewhere. Then pick a random key by generating an index using Random.
String[] data = db.getRecord(IDSet.get(new Random().nextInt(IDSet.size())));
Check out the docs for more information.
Returns a pseudorandom, uniformly distributed int value between 0 (inclusive) and the specified value (exclusive), drawn from this random number generator's sequence.
If you're considering a DB query solution
A better alternative to using ORDER BY RANDOM() (which is known to not scale well as the number of rows in your table grows) is to let SQLite return a random row using an OFFSET.
First save the total number of rows num somewhere.
SELECT COUNT(*) AS num FROM spells;
Then choose a random number rnum between (0, num) using Random and use the query
SELECT * FROM spells LIMIT 1 OFFSET rnum;
I have a sqlite database where all rows have a UUID as the primary key, the db column is defined as a BLOB.
The keys are inserted as byte[] instead of Strings to avoid wasting storage and index spaces. I can insert, update and delete rows using SQLiteDatabase.compileStatement and using bindBlob on the SQLiteStatement but I can't find anyway to bind a blob to the parameters of a SELECT query.
Both SQLiteDatabase.query and .rawQuery expects my WHERE arguments to be Strings which will never match my byte array blobs. I can find my row if I construct my WHERE manually using a BLOB literal like this:
final Cursor query = db.query(getTableName(),
getColumns(),
"id = X'" + bytesToHex(getByteArrayFromUUID(id)) + "'" ,
null,
null,
null,
null);
But then I am vulnerable to SQL injections...
In every other language I have used SQLite in this is not a problem, is there really no way to get a standard prepared SELECT statement with android SQLite?
Most of the APIs in Android sqlite expect the parameter to be strings.
You can use compileStatement() to prepare your query and then use bindBlob() to bind a blob argument to it. Getting useful results out from the query is not easy though, SQLiteStatement has methods for only a few 1x1 result sets.
On the other hand, using a blob as a key doesn't seem like a good idea.
I am new to Android, and having some basic problems. One of them is the use of queries.
I store a boolean value as either 1 or 0 in the table (INTEGER field). However, when I select either on 1 or 0 using the query below I get no results. What am I doing wrong?
Cursor cursor = _db.query(_objectName, _fields.keySet().toArray(new String[0]), "parentId=? AND published=?", new String[] {String.valueOf(menuItem), String.valueOf(1)}, null, null, "level");
There is nothing wrong with your query. The problem must be elsewhere. Check your code and table structure. Maybe you are not sending the right values for parentId and published columns or the data in the table is not in the format you expected.
Use raw query
"Select * from "+TABLE_NAME+" where published = '"+String.valueOf(1)+"'";
You can put your integer value insted of 1
I have a table containing a BLOB column (it's just 16 bytes). I want to run a query of the form SELECT * FROM myTable WHERE blobColumn = ? and bind a byte array to that column. Ideally I would be able to say
myDatabase.rawQuery("SELECT * FROM myTable WHERE blobColumn = ?", myByteArray)
or some variant thereof, but the rawQuery function only supports String arguments - although looking though the Android sources, it seems that the private methods do include bindBlob(int, byte[]).
I can, of course, just run the query SELECT * FROM myTable WHERE blobColumn = x'CAFE1234CAFE1234CAFE1234CAFE1234', but is there a way to do it that doesn't require converting the blob to a string?
Try this as your argument to your ? query:
new String [] { String.valueOf(mByteArray) }
I don't believe that you can compare the contents of a Blob field without first converting it. Could you create an additional column and load it with some type of key that could be unique and descriptive of it's associated Blob data?