juanchinovas/nativescript-sqlite-access

Corner case when inserting hexadecimal values as strings [iOS only]

bgohsman opened this issue · 1 comments

  • Make sure to check the demo app(s) for sample usage
  • Make sure to check the existing issues in this repository
  • If the demo apps cannot help and there is no issue for your problem, tell us about it. Please, ensure your title is less than 63 characters long and starts with a capital
    letter.

Which platform(s) does your issue occur on?

  • iOS/Android/Both
  • iOS/Android versions
  • emulator or device. What type of device?

Please, provide the following version numbers that your issue occurs with:

  • CLI: 6.5.0
  • Cross-platform modules: 6.5.0
  • Runtime(s): 6.5.0
  • Plugin(s):
"dependencies": {
    "@nativescript/theme": "^2.2.1",
    "axios": "^0.19.1",
    "binary-parser": "^1.5.0",
    "ml-regression": "^5.0.0",
    "nativescript-app-environment": "^1.0.0",
    "nativescript-audio": "^5.0.5",
    "nativescript-bluetooth": "^3.0.0-beta.10",
    "nativescript-fancyalert": "^3.0.9",
    "nativescript-geolocation": "^5.1.0",
    "nativescript-iqkeyboardmanager": "^1.5.1",
    "nativescript-permissions": "^1.3.8",
    "nativescript-picker": "^2.1.2",
    "nativescript-socketio": "^3.3.1",
    "nativescript-sqlite-access": "^1.0.3",
    "nativescript-toasty": "^2.0.1",
    "nativescript-ui-gauge": "^6.0.0",
    "nativescript-ui-listview": "^8.0.1",
    "nativescript-vue": "^2.4.0",
    "tns-core-modules": "^6.5.0",
    "vuex": "^3.1.2"
}
"devDependencies": {
    "@babel/core": "^7.7.5",
    "@babel/preset-env": "^7.7.6",
    "@types/binary-parser": "^1.3.1",
    "@types/node": "^12.12.16",
    "@typescript-eslint/eslint-plugin": "^2.10.0",
    "@typescript-eslint/parser": "^2.10.0",
    "babel-loader": "^8.0.6",
    "eslint": "^6.7.2",
    "eslint-config-prettier": "^6.7.0",
    "eslint-loader": "^3.0.2",
    "eslint-plugin-prettier": "^3.1.1",
    "eslint-plugin-react": "^7.17.0",
    "eslint-plugin-react-hooks": "^2.3.0",
    "eslint-plugin-vue": "^6.0.1",
    "nativescript-dev-webpack": "^1.4.0",
    "nativescript-vue-template-compiler": "^2.4.0",
    "nativescript-worker-loader": "~0.10.0",
    "node-sass": "^4.13.1",
    "prettier": "^1.19.1",
    "tns-platform-declarations": "^6.5.0",
    "typescript": "3.7.3",
    "vue": "^2.6.11",
    "vue-eslint-parser": "^7.0.0",
    "vue-loader": "^15.8.3"
  }

Please, tell us how to recreate the issue in as much detail as possible.

Describe the steps to reproduce it.

  • Create a table with a TEXT column such as title
  • Call the insert() method, passing a hexadecimal string in as the value for title such as "00389F19"
  • Build and run for iOS (device or emulator) and observe the app crashing

Is there any code involved?

I was able to track the issue to line# 181 in sqlite-access.ios.js

function __mapToAddOrUpdateValues(values, inserting) {
    if (inserting === void 0) { inserting = true; }
    var contentValues = [];
    for (var key in values) {
        if (values.hasOwnProperty(key)) {
            var value = values[key];
            value = !!parseFloat(value) ? Number(value) : "'" + value.replace("'", "''") + "'";
            contentValues.push(inserting && value || key + "=" + value);
        }
    }
    return contentValues.join(",");
}

Because parseFloat() will successfully parse a hexadecimal string (e.g. "00389F19" returns 389), the result is truthy. However, passing the same hexadecimal string to Number() will return NaN, which is falsey. This results in title=NaN being returned instead of a string value for the column title. This resulted in the following SQL value:

INSERT INTO mytable (id,title) VALUES(1,title=NaN)

Potential Solution
I temporarily replaced !!parseFloat(value) with a call to typeof and checked for string or number. Doing so safely left my string as a string and passed numbers as numbers. It also makes it possible to check/handle invalid types which may get passed (e.g. undefined).

Interim Workaround
In the mean time, crafting the INSERT statement outside of the plugin and simply passing it to the execSQL() method works (as long as you do not require a return value).

This issue was fixed in the version v1.0.4 and v1.05. @bgohsman