Closed Bug 738347 Opened 12 years ago Closed 12 years ago

SQLiteBridge does not check for nulls in ContentValues

Categories

(Firefox for Android Graveyard :: General, defect)

ARM
Android
defect
Not set
normal

Tracking

(blocking-fennec1.0 +)

RESOLVED FIXED
Firefox 14
Tracking Status
blocking-fennec1.0 --- +

People

(Reporter: rnewman, Assigned: sriram)

Details

Attachments

(2 files, 4 obsolete files)

while(valueIterator.hasNext()) {
            Entry<String, Object> value = valueIterator.next();
            keyNames.add(value.getKey());
            valueNames.add("?");
            valueBinds.add(value.getValue().toString());
        }

will NPE if you do:

  values.putNull("foo");

which is the only way to get an explicit null in an INSERT.

(Also, the code for building up "?, ?" strings is really inefficient.)
blocking-fennec1.0: --- → ?
blocking-fennec1.0: ? → -
Richard - Please re-nom if this is blocker-worthy
eg: does this cause anything to blow up, or is this a 'nice to have, good hygiene' sort of thing?
blocking-fennec1.0: - → ?
It doesn't cause any problems that we can't work around (in this case, the table defaults to NULL for that column), but this spikes the ability to use SQLiteBridge for tables with columns that don't default to NULL.

Requires some upstream defensive programming, but not a beta blocker.
Assignee: nobody → wjohnston
blocking-fennec1.0: ? → +
Attached patch Patch (obsolete) — Splinter Review
This patch adds null checks to "update" part of SQLiteBridge.
Attachment #611624 - Flags: review?(mark.finkle)
Comment on attachment 611624 [details] [diff] [review]
Patch

