Skip to content

Commit

Permalink
Merge branch 'common-src' into master-src
Browse files Browse the repository at this point in the history
CHANGES:
- Use per-db state to properly handle transactions requested on a database that is being opened (#184).
- Report an error upon attempt to close database multiple times.

TEST:
- Reproduce BUGs #204/#209
- Expand some existing scenarios
- Prepare test suite for upcoming fixes and improvements (#184/#204/#209/#210/#211/#213)
  • Loading branch information
Chris Brody committed Mar 18, 2015
2 parents da5c58c + 69c73ef commit 393e5e8
Show file tree
Hide file tree
Showing 8 changed files with 627 additions and 172 deletions.
54 changes: 41 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
# Cordova/PhoneGap SQLitePlugin

Native interface to sqlite in a Cordova/PhoneGap plugin for Android/iOS/WP(8), with HTML5 Web SQL API
Native interface to sqlite in a Cordova/PhoneGap plugin for Android/iOS/WP(8), with API similar to HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/).

License for Android & WP(8) versions: MIT or Apache 2.0

License for iOS version: MIT only

## Status

- SQLCipher integration is not supported by this project, to be supported in a separate project.
TBD

## Announcements

- [SQLCipher](https://www.zetetic.net/sqlcipher/) for Android & iOS is now supported by [brodysoft / Cordova-sqlcipher-adaptor](https://github.com/brodysoft/Cordova-sqlcipher-adaptor)
- New `openDatabase` and `deleteDatabase` `location` option to select database location (iOS *only*) and disable iCloud backup
- Pre-populated databases support for Android & iOS is now integrated, usage described below
- Fixes to work with PouchDB by [@nolanlawson](https://github.com/nolanlawson)
Expand Down Expand Up @@ -55,12 +56,13 @@ License for iOS version: MIT only

## Other versions and related projects

- [brodysoft / Cordova-sqlcipher-adaptor](https://github.com/brodysoft/Cordova-sqlcipher-adaptor) - supports [SQLCipher](https://www.zetetic.net/sqlcipher/) for Android & iOS.
- [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
- Original version for iOS (with a different API): [davibe / Phonegap-SQLitePlugin](https://github.com/davibe/Phonegap-SQLitePlugin)

# Usage

The idea is to emulate the HTML5 SQL API as closely as possible. The only major change is to use window.sqlitePlugin.openDatabase() (or sqlitePlugin.openDatabase()) instead of window.openDatabase(). If you see any other major change please report it, it is probably a bug.
The idea is to emulate the HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/) as closely as possible. The only major change is to use `window.sqlitePlugin.openDatabase()` (or `sqlitePlugin.openDatabase()`) instead of `window.openDatabase()`. If you see any other major change please report it, it is probably a bug.

## Opening a database

Expand Down Expand Up @@ -96,30 +98,41 @@ An [issue was reported](https://github.com/brodysoft/Cordova-SQLitePlugin/issues

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:
There is an optional workaround that simply closes and reopens the database file at the end of every transaction that is committed. The workaround is enabled by opening the database like:

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

**NOTE:** This workaround is *only* applied when using `db.transaction()` or `db.readTransaction()`, *not* applied when running `executeSql()` on the database object.

### Pre-populated database

For Android & iOS (*only*): put the database file in the `www` directory and open the database like:

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

or to disable iCloud backup:

```js
db = sqlitePlugin.openDatabase({name: "my.db", location: 2, createFromLocation: 1});
```

**IMPORTANT NOTES:**

- Put the pre-populated database file in the `www` subdirectory. This should work well with using the Cordova CLI to support both Android & iOS versions.
- The pre-populated database file name must match **exactly** the file name given in `openDatabase`. The automatic extension has been completely eliminated.
- The pre-populated database file is ignored if the database file with the same name already exists in your database file location.

**TIP:** If you don't see the data from the pre-populated database file, completely remove your app and try it again!

## Background processing

The threading model depends on which version is used:
- For Android & WP(8), one background thread per db (always);
- for iOS, background processing using a thread pool (always).
- For Android & WP(8), one background thread per db;
- for iOS, background processing using a thread pool.

# Sample with PRAGMA feature

Expand Down Expand Up @@ -192,7 +205,7 @@ function onDeviceReady() {
}
```

This case will also works with Safari (WebKit), assuming you replace window.sqlitePlugin.openDatabase with window.openDatabase.
This case will also works with Safari (WebKit), assuming you replace `window.sqlitePlugin.openDatabase` with `window.openDatabase`.

## Delete a database

Expand All @@ -204,16 +217,31 @@ window.sqlitePlugin.deleteDatabase({name: "my.db", location: 1}, successcb, erro

# Installing

**NOTE:** This plugin is now prepared to be installed using the `cordova` tool.
## Easy install with plugman tool

For Android:

## Easy install with cordova tool
plugman install --platform android --project path.to.my.project.folder --plugin https://github.com/brodysoft/Cordova-sqlcipher-adaptor

For iOS:

plugman install --platform ios --project path.to.my.project.folder --plugin https://github.com/brodysoft/Cordova-sqlcipher-adaptor

## Easy install with Cordova CLI tool

npm install -g cordova # if you don't have cordova
cordova create MyProjectFolder com.my.project MyProject && cd MyProjectFolder # if you are just starting
cordova plugin add https://github.com/brodysoft/Cordova-SQLitePlugin

You can find more details at [this writeup](http://iphonedevlog.wordpress.com/2014/04/07/installing-chris-brodys-sqlite-database-with-cordova-cli-android/).

**IMPORTANT:** sometimes you have to update the version for a platform before you can build, like: `cordova prepare ios`

**NOTE:** If you cannot build for a platform after `cordova prepare`, you may have to remove the platform and add it again, such as:

cordova platform rm ios
cordova platform add ios

## Source tree

- `SQLitePlugin.coffee.md`: platform-independent (Literate coffee-script, can be read by recent coffee-script compiler)
Expand Down Expand Up @@ -448,7 +476,7 @@ The adapter is now part of [PouchDB](http://pouchdb.com/) thanks to [@nolanlawso

# Contributing

**IMPORTANT NOTE:** It is better to push your change(s) from a separate branch. Sometimes they need to be reworked before acceptance. Otherwise your `master` branch could become a real mess if rework is needed.
**WARNING:** Please do NOT propose changes from your `master` branch. In general changes will be rebased using `git rebase` or `git cherry-pick` and not merged.

- Testimonials of apps that are using this plugin would be especially helpful.
- Reporting issues at [brodysoft / Cordova-SQLitePlugin / issues](https://github.com/brodysoft/Cordova-SQLitePlugin/issues) can help improve the quality of this plugin.
Expand Down
106 changes: 74 additions & 32 deletions SQLitePlugin.coffee.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# SQLitePlugin in Markdown (litcoffee)
# SQLite plugin in Markdown (litcoffee)

#### Use coffee compiler to compile this directly into Javascript

#### License for common script: MIT or Apache

# Top-level SQLitePlugin objects
# Top-level SQLite plugin objects

## root window object:

Expand All @@ -14,8 +14,19 @@

READ_ONLY_REGEX = /^\s*(?:drop|delete|insert|update|create)\s/i

# per-db state
DB_STATE_INIT = "INIT"
DB_STATE_OPEN = "OPEN"

## global(s):

# per-db map of locking and queueing
# XXX NOTE: This is NOT cleaned up when a db is closed and/or deleted.
# If the record is simply removed when a db is closed or deleted,
# it will cause some test failures and may break large-scale
# applications that repeatedly open and close the database.
# [BUG #210] TODO: better to abort and clean up the pending transaction state.
# XXX TBD this will be renamed and include some more per-db state.
txLocks = {}

## utility functions:
Expand Down Expand Up @@ -63,12 +74,14 @@
else
return fun.call this, []

## SQLitePlugin db-connection
## SQLite plugin db-connection handle

#### NOTE: there can be multipe SQLitePlugin db-connection handles per open db.

#### SQLitePlugin object is defined by a constructor function and prototype member functions:
#### SQLite plugin db connection handle object is defined by a constructor function and prototype member functions:

SQLitePlugin = (openargs, openSuccess, openError) ->
console.log "SQLitePlugin openargs: #{JSON.stringify openargs}"
# console.log "SQLitePlugin openargs: #{JSON.stringify openargs}"

if !(openargs and openargs['name'])
throw newSQLError "Cannot create a SQLitePlugin db instance without a db name"
Expand All @@ -95,17 +108,21 @@
return

SQLitePlugin::databaseFeatures = isSQLitePluginDatabase: true

# Keep track of state of open db connections
# XXX TBD this will be moved and renamed or
# combined with txLocks.
SQLitePlugin::openDBs = {}

SQLitePlugin::addTransaction = (t) ->
if !txLocks[@dbname]
txLocks[@dbname] = {
queue: []
inProgress: false
}
txLocks[@dbname].queue.push t
@startNextTransaction()
if @dbname of @openDBs && @openDBs[@dbname] isnt DB_STATE_INIT
@startNextTransaction()
return

SQLitePlugin::transaction = (fn, error, success) ->
Expand All @@ -129,43 +146,70 @@

nextTick () ->
txLock = txLocks[self.dbname]
if txLock.queue.length > 0 && !txLock.inProgress
if !txLock
# XXX TBD TODO (BUG #210/??): abort all pending transactions with error cb
return

else if txLock.queue.length > 0 && !txLock.inProgress
txLock.inProgress = true
txLock.queue.shift().start()
return

return

SQLitePlugin::open = (success, error) ->
onSuccess = () => success this
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()
# for a re-open run the success cb async so that the openDatabase return value
# can be used in the success handler as an alternative to the handler's
# db argument
nextTick =>
success @
return

else
@openDBs[@dbname] = true
cordova.exec onSuccess, error, "SQLitePlugin", "open", [ @openargs ]
opensuccesscb = =>
# NOTE: the db state is NOT stored (in @openDBs) if the db was closed or deleted.

# XXX TODO [BUG #210]:
#if !@openDBs[@dbname] then call open error cb, and abort pending tx if any

if @dbname of @openDBs
@openDBs[@dbname] = DB_STATE_OPEN
success @

txLock = txLocks[@dbname]
if !!txLock && txLock.queue.length > 0 && !txLock.inProgress
@startNextTransaction()
return

# store initial DB state:
@openDBs[@dbname] = DB_STATE_INIT

cordova.exec opensuccesscb, 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 newSQLError 'database cannot be closed while a transaction is in progress'
return

# XXX [BUG #209] closing one db handle disables other handles to same db
delete @openDBs[@dbname]

# XXX [BUG #210] TODO: when closing or deleting a db, abort any pending transactions (with error callback)
cordova.exec success, error, "SQLitePlugin", "close", [ { path: @dbname } ]

else
nextTick -> error()

return

SQLitePlugin::executeSql = (statement, params, success, error) ->
# XXX TODO: better to capture the result, and report it once
# the transaction has completely finished.
# This would fix BUG #204 (cannot close db in db.executeSql() callback).
mysuccess = (t, r) -> if !!success then success r
myerror = (t, e) -> if !!error then error e

Expand All @@ -176,11 +220,8 @@
@addTransaction new SQLitePluginTransaction(this, myfn, null, null, false, false)
return

## SQLitePluginTransaction object for batching:
## SQLite plugin transaction object for batching:

###
Transaction batching object:
###
SQLitePluginTransaction = (db, fn, error, success, txlock, readOnly) ->
if typeof(fn) != "function"
###
Expand Down Expand Up @@ -210,9 +251,7 @@
@fn this
@run()
catch err
###
If "fn" throws, we must report the whole transaction as failed.
###
# If "fn" throws, we must report the whole transaction as failed.
txLocks[@db.dbname].inProgress = false
@db.startNextTransaction()
if @error
Expand Down Expand Up @@ -254,7 +293,6 @@
qid: qid

sql: sql
#params: values || []
params: params

return
Expand Down Expand Up @@ -309,10 +347,8 @@
if txFailure
tx.abort txFailure
else if tx.executes.length > 0
###
new requests have been issued by the callback
handlers, so run another batch.
###
# new requests have been issued by the callback
# handlers, so run another batch.
tx.run()
else
tx.finish()
Expand Down Expand Up @@ -470,6 +506,7 @@
dblocation = if !!first.location then dblocations[first.location] else null
args.dblocation = dblocation || dblocations[0]

# XXX [BUG #210] TODO: when closing or deleting a db, abort any pending transactions (with error callback)
delete SQLitePlugin::openDBs[args.path]
cordova.exec success, error, "SQLitePlugin", "delete", [ args ]

Expand All @@ -482,3 +519,8 @@
openDatabase: SQLiteFactory.opendb
deleteDatabase: SQLiteFactory.deleteDb

## vim directives

#### vim: set filetype=coffee :
#### vim: set expandtab :

Loading

0 comments on commit 393e5e8

Please sign in to comment.