Skip to content

Commit

Permalink
Merge branch 'master-src' into master-rc
Browse files Browse the repository at this point in the history
Fixes:
- #193: workaround for Android db locking/closing issue
- #144: convert array parameters to string to match Web SQL
- #199: fix double-precision REAL values in result for iOS version
- #150/#153: close Android db before removing from map
- Fix truncation in iOS query result in case of UNICODE NULL (\0 or \u0000) (ref: PR #170)
- Some fixes for error handling to be consistent with Web SQL (ref: PR #170)

Testing ONLY:
- #147: testing with UNICODE line separator
- #195: Reproduce issue with double-precision REAL number on WP(8) ONLY
  • Loading branch information
Chris Brody committed Mar 10, 2015
2 parents 460ea0c + da5c58c commit c2a01f0
Show file tree
Hide file tree
Showing 7 changed files with 606 additions and 304 deletions.
30 changes: 19 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,17 @@ License for iOS version: MIT only
- The db version, display name, and size parameter values are not supported and will be ignored.
- The sqlite plugin will not work before the callback for the "deviceready" event has been fired, as described in **Usage**.
- The Android version cannot work with more than 100 open db files due to its threading model.
- UNICODE line separator (`\u2028`) is currently not supported and known to be broken in iOS version.
- UNICODE characters not working with WP(8) version

## Limited support (testing needed)

- Multi-page apps on WP(8)
- DB Triggers (as described above - known to be broken for Android)

## Other versions
## Other versions and related projects

- [MetaMemoryT / websql-client](https://github.com/MetaMemoryT/websql-client) - provides the same API and connects to [websql-server](https://github.com/MetaMemoryT/websql-server) through WebSockets.
- Original version for iOS (with a different API): https://github.com/davibe/Phonegap-SQLitePlugin

# Usage
Expand Down Expand Up @@ -87,6 +90,20 @@ function onDeviceReady() {

**NOTE:** The database file name should include the extension, if desired.

### Workaround for Android db locking issue

An [issue was reported](https://github.com/brodysoft/Cordova-SQLitePlugin/issues/193), as observed by several people that on some newer versions of the Android, if the app is stopped or aborted without closing the db then:
- (sometimes) there is an unexpected db lock
- the data that was inserted before is lost.

It is suspected that this issue is caused by [this Android sqlite commit](https://github.com/android/platform_external_sqlite/commit/d4f30d0d1544f8967ee5763c4a1680cb0553039f), which references and includes the sqlite commit at: http://www.sqlite.org/src/info/6c4c2b7dba

The workaround is enabled by opening the database like:

```js
var db = window.sqlitePlugin.openDatabase({name: "my.db", androidLockWorkaround: 1});
```

### Pre-populated database

For Android & iOS (*only*): put the database file in the `www` directory and open the database like:
Expand Down Expand Up @@ -364,23 +381,14 @@ If you still cannot get something to work:
- if the issue is with *adding* data to a table, that the test program includes the statements you used to open the database and create the table;
- if the issue is with *retrieving* data from a table, that the test program includes the statements you used to open the database, create the table, and enter the data you are trying to retrieve.

Then you can [raise the issue](https://github.com/brodysoft/Cordova-SQLitePlugin/issues/new).
Then you can [raise the new issue](https://github.com/brodysoft/Cordova-SQLitePlugin/issues/new).

## Community forum

If you have any questions about the plugin please post it to the [Cordova-SQLitePlugin forum](http://groups.google.com/group/Cordova-SQLitePlugin).

**NOTE:** Please report all bugs at [brodysoft / Cordova-SQLitePlugin / issues](https://github.com/brodysoft/Cordova-SQLitePlugin/issues) so they can be tracked properly.

## Support priorities

**High priority:**

1. Stability is first: immediate resolution or workaround for stability issues (crashing) is the goal.
2. Correctness: any issue with correctness should result in a new testcase together with the bug fix.

**Low priority:** issues with the API or application integration will be given lower priority until the Cordova CLI integration is finished for Windows (8+). Pull requests are very welcome for these kinds of issues.

# Unit tests

Unit testing is done in `test-www/`.
Expand Down
102 changes: 77 additions & 25 deletions SQLitePlugin.coffee.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,29 @@
## utility functions:

# Errors returned to callbacks must conform to `SqlError` with a code and message.
# Some errors are of type `Error` or `string` and must be converted.
newSQLError = (error, code) ->
sqlError = error
code = 0 if !code # unknown by default
if !sqlError
sqlError = new Error "a plugin had an error but provided no response"
sqlError.code = code
if typeof sqlError is "string"
sqlError = new Error error
sqlError.code = code
if !sqlError.code && sqlError.message
sqlError.code = code
if !sqlError.code && !sqlError.message
sqlError = new Error "an unknown error was returned: " + JSON.stringify(sqlError)
sqlError.code = code
return sqlError
nextTick = window.setImmediate || (fun) ->
window.setTimeout(fun, 0)
return
Expand Down Expand Up @@ -48,7 +71,7 @@
console.log "SQLitePlugin openargs: #{JSON.stringify openargs}"
if !(openargs and openargs['name'])
throw new Error("Cannot create a SQLitePlugin instance without a db name")
throw newSQLError "Cannot create a SQLitePlugin db instance without a db name"
dbname = openargs.name
Expand Down Expand Up @@ -87,15 +110,17 @@
SQLitePlugin::transaction = (fn, error, success) ->
if !@openDBs[@dbname]
error('database not open')
error newSQLError 'database not open'
return
@addTransaction new SQLitePluginTransaction(this, fn, error, success, true, false)
return
SQLitePlugin::readTransaction = (fn, error, success) ->
if !@openDBs[@dbname]
error('database not open')
error newSQLError 'database not open'
return
@addTransaction new SQLitePluginTransaction(this, fn, error, success, true, true)
return
Expand All @@ -112,24 +137,26 @@
SQLitePlugin::open = (success, error) ->
onSuccess = () => success this
unless @dbname of @openDBs
@openDBs[@dbname] = true
cordova.exec onSuccess, error, "SQLitePlugin", "open", [ @openargs ]
else
if @dbname of @openDBs
###
for a re-open run onSuccess async so that the openDatabase return value
can be used in the success handler as an alternative to the handler's
db argument
###
nextTick () -> onSuccess();
nextTick () -> onSuccess()
else
@openDBs[@dbname] = true
cordova.exec onSuccess, error, "SQLitePlugin", "open", [ @openargs ]
return
SQLitePlugin::close = (success, error) ->
#console.log "SQLitePlugin.prototype.close"
if @dbname of @openDBs
if txLocks[@dbname] && txLocks[@dbname].inProgress
error(new Error('database cannot be closed while a transaction is in progress'))
error newSQLError 'database cannot be closed while a transaction is in progress'
return
delete @openDBs[@dbname]
Expand All @@ -143,7 +170,7 @@
myerror = (t, e) -> if !!error then error e
myfn = (tx) ->
tx.executeSql(statement, params, mysuccess, myerror)
tx.addStatement(statement, params, mysuccess, myerror)
return
@addTransaction new SQLitePluginTransaction(this, myfn, null, null, false, false)
Expand All @@ -162,7 +189,7 @@
prevents us from stalling our txQueue if somebody passes a
false value for fn.
###
throw new Error("transaction expected a function")
throw newSQLError "transaction expected a function"
@db = db
@fn = fn
Expand All @@ -173,8 +200,8 @@
@executes = []
if txlock
@executeSql "BEGIN", [], null, (tx, err) ->
throw new Error("unable to begin transaction: " + err.message)
@addStatement "BEGIN", [], null, (tx, err) ->
throw newSQLError "unable to begin transaction: " + err.message, err.code
return
Expand All @@ -189,25 +216,46 @@
txLocks[@db.dbname].inProgress = false
@db.startNextTransaction()
if @error
@error err
@error newSQLError err
return
SQLitePluginTransaction::executeSql = (sql, values, success, error) ->
if @finalized
throw {message: 'InvalidStateError: DOM Exception 11: This transaction is already finalized. Transactions are committed after its success or failure handlers are called. If you are using a Promise to handle callbacks, be aware that implementations following the A+ standard adhere to run-to-completion semantics and so Promise resolution occurs on a subsequent tick and therefore after the transaction commits.', code: 11}
return
if @readOnly && READ_ONLY_REGEX.test(sql)
@handleStatementFailure(error, {message: 'invalid sql for a read-only transaction'})
return
@addStatement(sql, values, success, error)
return
# This method adds the SQL statement to the transaction queue but does not check for
# finalization since it is used to execute COMMIT and ROLLBACK.
SQLitePluginTransaction::addStatement = (sql, values, success, error) ->
qid = @executes.length
params = []
if !!values && values.constructor == Array
for v in values
t = typeof v
params.push (
if v == null || v == undefined || t == 'number' || t == 'string' then v
else if v instanceof Blob then v.valueOf()
else v.toString()
)
@executes.push
success: success
error: error
qid: qid
sql: sql
params: values || []
#params: values || []
params: params
return
Expand All @@ -232,9 +280,9 @@
SQLitePluginTransaction::handleStatementFailure = (handler, response) ->
if !handler
throw new Error "a statement with no error handler failed: " + response.message
if handler(this, response)
throw new Error "a statement error callback did not return false"
throw newSQLError "a statement with no error handler failed: " + response.message, response.code
if handler(this, response) isnt false
throw newSQLError "a statement error callback did not return false: " + response.message, response.code
return
SQLitePluginTransaction::run = ->
Expand All @@ -252,9 +300,10 @@
if didSucceed
tx.handleStatementSuccess batchExecutes[index].success, response
else
tx.handleStatementFailure batchExecutes[index].error, response
tx.handleStatementFailure batchExecutes[index].error, newSQLError(response)
catch err
txFailure = err unless txFailure
if !txFailure
txFailure = newSQLError(err)
if --waiting == 0
if txFailure
Expand Down Expand Up @@ -323,13 +372,13 @@
failed = (tx, err) ->
txLocks[tx.db.dbname].inProgress = false
tx.db.startNextTransaction()
if tx.error then tx.error new Error("error while trying to roll back: " + err.message)
if tx.error then tx.error newSQLError("error while trying to roll back: " + err.message, err.code)
return
@finalized = true
if @txlock
@executeSql "ROLLBACK", [], succeeded, failed
@addStatement "ROLLBACK", [], succeeded, failed
@run()
else
succeeded(tx)
Expand All @@ -349,13 +398,13 @@
failed = (tx, err) ->
txLocks[tx.db.dbname].inProgress = false
tx.db.startNextTransaction()
if tx.error then tx.error new Error("error while trying to commit: " + err.message)
if tx.error then tx.error newSQLError("error while trying to commit: " + err.message, err.code)
return
@finalized = true
if @txlock
@executeSql "COMMIT", [], succeeded, failed
@addStatement "COMMIT", [], succeeded, failed
@run()
else
succeeded(tx)
Expand Down Expand Up @@ -401,6 +450,9 @@
if !!openargs.createFromLocation and openargs.createFromLocation == 1
openargs.createFromResource = "1"
if !!openargs.androidLockWorkaround and openargs.androidLockWorkaround == 1
openargs.androidLockWorkaround = 1
new SQLitePlugin openargs, okcb, errorcb
deleteDb: (first, success, error) ->
Expand All @@ -413,7 +465,7 @@
else
#console.log "delete db args: #{JSON.stringify first}"
if !(first and first['name']) then throw new Error("Please specify db name")
if !(first and first['name']) then throw new Error "Please specify db name"
args.path = first.name
dblocation = if !!first.location then dblocations[first.location] else null
args.dblocation = dblocation || dblocations[0]
Expand Down
Loading

0 comments on commit c2a01f0

Please sign in to comment.