>         sb.append(" SET ");
>         while(valueIterator.hasNext()) {
>             Entry<String, Object> value = valueIterator.next();
>             sb.append(value.getKey() + " = ?");

Does sqlite handle "Xxx = NULL" or does it want "Xxx IS NULL" ?

Can the string concatenation be replaced with:

              sb.append(value.getKey());
              sb.append(value.getValue() == null ? " IS ?" : " = ?");

Let's figure out the "IS NULL" issue. Also, let's get Richard to give a thumbs up too
Attachment #611624 - Flags: review?(rnewman)
Attachment #611624 - Flags: review?(mark.finkle)
Attachment #611624 - Flags: review-
Comment on attachment 611624 [details] [diff] [review]
Patch

Review of attachment 611624 [details] [diff] [review]:
-----------------------------------------------------------------

You *definitely* need to add tests for this. Because this patch would fail them :D

::: mobile/android/base/sqlite/SQLiteBridge.java
@@ +164,5 @@
>          sb.append(" SET ");
>          while(valueIterator.hasNext()) {
>              Entry<String, Object> value = valueIterator.next();
>              sb.append(value.getKey() + " = ?");
> +            valueNames.add(value.getValue() == null ? "NULL" : value.getValue().toString());

This won't work. It'll add the string "NULL" to the database, not a NULL.

You might be able to do something like

  sb.append(value.getKey());
  Object v = value.getValue();
  if (v == null) {
    sb.append(" = NULL");
  } else {
    sb.append(" = ?");
    valueNames.add(v.toString());
  }

but you might need to adjust some counts somewhere.
Attachment #611624 - Flags: review?(rnewman) → review-
(In reply to Mark Finkle (:mfinkle) from comment #5)

> Does sqlite handle "Xxx = NULL" or does it want "Xxx IS NULL" ?

In UPDATE, the production is "SET column_name = expr", and NULL is an expr.

http://sqlite.org/lang_update.html

IS NULL is for WHERE clauses, because nothing is = to NULL.
Assignee: wjohnston → sriram
Attached patch Patch (obsolete) — Splinter Review
Added null checks and avoided binding "NULL" (as a string) in this patch. It's been long since I worked on SQL (like 8 years probably) and this is not my area of expertise. I'm still not sure if this is the needed fix. :(
Attachment #611624 - Attachment is obsolete: true
Attachment #611857 - Flags: review?(rnewman)
Attachment #611857 - Flags: review?(mark.finkle)
Comment on attachment 611857 [details] [diff] [review]
Patch

Review of attachment 611857 [details] [diff] [review]:
-----------------------------------------------------------------

A few adjustments, but most importantly you *need* tests for this. Doesn't have to be much; just one UPDATE that sets one column to null for some records, and queries to verify.

Clearing review flags until that.

::: mobile/android/base/sqlite/SQLiteBridge.java
@@ +160,3 @@
>          ArrayList<String> valueNames = new ArrayList<String>();
>  
> +        StringBuilder sb = new StringBuilder("UPDATE " + table + " SET ");

More efficient to use multiple appends here. You're concatenating a string then passing it to the StringBuilder.

@@ +160,4 @@
>          ArrayList<String> valueNames = new ArrayList<String>();
>  
> +        StringBuilder sb = new StringBuilder("UPDATE " + table + " SET ");
> +        boolean hasMore = false;

I think naming this 'isFirst' and defaulting to true would be clearer...

  is (isFirst)
    isFirst = false;
  else
    sb.append(", ");

@@ +174,5 @@
> +            if (val == null) {
> +                sb.append("NULL");
> +            } else {
> +                sb.append("?");
> +                valueNames.add(val.toString());

More efficient to do what I mentioned in my comment:

  sb.append(value.getKey());
  Object val = value.getValue();
  if (val == null) {
    sb.append(" = NULL");
  } else {
    sb.append(" = ?");
    valueNames.add(val.toString());
  }
Attachment #611857 - Flags: review?(rnewman)
Attachment #611857 - Flags: review?(mark.finkle)
Attachment #611857 - Flags: feedback+
Attached patch Patch (obsolete) — Splinter Review
I have made changes, avoiding string concatenations (Aah! I read a blog on why StringBuilder is better, but didn't quite apply it! :sigh: ).

How do I test this? Some pointers will be helpful.
Attachment #611857 - Attachment is obsolete: true
Attachment #611868 - Flags: review?(rnewman)
(In reply to Sriram Ramasubramanian [:sriram] from comment #10)

> How do I test this? Some pointers will be helpful.

You can probably figure something out from

https://wiki.mozilla.org/Auto-tools/Projects/Robocop/WritingTests#Creating_a_new_Content_Provider_test

-- this isn't a Content Provider test, but it sure sets up some databases and such. You could also write tests for a Content Provider that uses the SQLiteBridge, and call UPDATE through the CP with a null in the ContentValues object.
Comment on attachment 611868 [details] [diff] [review]
Patch

Review of attachment 611868 [details] [diff] [review]:
-----------------------------------------------------------------

Looks good, please re-r? when tests are ready.
Attachment #611868 - Flags: review?(rnewman) → feedback+
Those tests do not test this code path. They are for bookmarks and history, which don't use Gecko's sqlite. We do have some really simple tests for these two providers. Should be easy to modify them to test this. see testPasswordProvider and testFormHistory.
This changes the tests to have NULL values. Also, fixes CursorMatches() to avoid NPEs.
Attachment #611868 - Attachment is obsolete: true
Attachment #612647 - Flags: review?(rnewman)
This patch fixes handling NULL checks in insert() and update() part of SQLiteBridge.
Attachment #612648 - Flags: review?(rnewman)
Comment on attachment 612647 [details] [diff] [review]
Patch: Tests to add NULL in ContentValues in SQLiteBridge

Review of attachment 612647 [details] [diff] [review]:
-----------------------------------------------------------------

f+, not r+, because I'd like to see a specific case of an UPDATE nulling out a non-null value. That is:

* Insert a non-null value
* Check it's not null
* Update with a null
* Check it's null.

I believe this test now always uses a null GUID, which is half-way there...

Make sure to *continue* to test the case of INSERT with a null value, too.
Attachment #612647 - Flags: review?(rnewman) → feedback+
Comment on attachment 612648 [details] [diff] [review]
Patch: Add NULL checks in ContentValues in SQLiteBridge

Review of attachment 612648 [details] [diff] [review]:
-----------------------------------------------------------------

::: mobile/android/base/sqlite/SQLiteBridge.java
@@ +121,5 @@
>  
>      public long insert(String table, String nullColumnHack, ContentValues values)
>                 throws SQLiteBridgeException {
> +        if (values == null)
> +            return 0;

I might be inclined to throw an IllegalArgumentException here; match what the Android library does?

@@ +143,5 @@
>          StringBuilder sb = new StringBuilder("INSERT into ");
>          sb.append(table);
>  
>          sb.append(" (");
>          sb.append(TextUtils.join(", ", keyNames));

This makes me cry every time I see it.

@@ +160,5 @@
>  
>      public int update(String table, ContentValues values, String whereClause, String[] whereArgs)
>                 throws SQLiteBridgeException {
> +        if (values == null)
> +            return 0;

Same.
Attachment #612648 - Flags: review?(rnewman) → review+
This patch changes tests of FormHistory provider to check NULL insertion and NULL updation.
1. insert with NON-NULL value and update it to NULL.
2. insert with NULL value and update it to NON-NULL.

The fix patch posted runs fine without crashing! yaay! :)
Attachment #612647 - Attachment is obsolete: true
Attachment #612968 - Flags: review?(rnewman)
Attachment #612968 - Flags: review?(rnewman) → review+
Status: NEW → RESOLVED
Closed: 12 years ago
Resolution: --- → FIXED
Product: Firefox for Android → Firefox for Android Graveyard
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: