From 876defc6e50ecc3dac300b181b5b2940f3e0705e Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Tue, 18 Apr 2017 00:05:08 +0200 Subject: [PATCH 01/47] cordova-sqlite-legacy-express-core 1.0.0-pre1 fixes - Update Android/iOS src copyright year for 2017 in this version branch - Drop engine constraints in package.json & plugin.xml (in this version branch) - Support macOS platform with builtin libsqlite3.dylib framework in this version branch - Regenerate plugin JavaScript with CoffeeScript 1.12.4 - Test fixes - doc fixes --- AUTHORS.md | 3 +- CHANGES.md | 5 + LICENSE.md | 9 +- README.md | 254 ++++++++++++------ package.json | 14 +- plugin.xml | 26 +- spec/config.xml | 6 +- spec/www/spec/basic-misc-test.js | 5 +- spec/www/spec/browser-check-startup.js | 15 +- spec/www/spec/db-tx-sql-results.js | 5 +- spec/www/spec/db-tx-string-test.js | 32 ++- spec/www/spec/db-tx-value-bindings-test.js | 29 +- spec/www/spec/misc-tx-legacy.js | 43 ++- spec/www/spec/tx-semantics-test.js | 7 +- .../io/sqlc/SQLiteAndroidDatabase.java | 2 +- src/android/io/sqlc/SQLitePlugin.java | 2 +- src/ios/SQLitePlugin.h | 2 +- src/ios/SQLitePlugin.m | 2 +- www/SQLitePlugin.js | 4 +- 19 files changed, 279 insertions(+), 186 deletions(-) diff --git a/AUTHORS.md b/AUTHORS.md index 12cdf16f..76e8a28f 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -16,7 +16,7 @@ - Fixes to support old Android versions by @nolanlawson - Thanks to Mark Oppenheim for fixes to open/close callbacks and repeated open/close/delete operations -## iOS version +## iOS/macOS version - Original authors: @davibe (Davide Bertola ) and @joenoon (Joe Noon ) - Cordova 2.7+ port with background processing by @j3k0 (Jean-Christophe Hoelt ) @@ -26,3 +26,4 @@ - ~~SQLiteProxy.js by @vldmrrr (Vladimir Avdonin) and @brodybits (Chris Brody)~~ - ~~Using SQLite3-WinRT C++ classes and SQLite3JS (Javascript part) by @doo (doo GmbH)~~ +- ~~Thanks to @AllJoyn-Cordova for idea how to integrate Windows 8.1/Windows Phone 8.1 Visual C++ projects in plugin.xml~~ diff --git a/CHANGES.md b/CHANGES.md index 93f86939..7b096b9e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,10 @@ # Changes +###### cordova-sqlite-legacy-express-core 1.0.0-pre1 + +- Drop engine constraints in package.json & plugin.xml (in this version branch) +- Support macOS platform with builtin libsqlite3.dylib framework in this version branch + ## 1.2.2 - Self-test function to verify ability to open/populate/read/delete a test database diff --git a/LICENSE.md b/LICENSE.md index ff1fb366..e621188a 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -8,11 +8,13 @@ MIT or Apache 2.0 MIT or Apache 2.0 -## iOS version +## iOS/macOS version MIT only -## Windows (8.1) version +based on Phonegap-SQLitePlugin by @davibe (Davide Bertola ) and @joenoon (Joe Noon ) + +## REMOVED from this version branch: Windows (8.1/...) version MIT or Apache 2.0 @@ -22,3 +24,6 @@ by @doo (doo GmbH) MIT License +## SQLite3 + +Public domain diff --git a/README.md b/README.md index 78da5a44..8967c103 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,16 @@ -# Cordova/PhoneGap sqlite storage adapter (core version) +# Cordova/PhoneGap sqlite storage adapter (legacy express core version branch) -Native interface to sqlite in a Cordova/PhoneGap plugin for Android and iOS, with API similar to HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/). +Native interface to sqlite in a Cordova/PhoneGap plugin for Android/iOS/macOS, with API similar to HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/). -License for Android version: MIT or Apache 2.0 +License for Android platform version: MIT or Apache 2.0 -License for iOS version: MIT only +License for iOS/macOS platform version: MIT only -This version contains the source code for the Android and iOS versions. This version does not contain any libraries or source code from www.sqlite.org. This version uses the built-in sqlite libraries on Android and iOS. +## About this version branch -|Android Circle-CI (**full** suite)|iOS Travis-CI (*very* limited suite)| -|-----------------------|----------------------| -|[![Circle CI](https://circleci.com/gh/litehelpers/Cordova-sqlite-storage.svg?style=svg)](https://circleci.com/gh/litehelpers/Cordova-sqlite-storage)|[![Build Status](https://travis-ci.org/litehelpers/Cordova-sqlite-storage.svg?branch=core-master)](https://travis-ci.org/litehelpers/Cordova-sqlite-storage)| +This version branch contains the source code for the Android/iOS/macOS platforms (legacy express core version branch). This version does not contain any libraries or source code from www.sqlite.org. This version branch uses the built-in sqlite libraries on Android, iOS, and macOS. + + ## BREAKING CHANGE: Database location parameter is now mandatory @@ -43,66 +43,67 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase **NOTE:** Changing `BackupWebStorage` in `config.xml` has no effect on a database created by this plugin. `BackupWebStorage` applies only to local storage and/or Web SQL storage created in the WebView (*not* using this plugin). For reference: [phonegap/build#338 (comment)](https://github.com/phonegap/build/issues/338#issuecomment-113328140) -## Available for hire - -The primary author and maintainer [@brodybits (Christopher J. Brody aka Chris Brody)](https://github.com/brodybits) is available for contracting assignments. Part-time assignments would really help keep this project alive. ([@brodybits](https://github.com/brodybits) dedicates a significant amount of working time and has turned down multiple full-time work opportunities to maintain and improve this project.) - -[@brodybits](https://github.com/brodybits) can be contacted at: -- -- -- LinkedIn: - -Some other projects by [@brodybits](https://github.com/brodybits): -- [liteglue / Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector) - Lightweight SQLiteConnection library with abstraction layer over [liteglue / Android-sqlite-native-driver](https://github.com/liteglue/Android-sqlite-native-driver) -- [brodybits / node-uvhttp](https://github.com/brodybits/node-uvhttp) - HTTP server library that serves static content from native code - *experimental and extremely limited* -- [brodybits / java-node](https://github.com/brodybits/java-node) - two-way binding interface between Java and Node.js (Javascript) - *experimental and extremely limited* - ## Status -- A recent version of the Cordova CLI (such as `6.1.1`) is recommended. Cordova versions older than `6.0.0` are not supported by this project. Use of other systems such as PhoneGap CLI, PhoneGap Build, or plugman is not tested and no longer supported. +- A recent version of the Cordova CLI (such as `6.5.0`) is recommended. Cordova versions older than `6.0.0` are missing the `cordova-ios@4.0.0` security fixes. In addition it is *required* to use `cordova prepare` in case of cordova-ios older than `4.3.0` (Cordova CLI `6.4.0`). - iOS database location is now mandatory, as documented below. -- Android version in this version branch is now using the built-in Android SQLite database classes. Integration with the lightweight [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector) is available in [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) and [litehelpers / Cordova-sqlite-legacy](https://github.com/litehelpers/Cordova-sqlite-legacy). -- Windows 8.1/Windows Phone 8.1/Windows 10 version using the performant [doo / SQLite3-WinRT](https://github.com/doo/SQLite3-WinRT) component is available in [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) (with pre-populated database support) and [litehelpers / Cordova-sqlite-legacy](https://github.com/litehelpers/Cordova-sqlite-legacy). -- WP8 support (along with Windows 8.1/Windows Phone 8.1/Windows 10 support) is available in [litehelpers / Cordova-sqlite-legacy](https://github.com/litehelpers/Cordova-sqlite-legacy). +- Android platform version in this version branch is now using the built-in Android SQLite database classes. Integration with the lightweight [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector) is available in the default [litehelpers / Cordova-sqlite-storage](https://github.com/litehelpers/Cordova-sqlite-storage) version branch as well as other versions such as [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) and [litehelpers / Cordova-sqlite-legacy](https://github.com/litehelpers/Cordova-sqlite-legacy). +- iOS/macOS platform version in this version branch uses builtin `libsqlite3.dylib` framework library. Other versions such as the default [litehelpers / Cordova-sqlite-storage](https://github.com/litehelpers/Cordova-sqlite-storage) version branch, [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext), and [litehelpers / Cordova-sqlite-legacy](https://github.com/litehelpers/Cordova-sqlite-legacy) include a build of a recent sqlite3 amalgamation. +- Windows 10 UWP version using the performant [doo / SQLite3-WinRT](https://github.com/doo/SQLite3-WinRT) component is available in the default [litehelpers / Cordova-sqlite-storage](https://github.com/litehelpers/Cordova-sqlite-storage) version branch as well as other versions such as [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext). +- Support for WP8 along with Windows 8.1/Windows Phone 8.1/Windows 10 using Visual Studio 2015 is available in [litehelpers / Cordova-sqlite-legacy](https://github.com/litehelpers/Cordova-sqlite-legacy). - The following features are available in [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext): - - REGEXP support (Android/iOS) - - Pre-populated database (all platforms Android/iOS/Windows) + - REGEXP (Android/iOS/macOS) + - SELECT BLOB data in Base64 format (all platforms Android/iOS/macOS/Windows) + - Pre-populated database (Android/iOS/macOS/Windows) - Amazon Fire-OS is dropped due to lack of support by Cordova. Android version should be used to deploy to Fire-OS 5.0(+) devices. For reference: [cordova/cordova-discuss#32 (comment)](https://github.com/cordova/cordova-discuss/issues/32#issuecomment-167021676) -- FTS3 and FTS4 are tested working OK in this version branch (for all target platforms in this version branch Android/iOS) -- R-Tree is tested and supported for iOS *only* in this version branch +- FTS3 and FTS4 are tested working OK in this version branch (for all target platforms in this version branch Android/iOS/macOS) +- R-Tree is *not* tested or supported for Android in this version branch. - Android is supported back to SDK 10 (a.k.a. Gingerbread, Android 2.3.3); support for older versions is available upon request. -- iOS versions supported: 7.x/8.x/9.x -- In case of memory issues please use smaller transactions or use the version (with a different licensing scheme) at: [litehelpers / Cordova-sqlite-enterprise-free](https://github.com/litehelpers/Cordova-sqlite-enterprise-free) +- iOS versions supported: 8.x/9.x/10.x +- In case of memory issues please use smaller transactions or use the version (with GPL or commercial license options) at: [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) + + ## Announcements +- [brodybits / cordova-sqlite-test-app](https://github.com/brodybits/cordova-sqlite-test-app) project is a CC0 (public domain) starting point (NOTE that this plugin must be added) and may also be used to reproduce issues with this plugin. +- The Lawnchair adapter is now supported in [litehelpers / cordova-sqlite-lawnchair-adapter](https://github.com/litehelpers/cordova-sqlite-lawnchair-adapter). +- [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) now supports SELECT BLOB data in Base64 format on all platforms in addition to REGEXP (Android/iOS/macOS) and pre-populated database (all platforms). +- [brodybits / sql-promise-helper](https://github.com/brodybits/sql-promise-helper) provides a Promise-based API wrapper. +- [nolanlawson / pouchdb-adapter-cordova-sqlite](https://github.com/nolanlawson/pouchdb-adapter-cordova-sqlite) supports this plugin along with other implementations such as [nolanlawson / sqlite-plugin-2](https://github.com/nolanlawson/sqlite-plugin-2) and [Microsoft / cordova-plugin-websql](https://github.com/Microsoft/cordova-plugin-websql). +- macOS ("osx" platform) is now supported +- New [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) version with Android JSON and SQL statement handling implemented in C, as well as support for PhoneGap Build, Intel XDK, etc., available with GPL or commercial license options. Handles large SQL batches in less than half the time as this version. Also supports arbitrary database location on Android. +- Published [brodybits / Cordova-quick-start-checklist](https://github.com/brodybits/Cordova-quick-start-checklist) and [brodybits / Avoiding-some-Cordova-pitfalls](https://github.com/brodybits/Avoiding-some-Cordova-pitfalls). - Self-test functions to verify proper installation and operation of this plugin -- Windows 8.1/Windows Phone 8.1/Windows 10 version is available in [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) (with pre-populated database support) and [litehelpers / Cordova-sqlite-legacy](https://github.com/litehelpers/Cordova-sqlite-legacy) (with WP8 support). - More explicit `openDatabase` and `deleteDatabase` `iosDatabaseLocation` option -- Added simple sql batch query function -- All iOS operations are now using background processing (reported to resolve intermittent problems with cordova-ios@4.0.1) +- Added simple sql batch function - Published [brodybits / Cordova-quick-start-checklist](https://github.com/brodybits/Cordova-quick-start-checklist) and [brodybits / Cordova-troubleshooting-guide](https://github.com/brodybits/Cordova-troubleshooting-guide) -- A version with support for web workers is available (with a different licensing scheme) at: [litehelpers / cordova-sqlite-workers-evfree](https://github.com/litehelpers/cordova-sqlite-workers-evfree) -- ~~PhoneGap Build is now supported through the npm package: http://phonegap.com/blog/2015/05/26/npm-plugins-available/~~ (no longer supported due to reported issues) - [MetaMemoryT / websql-promise](https://github.com/MetaMemoryT/websql-promise) now provides a Promises-based interface to both Web SQL and this plugin -- [SQLCipher](https://www.zetetic.net/sqlcipher/) for Android/iOS/Windows is supported by [litehelpers / Cordova-sqlcipher-adapter](https://github.com/litehelpers/Cordova-sqlcipher-adapter) +- [SQLCipher](https://www.zetetic.net/sqlcipher/) for Android/iOS/macOS/Windows is supported by [litehelpers / Cordova-sqlcipher-adapter](https://github.com/litehelpers/Cordova-sqlcipher-adapter) + + ## Highlights - Drop-in replacement for HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/): the only change should be to replace the static `window.openDatabase()` factory call with `window.sqlitePlugin.openDatabase()`, with parameters as documented below. - Failure-safe nested transactions with batch processing optimizations (according to HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/)) +- API (based on HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/)) is designed to be as flexible as possible but does not allow the application to leave any transactions hanging open. - As described in [this posting](http://brodyspark.blogspot.com/2012/12/cordovaphonegap-sqlite-plugins-offer.html): - - Keeps sqlite database in a user data location that is known; can be reconfigured (iOS version); and synchronized to iCloud by default (iOS version; can be disabled as described below). + - Keeps sqlite database in a user data location that is known; can be reconfigured (iOS/macOS platform version); and may be synchronized to iCloud (iOS platform version). - No 5MB maximum, more information at: http://www.sqlite.org/limits.html - This project is self-contained: no dependencies on other plugins such as cordova-plugin-file -- Windows 8.1/Windows Phone 8.1/Windows 10 version available in [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) and [litehelpers / Cordova-sqlite-legacy](https://github.com/litehelpers/Cordova-sqlite-legacy) uses the performant C++ [doo / SQLite3-WinRT](https://github.com/doo/SQLite3-WinRT) component. -- [SQLCipher](https://www.zetetic.net/sqlcipher/) support for Android/iOS/Windows is available at: [litehelpers / Cordova-sqlcipher-adapter](https://github.com/litehelpers/Cordova-sqlcipher-adapter) +- Windows 10 UWP platform version available in [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) and Windows 8.1/Windows Phone 8.1/Windows 10 platform version available in [litehelpers / Cordova-sqlite-legacy](https://github.com/litehelpers/Cordova-sqlite-legacy) use the performant C++ [doo / SQLite3-WinRT](https://github.com/doo/SQLite3-WinRT) component. +- [SQLCipher](https://www.zetetic.net/sqlcipher/) support for Android/iOS/macOS/Windows is available in: [litehelpers / Cordova-sqlcipher-adapter](https://github.com/litehelpers/Cordova-sqlcipher-adapter) - Intellectual property: - All source code is tracked to the original author in git - Major authors are tracked in AUTHORS.md - - Licensing of each component is tracked in LICENSE.md + - License of each component is tracked in LICENSE.md - History of this project is also described in HISTORY.md +**TIP:** It is possible to migrate from Cordova to a pure native solution and continue using the data stored by this plugin. + + + ## Some apps using this plugin - [Trailforks Mountain Bike Trail Map App](http://www.trailforks.com/apps/map/) with a couple of nice videos at: @@ -111,43 +112,62 @@ Some other projects by [@brodybits](https://github.com/brodybits): - [Larkwire](http://www.larkwire.com/) (iOS version): Learn bird songs the fun way - [Tangorin](https://play.google.com/store/apps/details?id=com.tangorin.app) (Android) Japanese Dictionary at [tangorin.com](http://tangorin.com/) + + ## Known issues -- iOS version does not support certain rapidly repeated open-and-close or open-and-delete test scenarios due to how the implementation handles background processing +- iOS/macOS platform version does not support certain rapidly repeated open-and-close or open-and-delete test scenarios due to how the implementation handles background processing - As described below, auto-vacuum is NOT enabled by default. +- It is possible to request a SQL statement list such as "SELECT 1; SELECT 2" within a single SQL statement string, however the plugin will only execute the first statement and silently ignore the others ref: [litehelpers/Cordova-sqlite-storage#551](https://github.com/litehelpers/Cordova-sqlite-storage/issues/551) - INSERT statement that affects multiple rows (due to SELECT cause or using TRIGGER(s), for example) does not report proper rowsAffected on Android -- Memory issue observed when adding a large number of records due to the JSON implementation which is improved in [litehelpers / Cordova-sqlite-enterprise-free](https://github.com/litehelpers/Cordova-sqlite-enterprise-free) (available with a different licensing scheme) +- Memory issue observed when adding a large number of records due to the JSON implementation which is improved in [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (available with GPL or commercial license options) +- Infinity (positive or negative) values are not supported on Android/iOS/macOS due to issues described above including a possible crash on iOS/macOS ref: [litehelpers/Cordova-sqlite-storage#405](https://github.com/litehelpers/Cordova-sqlite-storage/issues/405) - A stability issue was reported on the iOS version when in use together with [SockJS](http://sockjs.org/) client such as [pusher-js](https://github.com/pusher/pusher-js) at the same time (see [litehelpers/Cordova-sqlite-storage#196](https://github.com/litehelpers/Cordova-sqlite-storage/issues/196)). The workaround is to call sqlite functions and [SockJS](http://sockjs.org/) client functions in separate ticks (using setTimeout with 0 timeout). - If a sql statement fails for which there is no error handler or the error handler does not return `false` to signal transaction recovery, the plugin fires the remaining sql callbacks before aborting the transaction. -- In case of an error, the error `code` member is bogus on Android (fixed for Android in [litehelpers / Cordova-sqlite-enterprise-free](https://github.com/litehelpers/Cordova-sqlite-enterprise-free)). -- Possible crash on Android when using Unicode emoji characters due to [Android bug 81341](https://code.google.com/p/android/issues/detail?id=81341), which _should_ be fixed in Android 6.x +- Possible crash on Android when using Unicode emoji and other 4-octet UTF-8 characters due to [Android bug 81341](https://code.google.com/p/android/issues/detail?id=81341), which *should* be fixed in Android 6.x - Close/delete database bugs described below. -- When a database is opened and deleted without closing, the iOS version is known to leak resources. -- It is NOT possible to open multiple databases with the same name but in different locations (iOS version). +- When a database is opened and deleted without closing, the iOS/macOS platform version is known to leak resources. +- It is NOT possible to open multiple databases with the same name but in different locations (iOS/macOS platform version). - Incorrect or missing insertId/rowsAffected in results for INSERT/UPDATE/DELETE SQL statements with extra semicolon(s) in the beginning for Android (android.database implementation) - readTransaction does *not* reject modification SQL statements with extra semicolon(s) in the beginning - Problems reported with PhoneGap Build in the past: - PhoneGap Build Hydration. - Apparently FIXED: ~~PhoneGap Build may fail to build the iOS version unless the name of the app starts with an uppercase and contains no spaces (see [litehelpers/Cordova-sqlite-storage#243](https://github.com/litehelpers/Cordova-sqlite-storage/issues/243); [Wizcorp/phonegap-facebook-plugin#830](https://github.com/Wizcorp/phonegap-facebook-plugin/issues/830); [phonegap/build#431](https://github.com/phonegap/build/issues/431)).~~ +Issues fixed in some newer version branches: +- In case of an error, the error `code` member is bogus on Android +- iOS platform version generates extra logging in release version +- iOS platform version may crash if deleteDatabase is called with an object in place of the database name +- readTransaction does not reject ALTER, REINDEX, and REPLACE operations +- readTransaction does not reject modification statements with extra semicolon(s) in the beginning +- extra executeSql callbacks triggered in a transaction after a failure that was not recovered by an error callback that returns false +- does not signal an error in case of excess parameter argument values given on iOS/macOS + + + ## Other limitations - ~~The db version, display name, and size parameter values are not supported and will be ignored.~~ (No longer supported by the API) - Absolute and relative subdirectory path(s) are not tested or supported. - This plugin will not work before the callback for the 'deviceready' event has been fired, as described in **Usage**. (This is consistent with the other Cordova plugins.) -- This version will not work within a web worker (not properly supported by the Cordova framework). Use within a web worker is supported for Android and iOS in: [litehelpers / cordova-sqlite-workers-evfree](https://github.com/litehelpers/cordova-sqlite-workers-evfree) (available with a different licensing scheme) +- Extremely large records are not supported by this plugin version. TBD: specify maximum record; FUTURE TBD: to be fixed in [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (available with GPL or commercial license options) +- This plugin version will not work within a web worker (not properly supported by the Cordova framework). Use within a web worker is supported for Android/iOS in: [litehelpers / Cordova-sqlite-evplus-legacy-workers-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-workers-free) (available with GPL or premium commercial license options) - In-memory database `db=window.sqlitePlugin.openDatabase({name: ':memory:', ...})` is currently not supported. - The Android version cannot work with more than 100 open db files (due to the threading model used). -- UNICODE `\u2028` (line separator) and `\u2029` (paragraph separator) characters are currently not supported and known to be broken in iOS version due to [Cordova bug CB-9435](https://issues.apache.org/jira/browse/CB-9435). There *may* be a similar issue with certain other UNICODE characters in the iOS version (needs further investigation). This is fixed in: [litehelpers / Cordova-sqlite-enterprise-free](https://github.com/litehelpers/Cordova-sqlite-enterprise-free) (available with a different licensing scheme) -- BLOB type is currently not supported. +- UNICODE `\u2028` (line separator) and `\u2029` (paragraph separator) characters are currently not supported and known to be broken in iOS, macOS, and Android version due to JSON issues reported in [Cordova bug CB-9435](https://issues.apache.org/jira/browse/CB-9435) and [cordova/cordova-discuss#57](https://github.com/cordova/cordova-discuss/issues/57). There *may* be a similar issue with certain other UNICODE characters in the iOS/macOS version (needs further investigation). This is fixed for iOS in: [litehelpers / Cordova-sqlite-evplus-legacy-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-free) and [litehelpers / Cordova-sqlite-evplus-legacy-attach-detach-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-attach-detach-free) (available with GPL or special commercial license options) as well as [litehelpers / Cordova-sqlite-evplus-legacy-workers-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-workers-free) (available with GPL or premium commercial license options) +- The BLOB data type is not fully supported by this version branch. SELECT BLOB in Base64 format is supported by [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) (permissive license terms) and [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (GPL or commercial license options). +- Truncation in case of UNICODE `\u0000` (same as `\0`) character on Android - Case-insensitive matching and other string manipulations on Unicode characters, which is provided by optional ICU integration in the sqlite source and working with recent versions of Android, is not supported for any target platforms. -- iOS version uses a thread pool but with only one thread working at a time due to "synchronized" database access -- Large query result can be slow, also due to JSON implementation -- ATTACH to another database file is not supported by this version. Attach/detach is supported (along with the memory and iOS UNICODE `\u2028` line separator / `\u2029` paragraph separator fixes) in: [litehelpers / Cordova-sqlite-evfree-ext](https://github.com/litehelpers/Cordova-sqlite-evfree-ext) (available with a different licensing scheme) -- UPDATE/DELETE with LIMIT or ORDER BY is not supported and known to be missing in older Android/iOS versions. +- iOS/macOS platform version uses a thread pool but with only one thread working at a time due to "synchronized" database access +- Some large query results may be slow, also due to the JSON implementation. +- ATTACH to another database file is not supported by this version. Attach/detach is supported (along with the memory and iOS UNICODE `\u2028` line separator / `\u2029` paragraph separator fixes) in: [litehelpers / Cordova-sqlite-evplus-legacy-attach-detach-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-attach-detach-free) (available with GPL or special commercial license options) +- UPDATE/DELETE with LIMIT or ORDER BY is not supported. +- WITH clause is not supported by some older Android/iOS versions. - User-defined savepoints are not supported and not expected to be compatible with the transaction locking mechanism used by this plugin. In addition, the use of BEGIN/COMMIT/ROLLBACK statements is not supported. - Problems have been reported when using this plugin with Crosswalk (for Android). It may help to install Crosswalk as a plugin instead of using Crosswalk to create the project. -- Does not work with [axemclion / react-native-cordova-plugin](https://github.com/axemclion/react-native-cordova-plugin) since the `window.sqlitePlugin` object *not* proprly exported (ES5 feature). It is recommended to use [andpor / react-native-sqlite-storage](https://github.com/andpor/react-native-sqlite-storage) for SQLite database access with React Native Android/iOS instead. +- Does not work with [axemclion / react-native-cordova-plugin](https://github.com/axemclion/react-native-cordova-plugin) since the `window.sqlitePlugin` object is *not* properly exported (ES5 feature). It is recommended to use [andpor / react-native-sqlite-storage](https://github.com/andpor/react-native-sqlite-storage) for SQLite database access with React Native Android/iOS instead. + + ## Further testing needed @@ -200,31 +220,37 @@ Some other projects by [@brodybits](https://github.com/brodybits): ### Other versions -- [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) - version with [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector), Windows 8.1/Windows Phone 8.1/Windows 10 (using [doo / SQLite3-WinRT](https://github.com/doo/SQLite3-WinRT)), REGEXP support for Android/iOS, and pre-populated database support for Android/iOS/Windows -- [litehelpers / Cordova-sqlite-legacy](https://github.com/litehelpers/Cordova-sqlite-legacy) - maintenance of WP8 version along with the other supported platforms Android/iOS/Windows 8.1/Windows Phone 8.1/Windows 10 -- [litehelpers / Cordova-sqlcipher-adapter](https://github.com/litehelpers/Cordova-sqlcipher-adapter) - supports [SQLCipher](https://www.zetetic.net/sqlcipher/) for Android, iOS, and Windows 8.1(+)/Windows Phone 8.1(+) -- [litehelpers / Cordova-sqlite-enterprise-free](https://github.com/litehelpers/Cordova-sqlite-enterprise-free) - internal memory improvements to support larger transactions (Android/iOS) and fix to support all Unicode characters (iOS) - with a different licensing scheme -- [litehelpers / Cordova-sqlite-evfree-ext](https://github.com/litehelpers/Cordova-sqlite-evfree-ext) - version with support for ATTACH, includes internal memory improvements to support larger transactions (Android/iOS) and fix to support all Unicode characters (with a different licensing scheme) -- [litehelpers / cordova-sqlite-workers-evfree](https://github.com/litehelpers/cordova-sqlite-workers-evfree) - version with support for web workers, includes internal memory improvements to support larger transactions (Android/iOS) and fix to support all Unicode characters (iOS) (with a different licensing scheme) +- [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) - version with REGEXP (Android/iOS/macOS), SELECT BLOB in Base64 format (all platforms Android/iOS/macOS/Windows), and pre-populated databases (all platforms Android/iOS/macOS/Windows) +- [litehelpers / Cordova-sqlite-legacy-build-support](https://github.com/litehelpers/Cordova-sqlite-legacy-build-support) - maintenance of WP8 version along with Windows 8.1/Windows Phone 8.1 and the other supported platforms Android/iOS/macOS/Windows 10; limited support for PhoneGap CLI/PhoneGap Build/plugman/Intel XDK; limited testing; limited updates +- [litehelpers / Cordova-sqlcipher-adapter](https://github.com/litehelpers/Cordova-sqlcipher-adapter) - supports [SQLCipher](https://www.zetetic.net/sqlcipher/) for Android/iOS/macOS/Windows +- [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) - Enhancements for Android: JSON and SQL statement handling implemented in C, supports larger transactions and handles large SQL batches in less than half the time as this version. Supports arbitrary database location on Android. Support for build environments such as PhoneGap Build and Intel XDK. Available with GPL or commercial license options. Also includes REGEXP (Android/iOS/macOS) and SELECT BLOB in Base64 format (all platforms Android/iOS/macOS/Windows). +- [litehelpers / Cordova-sqlite-evplus-legacy-workers-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-workers-free) - version with support for web workers, includes internal memory improvements to support larger transactions (Android/iOS) and fix to support all Unicode characters (iOS) (with GPL or premium commercial license options) +- [litehelpers / Cordova-sqlite-evplus-legacy-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-free) - internal memory improvements to support larger transactions (Android/iOS) and fix to support all Unicode characters (iOS) - with GPL or special commercial license options +- [litehelpers / Cordova-sqlite-evplus-legacy-attach-detach-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-attach-detach-free) - version with support for ATTACH, includes internal memory improvements to support larger transactions (Android/iOS) and fix to support all Unicode characters (with GPL or special commercial license options) - Adaptation for React Native Android and iOS: [andpor / react-native-sqlite-storage](https://github.com/andpor/react-native-sqlite-storage) - Original version for iOS (with a slightly different transaction API): [davibe / Phonegap-SQLitePlugin](https://github.com/davibe/Phonegap-SQLitePlugin) + + ### Other SQLite adapter projects - [object-layer / AnySQL](https://github.com/object-layer/anysql) - Unified SQL API over multiple database engines - [samikrc / CordovaSQLite](https://github.com/samikrc/CordovaSQLite) - Simpler sqlite plugin with a simpler API and browser platform -- [nolanlawson / sqlite-plugin-2](https://github.com/nolanlawson/sqlite-plugin-2) - Simpler fork/partial rewrite (TBD not sure how much of the code, such as iOS code by [@davibe](https://github.com/davibe), is copied/reused) +- [nolanlawson / sqlite-plugin-2](https://github.com/nolanlawson/sqlite-plugin-2) - Simpler fork/rewrite - [nolanlawson / node-websql](https://github.com/nolanlawson/node-websql) - Web SQL API implementation for Node.js - [an-rahulpandey / cordova-plugin-dbcopy](https://github.com/an-rahulpandey/cordova-plugin-dbcopy) - Alternative way to copy pre-populated database - [EionRobb / phonegap-win8-sqlite](https://github.com/EionRobb/phonegap-win8-sqlite) - WebSQL add-on for Win8/Metro apps (perhaps with a different API), using an old version of the C++ library from [SQLite3-WinRT Component](https://github.com/doo/SQLite3-WinRT) (as referenced by [01org / cordova-win8](https://github.com/01org/cordova-win8)) - [SQLite3-WinRT Component](https://github.com/doo/SQLite3-WinRT) - C++ component that provides a nice SQLite API with promises for WinJS - [01org / cordova-win8](https://github.com/01org/cordova-win8) - old, unofficial version of Cordova API support for Windows 8 Metro that includes an old version of the C++ [SQLite3-WinRT Component](https://github.com/doo/SQLite3-WinRT) -- [MSOpenTech / cordova-plugin-websql](https://github.com/MSOpenTech/cordova-plugin-websql) - Windows 8(+) and Windows Phone 8(+) WebSQL plugin versions in C# -- [Thinkwise / cordova-plugin-websql](https://github.com/Thinkwise/cordova-plugin-websql) - fork of [MSOpenTech / cordova-plugin-websql](https://github.com/MSOpenTech/cordova-plugin-websql) that supports asynchronous execution +- [Microsoft / cordova-plugin-websql](https://github.com/Microsoft/cordova-plugin-websql) - Windows 8(+) and Windows Phone 8(+) WebSQL plugin versions in C# +- [Thinkwise / cordova-plugin-websql](https://github.com/Thinkwise/cordova-plugin-websql) - fork of [Microsoft / cordova-plugin-websql](https://github.com/Microsoft/cordova-plugin-websql) that supports asynchronous execution - [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. + + ### Alternative solutions +- Use [phearme / cordova-ContentProviderPlugin](https://github.com/phearme/cordova-ContentProviderPlugin) to query content providers on Android devices - [ABB-Austin / cordova-plugin-indexeddb-async](https://github.com/ABB-Austin/cordova-plugin-indexeddb-async) - Asynchronous IndexedDB plugin for Cordova that uses [axemclion / IndexedDBShim](https://github.com/axemclion/IndexedDBShim) (Browser/iOS/Android/Windows) and [Thinkwise / cordova-plugin-websql](https://github.com/Thinkwise/cordova-plugin-websql) - (Windows) - Another sqlite binding for React-Native (iOS version): [almost/react-native-sqlite](https://github.com/almost/react-native-sqlite) - Use [NativeScript](https://www.nativescript.org) with its web view and [NathanaelA / nativescript-sqlite](https://github.com/Natha @@ -232,6 +258,8 @@ naelA/nativescript-sqlite) (Android and/or iOS) - Standard HTML5 [local storage](https://en.wikipedia.org/wiki/Web_storage#localStorage) - [Realm.io](https://realm.io/) + + # Usage ## Self-test functions @@ -266,7 +294,7 @@ var db = window.sqlitePlugin.openDatabase({name: 'my.db', location: 'default'}, **WARNING:** The new "default" location value is *NOT* the same as the old default location and would break an upgrade for an app that was using the old default value (0) on iOS. -To specify a different location (affects iOS *only*): +To specify a different location (affects iOS/macOS *only*): ```js var db = window.sqlitePlugin.openDatabase({name: 'my.db', iosDatabaseLocation: 'Library'}, successcb, errorcb); @@ -316,12 +344,19 @@ window.sqlitePlugin.openDatabase({name: 'my.db', location: 'default'}, function( If any sql statements or transactions are attempted on a database object before the openDatabase result is known, they will be queued and will be aborted in case the database cannot be opened. +**DATABASE NAME NOTES:** + +- Database file names with slash (`/`) character(s) are not supported and not expected to work. +- Database file names with ASCII control characters such as tab, vertical tab, carriage return, line feed, form feed, and backspace are not supported and do not work on Windows. +- Some other ASCII characters not supported and not working on Windows: `*` `<` `>` `?` `\` `"` `|` +- Database file names with emojis and other 4-octet UTF-8 characters are NOT RECOMMENDED. + **OTHER NOTES:** - The database file name should include the extension, if desired. - It is possible to open multiple database access handle objects for the same database. - The database handle access object can be closed as described below. -**TIP:** +**Web SQL replacement tip:** To overwrite `window.openDatabase`: @@ -337,23 +372,34 @@ window.openDatabase = function(dbname, ignored1, ignored2, ignored3) { - (sometimes) there is an unexpected database lock - the data that was inserted is lost. -This issue is suspected to be 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 cause of this issue remains unknown. Of interest: [android / platform_external_sqlite commit d4f30d0d15](https://github.com/android/platform_external_sqlite/commit/d4f30d0d1544f8967ee5763c4a1680cb0553039f) which references and includes the sqlite commit at: http://www.sqlite.org/src/info/6c4c2b7dba 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 with options as follows: ```js -var db = window.sqlitePlugin.openDatabase({name: 'my.db', location: 'default', androidLockWorkaround: 1}); +var db = window.sqlitePlugin.openDatabase({ + name: 'my.db', + location: 'default', + androidDatabaseImplementation: 2, + androidLockWorkaround: 1 +}); ``` -**IMPORTANT NOTE:** This workaround is *only* applied when using `db.transaction()`, *not* applied when running `executeSql()` on the database object. +**IMPORTANT NOTE:** This workaround is *only* applied when using `db.sqlBatch` or `db.transaction()`, *not* applied when running `executeSql()` on the database object. + + ## SQL transactions The following types of SQL transactions are supported by this version: - Single-statement transactions -- SQL batch query transactions +- SQL batch transactions - Standard asynchronous transactions +**NOTE:** Transaction requests are kept in one queue per database and executed in sequential order, according to the HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/). + +**WARNING:** It is possible to request a SQL statement list such as "SELECT 1; SELECT 2" within a single SQL statement string, however the plugin will only execute the first statement and silently ignore the others. This could result in data loss if such a SQL statement list with any INSERT or UPDATE statement(s) are included. For reference: [litehelpers/Cordova-sqlite-storage#551](https://github.com/litehelpers/Cordova-sqlite-storage/issues/551) + ### Single-statement transactions Sample with INSERT: @@ -388,7 +434,9 @@ db.executeSql("SELECT UPPER('First') AS uppertext", [], function (resultSet) { }); ``` -### SQL batch query transactions + + +### SQL batch transactions Sample: @@ -408,6 +456,8 @@ db.sqlBatch([ In case of an error, all changes in a sql batch are automatically discarded using ROLLBACK. + + ### Standard asynchronous transactions Standard asynchronous transactions follow the HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/) which is very well documented and uses BEGIN and COMMIT or ROLLBACK to keep the transactions failure-safe. Here is a simple example: @@ -419,7 +469,7 @@ db.transaction(function(tx) { tx.executeSql('INSERT INTO MyTable VALUES (?)', ['test-value'], function(tx, resultSet) { console.log('resultSet.insertId: ' + resultSet.insertId); console.log('resultSet.rowsAffected: ' + resultSet.rowsAffected); - }, function(error) { + }, function(tx, error) { console.log('INSERT error: ' + error.message); }); }, function(error) { @@ -435,7 +485,7 @@ In case of a read-only transaction, it is possible to use `readTransaction` whic db.readTransaction(function(tx) { tx.executeSql("SELECT UPPER('Some US-ASCII text') AS uppertext", [], function(tx, resultSet) { console.log("resultSet.rows.item(0).uppertext: " + resultSet.rows.item(0).uppertext); - }, function(error) { + }, function(tx, error) { console.log('SELECT error: ' + error.message); }); }, function(error) { @@ -492,11 +542,15 @@ db.readTransaction(function(tx) { **FUTURE TBD:** It should be possible to get a row result object using `resultSet.rows[rowNumber]`, also in case of a single-statement transaction. This is non-standard but is supported by the Chrome desktop browser. + + ## Background processing The threading model depends on which version is used: - For Android, one background thread per db; -- for iOS, background processing using a very limited thread pool (only one thread working at a time). +- for iOS/macOS, background processing using a very limited thread pool (only one thread working at a time). + + ## Sample with PRAGMA feature @@ -539,6 +593,8 @@ function onDeviceReady() { **NOTE:** PRAGMA statements must be executed in `executeSql()` on the database object (i.e. `db.executeSql()`) and NOT within a transaction. + + ## Sample with transaction-level nesting In this case, the same transaction in the first executeSql() callback is being reused to run executeSql() again. @@ -564,7 +620,7 @@ function onDeviceReady() { console.log("res.rows.item(0).cnt: " + res.rows.item(0).cnt + " -- should be 1"); }); - }, function(e) { + }, function(tx, e) { console.log("ERROR: " + e.message); }); }); @@ -573,6 +629,8 @@ function onDeviceReady() { This case will also works with Safari (WebKit), assuming you replace `window.sqlitePlugin.openDatabase` with `window.openDatabase`. + + ## Close a database object This will invalidate **all** handle access handle objects for the database that is closed: @@ -643,17 +701,21 @@ db.executeSql("SELECT LENGTH('tenletters') AS stringlength", [], function (res) **FUTURE TBD:** `dispose` method on the database access handle object, such that a database is closed once **all** access handle objects are disposed. + + ## Delete a database ```js window.sqlitePlugin.deleteDatabase({name: 'my.db', location: 'default'}, successcb, errorcb); ``` -with `location` or `iosDatabaseLocation` parameter *required* as described above for `openDatabase` (affects iOS *only*) +with `location` or `iosDatabaseLocation` parameter *required* as described above for `openDatabase` (affects iOS/macOS *only*) + +**BUG:** When a database is deleted, any queued transactions for that database are left hanging. TODO: All pending transactions should be errored when a database is deleted. -**BUG:** When a database is deleted, any queued transactions for that database are left hanging. All pending transactions should be errored when a database is deleted. + -# Database schema versions +## Database schema versions The transactional nature of the API makes it relatively straightforward to manage a database schema that may be upgraded over time (adding new columns or new tables, for example). Here is the recommended procedure to follow upon app startup: - Check your database schema version number (you can use `db.executeSql` since it should be a very simple query) @@ -663,6 +725,8 @@ The transactional nature of the API makes it relatively straightforward to manag **IMPORTANT:** Since we cannot be certain when the users will actually update their apps, old schema versions will have to be supported for a very long time. + + ## Use with Ionic/ngCordova/Angular It is recommended to follow the tutorial at: https://blog.nraboy.com/2014/11/use-sqlite-instead-local-storage-ionic-framework/ @@ -687,20 +751,27 @@ cordova platform rm ios cordova platform add ios ``` -You can find some more details in a nice writeup (though with old links and package names): . +or more drastically: + +```shell +rm -rf platforms +cordova platform add ios +``` + + ## Plugin installation sources - `cordova-sqlite-storage` - stable npm package version - https://github.com/litehelpers/Cordova-sqlite-storage - latest version + + ## Source tree - `SQLitePlugin.coffee.md`: platform-independent (Literate coffee-script, can be read by recent coffee-script compiler) - `www`: `SQLitePlugin.js` platform-independent Javascript as generated from `SQLitePlugin.coffee.md` (and checked in!) -- `src`: platform-specific source code: - - `android` - Java plugin code for Android - - `ios` - Objective-C plugin code for iOS +- `src`: platform-specific source code - `spec`: test suite using Jasmine (2.2.0) - `tests`: very simple Jasmine test suite that is run on Circle CI (Android version) and Travis CI (iOS version) (used as a placeholder) - `Lawnchair-adapter`: Lawnchair adaptor, based on the version from the Lawnchair repository, with the basic Lawnchair test suite in `test-www` subdirectory @@ -725,6 +796,8 @@ Assuming your app has a recent template as used by the Cordova create script, ad }); ``` + + # Support ## Policy @@ -774,7 +847,7 @@ Free support for issues with Angular/"ngCordova"/Ionic will only be provided if ## What information is needed for help Please include the following: -- Which platform(s) (Android or iOS) +- Which platform(s) (Android/iOS/macOS) - Clear description of the issue - A small, complete, self-contained program that demonstrates the problem, preferably as a Github project. ZIP/TGZ/BZ2 archive available from a public link is OK. No RAR or other such formats please! - A Cordova project is highly preferred. Intel, MS IDE, or similar project formats should be avoided. @@ -815,12 +888,16 @@ To run from a windows powershell (here is a sample for android target): .\bin\test.ps1 android + + # Adapters ## Lawnchair Adapter **BROKEN:** The Lawnchair adapter does not support the `openDatabase` options such as `location` or `iosDatabaseLocation` options and is therefore *not* expected to work with this plugin. +NOW SUPPORTED IN: [litehelpers / cordova-sqlite-lawnchair-adapter](https://github.com/litehelpers/cordova-sqlite-lawnchair-adapter) + ### Common adapter Please look at the `Lawnchair-adapter` tree that contains a common adapter, which should also work with the Android version, along with a test-www directory. @@ -857,10 +934,7 @@ ingredients = new Lawnchair({db: "cookbook", name: "ingredients", ...}, myCallba ## PouchDB -The adapter is part of [PouchDB](http://pouchdb.com/) as documented at: -- -- -- . +- [nolanlawson / pouchdb-adapter-cordova-sqlite](https://github.com/nolanlawson/pouchdb-adapter-cordova-sqlite) # Contributing @@ -882,6 +956,8 @@ The adapter is part of [PouchDB](http://pouchdb.com/) as documented at: - Always use `git mv` to move files & directories; - Never mix a move/rename operation with any other changes in the same commit. -# Contact + + +## Contact diff --git a/package.json b/package.json index 2f398915..ca479ab8 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { - "name": "cordova-sqlite-storage", - "version": "1.2.2", - "description": "Native interface to SQLite for PhoneGap/Cordova (core version)", + "name": "cordova-sqlite-legacy-express-core", + "version": "1.0.0-pre1", + "description": "Native interface to SQLite for PhoneGap/Cordova (legacy express core version)", "cordova": { - "id": "cordova-sqlite-storage", + "id": "cordova-sqlite-legacy-express-core", "platforms": [ "android", "ios" @@ -19,12 +19,6 @@ "cordova-android", "cordova-ios" ], - "engines": [ - { - "name": "cordova", - "version": ">=3.3.0" - } - ], "author": "various", "license": "MIT", "bugs": { diff --git a/plugin.xml b/plugin.xml index ef947748..ffd6362c 100644 --- a/plugin.xml +++ b/plugin.xml @@ -1,10 +1,10 @@ + id="cordova-sqlite-legacy-express-core" + version="1.0.0-pre1"> - Cordova sqlite storage plugin (core version) + Cordova sqlite storage plugin - legacy express core version MIT @@ -13,12 +13,6 @@ Native interface to SQLite for PhoneGap/Cordova. Allows you to use more storage and provides more flexibility than the standard Web SQL database (window.openDatabase). Litehelpers/Various - - - - - - @@ -34,7 +28,6 @@ - @@ -49,7 +42,20 @@ + + + + + + + + + + + + + diff --git a/spec/config.xml b/spec/config.xml index d018622a..4177ccb8 100644 --- a/spec/config.xml +++ b/spec/config.xml @@ -1,12 +1,12 @@ - Cordova SQLite Plugin Test Runner + Cordova-sqlite-spec Runs the unit tests suite for the Cordova SQLite plugin. - - Nolan Lawson + + Various diff --git a/spec/www/spec/basic-misc-test.js b/spec/www/spec/basic-misc-test.js index cf289b6a..2f941870 100644 --- a/spec/www/spec/basic-misc-test.js +++ b/spec/www/spec/basic-misc-test.js @@ -229,7 +229,7 @@ var mytests = function() { }, MYTIMEOUT); it(suiteName + 'create virtual table using R-Tree', function(done) { - if (isWebSql) pending('BROKEN (NOT IMPLEMENTED) for Web SQL'); + if (isWebSql) pending('SKIP for (Android/iOS WebKit) Web SQL'); if (isWP8) pending('NOT IMPLEMENTED for WP(8)'); // NOT IMPLEMENTED in CSharp-SQLite if (isAndroid) pending('NOT IMPLEMENTED for all versions of Android'); // NOT IMPLEMENTED for all versions of Android database (failed in Circle CI) @@ -261,7 +261,8 @@ var mytests = function() { }); }, MYTIMEOUT); - it(suiteName + 'DELETE LIMIT', function(done) { + // NOT supported by SQLite amalgamation ... + xit(suiteName + 'DELETE LIMIT', function(done) { if (isWP8) pending('NOT IMPLEMENTED for WP(8)'); if (isWindows) pending('NOT IMPLEMENTED for Windows'); if (isAndroid && !isWebSql) pending('SKIP for Android plugin'); // FUTURE TBD test with newer versions (android.database) diff --git a/spec/www/spec/browser-check-startup.js b/spec/www/spec/browser-check-startup.js index be0a4938..0e31eeb1 100644 --- a/spec/www/spec/browser-check-startup.js +++ b/spec/www/spec/browser-check-startup.js @@ -2,18 +2,15 @@ var MYTIMEOUT = 12000; -var isAndroid = /Android/.test(navigator.userAgent); var isWP8 = /IEMobile/.test(navigator.userAgent); // Matches WP(7/8/8.1) -//var isWindows = /Windows NT/.test(navigator.userAgent); // Windows [NT] (8.1) var isWindows = /Windows /.test(navigator.userAgent); // Windows (8.1) -//var isWindowsPC = /Windows NT/.test(navigator.userAgent); // Windows [NT] (8.1) -//var isWindowsPhone_8_1 = /Windows Phone 8.1/.test(navigator.userAgent); // Windows Phone 8.1 -//var isIE = isWindows || isWP8 || isWindowsPhone_8_1; -var isIE = isWindows || isWP8; -var isWebKit = !isIE; // TBD [Android or iOS] +var isAndroid = !isWindows && /Android/.test(navigator.userAgent); +var isMac = /Macintosh/.test(navigator.userAgent); window.hasBrowser = true; -window.hasWebKitBrowser = isWebKit; +// XXX FUTURE TODO rename to something like window.hasWebKitWebSQL here +// and in actual test scripts +window.hasWebKitBrowser = (!isWindows && !isWP8 && !isMac && (isAndroid || !(window.webkit && window.webkit.messageHandlers))); describe('check startup', function() { it('receives deviceready event', function(done) { @@ -24,7 +21,7 @@ describe('check startup', function() { }, MYTIMEOUT); it('has openDatabase', function() { - if (isWebKit) expect(window.openDatabase).toBeDefined(); + if (window.hasWebKitBrowser) expect(window.openDatabase).toBeDefined(); expect(window.sqlitePlugin).toBeDefined(); expect(window.sqlitePlugin.openDatabase).toBeDefined(); }); diff --git a/spec/www/spec/db-tx-sql-results.js b/spec/www/spec/db-tx-sql-results.js index 04c60614..c89bb567 100644 --- a/spec/www/spec/db-tx-sql-results.js +++ b/spec/www/spec/db-tx-sql-results.js @@ -328,8 +328,9 @@ var mytests = function() { if (isWebSql) { // Web SQL STANDARD: - // 1. this is a native object that is NOT affected by the change: - expect(temp1.data).toBe('test'); + // 1. this is a native object that is NOT affected by the change (SKIP for Android 5.x/+): + if (!isAndroid || /Android [1-4]/.test(navigator.userAgent)) + expect(temp1.data).toBe('test'); // 2. object returned by second resultSet.rows.item call not affected: expect(temp2.data).toBe('test'); } else { diff --git a/spec/www/spec/db-tx-string-test.js b/spec/www/spec/db-tx-string-test.js index 1ead5e3b..32848b46 100755 --- a/spec/www/spec/db-tx-string-test.js +++ b/spec/www/spec/db-tx-string-test.js @@ -200,10 +200,13 @@ var mytests = function() { }); }); - // NOTE: the next two tests show that for iOS: - // - UNICODE \u2028 line separator from Javascript to Objective-C is working ok - // - UNICODE \u2028 line separator from Objective-C to Javascript is BROKEN - // ref: litehelpers/Cordova-sqlite-storage#147 + // NOTE: the next 2 tests show that for iOS/macOS/Android: + // - UNICODE \u2028 line separator from JavaScript to native (Objective-C/Java) is working OK + // - UNICODE \u2028 line separator from native (Objective-C/Java) to JavaScript is BROKEN + // For reference: + // - litehelpers/Cordova-sqlite-storage#147 + // - Apache Cordova CB-9435 (issue with cordova-ios, also affects macOS) + // - cordova/cordova-discuss#57 (issue with cordova-android) it(suiteName + "UNICODE \\u2028 line separator string length", function(done) { if (isWP8) pending('BROKEN for WP(8)'); // [BUG #202] Certain UNICODE characters not working with WP(8) @@ -225,9 +228,9 @@ var mytests = function() { }); it(suiteName + ' handles UNICODE \\u2028 line separator correctly [string test]', function (done) { - if (isWP8) pending('BROKEN for WP(8)'); // [BUG #202] UNICODE characters not working with WP(8) - if (!(isWebSql || isAndroid || isIE)) pending('BROKEN for iOS'); // XXX [BUG #147] (no callback received) + if (!isWebSql && !isWindows && isAndroid) pending('SKIP for Android plugin (cordova-android 6.x BUG: cordova/cordova-discuss#57)'); + if (!isWebSql && !isWindows && !isAndroid && !isWP8) pending('SKIP for iOS/macOS plugin (Cordova BUG: CB-9435)'); // NOTE: since the above test shows the UNICODE line separator (\u2028) // is seen by the sqlite implementation OK, it is now concluded that @@ -248,11 +251,14 @@ var mytests = function() { }); }); - // NOTE: the next two tests repeat the above for UNICODE \u2029 paragraph separator - // for iOS: - // - UNICODE \u2029 line separator from Javascript to Objective-C is working ok - // - UNICODE \u2029 line separator from Objective-C to Javascript is BROKEN - // ref: litehelpers/Cordova-sqlite-storage#147 + // NOTE: the next 2 tests repeat the above for UNICODE \u2029 paragraph separator + // on iOS/macOS/Android: + // - UNICODE \u2029 paragraph separator from JavaScript to native (Objective-C/Java) is working OK + // - UNICODE \u2029 paragraph separator from native (Objective-C/Java) to JavaScript is BROKEN + // For reference: + // - litehelpers/Cordova-sqlite-storage#147 + // - Apache Cordova CB-9435 (issue with cordova-ios, also affects macOS) + // - cordova/cordova-discuss#57 (issue with cordova-android) it(suiteName + "UNICODE \\u2029 line separator string length", function(done) { if (isWP8) pending('BROKEN for WP(8)'); // [BUG #202] Certain UNICODE characters not working with WP(8) @@ -275,9 +281,9 @@ var mytests = function() { }); it(suiteName + ' handles UNICODE \\u2029 line separator correctly [string test]', function (done) { - if (isWP8) pending('BROKEN for WP(8)'); // [BUG #202] UNICODE characters not working with WP(8) - if (!(isWebSql || isAndroid || isIE)) pending('BROKEN for iOS'); // XXX [BUG #147] (no callback received) + if (!isWebSql && !isWindows && isAndroid) pending('SKIP for Android plugin (cordova-android 6.x BUG: cordova/cordova-discuss#57)'); + if (!isWebSql && !isWindows && !isAndroid && !isWP8) pending('SKIP for iOS/macOS plugin (Cordova BUG: CB-9435)'); // NOTE: since the above test shows the UNICODE paragraph separator (\u2029) // is seen by the sqlite implementation OK, it is now concluded that diff --git a/spec/www/spec/db-tx-value-bindings-test.js b/spec/www/spec/db-tx-value-bindings-test.js index 0ae8604e..674d7e88 100755 --- a/spec/www/spec/db-tx-value-bindings-test.js +++ b/spec/www/spec/db-tx-value-bindings-test.js @@ -30,15 +30,11 @@ function start(n) { if (wait == 0) test_it_done(); } -var isAndroid = /Android/.test(navigator.userAgent); var isWP8 = /IEMobile/.test(navigator.userAgent); // Matches WP(7/8/8.1) -//var isWindows = /Windows NT/.test(navigator.userAgent); // Windows [NT] (8.1) var isWindows = /Windows /.test(navigator.userAgent); // Windows (8.1) -//var isWindowsPC = /Windows NT/.test(navigator.userAgent); // Windows [NT] (8.1) -//var isWindowsPhone_8_1 = /Windows Phone 8.1/.test(navigator.userAgent); // Windows Phone 8.1 -//var isIE = isWindows || isWP8 || isWindowsPhone_8_1; -var isIE = isWindows || isWP8; -var isWebKit = !isIE; // TBD [Android or iOS] +var isAndroid = !isWindows && /Android/.test(navigator.userAgent); +var isMac = /Macintosh/.test(navigator.userAgent); +var isWKWebView = !isWindows && !isAndroid && !isWP8 && !isMac && !!window.webkit && !!window.webkit.messageHandlers; // NOTE: In the core-master branch there is no difference between the default // implementation and implementation #2. But the test will also apply @@ -180,10 +176,9 @@ var mytests = function() { var row = res.rows.item(0); expect(row.test_date).toBe(1424174959894); - // NOTE: storing big integer in TEXT field WORKING OK with WP(8) version. - // It is now suspected that the issue lies with the results handling. - // XXX Brody TODO: storing big number in TEXT field is different for Plugin vs. Web SQL! - if (isWebSql) + // NOTE: big number stored in field with TEXT affinity with different conversion + // in case of plugin (certain platforms) vs. Android/iOS WebKit Web SQL + if (isWebSql || isMac || isWKWebView) expect(row.test_text).toBe("1424174959894.0"); // ([Big] number inserted as string ok) else expect(row.test_text).toBe("1424174959894"); // (Big integer number inserted as string ok) @@ -377,13 +372,17 @@ var mytests = function() { }); }); - // XXX Brody NOTE: same issue is now reproduced in a string test. - // TBD ???: combine with other test - // BUG #147 iOS version of plugin BROKEN: + + // Issue with iOS/macOS/Android + // For reference: + // - litehelpers/Cordova-sqlite-storage#147 + // - Apache Cordova CB-9435 (issue with cordova-ios, also affects macOS) + // - cordova/cordova-discuss#57 (issue with cordova-android) test_it(suiteName + ' handles UNICODE \\u2028 line separator correctly [in database]', function () { if (isWP8) pending('BROKEN for WP(8)'); // [BUG #202] UNICODE characters not working with WP(8) - if (!(isWebSql || isAndroid || isIE)) pending('BROKEN for iOS'); // XXX [BUG #147] (no callback received) + if (!isWebSql && !isWindows && isAndroid) pending('SKIP for Android plugin (cordova-android 6.x BUG: cordova/cordova-discuss#57)'); + if (!isWebSql && !isWindows && !isAndroid && !isWP8) pending('SKIP for iOS/macOS plugin (Cordova BUG: CB-9435)'); var dbName = "Unicode-line-separator.db"; var db = openDatabase(dbName, "1.0", "Demo", DEFAULT_SIZE); diff --git a/spec/www/spec/misc-tx-legacy.js b/spec/www/spec/misc-tx-legacy.js index 4cf617a3..e06806b4 100755 --- a/spec/www/spec/misc-tx-legacy.js +++ b/spec/www/spec/misc-tx-legacy.js @@ -32,13 +32,7 @@ function start(n) { var isAndroid = /Android/.test(navigator.userAgent); var isWP8 = /IEMobile/.test(navigator.userAgent); // Matches WP(7/8/8.1) -//var isWindows = /Windows NT/.test(navigator.userAgent); // Windows [NT] (8.1) var isWindows = /Windows /.test(navigator.userAgent); // Windows (8.1) -//var isWindowsPC = /Windows NT/.test(navigator.userAgent); // Windows [NT] (8.1) -//var isWindowsPhone_8_1 = /Windows Phone 8.1/.test(navigator.userAgent); // Windows Phone 8.1 -//var isIE = isWindows || isWP8 || isWindowsPhone_8_1; -var isIE = isWindows || isWP8; -var isWebKit = !isIE; // TBD [Android or iOS] // NOTE: In the core-master branch there is no difference between the default // implementation and implementation #2. But the test will also apply @@ -57,7 +51,7 @@ var mytests = function() { for (var i=0; i Date: Thu, 27 Apr 2017 16:40:26 +0200 Subject: [PATCH 02/47] Some open/close/delete test updates --- spec/www/spec/db-open-close-delete-test.js | 230 ++++++++++----------- 1 file changed, 111 insertions(+), 119 deletions(-) diff --git a/spec/www/spec/db-open-close-delete-test.js b/spec/www/spec/db-open-close-delete-test.js index 3117d821..33957b6f 100755 --- a/spec/www/spec/db-open-close-delete-test.js +++ b/spec/www/spec/db-open-close-delete-test.js @@ -30,19 +30,14 @@ function start(n) { if (wait == 0) test_it_done(); } -var isAndroid = /Android/.test(navigator.userAgent); var isWP8 = /IEMobile/.test(navigator.userAgent); // Matches WP(7/8/8.1) -//var isWindows = /Windows NT/.test(navigator.userAgent); // Windows [NT] (8.1) -var isWindows = /Windows /.test(navigator.userAgent); // Windows (8.1) -//var isWindowsPC = /Windows NT/.test(navigator.userAgent); // Windows [NT] (8.1) -//var isWindowsPhone_8_1 = /Windows Phone 8.1/.test(navigator.userAgent); // Windows Phone 8.1 -//var isIE = isWindows || isWP8 || isWindowsPhone_8_1; -var isIE = isWindows || isWP8; -var isWebKit = !isIE; // TBD [Android or iOS] - -// NOTE: In the core-master branch there is no difference between the default -// implementation and implementation #2. But the test will also apply -// the androidLockWorkaround: 1 option in the case of implementation #2. +var isWindows = /Windows /.test(navigator.userAgent); +var isAndroid = !isWindows && /Android/.test(navigator.userAgent); + +// NOTE: While in certain version branches there is no difference between +// the default Android implementation and implementation #2, +// this test script will also apply the androidLockWorkaround: 1 option +// in case of implementation #2. var pluginScenarioList = [ isAndroid ? 'Plugin-implementation-default' : 'Plugin', 'Plugin-implementation-2' @@ -52,59 +47,47 @@ var pluginScenarioCount = isAndroid ? 2 : 1; var mytests = function() { - describe('Plugin - BASIC sqlitePlugin.openDatabase test(s)', function() { + describe('Basic sqlitePlugin.openDatabase parameter test(s)', function() { var suiteName = 'plugin: '; - it(suiteName + 'Open plugin database with Web SQL parameters (REJECTED with exception)', function(done) { + it('Open plugin database with Web SQL parameters - REJECTED with exception', function(done) { try { - var db = window.sqlitePlugin.openDatabase('open-with-web-sql-parameters-test.db', "1.0", "Demo", DEFAULT_SIZE); + // EXPECTED to throw: + var db = window.sqlitePlugin.openDatabase('open-with-web-sql-parameters-test.db', '1.0', 'test', DEFAULT_SIZE); - // NOT EXPECTED: - // window.sqlitePlugin.openDatabase did not throw + // NOT EXPECTED to get here: expect(false).toBe(true); - - // check returned db object: - expect(db).toBeDefined(); - expect(db.executeSql).toBeDefined(); - expect(db.transaction).toBeDefined(); - expect(db.close).toBeDefined(); - - //done(); - // IMPORTANT FIX: avoid the risk of over 100 db handles open when running the full test suite - db.close(done, done); + done(); } catch (e) { // EXPECTED RESULT: - expect(true).toBe(true); + expect(e).toBeDefined(); done(); } }, MYTIMEOUT); - // NOTE: this was an issue due to the inconsistency ng cordova documentation and source code which - // triggered problems reported in litehelpers/Cordova-sqlite-storage#246 and + // NOTE: this was an issue due to a past inconsistency between the + // ngCordova documentation and source code which triggered problems + // reported in litehelpers/Cordova-sqlite-storage#246 and // litehelpers/Cordova-sqlcipher-adapter#5. // The implementation now avoids this problem *by throwing an exception*. - // It could be nicer to just signal an error in the error callback, if present, - // through throwing an exception does prevent the user from using an invalid db object. // Brody TBD: check how the Web SQL API would handle this condition? it(suiteName + 'check that db name is really a string', function(done) { - var p1 = { name: 'my.db.name', location: 1 }; + var p1 = { name: 'my.db.name', location: 'default' }; try { - window.sqlitePlugin.openDatabase({ name: p1 }, function(db) { - // not expected: + window.sqlitePlugin.openDatabase({ name: p1, location: 'default' }, function(db) { + // NOT EXPECTED: expect(false).toBe(true); done(); }, function(error) { // OK but NOT EXPECTED: - expect(true).toBe(true); - // XXX BRODY TODO: - //expect('Behavior changed, please update this test').toBe('--'); + expect('Behavior changed, please update this test').toBe('--'); done(); }); } catch (e) { - // stopped by the implementation: - expect(true).toBe(true); - done(); + // EXPECTED RESULT - stopped by the implementation: + expect(e).toBeDefined(); + done(); } }, MYTIMEOUT); @@ -117,14 +100,10 @@ var mytests = function() { describe(pluginScenarioList[i] + ': basic sqlitePlugin.deleteDatabase test(s)', function() { var scenarioName = pluginScenarioList[i]; var suiteName = scenarioName + ': '; - var isOldAndroidImpl = (i === 1); + var isImpl2 = (i === 1); // NOTE: MUST be defined in function scope, NOT outer scope: var openDatabase = function(first, second, third, fourth, fifth, sixth) { - //if (!isOldAndroidImpl) { - // return window.sqlitePlugin.openDatabase(first, second, third, fourth, fifth, sixth); - //} - var dbname, okcb, errorcb; if (first.constructor === String ) { @@ -137,25 +116,30 @@ var mytests = function() { errorcb = third; } - if (!isOldAndroidImpl) { - return window.sqlitePlugin.openDatabase({name: dbname, location: 0}, okcb, errorcb); + if (!isImpl2) { + return window.sqlitePlugin.openDatabase({name: dbname, iosDatabaseLocation: 'default'}, okcb, errorcb); } var dbopts = { name: 'i2-'+dbname, + // database location setting needed in this version branch: + location: 1, // (value ignored on Android) androidDatabaseImplementation: 2, - androidLockWorkaround: 1, - location: 1 + androidLockWorkaround: 1 }; return window.sqlitePlugin.openDatabase(dbopts, okcb, errorcb); } var deleteDatabase = function(first, second, third) { - if (!isOldAndroidImpl) { - window.sqlitePlugin.deleteDatabase({name: first, location: 0}, second, third); + if (!isImpl2) { + window.sqlitePlugin.deleteDatabase({name: first, iosDatabaseLocation: 'default'}, second, third); } else { - window.sqlitePlugin.deleteDatabase({name: 'i2-'+first, location: 0}, second, third); + window.sqlitePlugin.deleteDatabase({ + name: 'i2-'+first, + // database location setting needed in this version branch: + location: 1 // (value ignored on Android) + }, second, third); } } @@ -236,11 +220,11 @@ var mytests = function() { describe(pluginScenarioList[i] + ': basic plugin open-close test(s)', function() { var scenarioName = pluginScenarioList[i]; var suiteName = scenarioName + ': '; - var isOldAndroidImpl = (i === 1); + var isImpl2 = (i === 1); // NOTE: MUST be defined in function scope, NOT outer scope: var openDatabase = function(first, second, third, fourth, fifth, sixth) { - //if (!isOldAndroidImpl) { + //if (!isImpl2) { // return window.sqlitePlugin.openDatabase(first, second, third, fourth, fifth, sixth); //} @@ -256,15 +240,17 @@ var mytests = function() { errorcb = third; } - if (!isOldAndroidImpl) { - return window.sqlitePlugin.openDatabase({name: dbname, location: 0}, okcb, errorcb); + if (!isImpl2) { + // database location setting needed in this version branch: + return window.sqlitePlugin.openDatabase({name: dbname, location: 2}, okcb, errorcb); } var dbopts = { name: 'i2-'+dbname, + // database location setting needed in this version branch: + location: 1, // (value ignored on Android) androidDatabaseImplementation: 2, - androidLockWorkaround: 1, - location: 1 + androidLockWorkaround: 1 }; return window.sqlitePlugin.openDatabase(dbopts, okcb, errorcb); @@ -285,8 +271,8 @@ var mytests = function() { }); test_it(suiteName + ' database.close (immediately after open) calls its success callback', function () { - // XXX POSSIBLY BROKEN on iOS due to current background processing implementation - if (!(isAndroid || isIE)) pending('POSSIBLY BROKEN on iOS (background processing implementation)'); + // TBD POSSIBLY BROKEN on iOS/macOS due to current background processing implementation: + if (!isAndroid && !isWindows && !isWP8) pending('POSSIBLY BROKEN on iOS/macOS (background processing implementation)'); // asynch test coming up stop(1); @@ -327,9 +313,9 @@ var mytests = function() { test_it(suiteName + ' database.close fails in transaction', function () { stop(2); + var dbName = "Database-Close-fail.db"; - var dbName = "Database-Close-fail"; - var db = openDatabase({name: dbName, location: 1}); + var db = openDatabase({name: dbName, location: 'default'}); db.readTransaction(function(tx) { tx.executeSql('SELECT 1', [], function(tx, results) { @@ -353,11 +339,11 @@ var mytests = function() { }); test_it(suiteName + ' attempt to close db twice', function () { - var dbName = "close-db-twice.db"; + var dbName = 'close-db-twice.db'; stop(1); - openDatabase({name: dbName}, function(db) { + openDatabase({name: dbName, location: 'default'}, function(db) { ok(!!db, 'valid db object'); db.close(function () { ok(true, 'db.close() success callback (first time)'); @@ -419,7 +405,7 @@ var mytests = function() { it(suiteName + ' REPRODUCE BUG: close DB in db.executeSql() callback', function (done) { var dbName = "Close-DB-in-db-executeSql-callback.db"; - openDatabase({name: dbName}, function (db) { + openDatabase({name: dbName, location: 'default'}, function (db) { db.executeSql("CREATE TABLE IF NOT EXISTS tt (test_data)", [], function() { db.close(function () { // FUTURE TBD EXPECTED RESULT: @@ -461,12 +447,13 @@ var mytests = function() { // Needed to support some large-scale applications: test_it(suiteName + ' open same database twice in [same] specified location works', function () { // XXX TODO [BROKEN]: same db name, different location should be different db! - var dbName = 'open-twice-same-location.db'; - stop(2); - var db1 = openDatabase({name: dbName, location: 2}, function () { - var db2 = openDatabase({name: dbName, location: 2}, function () { + var dbName = 'test-open-twice-in-same-location.db'; + var dbargs = {name: dbName, location: 1}; + + var db1 = openDatabase(dbargs, function () { + var db2 = openDatabase(dbargs, function () { db1.readTransaction(function(tx1) { tx1.executeSql('SELECT 1', [], function(tx1d, results) { ok(true, 'db1 transaction working'); @@ -499,14 +486,15 @@ var mytests = function() { test_it(suiteName + ' close then re-open (2x) allows subsequent queries to run', function () { // asynch test coming up stop(1); - - var dbName = "Database-Close-and-Reopen"; - openDatabase({name: dbName, location: 0}, function (db) { + var dbName = 'test-database-close-and-reopen.db'; + var dbargs = {name: dbName, location: 0}; + + openDatabase(dbargs, function (db) { db.close(function () { - openDatabase({name: dbName, location: 0}, function (db) { + openDatabase(dbargs, function (db) { db.close(function () { - openDatabase({name: dbName, location: 0}, function (db) { + openDatabase(dbargs, function (db) { db.readTransaction(function (tx) { tx.executeSql('SELECT 1', [], function (tx, results) { ok(true, 'database re-opened succesfully'); @@ -521,7 +509,7 @@ var mytests = function() { }, function(tx) { // close on transaction success not while executing // or commit will fail - db.close(); + db.close(); }); }, function (error) { ok(false, error.message); @@ -547,15 +535,16 @@ var mytests = function() { // Needed to support some large-scale applications: test_it(suiteName + " delete then re-open (location: 'default') allows subsequent queries to run", function () { - var dbName = "Database-delete-and-Reopen.db"; + var dbName = "test-database-delete-and-reopen.db"; + var dbargs = {name: dbName, iosDatabaseLocation: 'default'}; // async test coming up stop(1); - var db = openDatabase({name: dbName, location: 'default'}, function () { + var db = openDatabase(dbargs, function () { // success CB - deleteDatabase({name: dbName, location: 'default'}, function () { - db = openDatabase({name: dbName, location: 'default'}, function () { + deleteDatabase(dbargs, function () { + db = openDatabase(dbargs, function () { db.readTransaction(function (tx) { tx.executeSql('SELECT 1', [], function (tx, results) { ok(true, 'database re-opened succesfully'); @@ -588,20 +577,20 @@ var mytests = function() { // XXX SEE BELOW: repeat scenario but wait for open callback before close/delete/reopen // Needed to support some large-scale applications: test_it(suiteName + ' immediate close, then delete then re-open allows subsequent queries to run', function () { - - // XXX POSSIBLY BROKEN on iOS due to current background processing implementation - if (!(isAndroid || isIE)) pending('POSSIBLY BROKEN on iOS (background processing implementation)'); + // TBD POSSIBLY BROKEN on iOS/macOS due to current background processing implementation: + if (!isAndroid && !isWindows && !isWP8) pending('POSSIBLY BROKEN on iOS/macOS (background processing implementation)'); var dbName = "Immediate-close-delete-Reopen.db"; + var dbargs = {name: dbName, location: 'default'}; // asynch test coming up stop(1); - var db1 = openDatabase({name: dbName, iosDatabaseLocation: 'Documents'}); + var db1 = openDatabase(dbargs); db1.close(function () { - deleteDatabase({name: dbName, iosDatabaseLocation: 'Documents'}, function () { - openDatabase({name: dbName, iosDatabaseLocation: 'Documents'}, function(db) { + deleteDatabase(dbargs, function () { + openDatabase(dbargs, function(db) { db.readTransaction(function (tx) { tx.executeSql('SELECT 1', [], function (tx, results) { ok(true, 'database re-opened succesfully'); @@ -628,18 +617,18 @@ var mytests = function() { }); }); - test_it(suiteName + ' close (after open cb), then delete then re-open allows subsequent queries to run', function () { - - var dbName = "Close-after-opencb-delete-reopen.db"; + test_it(suiteName + ' close (after open cb), then delete & re-open allows subsequent queries to run', function () { + var dbName = 'test-close-after-opencb-then-delete-and-reopen.db'; + var dbargs = {name: dbName, iosDatabaseLocation: 'Library'}; // asynch test coming up stop(1); - openDatabase({name: dbName, iosDatabaseLocation: 'Library'}, function(db1) { + openDatabase(dbargs, function(db1) { db1.close(function () { - deleteDatabase({name: dbName, iosDatabaseLocation: 'Library'}, function () { - openDatabase({name: dbName, iosDatabaseLocation: 'Library'}, function(db) { + deleteDatabase(dbargs, function () { + openDatabase(dbargs, function(db) { db.readTransaction(function (tx) { tx.executeSql('SELECT 1', [], function (tx, results) { ok(true, 'database re-opened succesfully'); @@ -673,27 +662,28 @@ var mytests = function() { }); test_it(suiteName + ' repeatedly open and close database (4x)', function () { - var dbName = "repeatedly-open-and-close-db-4x.db"; + var dbName = 'test-repeatedly-open-and-close-db-4x.db'; + var dbargs = {name: dbName, location: 0}; // async test coming up stop(1); - openDatabase({name: dbName, location: 0}, function(db) { + openDatabase(dbargs, function(db) { ok(!!db, 'valid db object 1/4'); db.close(function () { ok(true, 'success 1/4'); - openDatabase({name: dbName, location: 0}, function(db) { + openDatabase(dbargs, function(db) { ok(!!db, 'valid db object 2/4'); db.close(function () { ok(true, 'success 2/4'); - openDatabase({name: dbName, location: 0}, function(db) { + openDatabase(dbargs, function(db) { ok(!!db, 'valid db object 3/4'); db.close(function () { ok(true, 'success 3/4'); - openDatabase({name: dbName, location: 0}, function(db) { + openDatabase(dbargs, function(db) { ok(!!db, 'valid db object 4/4'); db.close(function () { ok(true, 'success 4/4'); @@ -734,35 +724,36 @@ var mytests = function() { }); test_it(suiteName + ' repeatedly open and close database faster (5x)', function () { - // XXX CURRENTLY BROKEN on iOS due to current background processing implementation - if (!(isAndroid || isIE)) pending('CURRENTLY BROKEN on iOS (background processing implementation)'); + // TBD CURRENTLY BROKEN on iOS/macOS due to current background processing implementation: + if (!isAndroid && !isWindows && !isWP8) pending('CURRENTLY BROKEN on iOS/macOS (background processing implementation)'); - var dbName = "repeatedly-open-and-close-faster-5x.db"; + var dbName = 'repeatedly-open-and-close-faster-5x.db'; + var dbargs = {name: dbName, location: 'default'}; // async test coming up stop(1); - var db = openDatabase({name: dbName, location: 0}); + var db = openDatabase(dbargs); ok(!!db, 'valid db object 1/5'); db.close(function () { ok(true, 'success 1/5'); - db = openDatabase({name: dbName, location: 0}); + db = openDatabase(dbargs); ok(!!db, 'valid db object 2/5'); db.close(function () { ok(true, 'success 2/5'); - db = openDatabase({name: dbName, location: 0}); + db = openDatabase(dbargs); ok(!!db, 'valid db object 3/5'); db.close(function () { ok(true, 'success 3/5'); - db = openDatabase({name: dbName, location: 0}); + db = openDatabase(dbargs); ok(!!db, 'valid db object 4/5'); db.close(function () { ok(true, 'success 4/5'); - db = openDatabase({name: dbName, location: 0}); + db = openDatabase(dbargs); ok(!!db, 'valid db object 5/5'); db.close(function () { ok(true, 'success 5/5'); @@ -792,7 +783,7 @@ var mytests = function() { // Needed to support some large-scale applications: test_it(suiteName + ' repeatedly open and delete database (4x)', function () { - var dbName = "repeatedly-open-and-delete-4x.db"; + var dbName = 'test-repeatedly-open-and-delete-4x.db'; var dbargs = {name: dbName, iosDatabaseLocation: 'Documents'}; // async test coming up @@ -855,37 +846,38 @@ var mytests = function() { // Needed to support some large-scale applications: test_it(suiteName + ' repeatedly open and delete database faster (5x)', function () { - // XXX CURRENTLY BROKEN on iOS due to current background processing implementation - if (!(isAndroid || isIE)) pending('CURRENTLY BROKEN on iOS (background processing implementation)'); + // TBD CURRENTLY BROKEN on iOS/macOS due to current background processing implementation: + if (!isAndroid && !isWindows && !isWP8) pending('CURRENTLY BROKEN on iOS/macOS (background processing implementation)'); - var dbName = "repeatedly-open-and-delete-faster-5x.db"; + var dbName = 'repeatedly-open-and-delete-faster-5x.db'; + var dbargs = {name: dbName, location: 'default'}; // async test coming up stop(1); - var db = openDatabase({name: dbName, location: 0}); + var db = openDatabase(dbargs); ok(!!db, 'valid db object 1/5'); - sqlitePlugin.deleteDatabase({name: dbName, location: 0}, function () { + sqlitePlugin.deleteDatabase(dbargs, function () { ok(true, 'success 1/5'); - db = openDatabase({name: dbName, location: 0}); + db = openDatabase(dbargs); ok(!!db, 'valid db object 2/5'); - sqlitePlugin.deleteDatabase({name: dbName, location: 0}, function () { + sqlitePlugin.deleteDatabase(dbargs, function () { ok(true, 'success 2/5'); - db = openDatabase({name: dbName, location: 0}); + db = openDatabase(dbargs); ok(!!db, 'valid db object 3/5'); - sqlitePlugin.deleteDatabase({name: dbName, location: 0}, function () { + sqlitePlugin.deleteDatabase(dbargs, function () { ok(true, 'success 3/5'); - db = openDatabase({name: dbName, location: 0}); + db = openDatabase(dbargs); ok(!!db, 'valid db object 4/5'); - sqlitePlugin.deleteDatabase({name: dbName, location: 0}, function () { + sqlitePlugin.deleteDatabase(dbargs, function () { ok(true, 'success 4/5'); - db = openDatabase({name: dbName, location: 0}); + db = openDatabase(dbargs); ok(!!db, 'valid db object 5/5'); - sqlitePlugin.deleteDatabase({name: dbName, location: 0}, function () { + sqlitePlugin.deleteDatabase(dbargs, function () { ok(true, 'success 5/5'); start(1); From 05f5750bf92a653530b60103b91c3bab2afbf8dc Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Fri, 14 Apr 2017 02:12:55 +0200 Subject: [PATCH 03/47] selfTest simulate scenario in BUG litehelpers/Cordova-sqlite-storage#666 Includes string test and test of effects of location reload/change in this version branch along with another internal check NOTE: selfTest with these changes also succeeds on Windows & WP8 platforms. --- CHANGES.md | 3 +- README.md | 3 +- SQLitePlugin.coffee.md | 139 ++++++++++++++++++++++++++++++++++++++++- package.json | 2 +- plugin.xml | 2 +- www/SQLitePlugin.js | 120 +++++++++++++++++++++++++++++++++-- 6 files changed, 256 insertions(+), 13 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 7b096b9e..c2c73d7c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,7 +1,8 @@ # Changes -###### cordova-sqlite-legacy-express-core 1.0.0-pre1 +###### cordova-sqlite-legacy-express-core 1.0.0-pre2 +- selfTest simulate scenario in BUG litehelpers/Cordova-sqlite-storage#666 (also includes string test and test of effects of location reload/change in this version branch, along with another internal check) - Drop engine constraints in package.json & plugin.xml (in this version branch) - Support macOS platform with builtin libsqlite3.dylib framework in this version branch diff --git a/README.md b/README.md index 8967c103..315df540 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,7 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase - As described in [this posting](http://brodyspark.blogspot.com/2012/12/cordovaphonegap-sqlite-plugins-offer.html): - Keeps sqlite database in a user data location that is known; can be reconfigured (iOS/macOS platform version); and may be synchronized to iCloud (iOS platform version). - No 5MB maximum, more information at: http://www.sqlite.org/limits.html +- Also tested for multi-page applications with window location changes - This project is self-contained: no dependencies on other plugins such as cordova-plugin-file - Windows 10 UWP platform version available in [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) and Windows 8.1/Windows Phone 8.1/Windows 10 platform version available in [litehelpers / Cordova-sqlite-legacy](https://github.com/litehelpers/Cordova-sqlite-legacy) use the performant C++ [doo / SQLite3-WinRT](https://github.com/doo/SQLite3-WinRT) component. - [SQLCipher](https://www.zetetic.net/sqlcipher/) support for Android/iOS/macOS/Windows is available in: [litehelpers / Cordova-sqlcipher-adapter](https://github.com/litehelpers/Cordova-sqlcipher-adapter) @@ -116,6 +117,7 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase ## Known issues +- Transaction problem after page change WITH POSSIBLE DATA LOSS ref: [litehelpers/Cordova-sqlite-storage#666](https://github.com/litehelpers/Cordova-sqlite-storage/issues/666) - iOS/macOS platform version does not support certain rapidly repeated open-and-close or open-and-delete test scenarios due to how the implementation handles background processing - As described below, auto-vacuum is NOT enabled by default. - It is possible to request a SQL statement list such as "SELECT 1; SELECT 2" within a single SQL statement string, however the plugin will only execute the first statement and silently ignore the others ref: [litehelpers/Cordova-sqlite-storage#551](https://github.com/litehelpers/Cordova-sqlite-storage/issues/551) @@ -172,7 +174,6 @@ Issues fixed in some newer version branches: ## Further testing needed - Integration with PhoneGap developer app -- Multi-page apps - Use within [InAppBrowser](http://docs.phonegap.com/en/edge/cordova_inappbrowser_inappbrowser.md.html) - Use within an iframe (see [litehelpers/Cordova-sqlite-storage#368 (comment)](https://github.com/litehelpers/Cordova-sqlite-storage/issues/368#issuecomment-154046367)) - Actual behavior when using SAVEPOINT(s) diff --git a/SQLitePlugin.coffee.md b/SQLitePlugin.coffee.md index a5b187ff..4ff8e969 100644 --- a/SQLitePlugin.coffee.md +++ b/SQLitePlugin.coffee.md @@ -27,6 +27,7 @@ # 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. + # NOTE: In case txLocks is renamed or replaced the selfTest has to be adapted as well. txLocks = {} ## utility functions: @@ -116,6 +117,7 @@ # Keep track of state of open db connections # XXX TBD this will be moved and renamed or # combined with txLocks. + # NOTE: In case txLocks is renamed or replaced the selfTest has to be adapted as well. SQLitePlugin::openDBs = {} SQLitePlugin::addTransaction = (t) -> @@ -690,11 +692,127 @@ start: (successcb, errorcb) -> SQLiteFactory.deleteDatabase {name: SelfTest.DBNAME, location: 'default'}, - (-> SelfTest.start2(successcb, errorcb)), - (-> SelfTest.start2(successcb, errorcb)) + (-> SelfTest.step1(successcb, errorcb)), + (-> SelfTest.step1(successcb, errorcb)) + return + + step1: (successcb, errorcb) -> + SQLiteFactory.openDatabase {name: SelfTest.DBNAME, location: 'default'}, (db) -> + check1 = false + db.transaction (tx) -> + tx.executeSql 'SELECT UPPER("Test") AS upperText', [], (ignored, resutSet) -> + if !resutSet.rows + return SelfTest.finishWithError errorcb, 'Missing resutSet.rows' + + if !resutSet.rows.length + return SelfTest.finishWithError errorcb, 'Missing resutSet.rows.length' + + if resutSet.rows.length isnt 1 + return SelfTest.finishWithError errorcb, + "Incorrect resutSet.rows.length value: #{resutSet.rows.length} (expected: 1)" + + if !resutSet.rows.item(0).upperText + return SelfTest.finishWithError errorcb, + 'Missing resutSet.rows.item(0).upperText' + + if resutSet.rows.item(0).upperText isnt 'TEST' + return SelfTest.finishWithError errorcb, + "Incorrect resutSet.rows.item(0).upperText value: #{resutSet.rows.item(0).upperText} (expected: 'TEST')" + + check1 = true + return + + , (ignored, tx_sql_err) -> + return SelfTest.finishWithError errorcb, "TX SQL error: #{tx_sql_err}" + + return + + , (tx_err) -> + return SelfTest.finishWithError errorcb, "TRANSACTION error: #{tx_err}" + + , () -> + # tx success: + if !check1 + return SelfTest.finishWithError errorcb, + 'Did not get expected upperText result data' + + # SIMULATE SCENARIO IN BUG litehelpers/Cordova-sqlite-storage#666: + db.executeSql 'BEGIN', null, (ignored) -> nextTick -> # (nextTick needed for Windows) + # DELETE INTERNAL STATE to simulate the effects of location refresh or change: + delete db.openDBs[SelfTest.DBNAME] + delete txLocks[SelfTest.DBNAME] + nextTick -> + # VERIFY INTERNAL STATE IS DELETED: + db.transaction (tx2) -> + tx2.executeSql 'SELECT 1' + return + , (tx_err) -> + # EXPECTED RESULT: + if !tx_err + return SelfTest.finishWithError errorcb, 'Missing error object' + SelfTest.step2 successcb, errorcb + return + , () -> + # NOT EXPECTED: + return SelfTest.finishWithError errorcb, 'Missing error object' + return + return + + return + return - start2: (successcb, errorcb) -> + , (open_err) -> + SelfTest.finishWithError errorcb, "Open database error: #{open_err}" + return + + step2: (successcb, errorcb) -> + SQLiteFactory.openDatabase {name: SelfTest.DBNAME, location: 'default'}, (db) -> + # TX FAILURE EXPECTED DUE TO BUG litehelpers/Cordova-sqlite-storage#666: + db.transaction (tx) -> + tx.executeSql 'SELECT ? AS myResult', [null], (ignored, resutSet) -> + # Extra sql success callback ignored: + return + return + + , (txError) -> + # EXPECTED RESULT DUE TO BUG litehelpers/Cordova-sqlite-storage#666: + if !txError + return SelfTest.finishWithError errorcb, 'Missing txError object' + # second try should work: + db.transaction (tx2) -> + tx2.executeSql 'SELECT ? AS myResult', [null], (ignored, resutSet) -> + if !resutSet.rows + return SelfTest.finishWithError errorcb, 'Missing resutSet.rows' + if !resutSet.rows.length + return SelfTest.finishWithError errorcb, 'Missing resutSet.rows.length' + if resutSet.rows.length isnt 1 + return SelfTest.finishWithError errorcb, + SelfTest.step3 successcb, errorcb + return + return + , (tx2_err) -> + return SelfTest.finishWithError errorcb, "UNEXPECTED TRANSACTION ERROR: #{tx2_err}" + return + + , () -> + # TX SUCCESS POSSIBLE FOR Android (android.database) ONLY + # NOTE: Windows 10 (UWP) mobile platform also shows "Android" in navigator.userAgent, + # filtered out here. + # FUTURE TBD android.database implementation should be fixed to report error in this case. + if /Android/.test(navigator.userAgent) and not /Windows /.test(navigator.userAgent) + return SelfTest.step3 successcb, errorcb + # OTHERWISE: + # TX SUCCESS NOT EXPECTED DUE TO BUG litehelpers/Cordova-sqlite-storage#666: + return SelfTest.finishWithError errorcb, 'UNEXPECTED SUCCESS ref: litehelpers/Cordova-sqlite-storage#666' + return + + , (open_err) -> + SelfTest.finishWithError errorcb, "Open database error: #{open_err}" + return + + step3: (successcb, errorcb) -> SQLiteFactory.openDatabase {name: SelfTest.DBNAME, location: 'default'}, (db) -> + # FUTURE TBD TEST CRUD OPERATIONS (already fixed in a newer version branch) db.sqlBatch [ 'CREATE TABLE TestTable(TestColumn);' [ 'INSERT INTO TestTable (TestColumn) VALUES (?);', ['test-value'] ] @@ -751,9 +869,19 @@ # CLEANUP & FINISH: db.close () -> SQLiteFactory.deleteDatabase {name: SelfTest.DBNAME, location: 'default'}, successcb, (cleanup_err)-> + # TBD IGNORE THIS ERROR on Windows (and WP8): + if /Windows /.test(navigator.userAgent) or /IEMobile/.test(navigator.userAgent) + console.log "IGNORE CLEANUP (DELETE) ERROR: #{JSON.stringify cleanup_err} (Windows/WP8)" + successcb() + return SelfTest.finishWithError errorcb, "Cleanup error: #{cleanup_err}" , (close_err) -> + # TBD IGNORE THIS ERROR on Windows (and WP8): + if /Windows /.test(navigator.userAgent) or /IEMobile/.test(navigator.userAgent) + console.log "IGNORE close ERROR: #{JSON.stringify close_err} (Windows/WP8)" + SQLiteFactory.deleteDatabase {name: SelfTest.DBNAME, location: 'default'}, successcb, successcb + return SelfTest.finishWithError errorcb, "close error: #{close_err}" , (select_err) -> @@ -764,11 +892,16 @@ , (open_err) -> SelfTest.finishWithError errorcb, "Open database error: #{open_err}" + return finishWithError: (errorcb, message) -> + console.log "selfTest ERROR with message: #{message}" SQLiteFactory.deleteDatabase {name: SelfTest.DBNAME, location: 'default'}, -> errorcb newSQLError message + # FUTURE TODO: return + # FUTURE TODO log err2 , (err2)-> errorcb newSQLError "Cleanup error: #{err2} for error: #{message}" + return ## Exported API: diff --git a/package.json b/package.json index ca479ab8..be8847a4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cordova-sqlite-legacy-express-core", - "version": "1.0.0-pre1", + "version": "1.0.0-pre2", "description": "Native interface to SQLite for PhoneGap/Cordova (legacy express core version)", "cordova": { "id": "cordova-sqlite-legacy-express-core", diff --git a/plugin.xml b/plugin.xml index ffd6362c..5e027b26 100644 --- a/plugin.xml +++ b/plugin.xml @@ -2,7 +2,7 @@ + version="1.0.0-pre2"> Cordova sqlite storage plugin - legacy express core version diff --git a/www/SQLitePlugin.js b/www/SQLitePlugin.js index f6fd37fc..1b6c7da2 100644 --- a/www/SQLitePlugin.js +++ b/www/SQLitePlugin.js @@ -606,17 +606,111 @@ SelfTest = { DBNAME: '___$$$___litehelpers___$$$___test___$$$___.db', start: function(successcb, errorcb) { - return SQLiteFactory.deleteDatabase({ + SQLiteFactory.deleteDatabase({ name: SelfTest.DBNAME, location: 'default' }, (function() { - return SelfTest.start2(successcb, errorcb); + return SelfTest.step1(successcb, errorcb); }), (function() { - return SelfTest.start2(successcb, errorcb); + return SelfTest.step1(successcb, errorcb); })); }, - start2: function(successcb, errorcb) { - return SQLiteFactory.openDatabase({ + step1: function(successcb, errorcb) { + SQLiteFactory.openDatabase({ + name: SelfTest.DBNAME, + location: 'default' + }, function(db) { + var check1; + check1 = false; + db.transaction(function(tx) { + tx.executeSql('SELECT UPPER("Test") AS upperText', [], function(ignored, resutSet) { + if (!resutSet.rows) { + return SelfTest.finishWithError(errorcb, 'Missing resutSet.rows'); + } + if (!resutSet.rows.length) { + return SelfTest.finishWithError(errorcb, 'Missing resutSet.rows.length'); + } + if (resutSet.rows.length !== 1) { + return SelfTest.finishWithError(errorcb, "Incorrect resutSet.rows.length value: " + resutSet.rows.length + " (expected: 1)"); + } + if (!resutSet.rows.item(0).upperText) { + return SelfTest.finishWithError(errorcb, 'Missing resutSet.rows.item(0).upperText'); + } + if (resutSet.rows.item(0).upperText !== 'TEST') { + return SelfTest.finishWithError(errorcb, "Incorrect resutSet.rows.item(0).upperText value: " + (resutSet.rows.item(0).upperText) + " (expected: 'TEST')"); + } + check1 = true; + }, function(ignored, tx_sql_err) { + return SelfTest.finishWithError(errorcb, "TX SQL error: " + tx_sql_err); + }); + }, function(tx_err) { + return SelfTest.finishWithError(errorcb, "TRANSACTION error: " + tx_err); + }, function() { + if (!check1) { + return SelfTest.finishWithError(errorcb, 'Did not get expected upperText result data'); + } + db.executeSql('BEGIN', null, function(ignored) { + return nextTick(function() { + delete db.openDBs[SelfTest.DBNAME]; + delete txLocks[SelfTest.DBNAME]; + nextTick(function() { + db.transaction(function(tx2) { + tx2.executeSql('SELECT 1'); + }, function(tx_err) { + if (!tx_err) { + return SelfTest.finishWithError(errorcb, 'Missing error object'); + } + SelfTest.step2(successcb, errorcb); + }, function() { + return SelfTest.finishWithError(errorcb, 'Missing error object'); + }); + }); + }); + }); + }); + }, function(open_err) { + return SelfTest.finishWithError(errorcb, "Open database error: " + open_err); + }); + }, + step2: function(successcb, errorcb) { + SQLiteFactory.openDatabase({ + name: SelfTest.DBNAME, + location: 'default' + }, function(db) { + db.transaction(function(tx) { + tx.executeSql('SELECT ? AS myResult', [null], function(ignored, resutSet) {}); + }, function(txError) { + if (!txError) { + return SelfTest.finishWithError(errorcb, 'Missing txError object'); + } + db.transaction(function(tx2) { + tx2.executeSql('SELECT ? AS myResult', [null], function(ignored, resutSet) { + if (!resutSet.rows) { + return SelfTest.finishWithError(errorcb, 'Missing resutSet.rows'); + } + if (!resutSet.rows.length) { + return SelfTest.finishWithError(errorcb, 'Missing resutSet.rows.length'); + } + if (resutSet.rows.length !== 1) { + return SelfTest.finishWithError(errorcb); + } + SelfTest.step3(successcb, errorcb); + }); + }, function(tx2_err) { + return SelfTest.finishWithError(errorcb, "UNEXPECTED TRANSACTION ERROR: " + tx2_err); + }); + }, function() { + if (/Android/.test(navigator.userAgent) && !/Windows /.test(navigator.userAgent)) { + return SelfTest.step3(successcb, errorcb); + } + return SelfTest.finishWithError(errorcb, 'UNEXPECTED SUCCESS ref: litehelpers/Cordova-sqlite-storage#666'); + }); + }, function(open_err) { + return SelfTest.finishWithError(errorcb, "Open database error: " + open_err); + }); + }, + step3: function(successcb, errorcb) { + SQLiteFactory.openDatabase({ name: SelfTest.DBNAME, location: 'default' }, function(db) { @@ -673,9 +767,22 @@ name: SelfTest.DBNAME, location: 'default' }, successcb, function(cleanup_err) { + if (/Windows /.test(navigator.userAgent) || /IEMobile/.test(navigator.userAgent)) { + console.log("IGNORE CLEANUP (DELETE) ERROR: " + (JSON.stringify(cleanup_err)) + " (Windows/WP8)"); + successcb(); + return; + } return SelfTest.finishWithError(errorcb, "Cleanup error: " + cleanup_err); }); }, function(close_err) { + if (/Windows /.test(navigator.userAgent) || /IEMobile/.test(navigator.userAgent)) { + console.log("IGNORE close ERROR: " + (JSON.stringify(close_err)) + " (Windows/WP8)"); + SQLiteFactory.deleteDatabase({ + name: SelfTest.DBNAME, + location: 'default' + }, successcb, successcb); + return; + } return SelfTest.finishWithError(errorcb, "close error: " + close_err); }); }); @@ -691,7 +798,8 @@ }); }, finishWithError: function(errorcb, message) { - return SQLiteFactory.deleteDatabase({ + console.log("selfTest ERROR with message: " + message); + SQLiteFactory.deleteDatabase({ name: SelfTest.DBNAME, location: 'default' }, function() { From a760cc65c85f4ae4b82dd565392b30dfbf438fb9 Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Thu, 20 Apr 2017 20:34:51 +0200 Subject: [PATCH 04/47] Workaround solution to BUG litehelpers/Cordova-sqlite-storage#666 Internally verified by selfTest function. --- CHANGES.md | 5 ++- README.md | 2 +- SQLitePlugin.coffee.md | 98 ++++++++++++++++++++++++------------------ package.json | 2 +- plugin.xml | 2 +- www/SQLitePlugin.js | 46 +++++++++----------- 6 files changed, 81 insertions(+), 74 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index c2c73d7c..c4230c52 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,8 +1,9 @@ # Changes -###### cordova-sqlite-legacy-express-core 1.0.0-pre2 +###### cordova-sqlite-legacy-express-core 1.0.0-pre3 -- selfTest simulate scenario in BUG litehelpers/Cordova-sqlite-storage#666 (also includes string test and test of effects of location reload/change in this version branch, along with another internal check) +- Workaround solution to BUG litehelpers/Cordova-sqlite-storage#666 (hanging transaction in case of location reload/change) +- selfTest simulate scenario & test solution to BUG litehelpers/Cordova-sqlite-storage#666 (also includes string test and test of effects of location reload/change in this version branch, along with another internal check) - Drop engine constraints in package.json & plugin.xml (in this version branch) - Support macOS platform with builtin libsqlite3.dylib framework in this version branch diff --git a/README.md b/README.md index 315df540..0045455c 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,7 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase ## Announcements +- Resolved transaction problem after window.location (page) change with possible data loss ref: [litehelpers/Cordova-sqlite-storage#666](https://github.com/litehelpers/Cordova-sqlite-storage/issues/666) - [brodybits / cordova-sqlite-test-app](https://github.com/brodybits/cordova-sqlite-test-app) project is a CC0 (public domain) starting point (NOTE that this plugin must be added) and may also be used to reproduce issues with this plugin. - The Lawnchair adapter is now supported in [litehelpers / cordova-sqlite-lawnchair-adapter](https://github.com/litehelpers/cordova-sqlite-lawnchair-adapter). - [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) now supports SELECT BLOB data in Base64 format on all platforms in addition to REGEXP (Android/iOS/macOS) and pre-populated database (all platforms). @@ -117,7 +118,6 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase ## Known issues -- Transaction problem after page change WITH POSSIBLE DATA LOSS ref: [litehelpers/Cordova-sqlite-storage#666](https://github.com/litehelpers/Cordova-sqlite-storage/issues/666) - iOS/macOS platform version does not support certain rapidly repeated open-and-close or open-and-delete test scenarios due to how the implementation handles background processing - As described below, auto-vacuum is NOT enabled by default. - It is possible to request a SQL statement list such as "SELECT 1; SELECT 2" within a single SQL statement string, however the plugin will only execute the first statement and silently ignore the others ref: [litehelpers/Cordova-sqlite-storage#551](https://github.com/litehelpers/Cordova-sqlite-storage/issues/551) diff --git a/SQLitePlugin.coffee.md b/SQLitePlugin.coffee.md index 4ff8e969..6e131300 100644 --- a/SQLitePlugin.coffee.md +++ b/SQLitePlugin.coffee.md @@ -84,7 +84,7 @@ SQLitePlugin = (openargs, openSuccess, openError) -> # console.log "SQLitePlugin openargs: #{JSON.stringify openargs}" - # _should_ already be checked by openDatabase: + # SHOULD already be checked by openDatabase: if !(openargs and openargs['name']) throw newSQLError "Cannot create a SQLitePlugin db instance without a db name" @@ -115,8 +115,8 @@ SQLitePlugin::databaseFeatures = isSQLitePluginDatabase: true # Keep track of state of open db connections - # XXX TBD this will be moved and renamed or - # combined with txLocks. + # XXX FUTURE TBD this *may* be moved and renamed, + # or even combined with txLocks if possible. # NOTE: In case txLocks is renamed or replaced the selfTest has to be adapted as well. SQLitePlugin::openDBs = {} @@ -128,14 +128,17 @@ } txLocks[@dbname].queue.push t if @dbname of @openDBs && @openDBs[@dbname] isnt DB_STATE_INIT - # XXX TODO: only when queue has length of 1 [and test it!!] + # FUTURE TBD: rename startNextTransaction to something like + # triggerTransactionQueue + # ALT TBD: only when queue has length of 1 (and test)?? @startNextTransaction() else if @dbname of @openDBs console.log 'new transaction is waiting for open operation' else - # XXX TBD TODO: in this case (which should not happen), should abort and discard the transaction. + # XXX SHOULD NOT GET HERE. + # FUTURE TBD TODO: in this exceptional case abort and discard the transaction. console.log 'database is closed, new transaction is [stuck] waiting until db is opened again!' return @@ -220,7 +223,7 @@ #if !@openDBs[@dbname] then call open error cb, and abort pending tx if any if !@openDBs[@dbname] console.log 'database was closed during open operation' - # XXX TODO [BUG #210] (and test!!): + # XXX TODO (WITH TEST) ref BUG litehelpers/Cordova-sqlite-storage#210: # if !!error then error newSQLError 'database closed during open operation' # @abortAllPendingTransactions() @@ -245,6 +248,18 @@ # store initial DB state: @openDBs[@dbname] = DB_STATE_INIT + # As a WORKAROUND SOLUTION to BUG litehelpers/Cordova-sqlite-storage#666: + # If the database was never opened on the JavaScript side + # start an extra ROLLBACK statement to abort any pending transaction + # (does not matter whether it succeeds or fails here). + # FUTURE TBD a better solution would be to send a special signal or parameter + # if the database was never opened on the JavaScript side. + if not txLocks[@dbname] + myfn = (tx) -> + tx.addStatement 'ROLLBACK' + return + @addTransaction new SQLitePluginTransaction @, myfn, null, null, false, false + cordova.exec opensuccesscb, openerrorcb, "SQLitePlugin", "open", [ @openargs ] return @@ -252,20 +267,30 @@ SQLitePlugin::close = (success, error) -> if @dbname of @openDBs if txLocks[@dbname] && txLocks[@dbname].inProgress - # XXX TBD: wait for current tx then close (??) + # FUTURE TBD TODO ref BUG litehelpers/Cordova-sqlite-storage#210: + # Wait for current tx to finish then close, + # then abort any other pending transactions + # (and cleanup any other internal resources). + # (This would need testing!!) console.log 'cannot close: transaction is in progress' error newSQLError 'database cannot be closed while a transaction is in progress' return console.log 'CLOSE database: ' + @dbname - # XXX [BUG #209] closing one db handle disables other handles to same db + # NOTE: closing one db handle disables other handles to same db + # FUTURE TBD TODO ref litehelpers/Cordova-sqlite-storage#210: + # Add a dispose method to simply invalidate the + # current database object ("this") delete @openDBs[@dbname] if txLocks[@dbname] then console.log 'closing db with transaction queue length: ' + txLocks[@dbname].queue.length else console.log 'closing db with no transaction lock state' - # XXX [BUG #210] TODO: when closing or deleting a db, abort any pending transactions [and test it!!] + # XXX TODO BUG litehelpers/Cordova-sqlite-storage#210: + # abort all pending transactions (with error callback) + # when closing a database (needs testing!!) + # (and cleanup any other internal resources) cordova.exec success, error, "SQLitePlugin", "close", [ { path: @dbname } ] @@ -645,6 +670,12 @@ new SQLitePlugin openargs, okcb, errorcb deleteDatabase: (first, success, error) -> + # XXX TODO BUG litehelpers/Cordova-sqlite-storage#367: + # abort all pending transactions (with error callback) + # when deleting a database + # (and cleanup any other internal resources) + # NOTE: This should properly close the database + # (at least on the JavaScript side) before deleting. args = {} if first.constructor == String @@ -681,7 +712,10 @@ args.dblocation = dblocation - # XXX [BUG #210] TODO: when closing or deleting a db, abort any pending transactions (with error callback) + # XXX TODO BUG litehelpers/Cordova-sqlite-storage#367 (repeated here): + # abort all pending transactions (with error callback) + # when deleting a database + # (and cleanup any other internal resources) delete SQLitePlugin::openDBs[args.path] cordova.exec success, error, "SQLitePlugin", "delete", [ args ] @@ -767,45 +801,23 @@ step2: (successcb, errorcb) -> SQLiteFactory.openDatabase {name: SelfTest.DBNAME, location: 'default'}, (db) -> - # TX FAILURE EXPECTED DUE TO BUG litehelpers/Cordova-sqlite-storage#666: + # TX SHOULD SUCCEED to demonstrate solution to BUG litehelpers/Cordova-sqlite-storage#666: db.transaction (tx) -> tx.executeSql 'SELECT ? AS myResult', [null], (ignored, resutSet) -> - # Extra sql success callback ignored: + if !resutSet.rows + return SelfTest.finishWithError errorcb, 'Missing resutSet.rows' + if !resutSet.rows.length + return SelfTest.finishWithError errorcb, 'Missing resutSet.rows.length' + if resutSet.rows.length isnt 1 + return SelfTest.finishWithError errorcb, + "Incorrect resutSet.rows.length value: #{resutSet.rows.length} (expected: 1)" + SelfTest.step3 successcb, errorcb return return - , (txError) -> - # EXPECTED RESULT DUE TO BUG litehelpers/Cordova-sqlite-storage#666: - if !txError - return SelfTest.finishWithError errorcb, 'Missing txError object' - # second try should work: - db.transaction (tx2) -> - tx2.executeSql 'SELECT ? AS myResult', [null], (ignored, resutSet) -> - if !resutSet.rows - return SelfTest.finishWithError errorcb, 'Missing resutSet.rows' - if !resutSet.rows.length - return SelfTest.finishWithError errorcb, 'Missing resutSet.rows.length' - if resutSet.rows.length isnt 1 - return SelfTest.finishWithError errorcb, - SelfTest.step3 successcb, errorcb - return - return - , (tx2_err) -> - return SelfTest.finishWithError errorcb, "UNEXPECTED TRANSACTION ERROR: #{tx2_err}" - return - - , () -> - # TX SUCCESS POSSIBLE FOR Android (android.database) ONLY - # NOTE: Windows 10 (UWP) mobile platform also shows "Android" in navigator.userAgent, - # filtered out here. - # FUTURE TBD android.database implementation should be fixed to report error in this case. - if /Android/.test(navigator.userAgent) and not /Windows /.test(navigator.userAgent) - return SelfTest.step3 successcb, errorcb - # OTHERWISE: - # TX SUCCESS NOT EXPECTED DUE TO BUG litehelpers/Cordova-sqlite-storage#666: - return SelfTest.finishWithError errorcb, 'UNEXPECTED SUCCESS ref: litehelpers/Cordova-sqlite-storage#666' + # NOT EXPECTED: + return SelfTest.finishWithError errorcb, "UNEXPECTED TRANSACTION ERROR: #{txError}" return - , (open_err) -> SelfTest.finishWithError errorcb, "Open database error: #{open_err}" return diff --git a/package.json b/package.json index be8847a4..04e2417d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cordova-sqlite-legacy-express-core", - "version": "1.0.0-pre2", + "version": "1.0.0-pre3", "description": "Native interface to SQLite for PhoneGap/Cordova (legacy express core version)", "cordova": { "id": "cordova-sqlite-legacy-express-core", diff --git a/plugin.xml b/plugin.xml index 5e027b26..386d19d1 100644 --- a/plugin.xml +++ b/plugin.xml @@ -2,7 +2,7 @@ + version="1.0.0-pre3"> Cordova sqlite storage plugin - legacy express core version diff --git a/www/SQLitePlugin.js b/www/SQLitePlugin.js index 1b6c7da2..476349c8 100644 --- a/www/SQLitePlugin.js +++ b/www/SQLitePlugin.js @@ -162,7 +162,7 @@ }; SQLitePlugin.prototype.open = function(success, error) { - var openerrorcb, opensuccesscb; + var myfn, openerrorcb, opensuccesscb; if (this.dbname in this.openDBs) { console.log('database already open: ' + this.dbname); nextTick((function(_this) { @@ -201,6 +201,12 @@ }; })(this); this.openDBs[this.dbname] = DB_STATE_INIT; + if (!txLocks[this.dbname]) { + myfn = function(tx) { + tx.addStatement('ROLLBACK'); + }; + this.addTransaction(new SQLitePluginTransaction(this, myfn, null, null, false, false)); + } cordova.exec(opensuccesscb, openerrorcb, "SQLitePlugin", "open", [this.openargs]); } }; @@ -678,32 +684,20 @@ location: 'default' }, function(db) { db.transaction(function(tx) { - tx.executeSql('SELECT ? AS myResult', [null], function(ignored, resutSet) {}); - }, function(txError) { - if (!txError) { - return SelfTest.finishWithError(errorcb, 'Missing txError object'); - } - db.transaction(function(tx2) { - tx2.executeSql('SELECT ? AS myResult', [null], function(ignored, resutSet) { - if (!resutSet.rows) { - return SelfTest.finishWithError(errorcb, 'Missing resutSet.rows'); - } - if (!resutSet.rows.length) { - return SelfTest.finishWithError(errorcb, 'Missing resutSet.rows.length'); - } - if (resutSet.rows.length !== 1) { - return SelfTest.finishWithError(errorcb); - } - SelfTest.step3(successcb, errorcb); - }); - }, function(tx2_err) { - return SelfTest.finishWithError(errorcb, "UNEXPECTED TRANSACTION ERROR: " + tx2_err); + tx.executeSql('SELECT ? AS myResult', [null], function(ignored, resutSet) { + if (!resutSet.rows) { + return SelfTest.finishWithError(errorcb, 'Missing resutSet.rows'); + } + if (!resutSet.rows.length) { + return SelfTest.finishWithError(errorcb, 'Missing resutSet.rows.length'); + } + if (resutSet.rows.length !== 1) { + return SelfTest.finishWithError(errorcb, "Incorrect resutSet.rows.length value: " + resutSet.rows.length + " (expected: 1)"); + } + SelfTest.step3(successcb, errorcb); }); - }, function() { - if (/Android/.test(navigator.userAgent) && !/Windows /.test(navigator.userAgent)) { - return SelfTest.step3(successcb, errorcb); - } - return SelfTest.finishWithError(errorcb, 'UNEXPECTED SUCCESS ref: litehelpers/Cordova-sqlite-storage#666'); + }, function(txError) { + return SelfTest.finishWithError(errorcb, "UNEXPECTED TRANSACTION ERROR: " + txError); }); }, function(open_err) { return SelfTest.finishWithError(errorcb, "Open database error: " + open_err); From b751ea907ea82d5568fa74f28eb6512c5a31c1d4 Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Fri, 21 Apr 2017 15:45:35 +0200 Subject: [PATCH 05/47] Remove Lawnchair adapter from this version branch --- CHANGES.md | 3 +- Lawnchair-adapter/Lawnchair-sqlitePlugin.js | 201 --- .../test-www/Lawnchair-sqlitePlugin.js | 198 --- Lawnchair-adapter/test-www/index.html | 76 - Lawnchair-adapter/test-www/lawnchair-spec.js | 431 ----- Lawnchair-adapter/test-www/lib/json2.js | 482 ------ Lawnchair-adapter/test-www/lib/lawnchair.js | 151 -- Lawnchair-adapter/test-www/lib/qunit.css | 225 --- Lawnchair-adapter/test-www/lib/qunit.js | 1448 ----------------- README.md | 41 +- package.json | 2 +- plugin.xml | 2 +- 12 files changed, 6 insertions(+), 3254 deletions(-) delete mode 100644 Lawnchair-adapter/Lawnchair-sqlitePlugin.js delete mode 100644 Lawnchair-adapter/test-www/Lawnchair-sqlitePlugin.js delete mode 100644 Lawnchair-adapter/test-www/index.html delete mode 100755 Lawnchair-adapter/test-www/lawnchair-spec.js delete mode 100644 Lawnchair-adapter/test-www/lib/json2.js delete mode 100644 Lawnchair-adapter/test-www/lib/lawnchair.js delete mode 100644 Lawnchair-adapter/test-www/lib/qunit.css delete mode 100644 Lawnchair-adapter/test-www/lib/qunit.js diff --git a/CHANGES.md b/CHANGES.md index c4230c52..f4a1df96 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,10 +1,11 @@ # Changes -###### cordova-sqlite-legacy-express-core 1.0.0-pre3 +###### cordova-sqlite-legacy-express-core 1.0.0-pre4 - Workaround solution to BUG litehelpers/Cordova-sqlite-storage#666 (hanging transaction in case of location reload/change) - selfTest simulate scenario & test solution to BUG litehelpers/Cordova-sqlite-storage#666 (also includes string test and test of effects of location reload/change in this version branch, along with another internal check) - Drop engine constraints in package.json & plugin.xml (in this version branch) +- Remove Lawnchair adapter from this version branch - Support macOS platform with builtin libsqlite3.dylib framework in this version branch ## 1.2.2 diff --git a/Lawnchair-adapter/Lawnchair-sqlitePlugin.js b/Lawnchair-adapter/Lawnchair-sqlitePlugin.js deleted file mode 100644 index 9af1033f..00000000 --- a/Lawnchair-adapter/Lawnchair-sqlitePlugin.js +++ /dev/null @@ -1,201 +0,0 @@ -Lawnchair.adapter('cordova-sqlite', (function () { - // private methods - var fail = function (e, i) { console.log('error in sqlite adaptor!', e, i) } - , now = function () { return new Date() } // FIXME need to use better date fn - // not entirely sure if this is needed... - if (!Function.prototype.bind) { - Function.prototype.bind = function( obj ) { - var slice = [].slice - , args = slice.call(arguments, 1) - , self = this - , nop = function () {} - , bound = function () { - return self.apply(this instanceof nop ? this : (obj || {}), args.concat(slice.call(arguments))) - } - nop.prototype = self.prototype - bound.prototype = new nop() - return bound - } - } - - // public methods - return { - - valid: function() { return !!(sqlitePlugin.openDatabase) }, - - init: function (options, callback) { - var that = this - , cb = that.fn(that.name, callback) - , dbname = options.db || this.name - , bgType = options.bgType || 1 - , create = "CREATE TABLE IF NOT EXISTS " + this.name + " (id NVARCHAR(32) UNIQUE PRIMARY KEY, value TEXT, timestamp REAL)" - , win = function(){ return cb.call(that, that); } - // open a connection and create the db if it doesn't exist - this.db = sqlitePlugin.openDatabase({name:dbname,bgType:bgType}) - this.db.transaction(function (t) { - t.executeSql(create, [], win, fail) - }) - }, - - keys: function (callback) { - var cb = this.lambda(callback) - , that = this - , keys = "SELECT id FROM " + this.name + " ORDER BY timestamp DESC" - - this.db.transaction(function(t) { - var win = function (xxx, results) { - if (results.rows.length == 0 ) { - cb.call(that, []) - } else { - var r = []; - for (var i = 0, l = results.rows.length; i < l; i++) { - r.push(results.rows.item(i).id); - } - cb.call(that, r) - } - } - t.executeSql(keys, [], win, fail) - }) - return this - }, - // you think thats air you're breathing now? - save: function (obj, callback) { - var that = this - , id = obj.key || that.uuid() - , ins = "INSERT INTO " + this.name + " (value, timestamp, id) VALUES (?,?,?)" - , up = "UPDATE " + this.name + " SET value=?, timestamp=? WHERE id=?" - , win = function () { if (callback) { obj.key = id; that.lambda(callback).call(that, obj) }} - , val = [now(), id] - // existential - that.exists(obj.key, function(exists) { - // transactions are like condoms - that.db.transaction(function(t) { - // TODO move timestamp to a plugin - var insert = function (obj) { - val.unshift(JSON.stringify(obj)) - t.executeSql(ins, val, win, fail) - } - // TODO move timestamp to a plugin - var update = function (obj) { - delete(obj.key) - val.unshift(JSON.stringify(obj)) - t.executeSql(up, val, win, fail) - } - // pretty - exists ? update(obj) : insert(obj) - }) - }); - return this - }, - - // FIXME this should be a batch insert / just getting the test to pass... - batch: function (objs, cb) { - - var results = [] - , done = false - , that = this - - var updateProgress = function(obj) { - results.push(obj) - done = results.length === objs.length - } - - var checkProgress = setInterval(function() { - if (done) { - if (cb) that.lambda(cb).call(that, results) - clearInterval(checkProgress) - } - }, 200) - - for (var i = 0, l = objs.length; i < l; i++) - this.save(objs[i], updateProgress) - - return this - }, - - get: function (keyOrArray, cb) { - var that = this - , sql = '' - // batch selects support - if (this.isArray(keyOrArray)) { - sql = 'SELECT id, value FROM ' + this.name + " WHERE id IN ('" + keyOrArray.join("','") + "')" - } else { - sql = 'SELECT id, value FROM ' + this.name + " WHERE id = '" + keyOrArray + "'" - } - // FIXME - // will always loop the results but cleans it up if not a batch return at the end.. - // in other words, this could be faster - var win = function (xxx, results) { - var o = null - , r = [] - if (results.rows.length) { - for (var i = 0, l = results.rows.length; i < l; i++) { - o = JSON.parse(results.rows.item(i).value) - o.key = results.rows.item(i).id - r.push(o) - } - } - if (!that.isArray(keyOrArray)) r = r.length ? r[0] : null - if (cb) that.lambda(cb).call(that, r) - } - this.db.transaction(function(t){ t.executeSql(sql, [], win, fail) }) - return this - }, - - exists: function (key, cb) { - var is = "SELECT * FROM " + this.name + " WHERE id = ?" - , that = this - , win = function(xxx, results) { if (cb) that.fn('exists', cb).call(that, (results.rows.length > 0)) } - this.db.transaction(function(t){ t.executeSql(is, [key], win, fail) }) - return this - }, - - all: function (callback) { - var that = this - , all = "SELECT * FROM " + this.name - , r = [] - , cb = this.fn(this.name, callback) || undefined - , win = function (xxx, results) { - if (results.rows.length != 0) { - for (var i = 0, l = results.rows.length; i < l; i++) { - var obj = JSON.parse(results.rows.item(i).value) - obj.key = results.rows.item(i).id - r.push(obj) - } - } - if (cb) cb.call(that, r) - } - - this.db.transaction(function (t) { - t.executeSql(all, [], win, fail) - }) - return this - }, - - remove: function (keyOrObj, cb) { - var that = this - , key = typeof keyOrObj === 'string' ? keyOrObj : (Array.isArray(keyOrObj) ? null : keyOrObj.key) - , del = "DELETE FROM " + this.name + (Array.isArray(keyOrObj) ? " WHERE id IN ('" + keyOrObj.join("','") + "')" : " WHERE id = ?") - , win = function () { if (cb) that.lambda(cb).call(that) } - - this.db.transaction(function (t) { - if (key == null) - t.executeSql(del, [], win, fail); - else - t.executeSql(del, [key], win, fail); - }); - - return this; - }, - - nuke: function (cb) { - var nuke = "DELETE FROM " + this.name - , that = this - , win = cb ? function() { that.lambda(cb).call(that) } : function(){} - this.db.transaction(function (t) { - t.executeSql(nuke, [], win, fail) - }) - return this - } -////// -}})()) diff --git a/Lawnchair-adapter/test-www/Lawnchair-sqlitePlugin.js b/Lawnchair-adapter/test-www/Lawnchair-sqlitePlugin.js deleted file mode 100644 index e4ca0d58..00000000 --- a/Lawnchair-adapter/test-www/Lawnchair-sqlitePlugin.js +++ /dev/null @@ -1,198 +0,0 @@ -Lawnchair.adapter('webkit-sqlite', (function () { - // private methods - var fail = function (e, i) { console.log('error in sqlite adaptor!', e, i) } - , now = function () { return new Date() } // FIXME need to use better date fn - // not entirely sure if this is needed... - if (!Function.prototype.bind) { - Function.prototype.bind = function( obj ) { - var slice = [].slice - , args = slice.call(arguments, 1) - , self = this - , nop = function () {} - , bound = function () { - return self.apply(this instanceof nop ? this : (obj || {}), args.concat(slice.call(arguments))) - } - nop.prototype = self.prototype - bound.prototype = new nop() - return bound - } - } - - // public methods - return { - - valid: function() { return !!(sqlitePlugin.openDatabase) }, - - init: function (options, callback) { - var that = this - , cb = that.fn(that.name, callback) - , dbname = options.db || this.name - , bgType = options.bgType || 1 - , create = "CREATE TABLE IF NOT EXISTS " + this.name + " (id NVARCHAR(32) UNIQUE PRIMARY KEY, value TEXT, timestamp REAL)" - , win = function(){ return cb.call(that, that); } - // open a connection and create the db if it doesn't exist - this.db = sqlitePlugin.openDatabase({name:dbname,bgType:bgType}) - this.db.transaction(function (t) { - t.executeSql(create, [], win, fail) - }) - }, - - keys: function (callback) { - var cb = this.lambda(callback) - , that = this - , keys = "SELECT id FROM " + this.name + " ORDER BY timestamp DESC" - - this.db.transaction(function(t) { - var win = function (xxx, results) { - if (results.rows.length == 0 ) { - cb.call(that, []) - } else { - var r = []; - for (var i = 0, l = results.rows.length; i < l; i++) { - r.push(results.rows.item(i).id); - } - cb.call(that, r) - } - } - t.executeSql(keys, [], win, fail) - }) - return this - }, - // you think thats air you're breathing now? - save: function (obj, callback) { - var that = this - , id = obj.key || that.uuid() - , ins = "INSERT INTO " + this.name + " (value, timestamp, id) VALUES (?,?,?)" - , up = "UPDATE " + this.name + " SET value=?, timestamp=? WHERE id=?" - , win = function () { if (callback) { obj.key = id; that.lambda(callback).call(that, obj) }} - , val = [now(), id] - // existential - that.exists(obj.key, function(exists) { - // transactions are like condoms - that.db.transaction(function(t) { - // TODO move timestamp to a plugin - var insert = function (obj) { - val.unshift(JSON.stringify(obj)) - t.executeSql(ins, val, win, fail) - } - // TODO move timestamp to a plugin - var update = function (obj) { - delete(obj.key) - val.unshift(JSON.stringify(obj)) - t.executeSql(up, val, win, fail) - } - // pretty - exists ? update(obj) : insert(obj) - }) - }); - return this - }, - - // FIXME this should be a batch insert / just getting the test to pass... - batch: function (objs, cb) { - - var results = [] - , done = false - , that = this - - var updateProgress = function(obj) { - results.push(obj) - done = results.length === objs.length - } - - var checkProgress = setInterval(function() { - if (done) { - if (cb) that.lambda(cb).call(that, results) - clearInterval(checkProgress) - } - }, 200) - - for (var i = 0, l = objs.length; i < l; i++) - this.save(objs[i], updateProgress) - - return this - }, - - get: function (keyOrArray, cb) { - var that = this - , sql = '' - // batch selects support - if (this.isArray(keyOrArray)) { - sql = 'SELECT id, value FROM ' + this.name + " WHERE id IN ('" + keyOrArray.join("','") + "')" - } else { - sql = 'SELECT id, value FROM ' + this.name + " WHERE id = '" + keyOrArray + "'" - } - // FIXME - // will always loop the results but cleans it up if not a batch return at the end.. - // in other words, this could be faster - var win = function (xxx, results) { - var o = null - , r = [] - if (results.rows.length) { - for (var i = 0, l = results.rows.length; i < l; i++) { - o = JSON.parse(results.rows.item(i).value) - o.key = results.rows.item(i).id - r.push(o) - } - } - if (!that.isArray(keyOrArray)) r = r.length ? r[0] : null - if (cb) that.lambda(cb).call(that, r) - } - this.db.transaction(function(t){ t.executeSql(sql, [], win, fail) }) - return this - }, - - exists: function (key, cb) { - var is = "SELECT * FROM " + this.name + " WHERE id = ?" - , that = this - , win = function(xxx, results) { if (cb) that.fn('exists', cb).call(that, (results.rows.length > 0)) } - this.db.transaction(function(t){ t.executeSql(is, [key], win, fail) }) - return this - }, - - all: function (callback) { - var that = this - , all = "SELECT * FROM " + this.name - , r = [] - , cb = this.fn(this.name, callback) || undefined - , win = function (xxx, results) { - if (results.rows.length != 0) { - for (var i = 0, l = results.rows.length; i < l; i++) { - var obj = JSON.parse(results.rows.item(i).value) - obj.key = results.rows.item(i).id - r.push(obj) - } - } - if (cb) cb.call(that, r) - } - - this.db.transaction(function (t) { - t.executeSql(all, [], win, fail) - }) - return this - }, - - remove: function (keyOrObj, cb) { - var that = this - , key = typeof keyOrObj === 'string' ? keyOrObj : keyOrObj.key - , del = "DELETE FROM " + this.name + " WHERE id = ?" - , win = function () { if (cb) that.lambda(cb).call(that) } - - this.db.transaction( function (t) { - t.executeSql(del, [key], win, fail); - }); - - return this; - }, - - nuke: function (cb) { - var nuke = "DELETE FROM " + this.name - , that = this - , win = cb ? function() { that.lambda(cb).call(that) } : function(){} - this.db.transaction(function (t) { - t.executeSql(nuke, [], win, fail) - }) - return this - } -////// -}})()) diff --git a/Lawnchair-adapter/test-www/index.html b/Lawnchair-adapter/test-www/index.html deleted file mode 100644 index f07373d1..00000000 --- a/Lawnchair-adapter/test-www/index.html +++ /dev/null @@ -1,76 +0,0 @@ - - - - Lawnchair Spec - - - - - - - - - - - - - - - - - - - -

Lawnchair Spec

-

-

-
    - - diff --git a/Lawnchair-adapter/test-www/lawnchair-spec.js b/Lawnchair-adapter/test-www/lawnchair-spec.js deleted file mode 100755 index 7b373ac3..00000000 --- a/Lawnchair-adapter/test-www/lawnchair-spec.js +++ /dev/null @@ -1,431 +0,0 @@ -module('Lawnchair construction/destruction', { - setup:function() { - }, - teardown:function() { - } -}); - -test('ctor requires callbacks in each form', function() { - QUnit.stop(); - QUnit.expect(6); - - // raise exception if no ctor callback is supplied - try { - var lc2 = new Lawnchair(); - } catch(e) { - ok(true, 'exception raised if no callback supplied to init'); - } - try { - var lc3 = new Lawnchair({}, {}); - } catch(e) { - ok(true, 'exception raised if no callback supplied to init, but two args are present'); - } - try { - var lc3 = new Lawnchair({}); - } catch(e) { - ok(true, 'exception raised if no callback supplied to init, but one arg is present'); - } - - var lc = new Lawnchair({name:store.name}, function(ref) { - ok(true, 'should call passed in callback when using obj+function ctor form') - equals(this, ref, "lawnchair callback scoped to lawnchair instance") - equals(ref, this, "lawnchair passes self into callback too") - QUnit.start() - }); -}); - -/** NOTE: may cause a failure due to difference in SQLitePlugin database initialization. -test('independent data stores', function() { - - var store1 = new Lawnchair({name: "store1"}, function() {}); - - store1 .save({key: 'apple', quantity: 3}, function() { - - var store2 = new Lawnchair({name: "store2"}, function() {}); - - store1.all(function(r) { - equals(r.length, 1); - }); - - store2.all(function(r) { - equals(r.length, 0); - }); - - }) - - -}) -**/ - -module('all()', { - setup:function() { - QUnit.stop(); - - // I like to make all my variables globals. Starting a new trend. - me = {name:'brian', age:30}; - store.nuke(function() { QUnit.start(); }); - }, - teardown:function() { - me = null; - } -}) - -test('chainable', function() { - QUnit.stop(); - QUnit.expect(1); - - same(store.all(function(r) { QUnit.start(); }), store, 'should be chainable (return itself)'); -}) - -test('full callback syntax', function() { - QUnit.stop(); - QUnit.expect(4); - - store.all(function(r) { - ok(true, 'calls callback'); - ok(r instanceof Array, 'should provide array as parameter'); - equals(r.length, 0, 'parameter should initially have zero length'); - same(this, store, '"this" should be scoped to the lawnchair object inside callback'); - QUnit.start(); - }); -}) - -test('adding, nuking and size tests', function() { - QUnit.stop(); - QUnit.expect(2); - - store.save(me, function() { - store.all(function(r) { - equals(r.length, 1, 'parameter should have length 1 after saving a single record'); - store.nuke(function() { - store.all(function(r) { - equals(r.length, 0, 'parameter should have length 0 after nuking'); - QUnit.start(); - }); - }); - }); - }); -}) - -test( 'shorthand callback syntax', function() { - QUnit.stop(); - QUnit.expect(2); - - store.all('ok(true, "shorthand syntax callback gets evaled"); same(this, store, "`this` should be scoped to the Lawnchair instance"); QUnit.start();'); - - // Is this test block necessary? - // - // var tmp = new Lawnchair({name:'temps', record:'tmp'}, function(){ - // QUnit.start() - // var Temps = this; - // equals(this, Temps, 'this is bound to Lawnchair') - // QUnit.stop() - // Temps.all('ok(temps, "this.name is passed to all callback"); QUnit.start()') - // }) -}) - -/** TBD issue with Android: -test('scoped variable in shorthand callback', function() { - QUnit.expect(1); - QUnit.stop(); - - // FIXME fkn qunit being weird here... expect(1) - var tmp = new Lawnchair({name:'temps', record:'tmp'}, function() { - this.nuke(function() { - this.save({a:1}, function() { - this.each('ok(tmp, "this.record is passed to each callback"); QUnit.start()') - }) - }) - }) -}) -**/ - -module('nuke()', { - setup:function() { - QUnit.stop(); - store.nuke(function() { - QUnit.start() - }); - }, - teardown:function() { - } -}) - -test( 'chainable', function() { - QUnit.expect(1); - QUnit.stop() - - same(store.nuke(function() { QUnit.start() }), store, 'should be chainable'); -}) - -test( 'full callback syntax', function() { - QUnit.stop(); - QUnit.expect(2); - - store.nuke(function() { - ok(true, "should call callback in nuke"); - same(this, store, '"this" should be scoped to the Lawnchair instance'); - QUnit.start(); - }); -}) - -test( 'shorthand callback syntax', function() { - QUnit.stop(); - QUnit.expect(2); - - store.nuke('ok(true, "shorthand syntax callback gets evaled"); same(this, store, "`this` should be scoped to the Lawnchair instance"); QUnit.start();'); -}) - -module('save()', { - setup:function() { - QUnit.stop(); - - // I like to make all my variables globals. Starting a new trend. - me = {name:'brian', age:30}; - store.nuke(function() { QUnit.start(); }); - }, - teardown:function() { - me = null; - } -}) - -test( 'chainable', function() { - QUnit.stop(); - QUnit.expect(1); - - same(store.save(me, function() { QUnit.start(); }), store, 'should be chainable'); -}) - -test( 'full callback syntax', function() { - QUnit.stop(); - QUnit.expect(2); - - store.save(me, function(it) { - ok(true, 'should call passed in callback'); - same(it, me, 'should pass in original saved object in callback'); - QUnit.start(); - }); -}) - -test( 'shorthand callback syntax', function() { - QUnit.stop(); - QUnit.expect(2); - - store.save(me, 'ok(true, "shorthand syntax callback gets evaled"); same(this, store, "`this` should be scoped to the Lawnchair instance"); QUnit.start();'); -}) - -test( 'saving objects', function() { - QUnit.stop(); - QUnit.expect(1); - - store.save(me, function() { - store.save({key:"something", value:"else"}, function(r) { - store.all(function(r) { - equals(r.length, 2, 'after saving two keys, num. records should equal to 2'); - QUnit.start(); - }); - }); - }) -}) - -test( 'save without callback', function() { - - QUnit.stop(); - QUnit.expect(1); - - store.save(me, function(obj) { - var key = obj.key; - store.save(obj); - equals(obj.key, key, "save without callback retains key"); - QUnit.start(); - }) - -}); - -module('batch()', { - setup:function() { - QUnit.stop(); - - // I like to make all my variables globals. Starting a new trend. - me = {name:'brian', age:30}; - store.nuke(function() { QUnit.start(); }); - }, - teardown:function() { - me = null; - } -}) - -test('batch insertion', function(){ - QUnit.expect(3); - QUnit.stop(); - - ok(store.batch, 'batch implemented'); - equals(store.batch([]), store, 'chainable') - - store.batch([{i:1},{i:2}], function() { - store.all(function(r){ - equals(r.length, 2, 'should be two records from batch insert with array of two objects'); - QUnit.start(); - }); - }); -}) - -test( 'full callback syntax', function() { - QUnit.stop(1500); // timing changed by batch processing improvements - QUnit.expect(2); - - store.batch([{j:'k'}], function() { - ok(true, 'callback called with full syntax'); - same(this, store, '"this" should be the LAwnchair instance'); - QUnit.start(); - }) -}) - -test( 'shorthand callback syntax', function() { - QUnit.stop(1500); // timing changed by batch processing improvements - QUnit.expect(2); - - store.batch([{o:'k'}], 'ok(true, "shorthand syntax callback gets evaled"); same(this, store, "`this` should be scoped to the Lawnchair instance"); QUnit.start();') -}) - -module('get()', { - setup:function() { - QUnit.stop(); - - // I like to make all my variables globals. Starting a new trend. - me = {name:'brian', age:30}; - store.nuke(function() { QUnit.start(); }); - }, - teardown:function() { - me = null; - } -}); - -test( 'should it be chainable?', function() { - QUnit.expect(1); - QUnit.stop(); - - equals(store.get('foo', function() { QUnit.start(); }), store, 'get chainable'); -}); - -test('get functionality', function() { - QUnit.expect(4); - QUnit.stop(); - - store.save({key:'xyz', name:'tim'}, function() { - store.get('xyz', function(r) { - equals(r.key, 'xyz', 'should return key in loaded object'); - equals(r.name, 'tim', 'should return proper object when calling get with a key'); - store.get('doesntexist', function(s) { - ok(true, 'should call callback even for non-existent key'); - equals(s, null, 'should return null for non-existent key'); - QUnit.start(); - }); - }); - }); -}); - -test('get batch functionality', function() { - QUnit.expect(3); - QUnit.stop(1500); // timing changed by batch processing improvements - - var t = [{key:'test-get'},{key:'test-get-1'}] - store.batch(t, function() { - this.get(['test-get','test-get-1'], function(r) { - equals(r[0].key, 'test-get', "get first object"); - equals(r[1].key, 'test-get-1', "get second object"); - equals(r.length, t.length, "should batch get") - QUnit.start() - }) - }) -}); - -test( 'full callback syntax', function() { - QUnit.stop(); - QUnit.expect(2); - - store.get('somekey', function(r){ - ok(true, 'callback got called'); - same(this, store, '"this" should be teh Lawnchair instance'); - QUnit.start(); - }); -}); - -test('short callback syntax', function() { - QUnit.stop(); - QUnit.expect(2); - - store.get('somekey', 'ok(true, "shorthand syntax callback gets evaled"); same(this, store, "`this` should be scoped to the Lawnchair instance"); QUnit.start();'); -}); - -module('remove()', { - setup:function() { - QUnit.stop(); - - // I like to make all my variables globals. Starting a new trend. - me = {name:'brian', age:30}; - store.nuke(function() { QUnit.start(); }); - }, - teardown:function() { - me = null; - } -}); - - -test( 'chainable', function() { - QUnit.expect(1); - QUnit.stop(); - - store.save({key:'me', name:'brian'}, function() { - same(store.remove('me', function() { - QUnit.start(); - }), store, 'should be chainable'); - - }); -}); - -test( 'full callback syntax', function() { - QUnit.stop(); - QUnit.expect(2); - - store.save({key:'somekey', name:'something'}, function() { - store.remove('somekey', function(r){ - ok(true, 'callback got called'); - same(this, store, '"this" should be teh Lawnchair instance'); - QUnit.start(); - }); - }); -}); - -test('short callback syntax', function() { - QUnit.stop(); - QUnit.expect(2); - - store.save({key:'somekey', name:'something'}, function() { - store.remove('somekey', 'ok(true, "shorthand syntax callback gets evaled"); same(this, store, "`this` should be scoped to the Lawnchair instance"); QUnit.start();'); - }); -}); - -// FIXME need to add tests for batch deletion -test( 'remove functionality', function() { - QUnit.stop(); - QUnit.expect(2); - - store.save({name:'joni'}, function(r) { - //store.find("r.name == 'joni'", function(r){ - store.remove(r, function(r) { - store.all(function(all) { - equals(all.length, 0, "should have length 0 after saving, finding, and removing a record using entire object"); - store.save({key:'die', name:'dudeman'}, function(r) { - store.remove('die', function(r){ - store.all(function(rec) { - equals(rec.length, 0, "should have length 0 after saving and removing by string key"); - QUnit.start(); - }); - }); - }); - }); - }); - //}); - }); -}); diff --git a/Lawnchair-adapter/test-www/lib/json2.js b/Lawnchair-adapter/test-www/lib/json2.js deleted file mode 100644 index a1a3b170..00000000 --- a/Lawnchair-adapter/test-www/lib/json2.js +++ /dev/null @@ -1,482 +0,0 @@ -/* - http://www.JSON.org/json2.js - 2010-03-20 - - Public Domain. - - NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. - - See http://www.JSON.org/js.html - - - This code should be minified before deployment. - See http://javascript.crockford.com/jsmin.html - - USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO - NOT CONTROL. - - - This file creates a global JSON object containing two methods: stringify - and parse. - - JSON.stringify(value, replacer, space) - value any JavaScript value, usually an object or array. - - replacer an optional parameter that determines how object - values are stringified for objects. It can be a - function or an array of strings. - - space an optional parameter that specifies the indentation - of nested structures. If it is omitted, the text will - be packed without extra whitespace. If it is a number, - it will specify the number of spaces to indent at each - level. If it is a string (such as '\t' or ' '), - it contains the characters used to indent at each level. - - This method produces a JSON text from a JavaScript value. - - When an object value is found, if the object contains a toJSON - method, its toJSON method will be called and the result will be - stringified. A toJSON method does not serialize: it returns the - value represented by the name/value pair that should be serialized, - or undefined if nothing should be serialized. The toJSON method - will be passed the key associated with the value, and this will be - bound to the value - - For example, this would serialize Dates as ISO strings. - - Date.prototype.toJSON = function (key) { - function f(n) { - // Format integers to have at least two digits. - return n < 10 ? '0' + n : n; - } - - return this.getUTCFullYear() + '-' + - f(this.getUTCMonth() + 1) + '-' + - f(this.getUTCDate()) + 'T' + - f(this.getUTCHours()) + ':' + - f(this.getUTCMinutes()) + ':' + - f(this.getUTCSeconds()) + 'Z'; - }; - - You can provide an optional replacer method. It will be passed the - key and value of each member, with this bound to the containing - object. The value that is returned from your method will be - serialized. If your method returns undefined, then the member will - be excluded from the serialization. - - If the replacer parameter is an array of strings, then it will be - used to select the members to be serialized. It filters the results - such that only members with keys listed in the replacer array are - stringified. - - Values that do not have JSON representations, such as undefined or - functions, will not be serialized. Such values in objects will be - dropped; in arrays they will be replaced with null. You can use - a replacer function to replace those with JSON values. - JSON.stringify(undefined) returns undefined. - - The optional space parameter produces a stringification of the - value that is filled with line breaks and indentation to make it - easier to read. - - If the space parameter is a non-empty string, then that string will - be used for indentation. If the space parameter is a number, then - the indentation will be that many spaces. - - Example: - - text = JSON.stringify(['e', {pluribus: 'unum'}]); - // text is '["e",{"pluribus":"unum"}]' - - - text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); - // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' - - text = JSON.stringify([new Date()], function (key, value) { - return this[key] instanceof Date ? - 'Date(' + this[key] + ')' : value; - }); - // text is '["Date(---current time---)"]' - - - JSON.parse(text, reviver) - This method parses a JSON text to produce an object or array. - It can throw a SyntaxError exception. - - The optional reviver parameter is a function that can filter and - transform the results. It receives each of the keys and values, - and its return value is used instead of the original value. - If it returns what it received, then the structure is not modified. - If it returns undefined then the member is deleted. - - Example: - - // Parse the text. Values that look like ISO date strings will - // be converted to Date objects. - - myData = JSON.parse(text, function (key, value) { - var a; - if (typeof value === 'string') { - a = -/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); - if (a) { - return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], - +a[5], +a[6])); - } - } - return value; - }); - - myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { - var d; - if (typeof value === 'string' && - value.slice(0, 5) === 'Date(' && - value.slice(-1) === ')') { - d = new Date(value.slice(5, -1)); - if (d) { - return d; - } - } - return value; - }); - - - This is a reference implementation. You are free to copy, modify, or - redistribute. -*/ - -/*jslint evil: true, strict: false */ - -/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, - call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, - getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, - lastIndex, length, parse, prototype, push, replace, slice, stringify, - test, toJSON, toString, valueOf -*/ - - -// Create a JSON object only if one does not already exist. We create the -// methods in a closure to avoid creating global variables. - -if (!this.JSON) { - this.JSON = {}; -} - -(function () { - - function f(n) { - // Format integers to have at least two digits. - return n < 10 ? '0' + n : n; - } - - if (typeof Date.prototype.toJSON !== 'function') { - - Date.prototype.toJSON = function (key) { - - return isFinite(this.valueOf()) ? - this.getUTCFullYear() + '-' + - f(this.getUTCMonth() + 1) + '-' + - f(this.getUTCDate()) + 'T' + - f(this.getUTCHours()) + ':' + - f(this.getUTCMinutes()) + ':' + - f(this.getUTCSeconds()) + 'Z' : null; - }; - - String.prototype.toJSON = - Number.prototype.toJSON = - Boolean.prototype.toJSON = function (key) { - return this.valueOf(); - }; - } - - var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, - escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, - gap, - indent, - meta = { // table of character substitutions - '\b': '\\b', - '\t': '\\t', - '\n': '\\n', - '\f': '\\f', - '\r': '\\r', - '"' : '\\"', - '\\': '\\\\' - }, - rep; - - - function quote(string) { - -// If the string contains no control characters, no quote characters, and no -// backslash characters, then we can safely slap some quotes around it. -// Otherwise we must also replace the offending characters with safe escape -// sequences. - - escapable.lastIndex = 0; - return escapable.test(string) ? - '"' + string.replace(escapable, function (a) { - var c = meta[a]; - return typeof c === 'string' ? c : - '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); - }) + '"' : - '"' + string + '"'; - } - - - function str(key, holder) { - -// Produce a string from holder[key]. - - var i, // The loop counter. - k, // The member key. - v, // The member value. - length, - mind = gap, - partial, - value = holder[key]; - -// If the value has a toJSON method, call it to obtain a replacement value. - - if (value && typeof value === 'object' && - typeof value.toJSON === 'function') { - value = value.toJSON(key); - } - -// If we were called with a replacer function, then call the replacer to -// obtain a replacement value. - - if (typeof rep === 'function') { - value = rep.call(holder, key, value); - } - -// What happens next depends on the value's type. - - switch (typeof value) { - case 'string': - return quote(value); - - case 'number': - -// JSON numbers must be finite. Encode non-finite numbers as null. - - return isFinite(value) ? String(value) : 'null'; - - case 'boolean': - case 'null': - -// If the value is a boolean or null, convert it to a string. Note: -// typeof null does not produce 'null'. The case is included here in -// the remote chance that this gets fixed someday. - - return String(value); - -// If the type is 'object', we might be dealing with an object or an array or -// null. - - case 'object': - -// Due to a specification blunder in ECMAScript, typeof null is 'object', -// so watch out for that case. - - if (!value) { - return 'null'; - } - -// Make an array to hold the partial results of stringifying this object value. - - gap += indent; - partial = []; - -// Is the value an array? - - if (Object.prototype.toString.apply(value) === '[object Array]') { - -// The value is an array. Stringify every element. Use null as a placeholder -// for non-JSON values. - - length = value.length; - for (i = 0; i < length; i += 1) { - partial[i] = str(i, value) || 'null'; - } - -// Join all of the elements together, separated with commas, and wrap them in -// brackets. - - v = partial.length === 0 ? '[]' : - gap ? '[\n' + gap + - partial.join(',\n' + gap) + '\n' + - mind + ']' : - '[' + partial.join(',') + ']'; - gap = mind; - return v; - } - -// If the replacer is an array, use it to select the members to be stringified. - - if (rep && typeof rep === 'object') { - length = rep.length; - for (i = 0; i < length; i += 1) { - k = rep[i]; - if (typeof k === 'string') { - v = str(k, value); - if (v) { - partial.push(quote(k) + (gap ? ': ' : ':') + v); - } - } - } - } else { - -// Otherwise, iterate through all of the keys in the object. - - for (k in value) { - if (Object.hasOwnProperty.call(value, k)) { - v = str(k, value); - if (v) { - partial.push(quote(k) + (gap ? ': ' : ':') + v); - } - } - } - } - -// Join all of the member texts together, separated with commas, -// and wrap them in braces. - - v = partial.length === 0 ? '{}' : - gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + - mind + '}' : '{' + partial.join(',') + '}'; - gap = mind; - return v; - } - } - -// If the JSON object does not yet have a stringify method, give it one. - - if (typeof JSON.stringify !== 'function') { - JSON.stringify = function (value, replacer, space) { - -// The stringify method takes a value and an optional replacer, and an optional -// space parameter, and returns a JSON text. The replacer can be a function -// that can replace values, or an array of strings that will select the keys. -// A default replacer method can be provided. Use of the space parameter can -// produce text that is more easily readable. - - var i; - gap = ''; - indent = ''; - -// If the space parameter is a number, make an indent string containing that -// many spaces. - - if (typeof space === 'number') { - for (i = 0; i < space; i += 1) { - indent += ' '; - } - -// If the space parameter is a string, it will be used as the indent string. - - } else if (typeof space === 'string') { - indent = space; - } - -// If there is a replacer, it must be a function or an array. -// Otherwise, throw an error. - - rep = replacer; - if (replacer && typeof replacer !== 'function' && - (typeof replacer !== 'object' || - typeof replacer.length !== 'number')) { - throw new Error('JSON.stringify'); - } - -// Make a fake root object containing our value under the key of ''. -// Return the result of stringifying the value. - - return str('', {'': value}); - }; - } - - -// If the JSON object does not yet have a parse method, give it one. - - if (typeof JSON.parse !== 'function') { - JSON.parse = function (text, reviver) { - -// The parse method takes a text and an optional reviver function, and returns -// a JavaScript value if the text is a valid JSON text. - - var j; - - function walk(holder, key) { - -// The walk method is used to recursively walk the resulting structure so -// that modifications can be made. - - var k, v, value = holder[key]; - if (value && typeof value === 'object') { - for (k in value) { - if (Object.hasOwnProperty.call(value, k)) { - v = walk(value, k); - if (v !== undefined) { - value[k] = v; - } else { - delete value[k]; - } - } - } - } - return reviver.call(holder, key, value); - } - - -// Parsing happens in four stages. In the first stage, we replace certain -// Unicode characters with escape sequences. JavaScript handles many characters -// incorrectly, either silently deleting them, or treating them as line endings. - - text = String(text); - cx.lastIndex = 0; - if (cx.test(text)) { - text = text.replace(cx, function (a) { - return '\\u' + - ('0000' + a.charCodeAt(0).toString(16)).slice(-4); - }); - } - -// In the second stage, we run the text against regular expressions that look -// for non-JSON patterns. We are especially concerned with '()' and 'new' -// because they can cause invocation, and '=' because it can cause mutation. -// But just to be safe, we want to reject all unexpected forms. - -// We split the second stage into 4 regexp operations in order to work around -// crippling inefficiencies in IE's and Safari's regexp engines. First we -// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we -// replace all simple value tokens with ']' characters. Third, we delete all -// open brackets that follow a colon or comma or that begin the text. Finally, -// we look to see that the remaining characters are only whitespace or ']' or -// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. - - if (/^[\],:{}\s]*$/. -test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@'). -replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']'). -replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { - -// In the third stage we use the eval function to compile the text into a -// JavaScript structure. The '{' operator is subject to a syntactic ambiguity -// in JavaScript: it can begin a block or an object literal. We wrap the text -// in parens to eliminate the ambiguity. - - j = eval('(' + text + ')'); - -// In the optional fourth stage, we recursively walk the new structure, passing -// each name/value pair to a reviver function for possible transformation. - - return typeof reviver === 'function' ? - walk({'': j}, '') : j; - } - -// If the text is not JSON parseable, then a SyntaxError is thrown. - - throw new SyntaxError('JSON.parse'); - }; - } -}()); diff --git a/Lawnchair-adapter/test-www/lib/lawnchair.js b/Lawnchair-adapter/test-www/lib/lawnchair.js deleted file mode 100644 index 3f67835a..00000000 --- a/Lawnchair-adapter/test-www/lib/lawnchair.js +++ /dev/null @@ -1,151 +0,0 @@ -/** - * Lawnchair! - * --- - * clientside json store - * - */ -var Lawnchair = function (options, callback) { - // ensure Lawnchair was called as a constructor - if (!(this instanceof Lawnchair)) return new Lawnchair(options, callback); - - // lawnchair requires json - if (!JSON) throw 'JSON unavailable! Include http://www.json.org/json2.js to fix.' - // options are optional; callback is not - if (arguments.length <= 2 && arguments.length > 0) { - callback = (typeof arguments[0] === 'function') ? arguments[0] : arguments[1]; - options = (typeof arguments[0] === 'function') ? {} : arguments[0]; - } else { - throw 'Incorrect # of ctor args!' - } - // TODO perhaps allow for pub/sub instead? - if (typeof callback !== 'function') throw 'No callback was provided'; - - // default configuration - this.record = options.record || 'record' // default for records - this.name = options.name || 'records' // default name for underlying store - - // mixin first valid adapter - var adapter - // if the adapter is passed in we try to load that only - if (options.adapter) { - for (var i = 0, l = Lawnchair.adapters.length; i < l; i++) { - if (Lawnchair.adapters[i].adapter === options.adapter) { - adapter = Lawnchair.adapters[i].valid() ? Lawnchair.adapters[i] : undefined; - break; - } - } - // otherwise find the first valid adapter for this env - } - else { - for (var i = 0, l = Lawnchair.adapters.length; i < l; i++) { - adapter = Lawnchair.adapters[i].valid() ? Lawnchair.adapters[i] : undefined - if (adapter) break - } - } - - // we have failed - if (!adapter) throw 'No valid adapter.' - - // yay! mixin the adapter - for (var j in adapter) - this[j] = adapter[j] - - // call init for each mixed in plugin - for (var i = 0, l = Lawnchair.plugins.length; i < l; i++) - Lawnchair.plugins[i].call(this) - - // init the adapter - this.init(options, callback) -} - -Lawnchair.adapters = [] - -/** - * queues an adapter for mixin - * === - * - ensures an adapter conforms to a specific interface - * - */ -Lawnchair.adapter = function (id, obj) { - // add the adapter id to the adapter obj - // ugly here for a cleaner dsl for implementing adapters - obj['adapter'] = id - // methods required to implement a lawnchair adapter - var implementing = 'adapter valid init keys save batch get exists all remove nuke'.split(' ') - , indexOf = this.prototype.indexOf - // mix in the adapter - for (var i in obj) { - if (indexOf(implementing, i) === -1) throw 'Invalid adapter! Nonstandard method: ' + i - } - // if we made it this far the adapter interface is valid - // insert the new adapter as the preferred adapter - Lawnchair.adapters.splice(0,0,obj) -} - -Lawnchair.plugins = [] - -/** - * generic shallow extension for plugins - * === - * - if an init method is found it registers it to be called when the lawnchair is inited - * - yes we could use hasOwnProp but nobody here is an asshole - */ -Lawnchair.plugin = function (obj) { - for (var i in obj) - i === 'init' ? Lawnchair.plugins.push(obj[i]) : this.prototype[i] = obj[i] -} - -/** - * helpers - * - */ -Lawnchair.prototype = { - - isArray: Array.isArray || function(o) { return Object.prototype.toString.call(o) === '[object Array]' }, - - /** - * this code exists for ie8... for more background see: - * http://www.flickr.com/photos/westcoastlogic/5955365742/in/photostream - */ - indexOf: function(ary, item, i, l) { - if (ary.indexOf) return ary.indexOf(item) - for (i = 0, l = ary.length; i < l; i++) if (ary[i] === item) return i - return -1 - }, - - // awesome shorthand callbacks as strings. this is shameless theft from dojo. - lambda: function (callback) { - return this.fn(this.record, callback) - }, - - // first stab at named parameters for terse callbacks; dojo: first != best // ;D - fn: function (name, callback) { - return typeof callback == 'string' ? new Function(name, callback) : callback - }, - - // returns a unique identifier (by way of Backbone.localStorage.js) - // TODO investigate smaller UUIDs to cut on storage cost - uuid: function () { - var S4 = function () { - return (((1+Math.random())*0x10000)|0).toString(16).substring(1); - } - return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4()); - }, - - // a classic iterator - each: function (callback) { - var cb = this.lambda(callback) - // iterate from chain - if (this.__results) { - for (var i = 0, l = this.__results.length; i < l; i++) cb.call(this, this.__results[i], i) - } - // otherwise iterate the entire collection - else { - this.all(function(r) { - for (var i = 0, l = r.length; i < l; i++) cb.call(this, r[i], i) - }) - } - return this - } -// -- -}; diff --git a/Lawnchair-adapter/test-www/lib/qunit.css b/Lawnchair-adapter/test-www/lib/qunit.css deleted file mode 100644 index b3c6db52..00000000 --- a/Lawnchair-adapter/test-www/lib/qunit.css +++ /dev/null @@ -1,225 +0,0 @@ -/** - * QUnit - A JavaScript Unit Testing Framework - * - * http://docs.jquery.com/QUnit - * - * Copyright (c) 2011 John Resig, Jörn Zaefferer - * Dual licensed under the MIT (MIT-LICENSE.txt) - * or GPL (GPL-LICENSE.txt) licenses. - */ - -/** Font Family and Sizes */ - -#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult { - font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif; -} - -#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } -#qunit-tests { font-size: smaller; } - - -/** Resets */ - -#qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult { - margin: 0; - padding: 0; -} - - -/** Header */ - -#qunit-header { - padding: 0.5em 0 0.5em 1em; - - color: #8699a4; - background-color: #0d3349; - - font-size: 1.5em; - line-height: 1em; - font-weight: normal; - - border-radius: 15px 15px 0 0; - -moz-border-radius: 15px 15px 0 0; - -webkit-border-top-right-radius: 15px; - -webkit-border-top-left-radius: 15px; -} - -#qunit-header a { - text-decoration: none; - color: #c2ccd1; -} - -#qunit-header a:hover, -#qunit-header a:focus { - color: #fff; -} - -#qunit-banner { - height: 5px; -} - -#qunit-testrunner-toolbar { - padding: 0.5em 0 0.5em 2em; - color: #5E740B; - background-color: #eee; -} - -#qunit-userAgent { - padding: 0.5em 0 0.5em 2.5em; - background-color: #2b81af; - color: #fff; - text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; -} - - -/** Tests: Pass/Fail */ - -#qunit-tests { - list-style-position: inside; -} - -#qunit-tests li { - padding: 0.4em 0.5em 0.4em 2.5em; - border-bottom: 1px solid #fff; - list-style-position: inside; -} - -#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running { - display: none; -} - -#qunit-tests li strong { - cursor: pointer; -} - -#qunit-tests li a { - padding: 0.5em; - color: #c2ccd1; - text-decoration: none; -} -#qunit-tests li a:hover, -#qunit-tests li a:focus { - color: #000; -} - -#qunit-tests ol { - margin-top: 0.5em; - padding: 0.5em; - - background-color: #fff; - - border-radius: 15px; - -moz-border-radius: 15px; - -webkit-border-radius: 15px; - - box-shadow: inset 0px 2px 13px #999; - -moz-box-shadow: inset 0px 2px 13px #999; - -webkit-box-shadow: inset 0px 2px 13px #999; -} - -#qunit-tests table { - border-collapse: collapse; - margin-top: .2em; -} - -#qunit-tests th { - text-align: right; - vertical-align: top; - padding: 0 .5em 0 0; -} - -#qunit-tests td { - vertical-align: top; -} - -#qunit-tests pre { - margin: 0; - white-space: pre-wrap; - word-wrap: break-word; -} - -#qunit-tests del { - background-color: #e0f2be; - color: #374e0c; - text-decoration: none; -} - -#qunit-tests ins { - background-color: #ffcaca; - color: #500; - text-decoration: none; -} - -/*** Test Counts */ - -#qunit-tests b.counts { color: black; } -#qunit-tests b.passed { color: #5E740B; } -#qunit-tests b.failed { color: #710909; } - -#qunit-tests li li { - margin: 0.5em; - padding: 0.4em 0.5em 0.4em 0.5em; - background-color: #fff; - border-bottom: none; - list-style-position: inside; -} - -/*** Passing Styles */ - -#qunit-tests li li.pass { - color: #5E740B; - background-color: #fff; - border-left: 26px solid #C6E746; -} - -#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } -#qunit-tests .pass .test-name { color: #366097; } - -#qunit-tests .pass .test-actual, -#qunit-tests .pass .test-expected { color: #999999; } - -#qunit-banner.qunit-pass { background-color: #C6E746; } - -/*** Failing Styles */ - -#qunit-tests li li.fail { - color: #710909; - background-color: #fff; - border-left: 26px solid #EE5757; -} - -#qunit-tests > li:last-child { - border-radius: 0 0 15px 15px; - -moz-border-radius: 0 0 15px 15px; - -webkit-border-bottom-right-radius: 15px; - -webkit-border-bottom-left-radius: 15px; -} - -#qunit-tests .fail { color: #000000; background-color: #EE5757; } -#qunit-tests .fail .test-name, -#qunit-tests .fail .module-name { color: #000000; } - -#qunit-tests .fail .test-actual { color: #EE5757; } -#qunit-tests .fail .test-expected { color: green; } - -#qunit-banner.qunit-fail { background-color: #EE5757; } - - -/** Result */ - -#qunit-testresult { - padding: 0.5em 0.5em 0.5em 2.5em; - - color: #2b81af; - background-color: #D2E0E6; - - border-bottom: 1px solid white; -} - -/** Fixture */ - -#qunit-fixture { - position: absolute; - top: -10000px; - left: -10000px; -} diff --git a/Lawnchair-adapter/test-www/lib/qunit.js b/Lawnchair-adapter/test-www/lib/qunit.js deleted file mode 100644 index 3d640c73..00000000 --- a/Lawnchair-adapter/test-www/lib/qunit.js +++ /dev/null @@ -1,1448 +0,0 @@ -/** - * QUnit - A JavaScript Unit Testing Framework - * - * http://docs.jquery.com/QUnit - * - * Copyright (c) 2011 John Resig, Jörn Zaefferer - * Dual licensed under the MIT (MIT-LICENSE.txt) - * or GPL (GPL-LICENSE.txt) licenses. - */ - -(function(window) { - -var defined = { - setTimeout: typeof window.setTimeout !== "undefined", - sessionStorage: (function() { - try { - return !!sessionStorage.getItem; - } catch(e){ - return false; - } - })() -}; - -var testId = 0; - -var Test = function(name, testName, expected, testEnvironmentArg, async, callback) { - this.name = name; - this.testName = testName; - this.expected = expected; - this.testEnvironmentArg = testEnvironmentArg; - this.async = async; - this.callback = callback; - this.assertions = []; -}; -Test.prototype = { - init: function() { - var tests = id("qunit-tests"); - if (tests) { - var b = document.createElement("strong"); - b.innerHTML = "Running " + this.name; - var li = document.createElement("li"); - li.appendChild( b ); - li.className = "running"; - li.id = this.id = "test-output" + testId++; - tests.appendChild( li ); - } - }, - setup: function() { - if (this.module != config.previousModule) { - if ( config.previousModule ) { - QUnit.moduleDone( { - name: config.previousModule, - failed: config.moduleStats.bad, - passed: config.moduleStats.all - config.moduleStats.bad, - total: config.moduleStats.all - } ); - } - config.previousModule = this.module; - config.moduleStats = { all: 0, bad: 0 }; - QUnit.moduleStart( { - name: this.module - } ); - } - - config.current = this; - this.testEnvironment = extend({ - setup: function() {}, - teardown: function() {} - }, this.moduleTestEnvironment); - if (this.testEnvironmentArg) { - extend(this.testEnvironment, this.testEnvironmentArg); - } - - QUnit.testStart( { - name: this.testName - } ); - - // allow utility functions to access the current test environment - // TODO why?? - QUnit.current_testEnvironment = this.testEnvironment; - - try { - if ( !config.pollution ) { - saveGlobal(); - } - - this.testEnvironment.setup.call(this.testEnvironment); - } catch(e) { - QUnit.ok( false, "Setup failed on " + this.testName + ": " + e.message ); - } - }, - run: function() { - if ( this.async ) { - QUnit.stop(); - } - - if ( config.notrycatch ) { - this.callback.call(this.testEnvironment); - return; - } - try { - this.callback.call(this.testEnvironment); - } catch(e) { - fail("Test " + this.testName + " died, exception and test follows", e, this.callback); - QUnit.ok( false, "Died on test #" + (this.assertions.length + 1) + ": " + e.message + " - " + QUnit.jsDump.parse(e) ); - // else next test will carry the responsibility - saveGlobal(); - - // Restart the tests if they're blocking - if ( config.blocking ) { - start(); - } - } - }, - teardown: function() { - try { - this.testEnvironment.teardown.call(this.testEnvironment); - checkPollution(); - } catch(e) { - QUnit.ok( false, "Teardown failed on " + this.testName + ": " + e.message ); - } - }, - finish: function() { - if ( this.expected && this.expected != this.assertions.length ) { - QUnit.ok( false, "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run" ); - } - - var good = 0, bad = 0, - tests = id("qunit-tests"); - - config.stats.all += this.assertions.length; - config.moduleStats.all += this.assertions.length; - - if ( tests ) { - var ol = document.createElement("ol"); - - for ( var i = 0; i < this.assertions.length; i++ ) { - var assertion = this.assertions[i]; - - var li = document.createElement("li"); - li.className = assertion.result ? "pass" : "fail"; - li.innerHTML = assertion.message || (assertion.result ? "okay" : "failed"); - ol.appendChild( li ); - - if ( assertion.result ) { - good++; - } else { - bad++; - config.stats.bad++; - config.moduleStats.bad++; - } - } - - // store result when possible - if ( QUnit.config.reorder && defined.sessionStorage ) { - if (bad) { - sessionStorage.setItem("qunit-" + this.module + "-" + this.testName, bad); - } else { - sessionStorage.removeItem("qunit-" + this.module + "-" + this.testName); - } - } - - if (bad == 0) { - ol.style.display = "none"; - } - - var b = document.createElement("strong"); - b.innerHTML = this.name + " (" + bad + ", " + good + ", " + this.assertions.length + ")"; - - var a = document.createElement("a"); - a.innerHTML = "Rerun"; - a.href = QUnit.url({ filter: getText([b]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") }); - - addEvent(b, "click", function() { - var next = b.nextSibling.nextSibling, - display = next.style.display; - next.style.display = display === "none" ? "block" : "none"; - }); - - addEvent(b, "dblclick", function(e) { - var target = e && e.target ? e.target : window.event.srcElement; - if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) { - target = target.parentNode; - } - if ( window.location && target.nodeName.toLowerCase() === "strong" ) { - window.location = QUnit.url({ filter: getText([target]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") }); - } - }); - - var li = id(this.id); - li.className = bad ? "fail" : "pass"; - li.removeChild( li.firstChild ); - li.appendChild( b ); - li.appendChild( a ); - li.appendChild( ol ); - - } else { - for ( var i = 0; i < this.assertions.length; i++ ) { - if ( !this.assertions[i].result ) { - bad++; - config.stats.bad++; - config.moduleStats.bad++; - } - } - } - - try { - QUnit.reset(); - } catch(e) { - fail("reset() failed, following Test " + this.testName + ", exception and reset fn follows", e, QUnit.reset); - } - - QUnit.testDone( { - name: this.testName, - failed: bad, - passed: this.assertions.length - bad, - total: this.assertions.length - } ); - }, - - queue: function() { - var test = this; - synchronize(function() { - test.init(); - }); - function run() { - // each of these can by async - synchronize(function() { - test.setup(); - }); - synchronize(function() { - test.run(); - }); - synchronize(function() { - test.teardown(); - }); - synchronize(function() { - test.finish(); - }); - } - // defer when previous test run passed, if storage is available - var bad = QUnit.config.reorder && defined.sessionStorage && +sessionStorage.getItem("qunit-" + this.module + "-" + this.testName); - if (bad) { - run(); - } else { - synchronize(run); - }; - } - -}; - -var QUnit = { - - // call on start of module test to prepend name to all tests - module: function(name, testEnvironment) { - config.currentModule = name; - config.currentModuleTestEnviroment = testEnvironment; - }, - - asyncTest: function(testName, expected, callback) { - if ( arguments.length === 2 ) { - callback = expected; - expected = 0; - } - - QUnit.test(testName, expected, callback, true); - }, - - test: function(testName, expected, callback, async) { - var name = '' + testName + '', testEnvironmentArg; - - if ( arguments.length === 2 ) { - callback = expected; - expected = null; - } - // is 2nd argument a testEnvironment? - if ( expected && typeof expected === 'object') { - testEnvironmentArg = expected; - expected = null; - } - - if ( config.currentModule ) { - name = '' + config.currentModule + ": " + name; - } - - if ( !validTest(config.currentModule + ": " + testName) ) { - return; - } - - var test = new Test(name, testName, expected, testEnvironmentArg, async, callback); - test.module = config.currentModule; - test.moduleTestEnvironment = config.currentModuleTestEnviroment; - test.queue(); - }, - - /** - * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through. - */ - expect: function(asserts) { - config.current.expected = asserts; - }, - - /** - * Asserts true. - * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" ); - */ - ok: function(a, msg) { - a = !!a; - var details = { - result: a, - message: msg - }; - msg = escapeHtml(msg); - QUnit.log(details); - config.current.assertions.push({ - result: a, - message: msg - }); - }, - - /** - * Checks that the first two arguments are equal, with an optional message. - * Prints out both actual and expected values. - * - * Prefered to ok( actual == expected, message ) - * - * @example equal( format("Received {0} bytes.", 2), "Received 2 bytes." ); - * - * @param Object actual - * @param Object expected - * @param String message (optional) - */ - equal: function(actual, expected, message) { - QUnit.push(expected == actual, actual, expected, message); - }, - - notEqual: function(actual, expected, message) { - QUnit.push(expected != actual, actual, expected, message); - }, - - deepEqual: function(actual, expected, message) { - QUnit.push(QUnit.equiv(actual, expected), actual, expected, message); - }, - - notDeepEqual: function(actual, expected, message) { - QUnit.push(!QUnit.equiv(actual, expected), actual, expected, message); - }, - - strictEqual: function(actual, expected, message) { - QUnit.push(expected === actual, actual, expected, message); - }, - - notStrictEqual: function(actual, expected, message) { - QUnit.push(expected !== actual, actual, expected, message); - }, - - raises: function(block, expected, message) { - var actual, ok = false; - - if (typeof expected === 'string') { - message = expected; - expected = null; - } - - try { - block(); - } catch (e) { - actual = e; - } - - if (actual) { - // we don't want to validate thrown error - if (!expected) { - ok = true; - // expected is a regexp - } else if (QUnit.objectType(expected) === "regexp") { - ok = expected.test(actual); - // expected is a constructor - } else if (actual instanceof expected) { - ok = true; - // expected is a validation function which returns true is validation passed - } else if (expected.call({}, actual) === true) { - ok = true; - } - } - - QUnit.ok(ok, message); - }, - - start: function() { - config.semaphore--; - if (config.semaphore > 0) { - // don't start until equal number of stop-calls - return; - } - if (config.semaphore < 0) { - // ignore if start is called more often then stop - config.semaphore = 0; - } - // A slight delay, to avoid any current callbacks - if ( defined.setTimeout ) { - window.setTimeout(function() { - if ( config.timeout ) { - clearTimeout(config.timeout); - } - - config.blocking = false; - process(); - }, 13); - } else { - config.blocking = false; - process(); - } - }, - - stop: function(timeout) { - config.semaphore++; - config.blocking = true; - - if ( timeout && defined.setTimeout ) { - clearTimeout(config.timeout); - config.timeout = window.setTimeout(function() { - QUnit.ok( false, "Test timed out" ); - QUnit.start(); - }, timeout); - } - } -}; - -// Backwards compatibility, deprecated -QUnit.equals = QUnit.equal; -QUnit.same = QUnit.deepEqual; - -// Maintain internal state -var config = { - // The queue of tests to run - queue: [], - - // block until document ready - blocking: true, - - // by default, run previously failed tests first - // very useful in combination with "Hide passed tests" checked - reorder: true, - - noglobals: false, - notrycatch: false -}; - -// Load paramaters -(function() { - var location = window.location || { search: "", protocol: "file:" }, - params = location.search.slice( 1 ).split( "&" ), - length = params.length, - urlParams = {}, - current; - - if ( params[ 0 ] ) { - for ( var i = 0; i < length; i++ ) { - current = params[ i ].split( "=" ); - current[ 0 ] = decodeURIComponent( current[ 0 ] ); - // allow just a key to turn on a flag, e.g., test.html?noglobals - current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true; - urlParams[ current[ 0 ] ] = current[ 1 ]; - if ( current[ 0 ] in config ) { - config[ current[ 0 ] ] = current[ 1 ]; - } - } - } - - QUnit.urlParams = urlParams; - config.filter = urlParams.filter; - - // Figure out if we're running the tests from a server or not - QUnit.isLocal = !!(location.protocol === 'file:'); -})(); - -// Expose the API as global variables, unless an 'exports' -// object exists, in that case we assume we're in CommonJS -if ( typeof exports === "undefined" || typeof require === "undefined" ) { - extend(window, QUnit); - window.QUnit = QUnit; -} else { - extend(exports, QUnit); - exports.QUnit = QUnit; -} - -// define these after exposing globals to keep them in these QUnit namespace only -extend(QUnit, { - config: config, - - // Initialize the configuration options - init: function() { - extend(config, { - stats: { all: 0, bad: 0 }, - moduleStats: { all: 0, bad: 0 }, - started: +new Date, - updateRate: 1000, - blocking: false, - autostart: true, - autorun: false, - filter: "", - queue: [], - semaphore: 0 - }); - - var tests = id( "qunit-tests" ), - banner = id( "qunit-banner" ), - result = id( "qunit-testresult" ); - - if ( tests ) { - tests.innerHTML = ""; - } - - if ( banner ) { - banner.className = ""; - } - - if ( result ) { - result.parentNode.removeChild( result ); - } - - if ( tests ) { - result = document.createElement( "p" ); - result.id = "qunit-testresult"; - result.className = "result"; - tests.parentNode.insertBefore( result, tests ); - result.innerHTML = 'Running...
     '; - } - }, - - /** - * Resets the test setup. Useful for tests that modify the DOM. - * - * If jQuery is available, uses jQuery's html(), otherwise just innerHTML. - */ - reset: function() { - if ( window.jQuery ) { - jQuery( "#qunit-fixture" ).html( config.fixture ); - } else { - var main = id( 'qunit-fixture' ); - if ( main ) { - main.innerHTML = config.fixture; - } - } - }, - - /** - * Trigger an event on an element. - * - * @example triggerEvent( document.body, "click" ); - * - * @param DOMElement elem - * @param String type - */ - triggerEvent: function( elem, type, event ) { - if ( document.createEvent ) { - event = document.createEvent("MouseEvents"); - event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView, - 0, 0, 0, 0, 0, false, false, false, false, 0, null); - elem.dispatchEvent( event ); - - } else if ( elem.fireEvent ) { - elem.fireEvent("on"+type); - } - }, - - // Safe object type checking - is: function( type, obj ) { - return QUnit.objectType( obj ) == type; - }, - - objectType: function( obj ) { - if (typeof obj === "undefined") { - return "undefined"; - - // consider: typeof null === object - } - if (obj === null) { - return "null"; - } - - var type = Object.prototype.toString.call( obj ) - .match(/^\[object\s(.*)\]$/)[1] || ''; - - switch (type) { - case 'Number': - if (isNaN(obj)) { - return "nan"; - } else { - return "number"; - } - case 'String': - case 'Boolean': - case 'Array': - case 'Date': - case 'RegExp': - case 'Function': - return type.toLowerCase(); - } - if (typeof obj === "object") { - return "object"; - } - return undefined; - }, - - push: function(result, actual, expected, message) { - var details = { - result: result, - message: message, - actual: actual, - expected: expected - }; - - message = escapeHtml(message) || (result ? "okay" : "failed"); - message = '' + message + ""; - expected = escapeHtml(QUnit.jsDump.parse(expected)); - actual = escapeHtml(QUnit.jsDump.parse(actual)); - var output = message + ''; - if (actual != expected) { - output += ''; - output += ''; - } - if (!result) { - var source = sourceFromStacktrace(); - if (source) { - details.source = source; - output += ''; - } - } - output += "
    Expected:
    ' + expected + '
    Result:
    ' + actual + '
    Diff:
    ' + QUnit.diff(expected, actual) +'
    Source:
    ' + source +'
    "; - - QUnit.log(details); - - config.current.assertions.push({ - result: !!result, - message: output - }); - }, - - url: function( params ) { - params = extend( extend( {}, QUnit.urlParams ), params ); - var querystring = "?", - key; - for ( key in params ) { - querystring += encodeURIComponent( key ) + "=" + - encodeURIComponent( params[ key ] ) + "&"; - } - return window.location.pathname + querystring.slice( 0, -1 ); - }, - - // Logging callbacks; all receive a single argument with the listed properties - // run test/logs.html for any related changes - begin: function() {}, - // done: { failed, passed, total, runtime } - done: function() {}, - // log: { result, actual, expected, message } - log: function() {}, - // testStart: { name } - testStart: function() {}, - // testDone: { name, failed, passed, total } - testDone: function() {}, - // moduleStart: { name } - moduleStart: function() {}, - // moduleDone: { name, failed, passed, total } - moduleDone: function() {} -}); - -if ( typeof document === "undefined" || document.readyState === "complete" ) { - config.autorun = true; -} - -addEvent(window, "load", function() { - QUnit.begin({}); - - // Initialize the config, saving the execution queue - var oldconfig = extend({}, config); - QUnit.init(); - extend(config, oldconfig); - - config.blocking = false; - - var userAgent = id("qunit-userAgent"); - if ( userAgent ) { - userAgent.innerHTML = navigator.userAgent; - } - var banner = id("qunit-header"); - if ( banner ) { - banner.innerHTML = ' ' + banner.innerHTML + ' ' + - '' + - ''; - addEvent( banner, "change", function( event ) { - var params = {}; - params[ event.target.name ] = event.target.checked ? true : undefined; - window.location = QUnit.url( params ); - }); - } - - var toolbar = id("qunit-testrunner-toolbar"); - if ( toolbar ) { - var filter = document.createElement("input"); - filter.type = "checkbox"; - filter.id = "qunit-filter-pass"; - addEvent( filter, "click", function() { - var ol = document.getElementById("qunit-tests"); - if ( filter.checked ) { - ol.className = ol.className + " hidepass"; - } else { - var tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " "; - ol.className = tmp.replace(/ hidepass /, " "); - } - if ( defined.sessionStorage ) { - if (filter.checked) { - sessionStorage.setItem("qunit-filter-passed-tests", "true"); - } else { - sessionStorage.removeItem("qunit-filter-passed-tests"); - } - } - }); - if ( defined.sessionStorage && sessionStorage.getItem("qunit-filter-passed-tests") ) { - filter.checked = true; - var ol = document.getElementById("qunit-tests"); - ol.className = ol.className + " hidepass"; - } - toolbar.appendChild( filter ); - - var label = document.createElement("label"); - label.setAttribute("for", "qunit-filter-pass"); - label.innerHTML = "Hide passed tests"; - toolbar.appendChild( label ); - } - - var main = id('qunit-fixture'); - if ( main ) { - config.fixture = main.innerHTML; - } - - if (config.autostart) { - QUnit.start(); - } -}); - -function done() { - config.autorun = true; - - // Log the last module results - if ( config.currentModule ) { - QUnit.moduleDone( { - name: config.currentModule, - failed: config.moduleStats.bad, - passed: config.moduleStats.all - config.moduleStats.bad, - total: config.moduleStats.all - } ); - } - - var banner = id("qunit-banner"), - tests = id("qunit-tests"), - runtime = +new Date - config.started, - passed = config.stats.all - config.stats.bad, - html = [ - 'Tests completed in ', - runtime, - ' milliseconds.
    ', - '', - passed, - ' tests of ', - config.stats.all, - ' passed, ', - config.stats.bad, - ' failed.' - ].join(''); - - if ( banner ) { - banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass"); - } - - if ( tests ) { - id( "qunit-testresult" ).innerHTML = html; - } - - if ( typeof document !== "undefined" && document.title ) { - // show ✖ for good, ✔ for bad suite result in title - // use escape sequences in case file gets loaded with non-utf-8-charset - document.title = (config.stats.bad ? "\u2716" : "\u2714") + " " + document.title; - } - - QUnit.done( { - failed: config.stats.bad, - passed: passed, - total: config.stats.all, - runtime: runtime - } ); -} - -function validTest( name ) { - var filter = config.filter, - run = false; - - if ( !filter ) { - return true; - } - - var not = filter.charAt( 0 ) === "!"; - if ( not ) { - filter = filter.slice( 1 ); - } - - if ( name.indexOf( filter ) !== -1 ) { - return !not; - } - - if ( not ) { - run = true; - } - - return run; -} - -// so far supports only Firefox, Chrome and Opera (buggy) -// could be extended in the future to use something like https://github.com/csnover/TraceKit -function sourceFromStacktrace() { - try { - throw new Error(); - } catch ( e ) { - if (e.stacktrace) { - // Opera - return e.stacktrace.split("\n")[6]; - } else if (e.stack) { - // Firefox, Chrome - return e.stack.split("\n")[4]; - } - } -} - -function escapeHtml(s) { - if (!s) { - return ""; - } - s = s + ""; - return s.replace(/[\&"<>\\]/g, function(s) { - switch(s) { - case "&": return "&"; - case "\\": return "\\\\"; - case '"': return '\"'; - case "<": return "<"; - case ">": return ">"; - default: return s; - } - }); -} - -function synchronize( callback ) { - config.queue.push( callback ); - - if ( config.autorun && !config.blocking ) { - process(); - } -} - -function process() { - var start = (new Date()).getTime(); - - while ( config.queue.length && !config.blocking ) { - if ( config.updateRate <= 0 || (((new Date()).getTime() - start) < config.updateRate) ) { - config.queue.shift()(); - } else { - window.setTimeout( process, 13 ); - break; - } - } - if (!config.blocking && !config.queue.length) { - done(); - } -} - -function saveGlobal() { - config.pollution = []; - - if ( config.noglobals ) { - for ( var key in window ) { - config.pollution.push( key ); - } - } -} - -function checkPollution( name ) { - var old = config.pollution; - saveGlobal(); - - var newGlobals = diff( config.pollution, old ); - if ( newGlobals.length > 0 ) { - ok( false, "Introduced global variable(s): " + newGlobals.join(", ") ); - } - - var deletedGlobals = diff( old, config.pollution ); - if ( deletedGlobals.length > 0 ) { - ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") ); - } -} - -// returns a new Array with the elements that are in a but not in b -function diff( a, b ) { - var result = a.slice(); - for ( var i = 0; i < result.length; i++ ) { - for ( var j = 0; j < b.length; j++ ) { - if ( result[i] === b[j] ) { - result.splice(i, 1); - i--; - break; - } - } - } - return result; -} - -function fail(message, exception, callback) { - if ( typeof console !== "undefined" && console.error && console.warn ) { - console.error(message); - console.error(exception); - console.warn(callback.toString()); - - } else if ( window.opera && opera.postError ) { - opera.postError(message, exception, callback.toString); - } -} - -function extend(a, b) { - for ( var prop in b ) { - if ( b[prop] === undefined ) { - delete a[prop]; - } else { - a[prop] = b[prop]; - } - } - - return a; -} - -function addEvent(elem, type, fn) { - if ( elem.addEventListener ) { - elem.addEventListener( type, fn, false ); - } else if ( elem.attachEvent ) { - elem.attachEvent( "on" + type, fn ); - } else { - fn(); - } -} - -function id(name) { - return !!(typeof document !== "undefined" && document && document.getElementById) && - document.getElementById( name ); -} - -// Test for equality any JavaScript type. -// Discussions and reference: http://philrathe.com/articles/equiv -// Test suites: http://philrathe.com/tests/equiv -// Author: Philippe Rathé -QUnit.equiv = function () { - - var innerEquiv; // the real equiv function - var callers = []; // stack to decide between skip/abort functions - var parents = []; // stack to avoiding loops from circular referencing - - // Call the o related callback with the given arguments. - function bindCallbacks(o, callbacks, args) { - var prop = QUnit.objectType(o); - if (prop) { - if (QUnit.objectType(callbacks[prop]) === "function") { - return callbacks[prop].apply(callbacks, args); - } else { - return callbacks[prop]; // or undefined - } - } - } - - var callbacks = function () { - - // for string, boolean, number and null - function useStrictEquality(b, a) { - if (b instanceof a.constructor || a instanceof b.constructor) { - // to catch short annotaion VS 'new' annotation of a declaration - // e.g. var i = 1; - // var j = new Number(1); - return a == b; - } else { - return a === b; - } - } - - return { - "string": useStrictEquality, - "boolean": useStrictEquality, - "number": useStrictEquality, - "null": useStrictEquality, - "undefined": useStrictEquality, - - "nan": function (b) { - return isNaN(b); - }, - - "date": function (b, a) { - return QUnit.objectType(b) === "date" && a.valueOf() === b.valueOf(); - }, - - "regexp": function (b, a) { - return QUnit.objectType(b) === "regexp" && - a.source === b.source && // the regex itself - a.global === b.global && // and its modifers (gmi) ... - a.ignoreCase === b.ignoreCase && - a.multiline === b.multiline; - }, - - // - skip when the property is a method of an instance (OOP) - // - abort otherwise, - // initial === would have catch identical references anyway - "function": function () { - var caller = callers[callers.length - 1]; - return caller !== Object && - typeof caller !== "undefined"; - }, - - "array": function (b, a) { - var i, j, loop; - var len; - - // b could be an object literal here - if ( ! (QUnit.objectType(b) === "array")) { - return false; - } - - len = a.length; - if (len !== b.length) { // safe and faster - return false; - } - - //track reference to avoid circular references - parents.push(a); - for (i = 0; i < len; i++) { - loop = false; - for(j=0;j= 0) { - type = "array"; - } else { - type = typeof obj; - } - return type; - }, - separator:function() { - return this.multiline ? this.HTML ? '
    ' : '\n' : this.HTML ? ' ' : ' '; - }, - indent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing - if ( !this.multiline ) - return ''; - var chr = this.indentChar; - if ( this.HTML ) - chr = chr.replace(/\t/g,' ').replace(/ /g,' '); - return Array( this._depth_ + (extra||0) ).join(chr); - }, - up:function( a ) { - this._depth_ += a || 1; - }, - down:function( a ) { - this._depth_ -= a || 1; - }, - setParser:function( name, parser ) { - this.parsers[name] = parser; - }, - // The next 3 are exposed so you can use them - quote:quote, - literal:literal, - join:join, - // - _depth_: 1, - // This is the list of parsers, to modify them, use jsDump.setParser - parsers:{ - window: '[Window]', - document: '[Document]', - error:'[ERROR]', //when no parser is found, shouldn't happen - unknown: '[Unknown]', - 'null':'null', - 'undefined':'undefined', - 'function':function( fn ) { - var ret = 'function', - name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE - if ( name ) - ret += ' ' + name; - ret += '('; - - ret = [ ret, QUnit.jsDump.parse( fn, 'functionArgs' ), '){'].join(''); - return join( ret, QUnit.jsDump.parse(fn,'functionCode'), '}' ); - }, - array: array, - nodelist: array, - arguments: array, - object:function( map ) { - var ret = [ ]; - QUnit.jsDump.up(); - for ( var key in map ) - ret.push( QUnit.jsDump.parse(key,'key') + ': ' + QUnit.jsDump.parse(map[key]) ); - QUnit.jsDump.down(); - return join( '{', ret, '}' ); - }, - node:function( node ) { - var open = QUnit.jsDump.HTML ? '<' : '<', - close = QUnit.jsDump.HTML ? '>' : '>'; - - var tag = node.nodeName.toLowerCase(), - ret = open + tag; - - for ( var a in QUnit.jsDump.DOMAttrs ) { - var val = node[QUnit.jsDump.DOMAttrs[a]]; - if ( val ) - ret += ' ' + a + '=' + QUnit.jsDump.parse( val, 'attribute' ); - } - return ret + close + open + '/' + tag + close; - }, - functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function - var l = fn.length; - if ( !l ) return ''; - - var args = Array(l); - while ( l-- ) - args[l] = String.fromCharCode(97+l);//97 is 'a' - return ' ' + args.join(', ') + ' '; - }, - key:quote, //object calls it internally, the key part of an item in a map - functionCode:'[code]', //function calls it internally, it's the content of the function - attribute:quote, //node calls it internally, it's an html attribute value - string:quote, - date:quote, - regexp:literal, //regex - number:literal, - 'boolean':literal - }, - DOMAttrs:{//attributes to dump from nodes, name=>realName - id:'id', - name:'name', - 'class':'className' - }, - HTML:false,//if true, entities are escaped ( <, >, \t, space and \n ) - indentChar:' ',//indentation unit - multiline:true //if true, items in a collection, are separated by a \n, else just a space. - }; - - return jsDump; -})(); - -// from Sizzle.js -function getText( elems ) { - var ret = "", elem; - - for ( var i = 0; elems[i]; i++ ) { - elem = elems[i]; - - // Get the text from text nodes and CDATA nodes - if ( elem.nodeType === 3 || elem.nodeType === 4 ) { - ret += elem.nodeValue; - - // Traverse everything else, except comment nodes - } else if ( elem.nodeType !== 8 ) { - ret += getText( elem.childNodes ); - } - } - - return ret; -}; - -/* - * Javascript Diff Algorithm - * By John Resig (http://ejohn.org/) - * Modified by Chu Alan "sprite" - * - * Released under the MIT license. - * - * More Info: - * http://ejohn.org/projects/javascript-diff-algorithm/ - * - * Usage: QUnit.diff(expected, actual) - * - * QUnit.diff("the quick brown fox jumped over", "the quick fox jumps over") == "the quick brown fox jumped jumps over" - */ -QUnit.diff = (function() { - function diff(o, n){ - var ns = new Object(); - var os = new Object(); - - for (var i = 0; i < n.length; i++) { - if (ns[n[i]] == null) - ns[n[i]] = { - rows: new Array(), - o: null - }; - ns[n[i]].rows.push(i); - } - - for (var i = 0; i < o.length; i++) { - if (os[o[i]] == null) - os[o[i]] = { - rows: new Array(), - n: null - }; - os[o[i]].rows.push(i); - } - - for (var i in ns) { - if (ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1) { - n[ns[i].rows[0]] = { - text: n[ns[i].rows[0]], - row: os[i].rows[0] - }; - o[os[i].rows[0]] = { - text: o[os[i].rows[0]], - row: ns[i].rows[0] - }; - } - } - - for (var i = 0; i < n.length - 1; i++) { - if (n[i].text != null && n[i + 1].text == null && n[i].row + 1 < o.length && o[n[i].row + 1].text == null && - n[i + 1] == o[n[i].row + 1]) { - n[i + 1] = { - text: n[i + 1], - row: n[i].row + 1 - }; - o[n[i].row + 1] = { - text: o[n[i].row + 1], - row: i + 1 - }; - } - } - - for (var i = n.length - 1; i > 0; i--) { - if (n[i].text != null && n[i - 1].text == null && n[i].row > 0 && o[n[i].row - 1].text == null && - n[i - 1] == o[n[i].row - 1]) { - n[i - 1] = { - text: n[i - 1], - row: n[i].row - 1 - }; - o[n[i].row - 1] = { - text: o[n[i].row - 1], - row: i - 1 - }; - } - } - - return { - o: o, - n: n - }; - } - - return function(o, n){ - o = o.replace(/\s+$/, ''); - n = n.replace(/\s+$/, ''); - var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/)); - - var str = ""; - - var oSpace = o.match(/\s+/g); - if (oSpace == null) { - oSpace = [" "]; - } - else { - oSpace.push(" "); - } - var nSpace = n.match(/\s+/g); - if (nSpace == null) { - nSpace = [" "]; - } - else { - nSpace.push(" "); - } - - if (out.n.length == 0) { - for (var i = 0; i < out.o.length; i++) { - str += '' + out.o[i] + oSpace[i] + ""; - } - } - else { - if (out.n[0].text == null) { - for (n = 0; n < out.o.length && out.o[n].text == null; n++) { - str += '' + out.o[n] + oSpace[n] + ""; - } - } - - for (var i = 0; i < out.n.length; i++) { - if (out.n[i].text == null) { - str += '' + out.n[i] + nSpace[i] + ""; - } - else { - var pre = ""; - - for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++) { - pre += '' + out.o[n] + oSpace[n] + ""; - } - str += " " + out.n[i].text + nSpace[i] + pre; - } - } - } - - return str; - }; -})(); - -})(this); \ No newline at end of file diff --git a/README.md b/README.md index 0045455c..3c2b0b3f 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase - Resolved transaction problem after window.location (page) change with possible data loss ref: [litehelpers/Cordova-sqlite-storage#666](https://github.com/litehelpers/Cordova-sqlite-storage/issues/666) - [brodybits / cordova-sqlite-test-app](https://github.com/brodybits/cordova-sqlite-test-app) project is a CC0 (public domain) starting point (NOTE that this plugin must be added) and may also be used to reproduce issues with this plugin. -- The Lawnchair adapter is now supported in [litehelpers / cordova-sqlite-lawnchair-adapter](https://github.com/litehelpers/cordova-sqlite-lawnchair-adapter). +- The Lawnchair adapter is now moved to [litehelpers / cordova-sqlite-lawnchair-adapter](https://github.com/litehelpers/cordova-sqlite-lawnchair-adapter). - [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) now supports SELECT BLOB data in Base64 format on all platforms in addition to REGEXP (Android/iOS/macOS) and pre-populated database (all platforms). - [brodybits / sql-promise-helper](https://github.com/brodybits/sql-promise-helper) provides a Promise-based API wrapper. - [nolanlawson / pouchdb-adapter-cordova-sqlite](https://github.com/nolanlawson/pouchdb-adapter-cordova-sqlite) supports this plugin along with other implementations such as [nolanlawson / sqlite-plugin-2](https://github.com/nolanlawson/sqlite-plugin-2) and [Microsoft / cordova-plugin-websql](https://github.com/Microsoft/cordova-plugin-websql). @@ -775,7 +775,6 @@ cordova platform add ios - `src`: platform-specific source code - `spec`: test suite using Jasmine (2.2.0) - `tests`: very simple Jasmine test suite that is run on Circle CI (Android version) and Travis CI (iOS version) (used as a placeholder) -- `Lawnchair-adapter`: Lawnchair adaptor, based on the version from the Lawnchair repository, with the basic Lawnchair test suite in `test-www` subdirectory ## Installation test @@ -895,43 +894,7 @@ To run from a windows powershell (here is a sample for android target): ## Lawnchair Adapter -**BROKEN:** The Lawnchair adapter does not support the `openDatabase` options such as `location` or `iosDatabaseLocation` options and is therefore *not* expected to work with this plugin. - -NOW SUPPORTED IN: [litehelpers / cordova-sqlite-lawnchair-adapter](https://github.com/litehelpers/cordova-sqlite-lawnchair-adapter) - -### Common adapter - -Please look at the `Lawnchair-adapter` tree that contains a common adapter, which should also work with the Android version, along with a test-www directory. - -### Included files - -Include the following Javascript files in your HTML: - -- `cordova.js` (don't forget!) -- `lawnchair.js` (you provide) -- `SQLitePlugin.js` (in case of Cordova pre-3.0) -- `Lawnchair-sqlitePlugin.js` (must come after `SQLitePlugin.js` in case of Cordova pre-3.0) - -### Sample - -The `name` option determines the sqlite database filename, *with no extension automatically added*. Optionally, you can change the db filename using the `db` option. - -In this example, you would be using/creating a database with filename `kvstore`: - -```Javascript -kvstore = new Lawnchair({name: "kvstore"}, function() { - // do stuff -); -``` - -Using the `db` option you can specify the filename with the desired extension and be able to create multiple stores in the same database file. (There will be one table per store.) - -```Javascript -recipes = new Lawnchair({db: "cookbook", name: "recipes", ...}, myCallback()); -ingredients = new Lawnchair({db: "cookbook", name: "ingredients", ...}, myCallback()); -``` - -**KNOWN ISSUE:** the new db options are *not* supported by the Lawnchair adapter. The workaround is to first open the database file using `sqlitePlugin.openDatabase()`. +- [litehelpers / cordova-sqlite-lawnchair-adapter](https://github.com/litehelpers/cordova-sqlite-lawnchair-adapter) ## PouchDB diff --git a/package.json b/package.json index 04e2417d..15c41957 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cordova-sqlite-legacy-express-core", - "version": "1.0.0-pre3", + "version": "1.0.0-pre4", "description": "Native interface to SQLite for PhoneGap/Cordova (legacy express core version)", "cordova": { "id": "cordova-sqlite-legacy-express-core", diff --git a/plugin.xml b/plugin.xml index 386d19d1..da9c2ced 100644 --- a/plugin.xml +++ b/plugin.xml @@ -2,7 +2,7 @@ + version="1.0.0-pre4"> Cordova sqlite storage plugin - legacy express core version From df326d4c78a761f5eca6a626289e257281a303c4 Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Wed, 26 Apr 2017 07:27:41 +0200 Subject: [PATCH 06/47] Some general test updates --- spec/www/index.html | 2 - spec/www/spec/basic-misc-test.js | 34 +++++++----- .../spec/db-simultaneous-tx-access-test.js | 21 ++++---- spec/www/spec/db-tx-sql-results.js | 19 ++++--- spec/www/spec/db-tx-string-test.js | 50 +++++++++--------- spec/www/spec/db-tx-value-bindings-test.js | 34 ++++++------ spec/www/spec/ext-tx-blob-test.js | 28 +++++----- spec/www/spec/misc-tx-legacy.js | 52 +++++++++++-------- spec/www/spec/regexp-test.js | 20 +++---- spec/www/spec/sql-batch-test.js | 24 ++++----- spec/www/spec/tx-semantics-test.js | 23 ++++---- 11 files changed, 162 insertions(+), 145 deletions(-) diff --git a/spec/www/index.html b/spec/www/index.html index aa9f660b..d84a43e6 100644 --- a/spec/www/index.html +++ b/spec/www/index.html @@ -15,8 +15,6 @@ - - diff --git a/spec/www/spec/basic-misc-test.js b/spec/www/spec/basic-misc-test.js index 2f941870..acb82048 100644 --- a/spec/www/spec/basic-misc-test.js +++ b/spec/www/spec/basic-misc-test.js @@ -8,9 +8,10 @@ var isWP8 = /IEMobile/.test(navigator.userAgent); // Matches WP(7/8/8.1) var isWindows = /Windows /.test(navigator.userAgent); // Windows var isAndroid = !isWindows && /Android/.test(navigator.userAgent); -// NOTE: In the core-master branch there is no difference between the default -// implementation and implementation #2. But the test will also apply -// the androidLockWorkaround: 1 option in the case of implementation #2. +// NOTE: While in certain version branches there is no difference between +// the default Android implementation and implementation #2, +// this test script will also apply the androidLockWorkaround: 1 option +// in case of implementation #2. var scenarioList = [ isAndroid ? 'Plugin-implementation-default' : 'Plugin', 'HTML5', @@ -19,7 +20,8 @@ var scenarioList = [ var scenarioCount = (!!window.hasWebKitBrowser) ? (isAndroid ? 3 : 2) : 1; -// XXX FUTURE TBD: split this into db feature & tx error handling test scripts +// FUTURE TBD (already done in newer version branches): +// Split this into db feature & tx error handling test scripts var mytests = function() { @@ -29,23 +31,25 @@ var mytests = function() { var scenarioName = scenarioList[i]; var suiteName = scenarioName + ': '; var isWebSql = (i === 1); - var isOldImpl = (i === 2); + var isImpl2 = (i === 2); // NOTE: MUST be defined in function scope, NOT outer scope: var openDatabase = function(name, ignored1, ignored2, ignored3) { - if (isOldImpl) { + if (isImpl2) { return window.sqlitePlugin.openDatabase({ // prevent reuse of database from default db implementation: name: 'i2-'+name, + // explicit database location: + location: 'default', androidDatabaseImplementation: 2, - androidLockWorkaround: 1, - location: 1 + androidLockWorkaround: 1 }); } if (isWebSql) { return window.openDatabase(name, '1.0', 'Test', DEFAULT_SIZE); } else { - return window.sqlitePlugin.openDatabase({name: name, location: 0}); + // explicit database location: + return window.sqlitePlugin.openDatabase({name: name, location: 'default'}); } } @@ -307,23 +311,25 @@ var mytests = function() { var scenarioName = scenarioList[i]; var suiteName = scenarioName + ': '; var isWebSql = (i === 1); - var isOldImpl = (i === 2); + var isImpl2 = (i === 2); // NOTE: MUST be defined in function scope, NOT outer scope: var openDatabase = function(name, ignored1, ignored2, ignored3) { - if (isOldImpl) { + if (isImpl2) { return window.sqlitePlugin.openDatabase({ // prevent reuse of database from default db implementation: name: 'i2-'+name, + // explicit database location: + location: 'default', androidDatabaseImplementation: 2, - androidLockWorkaround: 1, - location: 1 + androidLockWorkaround: 1 }); } if (isWebSql) { return window.openDatabase(name, '1.0', 'Test', DEFAULT_SIZE); } else { - return window.sqlitePlugin.openDatabase({name: name, location: 0}); + // explicit database location: + return window.sqlitePlugin.openDatabase({name: name, location: 'default'}); } } diff --git a/spec/www/spec/db-simultaneous-tx-access-test.js b/spec/www/spec/db-simultaneous-tx-access-test.js index 2c8fdd8f..929ef775 100755 --- a/spec/www/spec/db-simultaneous-tx-access-test.js +++ b/spec/www/spec/db-simultaneous-tx-access-test.js @@ -32,9 +32,10 @@ function start(n) { var isAndroid = /Android/.test(navigator.userAgent); -// NOTE: In the core-master branch there is no difference between the default -// implementation and implementation #2. But the test will also apply -// the androidLockWorkaround: 1 option in the case of implementation #2. +// NOTE: While in certain version branches there is no difference between +// the default Android implementation and implementation #2, +// this test script will also apply the androidLockWorkaround: 1 option +// in case of implementation #2. var scenarioList = [ isAndroid ? 'Plugin-implementation-default' : 'Plugin', 'HTML5', @@ -51,23 +52,25 @@ var mytests = function() { var scenarioName = scenarioList[i]; var suiteName = scenarioName + ': '; var isWebSql = (i === 1); - var isOldImpl = (i === 2); + var isImpl2 = (i === 2); // NOTE: MUST be defined in function scope, NOT outer scope: var openDatabase = function(name, ignored1, ignored2, ignored3) { - if (isOldImpl) { + if (isImpl2) { return window.sqlitePlugin.openDatabase({ // prevent reuse of database from default db implementation: name: 'i2-'+name, + // explicit database location: + location: 'default', androidDatabaseImplementation: 2, - androidLockWorkaround: 1, - location: 1 + androidLockWorkaround: 1 }); } if (isWebSql) { - return window.openDatabase(name, "1.0", "Demo", DEFAULT_SIZE); + return window.openDatabase(name, '1.0', 'Test', DEFAULT_SIZE); } else { - return window.sqlitePlugin.openDatabase({name: name, location: 0}); + // explicit database location: + return window.sqlitePlugin.openDatabase({name: name, location: 'default'}); } } diff --git a/spec/www/spec/db-tx-sql-results.js b/spec/www/spec/db-tx-sql-results.js index c89bb567..71182a82 100644 --- a/spec/www/spec/db-tx-sql-results.js +++ b/spec/www/spec/db-tx-sql-results.js @@ -8,9 +8,10 @@ var isWP8 = /IEMobile/.test(navigator.userAgent); // Matches WP(7/8/8.1) var isWindows = /Windows /.test(navigator.userAgent); // Windows var isAndroid = !isWindows && /Android/.test(navigator.userAgent); -// NOTE: In the core-master branch there is no difference between the default -// implementation and implementation #2. But the test will also apply -// the androidLockWorkaround: 1 option in the case of implementation #2. +// NOTE: While in certain version branches there is no difference between +// the default Android implementation and implementation #2, +// this test script will also apply the androidLockWorkaround: 1 option +// in case of implementation #2. var scenarioList = [ isAndroid ? 'Plugin-implementation-default' : 'Plugin', 'HTML5', @@ -27,23 +28,25 @@ var mytests = function() { var scenarioName = scenarioList[i]; var suiteName = scenarioName + ': '; var isWebSql = (i === 1); - var isOldImpl = (i === 2); + var isImpl2 = (i === 2); // NOTE: MUST be defined in function scope, NOT outer scope: var openDatabase = function(name, ignored1, ignored2, ignored3) { - if (isOldImpl) { + if (isImpl2) { return window.sqlitePlugin.openDatabase({ // prevent reuse of database from default db implementation: name: 'i2-'+name, + // explicit database location: + location: 'default', androidDatabaseImplementation: 2, - androidLockWorkaround: 1, - location: 1 + androidLockWorkaround: 1 }); } if (isWebSql) { return window.openDatabase(name, '1.0', 'Test', DEFAULT_SIZE); } else { - return window.sqlitePlugin.openDatabase({name: name, location: 0}); + // explicit database location: + return window.sqlitePlugin.openDatabase({name: name, location: 'default'}); } } diff --git a/spec/www/spec/db-tx-string-test.js b/spec/www/spec/db-tx-string-test.js index 32848b46..c4768269 100755 --- a/spec/www/spec/db-tx-string-test.js +++ b/spec/www/spec/db-tx-string-test.js @@ -10,17 +10,12 @@ function equal(a, b, desc) { expect(a).toEqual(b); } // '==' var isAndroid = /Android/.test(navigator.userAgent); var isWP8 = /IEMobile/.test(navigator.userAgent); // Matches WP(7/8/8.1) -//var isWindows = /Windows NT/.test(navigator.userAgent); // Windows [NT] (8.1) var isWindows = /Windows /.test(navigator.userAgent); // Windows (8.1) -//var isWindowsPC = /Windows NT/.test(navigator.userAgent); // Windows [NT] (8.1) -//var isWindowsPhone_8_1 = /Windows Phone 8.1/.test(navigator.userAgent); // Windows Phone 8.1 -//var isIE = isWindows || isWP8 || isWindowsPhone_8_1; -var isIE = isWindows || isWP8; -var isWebKit = !isIE; // TBD [Android or iOS] - -// NOTE: In the core-master branch there is no difference between the default -// implementation and implementation #2. But the test will also apply -// the androidLockWorkaround: 1 option in the case of implementation #2. + +// NOTE: While in certain version branches there is no difference between +// the default Android implementation and implementation #2, +// this test script will also apply the androidLockWorkaround: 1 option +// in case of implementation #2. var scenarioList = [ isAndroid ? 'Plugin-implementation-default' : 'Plugin', 'HTML5', @@ -37,23 +32,25 @@ var mytests = function() { var scenarioName = scenarioList[i]; var suiteName = scenarioName + ': '; var isWebSql = (i === 1); - var isOldImpl = (i === 2); + var isImpl2 = (i === 2); // NOTE: MUST be defined in function scope, NOT outer scope: var openDatabase = function(name, ignored1, ignored2, ignored3) { - if (isOldImpl) { + if (isImpl2) { return window.sqlitePlugin.openDatabase({ // prevent reuse of database from default db implementation: name: 'i2-'+name, + // explicit database location: + location: 'default', androidDatabaseImplementation: 2, - androidLockWorkaround: 1, - location: 1 + androidLockWorkaround: 1 }); } if (isWebSql) { - return window.openDatabase(name, "1.0", "Demo", DEFAULT_SIZE); + return window.openDatabase(name, '1.0', 'Test', DEFAULT_SIZE); } else { - return window.sqlitePlugin.openDatabase({name: name, location: 0}); + // explicit database location: + return window.sqlitePlugin.openDatabase({name: name, location: 'default'}); } } @@ -79,9 +76,10 @@ var mytests = function() { }); it(suiteName + ' string encoding test with UNICODE \\u0000', function (done) { - if (isWindows) pending('BROKEN for Windows'); // XXX - if (isWP8) pending('BROKEN for WP(8)'); // [BUG #202] UNICODE characters not working with WP(8) - if (isAndroid && !isWebSql && !isOldImpl) pending('BROKEN for Android (default sqlite-connector version)'); // XXX + if (isWP8) pending('BROKEN on WP(8)'); // [BUG #202] UNICODE characters not working with WP(8) + if (isWindows) pending('BROKEN on Windows'); // TBD (truncates on Windows) + // TBD NOT BROKEN for Android (no Android-sqlite-connector implementation) in this version branch: + //if (!isWebSql && !isWindows && isAndroid && !isImpl2) pending('BROKEN on Android-sqlite-connector implementation)'); var dbName = "Unicode-hex-test"; var db = openDatabase(dbName, "1.0", "Demo", DEFAULT_SIZE); @@ -153,7 +151,7 @@ var mytests = function() { }); it(suiteName + "String vertical tab test", function(done) { - if (isWP8) pending('BROKEN for WP(8)'); // [BUG #202] UNICODE characters not working with WP(8) + if (isWP8) pending('BROKEN on WP(8)'); // [BUG #202] UNICODE characters not working with WP(8) var db = openDatabase("String-vertical-tab-test.db", "1.0", "Demo", DEFAULT_SIZE); expect(db).toBeDefined(); @@ -169,7 +167,7 @@ var mytests = function() { }); it(suiteName + "String form feed test", function(done) { - if (isWP8) pending('BROKEN for WP(8)'); // [BUG #202] UNICODE characters not working with WP(8) + if (isWP8) pending('BROKEN on WP(8)'); // [BUG #202] UNICODE characters not working with WP(8) var db = openDatabase("String-form-feed-test.db", "1.0", "Demo", DEFAULT_SIZE); expect(db).toBeDefined(); @@ -185,7 +183,7 @@ var mytests = function() { }); it(suiteName + "String backspace test", function(done) { - if (isWP8) pending('BROKEN for WP(8)'); // [BUG #202] UNICODE characters not working with WP(8) + if (isWP8) pending('BROKEN on WP(8)'); // [BUG #202] UNICODE characters not working with WP(8) var db = openDatabase("String-backspace-test.db", "1.0", "Demo", DEFAULT_SIZE); expect(db).toBeDefined(); @@ -208,7 +206,7 @@ var mytests = function() { // - Apache Cordova CB-9435 (issue with cordova-ios, also affects macOS) // - cordova/cordova-discuss#57 (issue with cordova-android) it(suiteName + "UNICODE \\u2028 line separator string length", function(done) { - if (isWP8) pending('BROKEN for WP(8)'); // [BUG #202] Certain UNICODE characters not working with WP(8) + if (isWP8) pending('BROKEN on WP(8)'); // [BUG #202] Certain UNICODE characters not working with WP(8) // NOTE: this test verifies that the UNICODE line separator (\u2028) // is seen by the sqlite implementation OK: @@ -228,7 +226,7 @@ var mytests = function() { }); it(suiteName + ' handles UNICODE \\u2028 line separator correctly [string test]', function (done) { - if (isWP8) pending('BROKEN for WP(8)'); // [BUG #202] UNICODE characters not working with WP(8) + if (isWP8) pending('BROKEN on WP(8)'); // [BUG #202] UNICODE characters not working with WP(8) if (!isWebSql && !isWindows && isAndroid) pending('SKIP for Android plugin (cordova-android 6.x BUG: cordova/cordova-discuss#57)'); if (!isWebSql && !isWindows && !isAndroid && !isWP8) pending('SKIP for iOS/macOS plugin (Cordova BUG: CB-9435)'); @@ -260,7 +258,7 @@ var mytests = function() { // - Apache Cordova CB-9435 (issue with cordova-ios, also affects macOS) // - cordova/cordova-discuss#57 (issue with cordova-android) it(suiteName + "UNICODE \\u2029 line separator string length", function(done) { - if (isWP8) pending('BROKEN for WP(8)'); // [BUG #202] Certain UNICODE characters not working with WP(8) + if (isWP8) pending('BROKEN on WP(8)'); // [BUG #202] Certain UNICODE characters not working with WP(8) // NOTE: this test verifies that the UNICODE paragraph separator (\u2029) // is seen by the sqlite implementation OK: @@ -281,7 +279,7 @@ var mytests = function() { }); it(suiteName + ' handles UNICODE \\u2029 line separator correctly [string test]', function (done) { - if (isWP8) pending('BROKEN for WP(8)'); // [BUG #202] UNICODE characters not working with WP(8) + if (isWP8) pending('BROKEN on WP(8)'); // [BUG #202] UNICODE characters not working with WP(8) if (!isWebSql && !isWindows && isAndroid) pending('SKIP for Android plugin (cordova-android 6.x BUG: cordova/cordova-discuss#57)'); if (!isWebSql && !isWindows && !isAndroid && !isWP8) pending('SKIP for iOS/macOS plugin (Cordova BUG: CB-9435)'); diff --git a/spec/www/spec/db-tx-value-bindings-test.js b/spec/www/spec/db-tx-value-bindings-test.js index 674d7e88..23a760b5 100755 --- a/spec/www/spec/db-tx-value-bindings-test.js +++ b/spec/www/spec/db-tx-value-bindings-test.js @@ -36,9 +36,10 @@ var isAndroid = !isWindows && /Android/.test(navigator.userAgent); var isMac = /Macintosh/.test(navigator.userAgent); var isWKWebView = !isWindows && !isAndroid && !isWP8 && !isMac && !!window.webkit && !!window.webkit.messageHandlers; -// NOTE: In the core-master branch there is no difference between the default -// implementation and implementation #2. But the test will also apply -// the androidLockWorkaround: 1 option in the case of implementation #2. +// NOTE: While in certain version branches there is no difference between +// the default Android implementation and implementation #2, +// this test script will also apply the androidLockWorkaround: 1 option +// in case of implementation #2. var scenarioList = [ isAndroid ? 'Plugin-implementation-default' : 'Plugin', 'HTML5', @@ -55,23 +56,25 @@ var mytests = function() { var scenarioName = scenarioList[i]; var suiteName = scenarioName + ': '; var isWebSql = (i === 1); - var isOldImpl = (i === 2); + var isImpl2 = (i === 2); // NOTE: MUST be defined in function scope, NOT outer scope: var openDatabase = function(name, ignored1, ignored2, ignored3) { - if (isOldImpl) { + if (isImpl2) { return window.sqlitePlugin.openDatabase({ // prevent reuse of database from default db implementation: name: 'i2-'+name, + // explicit database location: + location: 'default', androidDatabaseImplementation: 2, - androidLockWorkaround: 1, - location: 1 + androidLockWorkaround: 1 }); } if (isWebSql) { - return window.openDatabase(name, "1.0", "Demo", DEFAULT_SIZE); + return window.openDatabase(name, '1.0', 'Test', DEFAULT_SIZE); } else { - return window.sqlitePlugin.openDatabase({name: name, location: 0}); + // explicit database location: + return window.sqlitePlugin.openDatabase({name: name, location: 'default'}); } } @@ -155,7 +158,7 @@ var mytests = function() { }); it(suiteName + "Big [integer] value bindings", function(done) { - if (isWP8) pending('BROKEN for WP(8)'); // XXX [BUG #195] + if (isWP8) pending('BROKEN on WP(8)'); // XXX [BUG #195] var db = openDatabase("Big-int-bindings.db", "1.0", "Demo", DEFAULT_SIZE); db.transaction(function(tx) { @@ -249,9 +252,10 @@ var mytests = function() { // FUTURE TODO: fix these tests to follow the Jasmine style: test_it(suiteName + ' stores [Unicode] string with \\u0000 correctly', function () { - if (isWindows) pending('BROKEN on Windows'); // XXX - if (isWP8) pending('BROKEN for WP(8)'); // [BUG #202] UNICODE characters not working with WP(8) - if (isAndroid && !isWebSql && !isOldImpl) pending('BROKEN for Android (default sqlite-connector version)'); // XXX + if (isWP8) pending('BROKEN on WP(8)'); // [BUG #202] UNICODE characters not working with WP(8) + if (isWindows) pending('BROKEN on Windows'); // TBD (truncates on Windows) + // TBD NOT BROKEN for Android (no Android-sqlite-connector implementation) in this version branch: + //if (!isWebSql && !isWindows && isAndroid && !isImpl2) pending('BROKEN on Android-sqlite-connector implementation)'); stop(); @@ -318,7 +322,7 @@ var mytests = function() { test_it(suiteName + ' returns [Unicode] string with \\u0000 correctly', function () { if (isWindows) pending('BROKEN on Windows'); // XXX - if (isWP8) pending('BROKEN for WP(8)'); // [BUG #202] UNICODE characters not working with WP(8) + if (isWP8) pending('BROKEN on WP(8)'); // [BUG #202] UNICODE characters not working with WP(8) stop(); @@ -380,7 +384,7 @@ var mytests = function() { // - cordova/cordova-discuss#57 (issue with cordova-android) test_it(suiteName + ' handles UNICODE \\u2028 line separator correctly [in database]', function () { - if (isWP8) pending('BROKEN for WP(8)'); // [BUG #202] UNICODE characters not working with WP(8) + if (isWP8) pending('BROKEN on WP(8)'); // [BUG #202] UNICODE characters not working with WP(8) if (!isWebSql && !isWindows && isAndroid) pending('SKIP for Android plugin (cordova-android 6.x BUG: cordova/cordova-discuss#57)'); if (!isWebSql && !isWindows && !isAndroid && !isWP8) pending('SKIP for iOS/macOS plugin (Cordova BUG: CB-9435)'); diff --git a/spec/www/spec/ext-tx-blob-test.js b/spec/www/spec/ext-tx-blob-test.js index a88cb264..1d78694b 100755 --- a/spec/www/spec/ext-tx-blob-test.js +++ b/spec/www/spec/ext-tx-blob-test.js @@ -9,17 +9,12 @@ function ok(test, desc) { expect(test).toBe(true); } var isAndroid = /Android/.test(navigator.userAgent); var isWP8 = /IEMobile/.test(navigator.userAgent); // Matches WP(7/8/8.1) -//var isWindows = /Windows NT/.test(navigator.userAgent); // Windows [NT] (8.1) var isWindows = /Windows /.test(navigator.userAgent); // Windows (8.1) -//var isWindowsPC = /Windows NT/.test(navigator.userAgent); // Windows [NT] (8.1) -//var isWindowsPhone_8_1 = /Windows Phone 8.1/.test(navigator.userAgent); // Windows Phone 8.1 -//var isIE = isWindows || isWP8 || isWindowsPhone_8_1; -var isIE = isWindows || isWP8; -var isWebKit = !isIE; // TBD [Android or iOS] - -// NOTE: In the core-master branch there is no difference between the default -// implementation and implementation #2. But the test will also apply -// the androidLockWorkaround: 1 option in the case of implementation #2. + +// NOTE: While in certain version branches there is no difference between +// the default Android implementation and implementation #2, +// this test script will also apply the androidLockWorkaround: 1 option +// in case of implementation #2. var scenarioList = [ isAndroid ? 'Plugin-implementation-default' : 'Plugin', 'HTML5', @@ -32,26 +27,29 @@ var mytests = function() { for (var i=0; i Date: Thu, 27 Apr 2017 22:11:51 +0200 Subject: [PATCH 07/47] cordova-sqlite-legacy-express-core 1.0.0 --- CHANGES.md | 64 +++++++++++++++++++-------------------- README.md | 85 +++++++++++++++++++++++++++++++++++++++++++++++----- package.json | 2 +- plugin.xml | 2 +- 4 files changed, 109 insertions(+), 44 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index f4a1df96..4bd1f14a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,6 @@ # Changes -###### cordova-sqlite-legacy-express-core 1.0.0-pre4 +###### cordova-sqlite-legacy-express-core 1.0.0 - Workaround solution to BUG litehelpers/Cordova-sqlite-storage#666 (hanging transaction in case of location reload/change) - selfTest simulate scenario & test solution to BUG litehelpers/Cordova-sqlite-storage#666 (also includes string test and test of effects of location reload/change in this version branch, along with another internal check) @@ -8,52 +8,48 @@ - Remove Lawnchair adapter from this version branch - Support macOS platform with builtin libsqlite3.dylib framework in this version branch -## 1.2.2 +### cordova-sqlite-storage 1.2.2 - Self-test function to verify ability to open/populate/read/delete a test database - Read BLOB as Base-64 DISABLED in Android version (was already disabled for iOS) -## 1.2.1 +### cordova-sqlite-storage 1.2.1 - Close Android SQLiteStatement after INSERT/UPDATE/DELETE - Specify minimum Cordova version 6.0.0 - Lawnchair adapter fix: Changed remove method to work with key array -## 1.2.0 +### cordova-sqlite-storage 1.2.0 - Rename Lawnchair adapter to prevent clash with standard webkit-sqlite adapter - Support location: 'default' setting in openDatabase & deleteDatabase -## 0.8.5 +### cordova-sqlite-storage 0.8.5 - More explicit iosDatabaseLocation option - iOS database location is now mandatory - Split-up of some more spec test scripts -## 0.8.2 +### cordova-sqlite-storage 0.8.2 - Workaround fix for empty readTransaction issue (litehelpers/Cordova-sqlite-storage#409) - Split spec/www/spec/legacy.js into db-open-close-delete-test.js & tx-extended.js -## 0.8.0 +### cordova-sqlite-storage 0.8.0 - Simple sql batch transaction function - Echo test function -- Remove extra runInBackground: step from iOS version -- Android-sqlite-connector (NDK) support removed from this version branch -- Windows version removed from this version branch -- Java source of Android version now using io.sqlc package - -## 0.7.15-pre - - All iOS operations are now using background processing (reported to resolve intermittent problems with cordova-ios@4.0.1) +- Java source of Android version now using io.sqlc package +- Drop Android-sqlite-connector support +- Drop WP(8) and Windows support -## 0.7.14 +### 0.7.14 - REGEXP support completely removed from this version branch - Remove src/android/libs/.gitignore (inadvertently added in 0.7.13) -## 0.7.13 +### 0.7.13 - REGEXP support partially removed from this version branch - Rename Windows C++ Database close function to closedb to resolve conflict for Windows Store certification @@ -62,79 +58,79 @@ - Amazon Fire-OS support removed - Fix conversion warnings in iOS version -## 0.7.12 +### 0.7.12 - Fix to Windows "Universal" version to support big integers - Implement database close and delete operations for Windows "Universal" - Fix readTransaction to skip BEGIN/COMMIT/ROLLBACK -## 0.7.11 +### 0.7.11 - Fix plugin ID in plugin.xml to match npm package ID - Unpacked sqlite-native-driver.so libraries from jar - Fix conversion of INTEGER type (iOS version) - Disable code to read BLOB as Base-64 (iOS version) due to https://issues.apache.org/jira/browse/CB-9638 -## 0.7.10 +### 0.7.10 - Use Android-sqlite-connector instead of sqlite4java -## 0.7.9 +### 0.7.9 - Build iOS and Windows versions with sqlite 3.8.10.2 embedded - Fix plugin id to match npm package id -## 0.7.8 +### 0.7.8 - Support FTS3/FTS4 and R-Tree in iOS and Windows "Universal" (8.1) versions - Build ARM target with Function Level Linking ref: http://www.monkey-x.com/Community/posts.php?topic=7739 - SQLite3.Windows.vcxproj and SQLite3.WindowsPhone.vcxproj in their own directories to avoid problems due to temporary files -## 0.7.7 +### 0.7.7 - include build of sqlite4java for Android x86_64 and arm-64 - clean publish to plugins.cordova.io -## 0.7.6 +### 0.7.6 - Small fix to plugin id - Disable use of gethostuuid() in sqlite3.c (only used in iOS version) - published to plugins.cordova.io - [BUG] published extra junk in workarea, causing problems with Windows (Universal) version -## 0.7.5 +### 0.7.5 - Windows (Universal) version now supports both Windows 8.1 and Windows Phone 8.1 - iOS and Windows versions are now built with sqlite 3.8.9 embedded - Improved locking style and other optimizations applied for iOS version -## 0.7.4 +### 0.7.4 - iOS and Windows (8.1) versions built to keep non-essential temporary sqlite files in memory - Option to use legacy Android database library, with Android locking/closing issue (BUG #193) workaround included again -## 0.7.3 +### 0.7.3 - insertId & rowsAffected implemented for Windows (8.1) - plugin id changed -## 0.7.2 +### 0.7.2 - Android version with sqlite4java (sqlite 3.8.7 embedded), which solves BUG #193: Android closing/locking issue (ICU-UNICODE integration is now missing) - iOS version fixed to override the correct pluginInitialize method and built with sqlite 3.8.8.3 embedded -## 0.7.1 +### 0.7.1 - Project renamed - Initial version for Windows (8.1) [with sqlite 3.8.8.3 embedded] - Abort initially pending transactions for db handle (due to incorrect password key, for example) [from Cordova-sqlcipher-storage] - WP7 build enabled (NOT TESTED) -## 1.0.6 +### 1.0.6 - Proper handling of transactions that may be requested before the database open operation is completed - Report an error upon attempt to close a database handle object multiple times. -## 1.0.5 +### 1.0.5 - Workaround for Android db locking/closing issue - Fix double-precision REAL values in result (iOS version) @@ -143,22 +139,22 @@ - Fix closing of Android database - Some fixes for SQL API error handling to be consistent with Web SQL -## 1.0.4 +### 1.0.4 - Pre-populated database option (Android/iOS) - Option to select database location to disable iCloud backup (iOS ONLY) - Safeguard against closing of database while transaction is pending - Fix to prevent double marshaling of data -## 1.0.3 +### 1.0.3 - Fixed issue with multi-page apps on Android (due to problem when closing & re-opening app) -## 1.0.2 +### 1.0.2 - Workaround for issue with multiple UPDATE statements WP(8) (#128) -## 1.0.1 +### 1.0.1 - Support Cordova 3.3.0/3.4.0 to support Amazon-FireOS - Fixes for WP(8): diff --git a/README.md b/README.md index 3c2b0b3f..5f6e44d3 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase ## Status - A recent version of the Cordova CLI (such as `6.5.0`) is recommended. Cordova versions older than `6.0.0` are missing the `cordova-ios@4.0.0` security fixes. In addition it is *required* to use `cordova prepare` in case of cordova-ios older than `4.3.0` (Cordova CLI `6.4.0`). -- iOS database location is now mandatory, as documented below. +- The iOS database location is now mandatory, as documented below. - Android platform version in this version branch is now using the built-in Android SQLite database classes. Integration with the lightweight [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector) is available in the default [litehelpers / Cordova-sqlite-storage](https://github.com/litehelpers/Cordova-sqlite-storage) version branch as well as other versions such as [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) and [litehelpers / Cordova-sqlite-legacy](https://github.com/litehelpers/Cordova-sqlite-legacy). - iOS/macOS platform version in this version branch uses builtin `libsqlite3.dylib` framework library. Other versions such as the default [litehelpers / Cordova-sqlite-storage](https://github.com/litehelpers/Cordova-sqlite-storage) version branch, [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext), and [litehelpers / Cordova-sqlite-legacy](https://github.com/litehelpers/Cordova-sqlite-legacy) include a build of a recent sqlite3 amalgamation. - Windows 10 UWP version using the performant [doo / SQLite3-WinRT](https://github.com/doo/SQLite3-WinRT) component is available in the default [litehelpers / Cordova-sqlite-storage](https://github.com/litehelpers/Cordova-sqlite-storage) version branch as well as other versions such as [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext). @@ -116,6 +116,32 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase +## Security + +### Security of sensitive data + +According to [Web SQL Database API 7.2 Sensitivity of data](https://www.w3.org/TR/webdatabase/#sensitivity-of-data): +>User agents should treat persistently stored data as potentially sensitive; it's quite possible for e-mails, calendar appointments, health records, or other confidential documents to be stored in this mechanism. +> +>To this end, user agents should ensure that when deleting data, it is promptly deleted from the underlying storage. + +Unfortunately this plugin will not actually overwrite the deleted content unless the [secure_delete PRAGMA](https://www.sqlite.org/pragma.html#pragma_secure_delete) is used. + +### SQL injection + +As "strongly recommended" by [Web SQL Database API 8.5 SQL injection](https://www.w3.org/TR/webdatabase/#sql-injection): +>Authors are strongly recommended to make use of the `?` placeholder feature of the `executeSql()` method, and to never construct SQL statements on the fly. + + + +# Avoiding data loss + +- Double-check that the application code follows the documented API for SQL statements, parameter values, success callbacks, and error callbacks. +- For standard Web SQL transactions include a transaction error callback with the proper logic that indicates to the user if data cannot be stored for any reason. In case of individual SQL error handlers be sure to indicate to the user if there is any issue with storing data. +- For single statement and batch transactions include an error callback with logic that indicates to the user if data cannot be stored for any reason. + + + ## Known issues - iOS/macOS platform version does not support certain rapidly repeated open-and-close or open-and-delete test scenarios due to how the implementation handles background processing @@ -176,36 +202,59 @@ Issues fixed in some newer version branches: - Integration with PhoneGap developer app - Use within [InAppBrowser](http://docs.phonegap.com/en/edge/cordova_inappbrowser_inappbrowser.md.html) - Use within an iframe (see [litehelpers/Cordova-sqlite-storage#368 (comment)](https://github.com/litehelpers/Cordova-sqlite-storage/issues/368#issuecomment-154046367)) +- Date/time handling +- Maximum record size supported - Actual behavior when using SAVEPOINT(s) - R-Tree is not fully tested with Android - UNICODE characters not fully tested - Use with TRIGGER(s), JOIN and ORDER BY RANDOM - UPDATE/DELETE with LIMIT or ORDER BY (newer Android/iOS versions) -- WITH clause (not supported by older sqlite3 versions) - Integration with JXCore for Cordova (must be built without sqlite(3) built-in) - Delete an open database inside a statement or transaction callback. +- WITH clause (not supported by some older sqlite3 versions) +- Handling of invalid transaction and transaction.executeSql arguments +- Use of database locations on macOS +- Extremely large and small INTEGER and REAL values ref: [litehelpers/Cordova-sqlite-storage#627](https://github.com/litehelpers/Cordova-sqlite-storage/issues/627)) +- More emojis and other 4-octet UTF-8 characters +- `?NNN`/`:AAA`/`@AAAA`/`$AAAA` parameter placeholders ref: , ) +- Single-statement and SQL batch transaction calls with invalid arguments (TBD behavior subject to change) + + ## Some tips and tricks - If you run into problems and your code follows the asynchronous HTML5/[Web SQL](http://www.w3.org/TR/webdatabase/) transaction API, you can try opening a test database using `window.openDatabase` and see if you get the same problems. - In case your database schema may change, it is recommended to keep a table with one row and one column to keep track of your own schema version number. It is possible to add it later. The recommended schema update procedure is described below. -## Common pitfall(s) + + +## Pitfalls +### Some common pitfall(s) + +- If a database is opened using the standard `window.openDatabase` call it will not have any of the benefits of this plugin and features such as the `sqlBatch` call would not be available. - It is NOT allowed to execute sql statements on a transaction that has already finished, as described below. This is consistent with the HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/). - The plugin class name starts with "SQL" in capital letters, but in Javascript the `sqlitePlugin` object name starts with "sql" in small letters. - Attempting to open a database before receiving the 'deviceready' event callback. - Inserting STRING into ID field - Auto-vacuum is NOT enabled by default. It is recommended to periodically VACUUM the database. +- Transactions on a database are run sequentially. A large transaction could block smaller transactions requested afterwards. -## Weird pitfall(s) +### Some weird pitfall(s) - intent whitelist: blocked intent such as external URL intent *may* cause this and perhaps certain Cordova plugin(s) to misbehave (see [litehelpers/Cordova-sqlite-storage#396](https://github.com/litehelpers/Cordova-sqlite-storage/issues/396)) -## Angular/ngCordova/Ionic-related pitfalls +### Angular/ngCordova/Ionic-related pitfalls - Angular/ngCordova/Ionic controller/factory/service callbacks may be triggered before the 'deviceready' event is fired - As discussed in [litehelpers/Cordova-sqlite-storage#355](https://github.com/litehelpers/Cordova-sqlite-storage/issues/355), it may be necessary to install ionic-plugin-keyboard +- Navigation items such as root page can be tricky on Ionic 2 ref: [litehelpers/Cordova-sqlite-storage#613](https://github.com/litehelpers/Cordova-sqlite-storage/issues/613) + +### General Cordova pitfalls + +Documented in: [brodybits / Avoiding-some-Cordova-pitfalls](https://github.com/brodybits/Avoiding-some-Cordova-pitfalls) + + ## Major TODOs @@ -281,7 +330,9 @@ window.sqlitePlugin.selfTest(successCallback, errorCallback); ## General -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()`) *with parameters as documented below* instead of `window.openDatabase()`. If you see any other major change please report it, it is probably a bug. +- Drop-in replacement for HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/): the only change should be to replace the static `window.openDatabase()` factory call with `window.sqlitePlugin.openDatabase()`, with parameters as documented below. (Some known deviations are documented in newer version branches.) +- Single-page application design is recommended. +- In case of a multi-page application the JavaScript used by each page must use `sqlitePlugin.openDatabase` to open the database access handle object before it can access the data. **NOTE:** If a sqlite statement in a transaction fails with an error, the error handler *must* return `false` in order to recover the transaction. This is correct according to the HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/) standard. This is different from the WebKit implementation of Web SQL in Android and iOS which recovers the transaction if a sql error hander returns a non-`true` value. @@ -730,9 +781,27 @@ The transactional nature of the API makes it relatively straightforward to manag ## Use with Ionic/ngCordova/Angular -It is recommended to follow the tutorial at: https://blog.nraboy.com/2014/11/use-sqlite-instead-local-storage-ionic-framework/ +### Ionic 2 + +Tutorials with Ionic 2: +- (title is somewhat misleading, "SQL storage" *does* use this sqlite plugin) +- (older tutorial) + +Sample for Ionic 2 wanted ref: [litehelpers/Cordova-sqlite-storage#585](https://github.com/litehelpers/Cordova-sqlite-storage/issues/585) + +### Ionic 1 + +Tutorial with Ionic 1: + +A sample for Ionic 1 is provided at: [litehelpers / Ionic-sqlite-database-example](https://github.com/litehelpers/Ionic-sqlite-database-example) + +Documentation at: + +Other resource (apparently for Ionic 1): + +**NOTE:** Some Ionic and other Angular pitfalls are described above. -Documentation at: http://ngcordova.com/docs/plugins/sqlite/ + # Installing diff --git a/package.json b/package.json index 15c41957..e2077ab4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cordova-sqlite-legacy-express-core", - "version": "1.0.0-pre4", + "version": "1.0.0", "description": "Native interface to SQLite for PhoneGap/Cordova (legacy express core version)", "cordova": { "id": "cordova-sqlite-legacy-express-core", diff --git a/plugin.xml b/plugin.xml index da9c2ced..7cb41b37 100644 --- a/plugin.xml +++ b/plugin.xml @@ -2,7 +2,7 @@ + version="1.0.0"> Cordova sqlite storage plugin - legacy express core version From 1e0fddfd1de8bb337176faa0c894b4bc05180c6a Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Fri, 27 Oct 2017 16:20:50 -0400 Subject: [PATCH 08/47] Fix bug 666 workaround to trigger ROLLBACK in next event tick (needed to support version with pre-populated database on Windows) --- CHANGES.md | 4 ++++ SQLitePlugin.coffee.md | 21 ++++++++++++--------- package.json | 2 +- plugin.xml | 2 +- spec/www/spec/db-open-close-delete-test.js | 12 ++++++++---- www/SQLitePlugin.js | 20 +++++++++++++------- 6 files changed, 39 insertions(+), 22 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 4bd1f14a..6b1c2daf 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Changes +###### cordova-sqlite-legacy-express-core 1.0.1 + +- Fix bug 666 workaround to trigger ROLLBACK in the next event tick (needed to support version with pre-populated database on Windows) + ###### cordova-sqlite-legacy-express-core 1.0.0 - Workaround solution to BUG litehelpers/Cordova-sqlite-storage#666 (hanging transaction in case of location reload/change) diff --git a/SQLitePlugin.coffee.md b/SQLitePlugin.coffee.md index 6e131300..a3dc8c06 100644 --- a/SQLitePlugin.coffee.md +++ b/SQLitePlugin.coffee.md @@ -213,12 +213,14 @@ success @ return + # (done) + else console.log 'OPEN database: ' + @dbname opensuccesscb = => # NOTE: the db state is NOT stored (in @openDBs) if the db was closed or deleted. - # console.log 'OPEN database: ' + @dbname + ' succeeded' + console.log 'OPEN database: ' + @dbname + ' ok' #if !@openDBs[@dbname] then call open error cb, and abort pending tx if any if !@openDBs[@dbname] @@ -248,19 +250,21 @@ # store initial DB state: @openDBs[@dbname] = DB_STATE_INIT - # As a WORKAROUND SOLUTION to BUG litehelpers/Cordova-sqlite-storage#666: + # As a WORKAROUND SOLUTION to BUG litehelpers/Cordova-sqlite-storage#666 + # (in the next event tick): # If the database was never opened on the JavaScript side # start an extra ROLLBACK statement to abort any pending transaction # (does not matter whether it succeeds or fails here). # FUTURE TBD a better solution would be to send a special signal or parameter # if the database was never opened on the JavaScript side. - if not txLocks[@dbname] - myfn = (tx) -> - tx.addStatement 'ROLLBACK' - return - @addTransaction new SQLitePluginTransaction @, myfn, null, null, false, false + nextTick => + if not txLocks[@dbname] + myfn = (tx) -> + tx.addStatement 'ROLLBACK' + return + @addTransaction new SQLitePluginTransaction @, myfn, null, null, false, false - cordova.exec opensuccesscb, openerrorcb, "SQLitePlugin", "open", [ @openargs ] + cordova.exec opensuccesscb, openerrorcb, "SQLitePlugin", "open", [ @openargs ] return @@ -942,4 +946,3 @@ #### vim: set filetype=coffee : #### vim: set expandtab : - diff --git a/package.json b/package.json index e2077ab4..65ee03f7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cordova-sqlite-legacy-express-core", - "version": "1.0.0", + "version": "1.0.1", "description": "Native interface to SQLite for PhoneGap/Cordova (legacy express core version)", "cordova": { "id": "cordova-sqlite-legacy-express-core", diff --git a/plugin.xml b/plugin.xml index 7cb41b37..c67c4697 100644 --- a/plugin.xml +++ b/plugin.xml @@ -2,7 +2,7 @@ + version="1.0.1"> Cordova sqlite storage plugin - legacy express core version diff --git a/spec/www/spec/db-open-close-delete-test.js b/spec/www/spec/db-open-close-delete-test.js index 33957b6f..31d710f8 100755 --- a/spec/www/spec/db-open-close-delete-test.js +++ b/spec/www/spec/db-open-close-delete-test.js @@ -577,8 +577,10 @@ var mytests = function() { // XXX SEE BELOW: repeat scenario but wait for open callback before close/delete/reopen // Needed to support some large-scale applications: test_it(suiteName + ' immediate close, then delete then re-open allows subsequent queries to run', function () { - // TBD POSSIBLY BROKEN on iOS/macOS due to current background processing implementation: - if (!isAndroid && !isWindows && !isWP8) pending('POSSIBLY BROKEN on iOS/macOS (background processing implementation)'); + // TBD POSSIBLY BROKEN on iOS/macOS ... + // if (!isAndroid && !isWindows && !isWP8) pending(...); + // TBD CURRENTLY BROKEN DUE TO BUG 666 WORKAROUND HACK + pending('CURRENTLY BROKEN DUE TO BUG 666 WORKAROUND HACK'); var dbName = "Immediate-close-delete-Reopen.db"; var dbargs = {name: dbName, location: 'default'}; @@ -846,8 +848,10 @@ var mytests = function() { // Needed to support some large-scale applications: test_it(suiteName + ' repeatedly open and delete database faster (5x)', function () { - // TBD CURRENTLY BROKEN on iOS/macOS due to current background processing implementation: - if (!isAndroid && !isWindows && !isWP8) pending('CURRENTLY BROKEN on iOS/macOS (background processing implementation)'); + // TBD POSSIBLY BROKEN on iOS/macOS ... + // if (!isAndroid && !isWindows && !isWP8) pending(...); + // TBD CURRENTLY BROKEN DUE TO BUG 666 WORKAROUND HACK + pending('CURRENTLY BROKEN DUE TO BUG 666 WORKAROUND HACK'); var dbName = 'repeatedly-open-and-delete-faster-5x.db'; var dbargs = {name: dbName, location: 'default'}; diff --git a/www/SQLitePlugin.js b/www/SQLitePlugin.js index 476349c8..6d612d44 100644 --- a/www/SQLitePlugin.js +++ b/www/SQLitePlugin.js @@ -162,7 +162,7 @@ }; SQLitePlugin.prototype.open = function(success, error) { - var myfn, openerrorcb, opensuccesscb; + var openerrorcb, opensuccesscb; if (this.dbname in this.openDBs) { console.log('database already open: ' + this.dbname); nextTick((function(_this) { @@ -175,6 +175,7 @@ opensuccesscb = (function(_this) { return function() { var txLock; + console.log('OPEN database: ' + _this.dbname + ' ok'); if (!_this.openDBs[_this.dbname]) { console.log('database was closed during open operation'); } @@ -201,13 +202,18 @@ }; })(this); this.openDBs[this.dbname] = DB_STATE_INIT; - if (!txLocks[this.dbname]) { - myfn = function(tx) { - tx.addStatement('ROLLBACK'); + nextTick((function(_this) { + return function() { + var myfn; + if (!txLocks[_this.dbname]) { + myfn = function(tx) { + tx.addStatement('ROLLBACK'); + }; + _this.addTransaction(new SQLitePluginTransaction(_this, myfn, null, null, false, false)); + } + return cordova.exec(opensuccesscb, openerrorcb, "SQLitePlugin", "open", [_this.openargs]); }; - this.addTransaction(new SQLitePluginTransaction(this, myfn, null, null, false, false)); - } - cordova.exec(opensuccesscb, openerrorcb, "SQLitePlugin", "open", [this.openargs]); + })(this)); } }; From 7fa8c53a503829adf4c75d53accaa4676cc97882 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 5 Sep 2017 19:36:04 -0400 Subject: [PATCH 09/47] @craig-at-rsg add PSPDFThreadSafeMutableDictionary for iOS/macOS PSPDFThreadSafeMutableDictionary.m from https://gist.github.com/steipete/5928916 ref: litehelpers/Cordova-sqlite-storage#716 --- LICENSE.md | 2 + package.json | 2 +- plugin.xml | 2 +- src/ios/PSPDFThreadSafeMutableDictionary.h | 28 ++++ src/ios/PSPDFThreadSafeMutableDictionary.m | 159 +++++++++++++++++++++ 5 files changed, 191 insertions(+), 2 deletions(-) create mode 100644 src/ios/PSPDFThreadSafeMutableDictionary.h create mode 100644 src/ios/PSPDFThreadSafeMutableDictionary.m diff --git a/LICENSE.md b/LICENSE.md index e621188a..d257c7fc 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -14,6 +14,8 @@ MIT only based on Phonegap-SQLitePlugin by @davibe (Davide Bertola ) and @joenoon (Joe Noon ) +includes PSPDFThreadSafeMutableDictionary (PSPDFThreadSafeMutableDictionary.m ) MIT license by @steipete () + ## REMOVED from this version branch: Windows (8.1/...) version MIT or Apache 2.0 diff --git a/package.json b/package.json index 65ee03f7..d2050a2d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cordova-sqlite-legacy-express-core", - "version": "1.0.1", + "version": "1.0.2-pre00", "description": "Native interface to SQLite for PhoneGap/Cordova (legacy express core version)", "cordova": { "id": "cordova-sqlite-legacy-express-core", diff --git a/plugin.xml b/plugin.xml index c67c4697..74aad5c4 100644 --- a/plugin.xml +++ b/plugin.xml @@ -2,7 +2,7 @@ + version="1.0.2-pre00"> Cordova sqlite storage plugin - legacy express core version diff --git a/src/ios/PSPDFThreadSafeMutableDictionary.h b/src/ios/PSPDFThreadSafeMutableDictionary.h new file mode 100644 index 00000000..05cd9ac1 --- /dev/null +++ b/src/ios/PSPDFThreadSafeMutableDictionary.h @@ -0,0 +1,28 @@ +// PSPDFThreadSafeMutableDictionary.h header copied from +// PSPDFThreadSafeMutableDictionary.m +// +// Copyright (c) 2013 Peter Steinberger, PSPDFKit GmbH. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import + +// Dictionary-Subclasss whose primitive operations are thread safe. +@interface PSPDFThreadSafeMutableDictionary : NSMutableDictionary +@end diff --git a/src/ios/PSPDFThreadSafeMutableDictionary.m b/src/ios/PSPDFThreadSafeMutableDictionary.m new file mode 100644 index 00000000..f8c53e21 --- /dev/null +++ b/src/ios/PSPDFThreadSafeMutableDictionary.m @@ -0,0 +1,159 @@ +// +// PSPDFThreadSafeMutableDictionary.m +// +// Copyright (c) 2013 Peter Steinberger, PSPDFKit GmbH. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import + +// Dictionary-Subclasss whose primitive operations are thread safe. +@interface PSPDFThreadSafeMutableDictionary : NSMutableDictionary +@end + +// ---------------------------------------------------------------- + +// +// PSPDFThreadSafeMutableDictionary.m +// PSPDFKit +// +// Copyright (c) 2013 PSPDFKit GmbH. All rights reserved. +// + +#import "PSPDFThreadSafeMutableDictionary.h" +#import + +#define LOCKED(...) OSSpinLockLock(&_lock); \ +__VA_ARGS__; \ +OSSpinLockUnlock(&_lock); + +@implementation PSPDFThreadSafeMutableDictionary { + OSSpinLock _lock; + NSMutableDictionary *_dictionary; // Class Cluster! +} + +/////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - NSObject + +- (id)init { + return [self initWithCapacity:0]; +} + +- (id)initWithObjects:(NSArray *)objects forKeys:(NSArray *)keys { + if ((self = [self initWithCapacity:objects.count])) { + [objects enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + _dictionary[keys[idx]] = obj; + }]; + } + return self; +} + +- (id)initWithCapacity:(NSUInteger)capacity { + if ((self = [super init])) { + _dictionary = [[NSMutableDictionary alloc] initWithCapacity:capacity]; + _lock = OS_SPINLOCK_INIT; + } + return self; +} + +/////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - NSMutableDictionary + +- (void)setObject:(id)anObject forKey:(id)aKey { + LOCKED(_dictionary[aKey] = anObject) +} + +- (void)addEntriesFromDictionary:(NSDictionary *)otherDictionary { + LOCKED([_dictionary addEntriesFromDictionary:otherDictionary]); +} + +- (void)setDictionary:(NSDictionary *)otherDictionary { + LOCKED([_dictionary setDictionary:otherDictionary]); +} + +- (void)removeObjectForKey:(id)aKey { + LOCKED([_dictionary removeObjectForKey:aKey]) +} + +- (void)removeAllObjects { + LOCKED([_dictionary removeAllObjects]); +} + +- (NSUInteger)count { + LOCKED(NSUInteger count = _dictionary.count) + return count; +} + +- (NSArray *)allKeys { + LOCKED(NSArray *allKeys = _dictionary.allKeys) + return allKeys; +} + +- (NSArray *)allValues { + LOCKED(NSArray *allValues = _dictionary.allValues) + return allValues; +} + +- (id)objectForKey:(id)aKey { + LOCKED(id obj = _dictionary[aKey]) + return obj; +} + +- (NSEnumerator *)keyEnumerator { + LOCKED(NSEnumerator *keyEnumerator = [_dictionary keyEnumerator]) + return keyEnumerator; +} + +- (id)copyWithZone:(NSZone *)zone { + return [self mutableCopyWithZone:zone]; +} + +- (id)mutableCopyWithZone:(NSZone *)zone { + LOCKED(id copiedDictionary = [[self.class allocWithZone:zone] initWithDictionary:_dictionary]) + return copiedDictionary; +} + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state + objects:(id __unsafe_unretained [])stackbuf + count:(NSUInteger)len { + LOCKED(NSUInteger count = [[_dictionary copy] countByEnumeratingWithState:state objects:stackbuf count:len]); + return count; +} + +- (void)performLockedWithDictionary:(void (^)(NSDictionary *dictionary))block { + if (block) LOCKED(block(_dictionary)); +} + +- (BOOL)isEqual:(id)object { + if (object == self) return YES; + + if ([object isKindOfClass:PSPDFThreadSafeMutableDictionary.class]) { + PSPDFThreadSafeMutableDictionary *other = object; + __block BOOL isEqual = NO; + [other performLockedWithDictionary:^(NSDictionary *dictionary) { + [self performLockedWithDictionary:^(NSDictionary *otherDictionary) { + isEqual = [dictionary isEqual:otherDictionary]; + }]; + }]; + return isEqual; + } + return NO; +} + +@end From 570d5e8a793f7441bb7996d809abd884569dc729 Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Tue, 5 Sep 2017 19:36:04 -0400 Subject: [PATCH 10/47] Fix PSPDFThreadSafeMutableDictionary.m redeclaration issue --- src/ios/PSPDFThreadSafeMutableDictionary.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ios/PSPDFThreadSafeMutableDictionary.m b/src/ios/PSPDFThreadSafeMutableDictionary.m index f8c53e21..2b39604b 100644 --- a/src/ios/PSPDFThreadSafeMutableDictionary.m +++ b/src/ios/PSPDFThreadSafeMutableDictionary.m @@ -23,9 +23,11 @@ #import +/* ** ALREADY INCLUDED BY #import "PSPDFThreadSafeMutableDictionary.h" // Dictionary-Subclasss whose primitive operations are thread safe. @interface PSPDFThreadSafeMutableDictionary : NSMutableDictionary @end +// */ // ---------------------------------------------------------------- From 6d946d0cb0c2acd93a37fb404e94ccf0d690e6dd Mon Sep 17 00:00:00 2001 From: craig-at-rsg Date: Tue, 5 Sep 2017 19:37:43 -0400 Subject: [PATCH 11/47] Update SQLitePlugin.m & plugin.xml to use PSPDFThreadSafeMutableDictionary (iOS/macOS) with minor changes by @brodybits: - import PSPDFThreadSafeMutableDictionary in SQLitePlugin.m instead of SQLitePlugin.h - update SQLitePlugin.m & plugin.xml in single commit - update plugin version - update docs ref: litehelpers/Cordova-sqlite-storage#716 --- CHANGES.md | 4 ++++ LICENSE.md | 2 +- README.md | 1 + package.json | 2 +- plugin.xml | 10 +++++++++- src/ios/SQLitePlugin.m | 4 +++- 6 files changed, 19 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 6b1c2daf..968eb4fe 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Changes +###### cordova-sqlite-legacy-express-core 1.0.2 + +- Use PSPDFThreadSafeMutableDictionary for iOS/macOS to avoid threading issue ref: litehelpers/Cordova-sqlite-storage#716 + ###### cordova-sqlite-legacy-express-core 1.0.1 - Fix bug 666 workaround to trigger ROLLBACK in the next event tick (needed to support version with pre-populated database on Windows) diff --git a/LICENSE.md b/LICENSE.md index d257c7fc..23233c14 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -14,7 +14,7 @@ MIT only based on Phonegap-SQLitePlugin by @davibe (Davide Bertola ) and @joenoon (Joe Noon ) -includes PSPDFThreadSafeMutableDictionary (PSPDFThreadSafeMutableDictionary.m ) MIT license by @steipete () +includes and uses PSPDFThreadSafeMutableDictionary (PSPDFThreadSafeMutableDictionary.m ) MIT license by @steipete () ## REMOVED from this version branch: Windows (8.1/...) version diff --git a/README.md b/README.md index 5f6e44d3..4e99f855 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,7 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase ## Announcements +- Fixed iOS/macOS platform version to use [PSPDFThreadSafeMutableDictionary.m](https://gist.github.com/steipete/5928916) to avoid threading issue ref: [litehelpers/Cordova-sqlite-storage#716](https://github.com/litehelpers/Cordova-sqlite-storage/issues/716) - Resolved transaction problem after window.location (page) change with possible data loss ref: [litehelpers/Cordova-sqlite-storage#666](https://github.com/litehelpers/Cordova-sqlite-storage/issues/666) - [brodybits / cordova-sqlite-test-app](https://github.com/brodybits/cordova-sqlite-test-app) project is a CC0 (public domain) starting point (NOTE that this plugin must be added) and may also be used to reproduce issues with this plugin. - The Lawnchair adapter is now moved to [litehelpers / cordova-sqlite-lawnchair-adapter](https://github.com/litehelpers/cordova-sqlite-lawnchair-adapter). diff --git a/package.json b/package.json index d2050a2d..9950f0d7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cordova-sqlite-legacy-express-core", - "version": "1.0.2-pre00", + "version": "1.0.2", "description": "Native interface to SQLite for PhoneGap/Cordova (legacy express core version)", "cordova": { "id": "cordova-sqlite-legacy-express-core", diff --git a/plugin.xml b/plugin.xml index 74aad5c4..a0a87385 100644 --- a/plugin.xml +++ b/plugin.xml @@ -2,7 +2,7 @@ + version="1.0.2"> Cordova sqlite storage plugin - legacy express core version @@ -41,6 +41,10 @@ + + + + @@ -55,6 +59,10 @@ + + + + diff --git a/src/ios/SQLitePlugin.m b/src/ios/SQLitePlugin.m index d1ff85c4..a56bf5ad 100755 --- a/src/ios/SQLitePlugin.m +++ b/src/ios/SQLitePlugin.m @@ -10,6 +10,8 @@ #import "sqlite3.h" +#import "PSPDFThreadSafeMutableDictionary.h" + // FUTURE TBD (in another version branch): //#define READ_BLOB_AS_BASE64 @@ -27,7 +29,7 @@ -(void)pluginInitialize NSLog(@"Initializing SQLitePlugin"); { - openDBs = [NSMutableDictionary dictionaryWithCapacity:0]; + openDBs = [PSPDFThreadSafeMutableDictionary dictionaryWithCapacity:0]; appDBPaths = [NSMutableDictionary dictionaryWithCapacity:0]; #if !__has_feature(objc_arc) [openDBs retain]; From 68d0406e39de5c7efa470990037732e6466f642a Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Thu, 2 Nov 2017 17:33:34 -0400 Subject: [PATCH 12/47] README.md document multiple sqlite problem ref: litehelpers/Cordova-sqlite-storage#626 --- README.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 83749407..162d7f76 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,16 @@ _XXX TBD ???:_ +## WARNING: Multiple SQLite problem on Android + +This plugin uses a non-standard [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector) implementation on Android. In case an application access the **same** database using multiple plugins there is a risk of data corruption ref: [litehelpers/Cordova-sqlite-storage#626](https://github.com/litehelpers/Cordova-sqlite-storage/issues/626)) as described in and . + +The workaround is to use the `androidDatabaseImplementation: 2` setting as described in the **Android sqlite implementation** section below: + +```js +var db = window.sqlitePlugin.openDatabase({name: "my.db", androidDatabaseImplementation: 2}); +``` + ## BREAKING CHANGE: Database location parameter is now mandatory The `location` or `iosDatabaseLocation` *must* be specified in the `openDatabase` and `deleteDatabase` calls, as documented below. @@ -361,6 +371,8 @@ var db = window.sqlitePlugin.openDatabase({name: 'my.db', location: 'default'}, **WARNING:** The new "default" location value is *NOT* the same as the old default location and would break an upgrade for an app that was using the old default value (0) on iOS. +**WARNING 2:** As described above: by default this plugin uses a non-standard [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector) implementation on Android. In case an application access the **same** database using multiple plugins there is a risk of data corruption ref: [litehelpers/Cordova-sqlite-storage#626](https://github.com/litehelpers/Cordova-sqlite-storage/issues/626)) as described in and . The workaround is to use the `androidDatabaseImplementation: 2` setting as described in the **Android sqlite implementation** section below. + To specify a different location (affects iOS/macOS *only*): ```js @@ -441,9 +453,13 @@ By default, this plugin uses [Android-sqlite-connector](https://github.com/liteg var db = window.sqlitePlugin.openDatabase({name: "my.db", androidDatabaseImplementation: 2}); ``` +**IMPORTANT:** +- As described above: by default this plugin uses a non-standard [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector) implementation on Android. In case an application access the **same** database using multiple plugins there is a risk of data corruption ref: [litehelpers/Cordova-sqlite-storage#626](https://github.com/litehelpers/Cordova-sqlite-storage/issues/626)) as described in and . The workaround is to use the `androidDatabaseImplementation: 2` setting as described here. +- In case of the `androidDatabaseImplementation: 2` setting, [litehelpers/Cordova-sqlite-storage#193](https://github.com/litehelpers/Cordova-sqlite-storage/issues/193) reported that in certain Android versions, if the app is stopped or aborted without closing the database then there is an unexpected database lock and the data that was inserted is lost. The workaround is described below. + ### Workaround for Android db locking issue -[litehelpers/Cordova-sqlite-storage#193](https://github.com/litehelpers/Cordova-sqlite-storage/issues/193) was reported (as observed by several app developers) that certain versions of the Android database classes, if the app is stopped or aborted without closing the database then: +[litehelpers/Cordova-sqlite-storage#193](https://github.com/litehelpers/Cordova-sqlite-storage/issues/193) reported (as observed by several app developers) that certain versions of the Android database classes, if the app is stopped or aborted without closing the database then: - (sometimes) there is an unexpected database lock - the data that was inserted is lost. From da62bbe03bdc76c8da2b9302551fe0bc2e7bdd83 Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Thu, 2 Nov 2017 19:18:08 -0400 Subject: [PATCH 13/47] Windows 10 (UWP) build with /SAFESEH flag on Win32 (x86) target ref: - litehelpers/Cordova-sqlite-storage#563 (comment) - litehelpers/Cordova-sqlite-storage#685 --- CHANGES.md | 4 +++- README.md | 1 + package.json | 2 +- plugin.xml | 2 +- .../SQLite3-Win-RT/SQLite3/SQLite3.UWP/SQLite3.UWP.vcxproj | 2 ++ 5 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 9b33cd4e..e09c5ef0 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,8 @@ # Changes -##### cordova-sqlite-legacy-core 1.0.0 +##### cordova-sqlite-legacy-core 1.0.1-pre1 + +- Windows 10 (UWP) build with /SAFESEH flag on Win32 (x86) target ###### cordova-sqlite-legacy-express-core 1.0.2 diff --git a/README.md b/README.md index 162d7f76..be883f32 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,7 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase ## Announcements +- Windows 10 (UWP) build with /SAFESEH flag on Win32 (x86) target to specify "Image has Safe Exception Handlers" as described in - Fixed iOS/macOS platform version to use [PSPDFThreadSafeMutableDictionary.m](https://gist.github.com/steipete/5928916) to avoid threading issue ref: [litehelpers/Cordova-sqlite-storage#716](https://github.com/litehelpers/Cordova-sqlite-storage/issues/716) - Resolved transaction problem after window.location (page) change with possible data loss ref: [litehelpers/Cordova-sqlite-storage#666](https://github.com/litehelpers/Cordova-sqlite-storage/issues/666) - [brodybits / cordova-sqlite-test-app](https://github.com/brodybits/cordova-sqlite-test-app) project is a CC0 (public domain) starting point (NOTE that this plugin must be added) and may also be used to reproduce issues with this plugin. diff --git a/package.json b/package.json index 48516d67..d38925a8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cordova-sqlite-legacy-core", - "version": "1.0.0", + "version": "1.0.1-pre1", "description": "Native interface to SQLite for PhoneGap/Cordova (legacy core version branch)", "cordova": { "id": "cordova-sqlite-legacy-core", diff --git a/plugin.xml b/plugin.xml index 53fc223c..12606633 100644 --- a/plugin.xml +++ b/plugin.xml @@ -2,7 +2,7 @@ + version="1.0.1-pre1"> Cordova sqlite storage plugin - legacy core version branch diff --git a/src/windows/SQLite3-Win-RT/SQLite3/SQLite3.UWP/SQLite3.UWP.vcxproj b/src/windows/SQLite3-Win-RT/SQLite3/SQLite3.UWP/SQLite3.UWP.vcxproj index 3c46ddcf..1241bd2c 100644 --- a/src/windows/SQLite3-Win-RT/SQLite3/SQLite3.UWP/SQLite3.UWP.vcxproj +++ b/src/windows/SQLite3-Win-RT/SQLite3/SQLite3.UWP/SQLite3.UWP.vcxproj @@ -131,6 +131,7 @@ Console false + /SAFESEH %(AdditionalOptions) @@ -147,6 +148,7 @@ Console false + /SAFESEH %(AdditionalOptions) From ece0c61fcc189a206e6a15715285fc541829c839 Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Fri, 3 Nov 2017 11:21:05 -0400 Subject: [PATCH 14/47] SQLite build with flag fixes in this version branch - SQLITE_THREADSAFE=2 on iOS/macOS (SQLITE_THREADSAFE=1 on Android/Windows) to avoid possible malformed database due to multithreaded access ref: litehelpers/Cordova-sqlite-storage#703 - Drop -DSQLITE_OMIT_BUILTIN_TEST (renamed & not recommended) - Certain LOCAL_CFLAGS added from https://www.sqlite.org/compile.html#recommended_compile_time_options - Minor reordering --- CHANGES.md | 3 ++- README.md | 17 ++++++++++++++++- package.json | 2 +- plugin.xml | 8 +++++--- .../SQLite3/SQLite3.Shared.vcxitems | 2 +- 5 files changed, 25 insertions(+), 7 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index e09c5ef0..0a5c51c2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,7 +1,8 @@ # Changes -##### cordova-sqlite-legacy-core 1.0.1-pre1 +##### cordova-sqlite-legacy-core 1.0.1 +- SQLite 3.15.2 build with SQLITE_THREADSAFE=2 on iOS/macOS (SQLITE_THREADSAFE=1 on Android/Windows) and other flag fixes in this version branch to avoid possible malformed database due to multithreaded access ref: litehelpers/Cordova-sqlite-storage#703 - Windows 10 (UWP) build with /SAFESEH flag on Win32 (x86) target ###### cordova-sqlite-legacy-express-core 1.0.2 diff --git a/README.md b/README.md index be883f32..81f57562 100644 --- a/README.md +++ b/README.md @@ -65,8 +65,23 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase - A recent version of the Cordova CLI (such as `6.5.0`) is recommended. Cordova versions older than `6.0.0` are missing the `cordova-ios@4.0.0` security fixes. In addition it is *required* to use `cordova prepare` in case of cordova-ios older than `4.3.0` (Cordova CLI `6.4.0`). - This version uses a `before_plugin_install` hook to install sqlite3 library dependencies from `cordova-sqlite-storage-dependencies` via npm. +- Build with SQLite (version `3.8.10.2`) with the following build settings for iOS/macOS/Windows: + - `SQLITE_THREADSAFE=1` (`SQLITE_THREADSAFE=2` on iOS/macOS) + - `SQLITE_DEFAULT_MEMSTATUS=0` + - `SQLITE_OMIT_DECLTYPE` + - `SQLITE_OMIT_DEPRECATED` + - `SQLITE_OMIT_PROGRESS_CALLBACK` + - `SQLITE_OMIT_SHARED_CACHE` + - `SQLITE_TEMP_STORE=2` + - `SQLITE_OMIT_LOAD_EXTENSION` + - `SQLITE_ENABLE_FTS3` + - `SQLITE_ENABLE_FTS3_PARENTHESIS` + - `SQLITE_ENABLE_FTS4` + - `SQLITE_ENABLE_RTREE` + - `SQLITE_DEFAULT_PAGE_SIZE=1024` and `SQLITE_DEFAULT_CACHE_SIZE=2000` to avoid "potentially distruptive change(s)" from SQLite 3.12.0 ref: + - `SQLITE_OS_WINRT` for Windows only +- Use of other systems such as Cordova Plugman, PhoneGap CLI, PhoneGap Build, and Intel XDK is no longer supported since they do not honor the `before_plugin_install` hook. The supported solution is to use [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (available with GPL or commercial license options) or [litehelpers / Cordova-sqlite-legacy-build-support](https://github.com/litehelpers/Cordova-sqlite-legacy-build-support) (limited testing, limited updates) - The iOS database location is now mandatory, as documented below. -- _SQLite version `3.8.10.2` is supported for ~~all supported platforms~~ Android/iOS/Windows; macOS platform version in this version branch uses the builtin `libsqlite3.dylib` framework library (**TODO** iOS and macOS should use the same sqlite version)_ - This version supports the use of two (2) possible Android sqlite database implementations: - default: lightweight [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector) - optional: built-in Android database classes (usage described below) diff --git a/package.json b/package.json index d38925a8..45ee9b34 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cordova-sqlite-legacy-core", - "version": "1.0.1-pre1", + "version": "1.0.1", "description": "Native interface to SQLite for PhoneGap/Cordova (legacy core version branch)", "cordova": { "id": "cordova-sqlite-legacy-core", diff --git a/plugin.xml b/plugin.xml index 12606633..ee5b2814 100644 --- a/plugin.xml +++ b/plugin.xml @@ -2,7 +2,7 @@ + version="1.0.1"> Cordova sqlite storage plugin - legacy core version branch @@ -60,7 +60,7 @@ + compiler-flags="-DSQLITE_THREADSAFE=2 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_TEMP_STORE=2 -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_RTREE -DSQLITE_DEFAULT_PAGE_SIZE=1024 -DSQLITE_DEFAULT_CACHE_SIZE=2000" /> @@ -78,7 +78,9 @@ - + + diff --git a/src/windows/SQLite3-Win-RT/SQLite3/SQLite3.Shared.vcxitems b/src/windows/SQLite3-Win-RT/SQLite3/SQLite3.Shared.vcxitems index fc0eeeb9..37144485 100644 --- a/src/windows/SQLite3-Win-RT/SQLite3/SQLite3.Shared.vcxitems +++ b/src/windows/SQLite3-Win-RT/SQLite3/SQLite3.Shared.vcxitems @@ -11,7 +11,7 @@ %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(MSBuildThisFileDirectory)..\..\..\..\node_modules\cordova-sqlite-storage-dependencies - /DSQLITE_TEMP_STORE=2 /DSQLITE_THREADSAFE=2 /DSQLITE_ENABLE_FTS3 /DSQLITE_ENABLE_FTS3_PARENTHESIS /DSQLITE_ENABLE_FTS4 /DSQLITE_ENABLE_COLUMN_METADATA /DSQLITE_ENABLE_RTREE /DSQLITE_OS_WINRT %(AdditionalOptions) + /DSQLITE_THREADSAFE=1 /DSQLITE_DEFAULT_MEMSTATUS=0 /DSQLITE_OMIT_DECLTYPE /DSQLITE_OMIT_DEPRECATED /DSQLITE_OMIT_PROGRESS_CALLBACK /DSQLITE_OMIT_SHARED_CACHE /DSQLITE_TEMP_STORE=2 /DSQLITE_OMIT_LOAD_EXTENSION /DSQLITE_ENABLE_FTS3 /DSQLITE_ENABLE_FTS3_PARENTHESIS /DSQLITE_ENABLE_FTS4 /DSQLITE_ENABLE_RTREE /DSQLITE_DEFAULT_PAGE_SIZE=1024 /DSQLITE_OS_WINRT %(AdditionalOptions) From 8ac39fa1d17a40b7bc703040f90b92f3a63a2a25 Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Sun, 5 Nov 2017 11:27:07 -0500 Subject: [PATCH 15/47] Fix log in case of transaction waiting for open to finish; test/doc fixes --- CHANGES.md | 3 +- README.md | 53 ++++++++++++++++++------------------ SQLitePlugin.coffee.md | 2 +- package.json | 2 +- plugin.xml | 2 +- spec/www/index.html | 4 ++- spec/www/spec/regexp-test.js | 6 ++-- www/SQLitePlugin.js | 2 +- 8 files changed, 39 insertions(+), 35 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 0a5c51c2..42296867 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,7 +1,8 @@ # Changes -##### cordova-sqlite-legacy-core 1.0.1 +##### cordova-sqlite-legacy-core 1.0.2 +- Fix log in case of transaction waiting for open to finish; doc fixes - SQLite 3.15.2 build with SQLITE_THREADSAFE=2 on iOS/macOS (SQLITE_THREADSAFE=1 on Android/Windows) and other flag fixes in this version branch to avoid possible malformed database due to multithreaded access ref: litehelpers/Cordova-sqlite-storage#703 - Windows 10 (UWP) build with /SAFESEH flag on Win32 (x86) target diff --git a/README.md b/README.md index 81f57562..0cc99440 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,22 @@ # Cordova/PhoneGap sqlite storage adapter (legacy core version branch) -Native interface to sqlite in a Cordova/PhoneGap plugin for _Android/iOS/macOS/Windows_, with API similar to HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/). +Native interface to sqlite in a Cordova/PhoneGap plugin for Android/iOS/macOS/Windows, with API similar to HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/). -License for Android and Windows versions: MIT or Apache 2.0 +License terms for Android and Windows platform versions: MIT or Apache 2.0 -License for iOS/macOS platform version: MIT only +License terms for iOS/macOS platform version: MIT only ## About this version branch -_This version branch contains the source code for the Android/iOS/macOS platforms (legacy core version branch)._ +This version branch contains the source code for the Android/iOS/macOS platforms (legacy core version branch). -_This version branch uses a `before_plugin_install` hook to install sqlite3 library dependencies from `cordova-sqlite-storage-dependencies` via npm._ - -_XXX TBD ???:_ +This version branch uses a `before_plugin_install` hook to install sqlite3 library dependencies from `cordova-sqlite-storage-dependencies` via npm. + @@ -80,7 +80,7 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase - `SQLITE_ENABLE_RTREE` - `SQLITE_DEFAULT_PAGE_SIZE=1024` and `SQLITE_DEFAULT_CACHE_SIZE=2000` to avoid "potentially distruptive change(s)" from SQLite 3.12.0 ref: - `SQLITE_OS_WINRT` for Windows only -- Use of other systems such as Cordova Plugman, PhoneGap CLI, PhoneGap Build, and Intel XDK is no longer supported since they do not honor the `before_plugin_install` hook. The supported solution is to use [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (available with GPL or commercial license options) or [litehelpers / Cordova-sqlite-legacy-build-support](https://github.com/litehelpers/Cordova-sqlite-legacy-build-support) (limited testing, limited updates) +- Use of other systems such as Cordova Plugman, PhoneGap CLI, PhoneGap Build, and Intel XDK is no longer supported since they do not honor the `before_plugin_install` hook. The supported solution is to use [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (available with GPL or commercial license terms) or [litehelpers / Cordova-sqlite-legacy-build-support](https://github.com/litehelpers/Cordova-sqlite-legacy-build-support) (limited testing, limited updates) - The iOS database location is now mandatory, as documented below. - This version supports the use of two (2) possible Android sqlite database implementations: - default: lightweight [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector) @@ -98,7 +98,7 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase - R-Tree is *not* tested or supported for Android in this version branch. - Android is supported back to SDK 10 (a.k.a. Gingerbread, Android 2.3.3); support for older versions is available upon request. - iOS versions supported: 8.x/9.x/10.x -- In case of memory issues please use smaller transactions or use the version (with GPL or commercial license options) at: [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) +- In case of memory issues please use smaller transactions or use the version at [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (GPL or commercial license terms). @@ -113,7 +113,7 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase - [brodybits / sql-promise-helper](https://github.com/brodybits/sql-promise-helper) provides a Promise-based API wrapper. - [nolanlawson / pouchdb-adapter-cordova-sqlite](https://github.com/nolanlawson/pouchdb-adapter-cordova-sqlite) supports this plugin along with other implementations such as [nolanlawson / sqlite-plugin-2](https://github.com/nolanlawson/sqlite-plugin-2) and [Microsoft / cordova-plugin-websql](https://github.com/Microsoft/cordova-plugin-websql). - macOS ("osx" platform) is now supported -- New [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) version with Android JSON and SQL statement handling implemented in C, as well as support for PhoneGap Build, Intel XDK, etc., available with GPL or commercial license options. Handles large SQL batches in less than half the time as this version. Also supports arbitrary database location on Android. +- New [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) version with Android JSON and SQL statement handling implemented in C, as well as support for PhoneGap Build, Intel XDK, etc. (GPL or commercial license terms). Handles large SQL batches in less than half the time as this version. Also supports arbitrary database location on Android. - Published [brodybits / Cordova-quick-start-checklist](https://github.com/brodybits/Cordova-quick-start-checklist) and [brodybits / Avoiding-some-Cordova-pitfalls](https://github.com/brodybits/Avoiding-some-Cordova-pitfalls). - Windows 8.1/Windows Phone 8.1/Windows 10 version is available **here** _(Visual Studio 2015 required, Visual Studio 2017 NOT supported by this version branch)_ as well as in [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) (with pre-populated database support) and [litehelpers / Cordova-sqlite-legacy](https://github.com/litehelpers/Cordova-sqlite-legacy) (with WP8 support). - Android version _currently uses_ the lightweight [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector) by default configuration (may be changed as described below) @@ -189,7 +189,7 @@ As "strongly recommended" by [Web SQL Database API 8.5 SQL injection](https://ww - As described below, auto-vacuum is NOT enabled by default. - It is possible to request a SQL statement list such as "SELECT 1; SELECT 2" within a single SQL statement string, however the plugin will only execute the first statement and silently ignore the others ref: [litehelpers/Cordova-sqlite-storage#551](https://github.com/litehelpers/Cordova-sqlite-storage/issues/551) - INSERT statement that affects multiple rows (due to SELECT cause or using TRIGGER(s), for example) does not report proper rowsAffected on Android -- Memory issue observed when adding a large number of records due to the JSON implementation which is improved in [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (available with GPL or commercial license options) +- Memory issue observed when adding a large number of records due to the JSON implementation which is improved in [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (GPL or commercial license terms) - Infinity (positive or negative) values are not supported on Android/iOS/macOS due to issues described above including a possible crash on iOS/macOS ref: [litehelpers/Cordova-sqlite-storage#405](https://github.com/litehelpers/Cordova-sqlite-storage/issues/405) - A stability issue was reported on the iOS version when in use together with [SockJS](http://sockjs.org/) client such as [pusher-js](https://github.com/pusher/pusher-js) at the same time (see [litehelpers/Cordova-sqlite-storage#196](https://github.com/litehelpers/Cordova-sqlite-storage/issues/196)). The workaround is to call sqlite functions and [SockJS](http://sockjs.org/) client functions in separate ticks (using setTimeout with 0 timeout). - If a sql statement fails for which there is no error handler or the error handler does not return `false` to signal transaction recovery, the plugin fires the remaining sql callbacks before aborting the transaction. @@ -220,17 +220,17 @@ Issues fixed in some newer version branches: - ~~The db version, display name, and size parameter values are not supported and will be ignored.~~ (No longer supported by the API) - Absolute and relative subdirectory path(s) are not tested or supported. - This plugin will not work before the callback for the 'deviceready' event has been fired, as described in **Usage**. (This is consistent with the other Cordova plugins.) -- Extremely large records are not supported by this plugin version. TBD: specify maximum record; FUTURE TBD: to be fixed in [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (available with GPL or commercial license options) -- This plugin version will not work within a web worker (not properly supported by the Cordova framework). Use within a web worker is supported for Android/iOS in: [litehelpers / Cordova-sqlite-evplus-legacy-workers-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-workers-free) (available with GPL or premium commercial license options) +- Extremely large records are not supported by this plugin version. TBD: specify maximum record; FUTURE TBD: to be fixed in [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (GPL or commercial license terms). +- This plugin version will not work within a web worker (not properly supported by the Cordova framework). Use within a web worker is supported for Android/iOS in [litehelpers / cordova-sqlite-evmax-ext-workers-legacy-build-free](https://github.com/litehelpers/cordova-sqlite-evmax-ext-workers-legacy-build-free) (GPL or premium commercial license terms). - In-memory database `db=window.sqlitePlugin.openDatabase({name: ':memory:', ...})` is currently not supported. - The Android version cannot work with more than 100 open db files (due to the threading model used). -- UNICODE `\u2028` (line separator) and `\u2029` (paragraph separator) characters are currently not supported and known to be broken _on_ iOS, macOS, and Android _platforms_ due to JSON issues reported in [Cordova bug CB-9435](https://issues.apache.org/jira/browse/CB-9435) and [cordova/cordova-discuss#57](https://github.com/cordova/cordova-discuss/issues/57). There *may* be a similar issue with certain other UNICODE characters in the iOS/macOS version (needs further investigation). This is fixed for iOS in: [litehelpers / Cordova-sqlite-evplus-legacy-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-free) and [litehelpers / Cordova-sqlite-evplus-legacy-attach-detach-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-attach-detach-free) (available with GPL or special commercial license options) as well as [litehelpers / Cordova-sqlite-evplus-legacy-workers-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-workers-free) (available with GPL or premium commercial license options) -- The BLOB data type is not fully supported by this version branch. SELECT BLOB in Base64 format is supported by [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) (permissive license terms) and [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (GPL or commercial license options). -- Truncation in case of UNICODE `\u0000` (same as `\0`) character on Android _(default Android-sqlite-connector database implementation) and Windows_ +- UNICODE `\u2028` (line separator) and `\u2029` (paragraph separator) characters are not supported and known to be broken on iOS, macOS, and Android platforms due to JSON issues reported in [Cordova bug CB-9435](https://issues.apache.org/jira/browse/CB-9435) and [cordova/cordova-discuss#57](https://github.com/cordova/cordova-discuss/issues/57). There *may* be a similar issue with certain other UNICODE characters in the iOS/macOS version (needs further investigation). This is fixed for iOS in [litehelpers / cordova-sqlite-evplus-ext-legacy-build-free](https://github.com/litehelpers/cordova-sqlite-evplus-ext-legacy-build-free) and [litehelpers / Cordova-sqlite-evplus-legacy-attach-detach-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-attach-detach-free) (GPL or special commercial license terms) as well as [litehelpers / cordova-sqlite-evmax-ext-workers-legacy-build-free](https://github.com/litehelpers/cordova-sqlite-evmax-ext-workers-legacy-build-free) (GPL or premium commercial license terms). +- The BLOB data type is not fully supported by this version branch. SELECT BLOB in Base64 format is supported by [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) (permissive license terms) and [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (GPL or commercial license terms). +- Truncation in case of UNICODE `\u0000` (same as `\0`) character on Android (default Android-sqlite-connector database implementation) and Windows. - Case-insensitive matching and other string manipulations on Unicode characters, which is provided by optional ICU integration in the sqlite source and working with recent versions of Android, is not supported for any target platforms. - iOS/macOS platform version uses a thread pool but with only one thread working at a time due to "synchronized" database access - Some large query results may be slow, also due to the JSON implementation. -- ATTACH to another database file is not supported by this version. Attach/detach is supported (along with the memory and iOS UNICODE `\u2028` line separator / `\u2029` paragraph separator fixes) in: [litehelpers / Cordova-sqlite-evplus-legacy-attach-detach-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-attach-detach-free) (available with GPL or special commercial license options) +- ATTACH to another database file is not supported by this version branch. Attach/detach is supported (along with the memory and iOS UNICODE `\u2028` line separator / `\u2029` paragraph separator fixes) in [litehelpers / Cordova-sqlite-evplus-legacy-attach-detach-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-attach-detach-free) (available with GPL or special commercial license terms). - UPDATE/DELETE with LIMIT or ORDER BY is not supported. - WITH clause is not supported by some older Android/iOS versions. - User-defined savepoints are not supported and not expected to be compatible with the transaction locking mechanism used by this plugin. In addition, the use of BEGIN/COMMIT/ROLLBACK statements is not supported. @@ -309,17 +309,18 @@ Documented in: [brodybits / Avoiding-some-Cordova-pitfalls](https://github.com/b ## Alternatives -### Other versions +### Comparison of sqlite plugin versions -- [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) - version with REGEXP (Android/iOS/macOS), SELECT BLOB in Base64 format (all platforms Android/iOS/macOS/Windows), and pre-populated databases (all platforms Android/iOS/macOS/Windows) -- [litehelpers / Cordova-sqlite-legacy-build-support](https://github.com/litehelpers/Cordova-sqlite-legacy-build-support) - maintenance of WP8 version along with Windows 8.1/Windows Phone 8.1 and the other supported platforms Android/iOS/macOS/Windows 10; limited support for PhoneGap CLI/PhoneGap Build/plugman/Intel XDK; limited testing; limited updates +- [litehelpers / Cordova-sqlite-storage](https://github.com/litehelpers/Cordova-sqlite-storage) - core version for Android/iOS/macOS/Windows (permissive license terms) +- [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) - version with REGEXP (Android/iOS/macOS), SELECT BLOB in Base64 format (all platforms Android/iOS/macOS/Windows), and pre-populated databases (all platforms Android/iOS/macOS/Windows). Permissive license terms. +- [litehelpers / Cordova-sqlite-legacy-build-support](https://github.com/litehelpers/Cordova-sqlite-legacy-build-support) - maintenance of WP8 version along with Windows 8.1/Windows Phone 8.1 and the other supported platforms Android/iOS/macOS/Windows 10; limited support for PhoneGap CLI/PhoneGap Build/plugman/Intel XDK; limited testing; limited updates. Permissive license terms. - [litehelpers / Cordova-sqlcipher-adapter](https://github.com/litehelpers/Cordova-sqlcipher-adapter) - supports [SQLCipher](https://www.zetetic.net/sqlcipher/) for Android/iOS/macOS/Windows -- [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) - Enhancements for Android: JSON and SQL statement handling implemented in C, supports larger transactions and handles large SQL batches in less than half the time as this version. Supports arbitrary database location on Android. Support for build environments such as PhoneGap Build and Intel XDK. Available with GPL or commercial license options. Also includes REGEXP (Android/iOS/macOS) and SELECT BLOB in Base64 format (all platforms Android/iOS/macOS/Windows). -- [litehelpers / Cordova-sqlite-evplus-legacy-workers-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-workers-free) - version with support for web workers, includes internal memory improvements to support larger transactions (Android/iOS) and fix to support all Unicode characters (iOS) (with GPL or premium commercial license options) -- [litehelpers / Cordova-sqlite-evplus-legacy-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-free) - internal memory improvements to support larger transactions (Android/iOS) and fix to support all Unicode characters (iOS) - with GPL or special commercial license options -- [litehelpers / Cordova-sqlite-evplus-legacy-attach-detach-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-attach-detach-free) - version with support for ATTACH, includes internal memory improvements to support larger transactions (Android/iOS) and fix to support all Unicode characters (with GPL or special commercial license options) -- Adaptation for React Native Android and iOS: [andpor / react-native-sqlite-storage](https://github.com/andpor/react-native-sqlite-storage) -- Original version for iOS (with a slightly different transaction API): [davibe / Phonegap-SQLitePlugin](https://github.com/davibe/Phonegap-SQLitePlugin) +- [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) - Enhancements for Android: JSON and SQL statement handling implemented in C, supports larger transactions and handles large SQL batches in less than half the time as this version. Supports arbitrary database location on Android. Support for build environments such as PhoneGap Build and Intel XDK. Also includes REGEXP (Android/iOS/macOS) and SELECT BLOB in Base64 format (all platforms Android/iOS/macOS/Windows). GPL or commercial license terms. +- [litehelpers / cordova-sqlite-evplus-ext-legacy-build-free](https://github.com/litehelpers/cordova-sqlite-evplus-ext-legacy-build-free) - internal memory improvements to support larger transactions (Android/iOS) and fix to support all Unicode characters (iOS). (GPL or special commercial license terms). +- [litehelpers / Cordova-sqlite-evplus-legacy-attach-detach-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-attach-detach-free) - version with support for ATTACH, includes internal memory improvements to support larger transactions (Android/iOS) and fix to support all Unicode characters (GPL or special commercial license terms). +- [litehelpers / cordova-sqlite-evmax-ext-workers-legacy-build-free](https://github.com/litehelpers/cordova-sqlite-evmax-ext-workers-legacy-build-free) - version with support for web workers, includes internal memory improvements to support larger transactions (Android/iOS) and fix to support all Unicode characters (iOS). (GPL or premium commercial license terms). +- Adaptation for React Native Android and iOS: [andpor / react-native-sqlite-storage](https://github.com/andpor/react-native-sqlite-storage) (permissive license terms) +- Original version for iOS (with a non-standard, outdated transaction API): [davibe / Phonegap-SQLitePlugin](https://github.com/davibe/Phonegap-SQLitePlugin) (permissive license terms) diff --git a/SQLitePlugin.coffee.md b/SQLitePlugin.coffee.md index a3dc8c06..dccb55d8 100644 --- a/SQLitePlugin.coffee.md +++ b/SQLitePlugin.coffee.md @@ -135,7 +135,7 @@ else if @dbname of @openDBs - console.log 'new transaction is waiting for open operation' + console.log 'new transaction is queued, waiting for open operation to finish' else # XXX SHOULD NOT GET HERE. # FUTURE TBD TODO: in this exceptional case abort and discard the transaction. diff --git a/package.json b/package.json index 45ee9b34..d2cdd750 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cordova-sqlite-legacy-core", - "version": "1.0.1", + "version": "1.0.2", "description": "Native interface to SQLite for PhoneGap/Cordova (legacy core version branch)", "cordova": { "id": "cordova-sqlite-legacy-core", diff --git a/plugin.xml b/plugin.xml index ee5b2814..13af87ae 100644 --- a/plugin.xml +++ b/plugin.xml @@ -2,7 +2,7 @@ + version="1.0.2"> Cordova sqlite storage plugin - legacy core version branch diff --git a/spec/www/index.html b/spec/www/index.html index d84a43e6..fa6e981e 100644 --- a/spec/www/index.html +++ b/spec/www/index.html @@ -16,8 +16,10 @@ - + + + diff --git a/spec/www/spec/regexp-test.js b/spec/www/spec/regexp-test.js index a0e0ee3d..368f24d9 100644 --- a/spec/www/spec/regexp-test.js +++ b/spec/www/spec/regexp-test.js @@ -40,11 +40,11 @@ var mytests = function() { it(suiteName + 'Simple REGEXP test', function(done) { - if (isWP8) pending('NOT IMPLEMENTED for WP8'); - if (isWindows) pending('NOT IMPLEMENTED for Windows'); + if (isWP8) pending('NOT IMPLEMENTED for WP8 (plugin)'); + if (isWindows) pending('NOT IMPLEMENTED for Windows (plugin)'); if (!isWebSql && !isWindows && isAndroid) pending('SKIP for Android plugin'); // TBD SKIP for Android plugin (for now) if (isWebSql && !isAndroid && !isWindows && !isWP8) pending('SKIP for iOS (WebKit) Web SQL'); - if (!isWebSql && !isAndroid && !isWindows && !isWP8) pending('NOT IMPLEMENTED for iOS/macOS'); + if (!isWebSql && !isAndroid && !isWindows && !isWP8) pending('NOT IMPLEMENTED for iOS/macOS plugin'); var db = openDatabase('simple-regexp-test.db', '1.0', 'test', DEFAULT_SIZE); diff --git a/www/SQLitePlugin.js b/www/SQLitePlugin.js index 6d612d44..51dee0cf 100644 --- a/www/SQLitePlugin.js +++ b/www/SQLitePlugin.js @@ -102,7 +102,7 @@ this.startNextTransaction(); } else { if (this.dbname in this.openDBs) { - console.log('new transaction is waiting for open operation'); + console.log('new transaction is queued, waiting for open operation to finish'); } else { console.log('database is closed, new transaction is [stuck] waiting until db is opened again!'); } From 060c0a0d76b9220df496e8602aabfba47f759120 Mon Sep 17 00:00:00 2001 From: Daniel Sogl Date: Fri, 2 Jun 2017 12:06:45 +0200 Subject: [PATCH 16/47] style(package): add macOS Platform ref: litehelpers/Cordova-sqlite-storage#695 --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index d2cdd750..b9f16b40 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "platforms": [ "android", "ios", + "osx", "windows" ] }, From 89cbb948dd18bb25e3033fb15eedc0002d0bbdcf Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Tue, 7 Nov 2017 16:04:37 -0500 Subject: [PATCH 17/47] Update Android platforms supported; other doc updates --- README.md | 79 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 42 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 0cc99440..f46fe938 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,8 @@ The workaround is to use the `androidDatabaseImplementation: 2` setting as descr var db = window.sqlitePlugin.openDatabase({name: "my.db", androidDatabaseImplementation: 2}); ``` + + ## BREAKING CHANGE: Database location parameter is now mandatory The `location` or `iosDatabaseLocation` *must* be specified in the `openDatabase` and `deleteDatabase` calls, as documented below. @@ -63,9 +65,9 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase ## Status -- A recent version of the Cordova CLI (such as `6.5.0`) is recommended. Cordova versions older than `6.0.0` are missing the `cordova-ios@4.0.0` security fixes. In addition it is *required* to use `cordova prepare` in case of cordova-ios older than `4.3.0` (Cordova CLI `6.4.0`). +- A recent version of the Cordova CLI (such as `6.5.0` or `7.1.0`) is recommended. Cordova versions older than `6.0.0` are missing the `cordova-ios@4.0.0` security fixes. In addition it is *required* to use `cordova prepare` in case of cordova-ios older than `4.3.0` (Cordova CLI `6.4.0`). - This version uses a `before_plugin_install` hook to install sqlite3 library dependencies from `cordova-sqlite-storage-dependencies` via npm. -- Build with SQLite (version `3.8.10.2`) with the following build settings for iOS/macOS/Windows: +- SQLite `3.8.10.2` included when building (all platforms), with the following definitions for iOS/macOS/Windows: - `SQLITE_THREADSAFE=1` (`SQLITE_THREADSAFE=2` on iOS/macOS) - `SQLITE_DEFAULT_MEMSTATUS=0` - `SQLITE_OMIT_DECLTYPE` @@ -85,19 +87,24 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase - This version supports the use of two (2) possible Android sqlite database implementations: - default: lightweight [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector) - optional: built-in Android database classes (usage described below) -- WP8 support is available in: _[litehelpers / Cordova-sqlite-legacy-build-support](https://github.com/litehelpers/Cordova-sqlite-legacy-build-support) (along with Windows 8.1/Windows Phone 8.1/Windows 10 using Visual Studio 2015)_ +- Support for WP8 is available in: [litehelpers / Cordova-sqlite-legacy-build-support](https://github.com/litehelpers/Cordova-sqlite-legacy-build-support) (along with Windows 8.1/Windows Phone 8.1/Windows 10 using Visual Studio 2015) - The following features are available in [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext): - REGEXP (Android/iOS/macOS) - SELECT BLOB data in Base64 format (all platforms Android/iOS/macOS/Windows) - Pre-populated database (Android/iOS/macOS/Windows) - Amazon Fire-OS is dropped due to lack of support by Cordova. Android version should be used to deploy to Fire-OS 5.0(+) devices. For reference: [cordova/cordova-discuss#32 (comment)](https://github.com/cordova/cordova-discuss/issues/32#issuecomment-167021676) -- Windows version using the performant C++ [doo / SQLite3-WinRT](https://github.com/doo/SQLite3-WinRT) component is in an alpha state: - - Issue with UNICODE `\u0000` character (same as `\0`) +- Windows version (using a customized version of the performant [doo / SQLite3-WinRT](https://github.com/doo/SQLite3-WinRT) C++ component) has the following known limitations: + - This version branch with dependency on platform toolset libraries included by Visual Studio 2015 ref: [litehelpers/Cordova-sqlite-storage#580](https://github.com/litehelpers/Cordova-sqlite-storage/issues/580) + - It is **not** possible to use this plugin with the default "Any CPU" target. A specific target CPU type **must** be specified when building an app with this plugin. + - Truncation issue with UNICODE `\u0000` character (same as `\0`) - No background processing -- FTS3 and FTS4 are tested working OK in this version branch (for all target platforms in this version branch Android/iOS/macOS) -- R-Tree is *not* tested or supported for Android in this version branch. -- Android is supported back to SDK 10 (a.k.a. Gingerbread, Android 2.3.3); support for older versions is available upon request. -- iOS versions supported: 8.x/9.x/10.x + - INCORRECT error code and INCONSISTENT error message in error callbacks ref: [litehelpers/Cordova-sqlite-storage#539](https://github.com/litehelpers/Cordova-sqlite-storage/issues/539) + - Not possible to read BLOB column values + - Windows platform version uses `UTF-16le` internal database encoding while the other platform versions use `UTF-8` internal encoding. (`UTF-8` internal encoding is preferred ref: [litehelpers/Cordova-sqlite-storage#652](https://github.com/litehelpers/Cordova-sqlite-storage/issues/652)) +- FTS3, FTS4, and R-Tree support is tested working OK in this version (for all target platforms in this version branch Android/iOS/macOS/Windows) +- Android versions supported: 2.3.3 - 7.1.1 (API level 10 - 25), depending on Cordova version ref: +minimum API level is supported back to SDK 10 (a.k.a. Gingerbread, Android 2.3.3); support for older versions is available upon request. +- iOS versions supported: 8.x / 9.x / 10.x / 11.x - In case of memory issues please use smaller transactions or use the version at [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (GPL or commercial license terms). @@ -115,8 +122,8 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase - macOS ("osx" platform) is now supported - New [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) version with Android JSON and SQL statement handling implemented in C, as well as support for PhoneGap Build, Intel XDK, etc. (GPL or commercial license terms). Handles large SQL batches in less than half the time as this version. Also supports arbitrary database location on Android. - Published [brodybits / Cordova-quick-start-checklist](https://github.com/brodybits/Cordova-quick-start-checklist) and [brodybits / Avoiding-some-Cordova-pitfalls](https://github.com/brodybits/Avoiding-some-Cordova-pitfalls). -- Windows 8.1/Windows Phone 8.1/Windows 10 version is available **here** _(Visual Studio 2015 required, Visual Studio 2017 NOT supported by this version branch)_ as well as in [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) (with pre-populated database support) and [litehelpers / Cordova-sqlite-legacy](https://github.com/litehelpers/Cordova-sqlite-legacy) (with WP8 support). -- Android version _currently uses_ the lightweight [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector) by default configuration (may be changed as described below) +- Windows 8.1/Windows Phone 8.1/Windows 10 version is available **here** (Visual Studio 2015 required, Visual Studio 2017 NOT supported by this version branch) as well as in [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) (with pre-populated database support) and [litehelpers / Cordova-sqlite-legacy](https://github.com/litehelpers/Cordova-sqlite-legacy) (with WP8 support). +- Android version currently uses the lightweight [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector) by default configuration (may be changed as described below). - Self-test functions to verify proper installation and operation of this plugin - More explicit `openDatabase` and `deleteDatabase` `iosDatabaseLocation` option - Added simple sql batch function @@ -129,13 +136,13 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase - Drop-in replacement for HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/): the only change should be to replace the static `window.openDatabase()` factory call with `window.sqlitePlugin.openDatabase()`, with parameters as documented below. - Failure-safe nested transactions with batch processing optimizations (according to HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/)) -- API (based on HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/)) is designed to be as flexible as possible but does not allow the application to leave any transactions hanging open. +- Transaction API (based on HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/)) is designed for maximum flexiblibility, does not allow any transactions to be left hanging open. - As described in [this posting](http://brodyspark.blogspot.com/2012/12/cordovaphonegap-sqlite-plugins-offer.html): - - Keeps sqlite database in a user data location that is known; can be reconfigured (iOS/macOS platform version); and may be synchronized to iCloud (iOS platform version). - - No 5MB maximum, more information at: http://www.sqlite.org/limits.html + - Keeps sqlite database in known, platform specific user data location on all platforms (Android/iOS/macOS/Windows), which can be reconfigured on iOS/macOS. Whether or not the database on the iOS platform is synchronized to iCloud depends on the selected database location. + - No arbitrary size limit. SQLite limits described at: - Also tested for multi-page applications with window location changes -- This project is self-contained (with sqlite3 dependencies auto-fetched by npm); no dependencies on other plugins such as cordova-plugin-file -- Windows 8.1/Windows Phone 8.1/Windows 10 version uses the performant C++ [doo / SQLite3-WinRT](https://github.com/doo/SQLite3-WinRT) component. +- This project is self-contained though with sqlite3 dependencies auto-fetched by npm. There are no dependencies on other plugins such as cordova-plugin-file. +- Windows platform version uses a customized version of the performant [doo / SQLite3-WinRT](https://github.com/doo/SQLite3-WinRT) C++ component. - [SQLCipher](https://www.zetetic.net/sqlcipher/) support for Android/iOS/macOS/Windows is available in: [litehelpers / Cordova-sqlcipher-adapter](https://github.com/litehelpers/Cordova-sqlcipher-adapter) - Intellectual property: - All source code is tracked to the original author in git @@ -152,7 +159,7 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase - [Trailforks Mountain Bike Trail Map App](http://www.trailforks.com/apps/map/) with a couple of nice videos at: - [Get It Done app](http://getitdoneapp.com/) by [marcucio.com](http://marcucio.com/) - [KAAHE Health Encyclopedia](http://www.kaahe.org/en/index.php?option=com_content&view=article&id=817): Official health app of the Kingdom of Saudi Arabia. -- [Larkwire](http://www.larkwire.com/) (iOS version): Learn bird songs the fun way +- [Larkwire](http://www.larkwire.com/) (iOS platform): Learn bird songs the fun way - [Tangorin](https://play.google.com/store/apps/details?id=com.tangorin.app) (Android) Japanese Dictionary at [tangorin.com](http://tangorin.com/) @@ -188,10 +195,10 @@ As "strongly recommended" by [Web SQL Database API 8.5 SQL injection](https://ww - iOS/macOS platform version does not support certain rapidly repeated open-and-close or open-and-delete test scenarios due to how the implementation handles background processing - As described below, auto-vacuum is NOT enabled by default. - It is possible to request a SQL statement list such as "SELECT 1; SELECT 2" within a single SQL statement string, however the plugin will only execute the first statement and silently ignore the others ref: [litehelpers/Cordova-sqlite-storage#551](https://github.com/litehelpers/Cordova-sqlite-storage/issues/551) -- INSERT statement that affects multiple rows (due to SELECT cause or using TRIGGER(s), for example) does not report proper rowsAffected on Android +- Execution of INSERT statement that affects multiple rows (due to SELECT cause or using TRIGGER(s), for example) reports incorrect rowsAffected on Android in case the built-in Android database used (using the `androidDatabaseImplementation` option in `window.sqlitePlugin.openDatabase`) - Memory issue observed when adding a large number of records due to the JSON implementation which is improved in [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (GPL or commercial license terms) - Infinity (positive or negative) values are not supported on Android/iOS/macOS due to issues described above including a possible crash on iOS/macOS ref: [litehelpers/Cordova-sqlite-storage#405](https://github.com/litehelpers/Cordova-sqlite-storage/issues/405) -- A stability issue was reported on the iOS version when in use together with [SockJS](http://sockjs.org/) client such as [pusher-js](https://github.com/pusher/pusher-js) at the same time (see [litehelpers/Cordova-sqlite-storage#196](https://github.com/litehelpers/Cordova-sqlite-storage/issues/196)). The workaround is to call sqlite functions and [SockJS](http://sockjs.org/) client functions in separate ticks (using setTimeout with 0 timeout). +- A stability issue was reported on the iOS platform version when in use together with [SockJS](http://sockjs.org/) client such as [pusher-js](https://github.com/pusher/pusher-js) at the same time (see [litehelpers/Cordova-sqlite-storage#196](https://github.com/litehelpers/Cordova-sqlite-storage/issues/196)). The workaround is to call sqlite functions and [SockJS](http://sockjs.org/) client functions in separate ticks (using setTimeout with 0 timeout). - If a sql statement fails for which there is no error handler or the error handler does not return `false` to signal transaction recovery, the plugin fires the remaining sql callbacks before aborting the transaction. - In case of an error, the error `code` member is bogus on _Android and Windows_. - Possible crash on Android when using Unicode emoji and other 4-octet UTF-8 characters due to [Android bug 81341](https://code.google.com/p/android/issues/detail?id=81341), which *should* be fixed in Android 6.x @@ -200,9 +207,6 @@ As "strongly recommended" by [Web SQL Database API 8.5 SQL injection](https://ww - It is NOT possible to open multiple databases with the same name but in different locations (iOS/macOS platform version). - Incorrect or missing insertId/rowsAffected in results for INSERT/UPDATE/DELETE SQL statements with extra semicolon(s) in the beginning for Android (android.database implementation) - readTransaction does *not* reject modification SQL statements with extra semicolon(s) in the beginning -- Problems reported with PhoneGap Build in the past: - - PhoneGap Build Hydration. - - Apparently FIXED: ~~PhoneGap Build may fail to build the iOS version unless the name of the app starts with an uppercase and contains no spaces (see [litehelpers/Cordova-sqlite-storage#243](https://github.com/litehelpers/Cordova-sqlite-storage/issues/243); [Wizcorp/phonegap-facebook-plugin#830](https://github.com/Wizcorp/phonegap-facebook-plugin/issues/830); [phonegap/build#431](https://github.com/phonegap/build/issues/431)).~~ Issues fixed in some newer version branches: - In case of an error, the error `code` member is bogus on Android @@ -220,19 +224,21 @@ Issues fixed in some newer version branches: - ~~The db version, display name, and size parameter values are not supported and will be ignored.~~ (No longer supported by the API) - Absolute and relative subdirectory path(s) are not tested or supported. - This plugin will not work before the callback for the 'deviceready' event has been fired, as described in **Usage**. (This is consistent with the other Cordova plugins.) -- Extremely large records are not supported by this plugin version. TBD: specify maximum record; FUTURE TBD: to be fixed in [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (GPL or commercial license terms). +- Extremely large records are not supported by this plugin. It is recommended to store images and similar binary data in separate files. TBD: specify maximum record. For future consideration: support in a version such as [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (available with GPL or commercial license options) - This plugin version will not work within a web worker (not properly supported by the Cordova framework). Use within a web worker is supported for Android/iOS in [litehelpers / cordova-sqlite-evmax-ext-workers-legacy-build-free](https://github.com/litehelpers/cordova-sqlite-evmax-ext-workers-legacy-build-free) (GPL or premium commercial license terms). - In-memory database `db=window.sqlitePlugin.openDatabase({name: ':memory:', ...})` is currently not supported. -- The Android version cannot work with more than 100 open db files (due to the threading model used). -- UNICODE `\u2028` (line separator) and `\u2029` (paragraph separator) characters are not supported and known to be broken on iOS, macOS, and Android platforms due to JSON issues reported in [Cordova bug CB-9435](https://issues.apache.org/jira/browse/CB-9435) and [cordova/cordova-discuss#57](https://github.com/cordova/cordova-discuss/issues/57). There *may* be a similar issue with certain other UNICODE characters in the iOS/macOS version (needs further investigation). This is fixed for iOS in [litehelpers / cordova-sqlite-evplus-ext-legacy-build-free](https://github.com/litehelpers/cordova-sqlite-evplus-ext-legacy-build-free) and [litehelpers / Cordova-sqlite-evplus-legacy-attach-detach-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-attach-detach-free) (GPL or special commercial license terms) as well as [litehelpers / cordova-sqlite-evmax-ext-workers-legacy-build-free](https://github.com/litehelpers/cordova-sqlite-evmax-ext-workers-legacy-build-free) (GPL or premium commercial license terms). -- The BLOB data type is not fully supported by this version branch. SELECT BLOB in Base64 format is supported by [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) (permissive license terms) and [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (GPL or commercial license terms). +- The Android platform version cannot properly support more than 100 open database files due to the threading model used. +- SQL error messages reported by Windows platform version are not consistent with Android/iOS/macOS platform versions. +- UNICODE `\u2028` (line separator) and `\u2029` (paragraph separator) characters are not supported and known to be broken on iOS, macOS, and Android platform versions due to JSON issues reported in [Cordova bug CB-9435](https://issues.apache.org/jira/browse/CB-9435) and [cordova/cordova-discuss#57](https://github.com/cordova/cordova-discuss/issues/57). +- UNICODE `\u2028` (line separator) and `\u2029` (paragraph separator) characters are currently not supported and known to be broken on iOS, macOS, and Android platform versions due to JSON issues reported in [Cordova bug CB-9435](https://issues.apache.org/jira/browse/CB-9435) and [cordova/cordova-discuss#57](https://github.com/cordova/cordova-discuss/issues/57). This is fixed with a workaround for iOS/macOS in: [litehelpers / Cordova-sqlite-evplus-legacy-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-free) and [litehelpers / Cordova-sqlite-evplus-legacy-attach-detach-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-attach-detach-free) (available with GPL or special commercial license options) as well as [litehelpers / Cordova-sqlite-evplus-legacy-workers-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-workers-free) (available with GPL or premium commercial license options) +- The BLOB data type is not fully supported by this version branch. SELECT BLOB in Base64 format is supported by [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) (permissive license terms) and [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (GPL or commercial license options). - Truncation in case of UNICODE `\u0000` (same as `\0`) character on Android (default Android-sqlite-connector database implementation) and Windows. - Case-insensitive matching and other string manipulations on Unicode characters, which is provided by optional ICU integration in the sqlite source and working with recent versions of Android, is not supported for any target platforms. -- iOS/macOS platform version uses a thread pool but with only one thread working at a time due to "synchronized" database access +- The iOS/macOS platform version uses a thread pool but with only one thread working at a time due to "synchronized" database access - Some large query results may be slow, also due to the JSON implementation. - ATTACH to another database file is not supported by this version branch. Attach/detach is supported (along with the memory and iOS UNICODE `\u2028` line separator / `\u2029` paragraph separator fixes) in [litehelpers / Cordova-sqlite-evplus-legacy-attach-detach-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-attach-detach-free) (available with GPL or special commercial license terms). - UPDATE/DELETE with LIMIT or ORDER BY is not supported. -- WITH clause is not supported by some older Android/iOS versions. +- WITH clause is not supported on some older Android platform versions in case the `androidDatabaseImplementation: 2` (built-in android.database implementation) option is used. - User-defined savepoints are not supported and not expected to be compatible with the transaction locking mechanism used by this plugin. In addition, the use of BEGIN/COMMIT/ROLLBACK statements is not supported. - Problems have been reported when using this plugin with Crosswalk (for Android). It may help to install Crosswalk as a plugin instead of using Crosswalk to create the project. - Does not work with [axemclion / react-native-cordova-plugin](https://github.com/axemclion/react-native-cordova-plugin) since the `window.sqlitePlugin` object is *not* properly exported (ES5 feature). It is recommended to use [andpor / react-native-sqlite-storage](https://github.com/andpor/react-native-sqlite-storage) for SQLite database access with React Native Android/iOS instead. @@ -322,7 +328,7 @@ Documented in: [brodybits / Avoiding-some-Cordova-pitfalls](https://github.com/b - Adaptation for React Native Android and iOS: [andpor / react-native-sqlite-storage](https://github.com/andpor/react-native-sqlite-storage) (permissive license terms) - Original version for iOS (with a non-standard, outdated transaction API): [davibe / Phonegap-SQLitePlugin](https://github.com/davibe/Phonegap-SQLitePlugin) (permissive license terms) - + ### Other SQLite adapter projects @@ -344,7 +350,6 @@ Documented in: [brodybits / Avoiding-some-Cordova-pitfalls](https://github.com/b - Use [phearme / cordova-ContentProviderPlugin](https://github.com/phearme/cordova-ContentProviderPlugin) to query content providers on Android devices - [ABB-Austin / cordova-plugin-indexeddb-async](https://github.com/ABB-Austin/cordova-plugin-indexeddb-async) - Asynchronous IndexedDB plugin for Cordova that uses [axemclion / IndexedDBShim](https://github.com/axemclion/IndexedDBShim) (Browser/iOS/Android/Windows) and [Thinkwise / cordova-plugin-websql](https://github.com/Thinkwise/cordova-plugin-websql) - (Windows) -- Another sqlite binding for React-Native (iOS version): [almost/react-native-sqlite](https://github.com/almost/react-native-sqlite) - Use [NativeScript](https://www.nativescript.org) with its web view and [NathanaelA / nativescript-sqlite](https://github.com/Natha naelA/nativescript-sqlite) (Android and/or iOS) - Standard HTML5 [local storage](https://en.wikipedia.org/wiki/Web_storage#localStorage) @@ -362,7 +367,7 @@ To verify that both the Javascript and native part of this plugin are installed window.sqlitePlugin.echoTest(successCallback, errorCallback); ``` -To verify that this plugin is able to open, populate, read, update, and delete a test database (named `___$$$___litehelpers___$$$___test___$$$___.db`) properly: +To verify that this plugin is able to open a database (named `___$$$___litehelpers___$$$___test___$$$___.db`), execute the CRUD (create, read, update, and delete) operations, and clean it up properly: ```js window.sqlitePlugin.selfTest(successCallback, errorCallback); @@ -372,7 +377,7 @@ window.sqlitePlugin.selfTest(successCallback, errorCallback); ## General -- Drop-in replacement for HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/): the only change should be to replace the static `window.openDatabase()` factory call with `window.sqlitePlugin.openDatabase()`, with parameters as documented below. (Some known deviations are documented in newer version branches.) +- Drop-in replacement for HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/): the only change should be to replace the static `window.openDatabase()` factory call with `window.sqlitePlugin.openDatabase()`, with parameters as documented below. Some known deviations are documented in TBD newer version branches. Reports of any additional deviations would be appreciated. - Single-page application design is recommended. - In case of a multi-page application the JavaScript used by each page must use `sqlitePlugin.openDatabase` to open the database access handle object before it can access the data. @@ -467,7 +472,7 @@ window.openDatabase = function(dbname, ignored1, ignored2, ignored3) { By default, this plugin uses [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector), which is lightweight and should be more efficient than the built-in Android database classes. To use the built-in Android database classes instead: ```js -var db = window.sqlitePlugin.openDatabase({name: "my.db", androidDatabaseImplementation: 2}); +var db = window.sqlitePlugin.openDatabase({name: 'my.db', location: 'default', androidDatabaseImplementation: 2}); ``` **IMPORTANT:** @@ -476,7 +481,7 @@ var db = window.sqlitePlugin.openDatabase({name: "my.db", androidDatabaseImpleme ### Workaround for Android db locking issue -[litehelpers/Cordova-sqlite-storage#193](https://github.com/litehelpers/Cordova-sqlite-storage/issues/193) reported (as observed by several app developers) that certain versions of the Android database classes, if the app is stopped or aborted without closing the database then: +[litehelpers/Cordova-sqlite-storage#193](https://github.com/litehelpers/Cordova-sqlite-storage/issues/193) reported (as observed by a number of app developers in the past) that when using the `androidDatabaseImplementation: 2` setting on certain Android versions and if the app is stopped or aborted without closing the database then: - (sometimes) there is an unexpected database lock - the data that was inserted is lost. @@ -658,7 +663,7 @@ db.readTransaction(function(tx) { The threading model depends on which version is used: - For Android, one background thread per db; -- for _iOS/macOS_, background processing using a very limited thread pool (only one thread working at a time); +- for iOS/macOS, background processing using a very limited thread pool (only one thread working at a time); - for Windows, no background processing. @@ -804,7 +809,7 @@ db.executeSql("SELECT LENGTH('tenletters') AS stringlength", [], function (res) }); ``` -**SECOND BUG:** When a database connection is closed, any queued transactions are left hanging. All pending transactions should be errored when a database connection is closed. +**SECOND BUG:** When a database connection is closed, any queued transactions are left hanging. TODO: All pending transactions should be errored whenever a database connection is closed. **NOTE:** As described above, if multiple database access handle objects are opened for the same database and one database handle access object is closed, the database is no longer available for the other database handle objects. Possible workarounds: - It is still possible to open one or more new database handle objects on a database that has been closed. @@ -902,7 +907,7 @@ cordova platform add ios - `www`: `SQLitePlugin.js` platform-independent Javascript as generated from `SQLitePlugin.coffee.md` (and checked in!) - `src`: platform-specific source code - `spec`: test suite using Jasmine (2.2.0) -- `tests`: very simple Jasmine test suite that is run on Circle CI (Android version) and Travis CI (iOS version) (used as a placeholder) +- `tests`: very simple Jasmine test suite that is run on Circle CI (Android platform) and Travis CI (iOS platform) (used as a placeholder) ## Installation test From 65fc769ebb67c705f92c9886c0df531408cd0dad Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Tue, 7 Nov 2017 14:50:36 -0500 Subject: [PATCH 18/47] Suppress warnings for sqlite3.c & PSPDFThreadSafeMutableDictionary.m on iOS/macOS Thanks to @cjpearson for the suggestion in litehelpers/Cordova-sqlite-storage#569 --- CHANGES.md | 4 ++++ package.json | 2 +- plugin.xml | 12 +++++++----- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 42296867..d41aebf8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Changes +##### cordova-sqlite-legacy-core 1.0.3 + +- Suppress warnings when building sqlite3.c & PSPDFThreadSafeMutableDictionary.m on iOS/macOS + ##### cordova-sqlite-legacy-core 1.0.2 - Fix log in case of transaction waiting for open to finish; doc fixes diff --git a/package.json b/package.json index b9f16b40..90ac5281 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cordova-sqlite-legacy-core", - "version": "1.0.2", + "version": "1.0.3", "description": "Native interface to SQLite for PhoneGap/Cordova (legacy core version branch)", "cordova": { "id": "cordova-sqlite-legacy-core", diff --git a/plugin.xml b/plugin.xml index 13af87ae..96155984 100644 --- a/plugin.xml +++ b/plugin.xml @@ -2,7 +2,7 @@ + version="1.0.3"> Cordova sqlite storage plugin - legacy core version branch @@ -56,11 +56,12 @@ - + + compiler-flags="-w -DSQLITE_THREADSAFE=2 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_TEMP_STORE=2 -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_RTREE -DSQLITE_DEFAULT_PAGE_SIZE=1024 -DSQLITE_DEFAULT_CACHE_SIZE=2000" /> @@ -76,11 +77,12 @@ - + + compiler-flags="-w -DSQLITE_THREADSAFE=2 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_TEMP_STORE=2 -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_RTREE -DSQLITE_DEFAULT_PAGE_SIZE=1024" /> From c2959bbb9277bb4e15624886333652c5cae489d1 Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Sun, 12 Nov 2017 15:45:44 -0500 Subject: [PATCH 19/47] New bug 666 workaround: close db before opening (ignore close error) ref: litehelpers/Cordova-sqlite-storage#666 --- CHANGES.md | 4 ++++ README.md | 2 +- SQLitePlugin.coffee.md | 21 +++++++-------------- package.json | 2 +- plugin.xml | 2 +- spec/www/spec/db-open-close-delete-test.js | 11 ++++------- www/SQLitePlugin.js | 18 ++++++++---------- 7 files changed, 26 insertions(+), 34 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index d41aebf8..8e937212 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Changes +##### cordova-sqlite-legacy-core 1.0.4 + +- New workaround solution to BUG 666: close db before opening (ignore close error) + ##### cordova-sqlite-legacy-core 1.0.3 - Suppress warnings when building sqlite3.c & PSPDFThreadSafeMutableDictionary.m on iOS/macOS diff --git a/README.md b/README.md index f46fe938..24c3df23 100644 --- a/README.md +++ b/README.md @@ -111,9 +111,9 @@ minimum API level is supported back to SDK 10 (a.k.a. Gingerbread, Android 2.3.3 ## Announcements +- New workaround solution to [BUG 666 (litehelpers/Cordova-sqlite-storage#666)](https://github.com/litehelpers/Cordova-sqlite-storage/issues/666) (possible transaction issue after window.location change with possible data loss): attempt to close database before opening (ignore close error) - Windows 10 (UWP) build with /SAFESEH flag on Win32 (x86) target to specify "Image has Safe Exception Handlers" as described in - Fixed iOS/macOS platform version to use [PSPDFThreadSafeMutableDictionary.m](https://gist.github.com/steipete/5928916) to avoid threading issue ref: [litehelpers/Cordova-sqlite-storage#716](https://github.com/litehelpers/Cordova-sqlite-storage/issues/716) -- Resolved transaction problem after window.location (page) change with possible data loss ref: [litehelpers/Cordova-sqlite-storage#666](https://github.com/litehelpers/Cordova-sqlite-storage/issues/666) - [brodybits / cordova-sqlite-test-app](https://github.com/brodybits/cordova-sqlite-test-app) project is a CC0 (public domain) starting point (NOTE that this plugin must be added) and may also be used to reproduce issues with this plugin. - The Lawnchair adapter is now moved to [litehelpers / cordova-sqlite-lawnchair-adapter](https://github.com/litehelpers/cordova-sqlite-lawnchair-adapter). - [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) now supports SELECT BLOB data in Base64 format on all platforms in addition to REGEXP (Android/iOS/macOS) and pre-populated database (all platforms). diff --git a/SQLitePlugin.coffee.md b/SQLitePlugin.coffee.md index dccb55d8..d9ffc12a 100644 --- a/SQLitePlugin.coffee.md +++ b/SQLitePlugin.coffee.md @@ -250,22 +250,15 @@ # store initial DB state: @openDBs[@dbname] = DB_STATE_INIT - # As a WORKAROUND SOLUTION to BUG litehelpers/Cordova-sqlite-storage#666 - # (in the next event tick): - # If the database was never opened on the JavaScript side - # start an extra ROLLBACK statement to abort any pending transaction - # (does not matter whether it succeeds or fails here). - # FUTURE TBD a better solution would be to send a special signal or parameter - # if the database was never opened on the JavaScript side. - nextTick => - if not txLocks[@dbname] - myfn = (tx) -> - tx.addStatement 'ROLLBACK' - return - @addTransaction new SQLitePluginTransaction @, myfn, null, null, false, false - + # NEW WORKAROUND SOLUTION to BUG litehelpers/Cordova-sqlite-storage#666: + # Request to native implementation to close existing database + # connection if it is already open. Wait for success or error + # response before opening the database. + openStep2 = => cordova.exec opensuccesscb, openerrorcb, "SQLitePlugin", "open", [ @openargs ] + cordova.exec openStep2, openStep2, 'SQLitePlugin', 'close', [ { path: @dbname } ] + return SQLitePlugin::close = (success, error) -> diff --git a/package.json b/package.json index 90ac5281..7addf8dd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cordova-sqlite-legacy-core", - "version": "1.0.3", + "version": "1.0.4", "description": "Native interface to SQLite for PhoneGap/Cordova (legacy core version branch)", "cordova": { "id": "cordova-sqlite-legacy-core", diff --git a/plugin.xml b/plugin.xml index 96155984..2121bc60 100644 --- a/plugin.xml +++ b/plugin.xml @@ -2,7 +2,7 @@ + version="1.0.4"> Cordova sqlite storage plugin - legacy core version branch diff --git a/spec/www/spec/db-open-close-delete-test.js b/spec/www/spec/db-open-close-delete-test.js index 31d710f8..b40012e4 100755 --- a/spec/www/spec/db-open-close-delete-test.js +++ b/spec/www/spec/db-open-close-delete-test.js @@ -272,7 +272,7 @@ var mytests = function() { test_it(suiteName + ' database.close (immediately after open) calls its success callback', function () { // TBD POSSIBLY BROKEN on iOS/macOS due to current background processing implementation: - if (!isAndroid && !isWindows && !isWP8) pending('POSSIBLY BROKEN on iOS/macOS (background processing implementation)'); + if (!isAndroid && !isWindows && !isWP8) pending('CURRENTLY BROKEN on iOS/macOS (background processing implementation)'); // asynch test coming up stop(1); @@ -577,10 +577,7 @@ var mytests = function() { // XXX SEE BELOW: repeat scenario but wait for open callback before close/delete/reopen // Needed to support some large-scale applications: test_it(suiteName + ' immediate close, then delete then re-open allows subsequent queries to run', function () { - // TBD POSSIBLY BROKEN on iOS/macOS ... - // if (!isAndroid && !isWindows && !isWP8) pending(...); - // TBD CURRENTLY BROKEN DUE TO BUG 666 WORKAROUND HACK - pending('CURRENTLY BROKEN DUE TO BUG 666 WORKAROUND HACK'); + if (!isAndroid && !isWindows && !isWP8) pending('CURRENTLY BROKEN on iOS/macOS (background processing implementation)'); var dbName = "Immediate-close-delete-Reopen.db"; var dbargs = {name: dbName, location: 'default'}; @@ -850,8 +847,8 @@ var mytests = function() { test_it(suiteName + ' repeatedly open and delete database faster (5x)', function () { // TBD POSSIBLY BROKEN on iOS/macOS ... // if (!isAndroid && !isWindows && !isWP8) pending(...); - // TBD CURRENTLY BROKEN DUE TO BUG 666 WORKAROUND HACK - pending('CURRENTLY BROKEN DUE TO BUG 666 WORKAROUND HACK'); + // TBD CURRENTLY BROKEN DUE TO BUG 666 WORKAROUND SOLUTION + pending('CURRENTLY BROKEN DUE TO BUG 666 WORKAROUND SOLUTION'); var dbName = 'repeatedly-open-and-delete-faster-5x.db'; var dbargs = {name: dbName, location: 'default'}; diff --git a/www/SQLitePlugin.js b/www/SQLitePlugin.js index 51dee0cf..a605e788 100644 --- a/www/SQLitePlugin.js +++ b/www/SQLitePlugin.js @@ -162,7 +162,7 @@ }; SQLitePlugin.prototype.open = function(success, error) { - var openerrorcb, opensuccesscb; + var openStep2, openerrorcb, opensuccesscb; if (this.dbname in this.openDBs) { console.log('database already open: ' + this.dbname); nextTick((function(_this) { @@ -202,18 +202,16 @@ }; })(this); this.openDBs[this.dbname] = DB_STATE_INIT; - nextTick((function(_this) { + openStep2 = (function(_this) { return function() { - var myfn; - if (!txLocks[_this.dbname]) { - myfn = function(tx) { - tx.addStatement('ROLLBACK'); - }; - _this.addTransaction(new SQLitePluginTransaction(_this, myfn, null, null, false, false)); - } return cordova.exec(opensuccesscb, openerrorcb, "SQLitePlugin", "open", [_this.openargs]); }; - })(this)); + })(this); + cordova.exec(openStep2, openStep2, 'SQLitePlugin', 'close', [ + { + path: this.dbname + } + ]); } }; From a6d74427101801f74a2a2368e9084b90042021a9 Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Thu, 7 Dec 2017 18:01:22 -0500 Subject: [PATCH 20/47] SKIP UNICODE \\u2028 line separator string test on Android Web SQL for now --- spec/www/spec/db-tx-string-test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/www/spec/db-tx-string-test.js b/spec/www/spec/db-tx-string-test.js index c4768269..82075c8e 100755 --- a/spec/www/spec/db-tx-string-test.js +++ b/spec/www/spec/db-tx-string-test.js @@ -229,6 +229,7 @@ var mytests = function() { if (isWP8) pending('BROKEN on WP(8)'); // [BUG #202] UNICODE characters not working with WP(8) if (!isWebSql && !isWindows && isAndroid) pending('SKIP for Android plugin (cordova-android 6.x BUG: cordova/cordova-discuss#57)'); if (!isWebSql && !isWindows && !isAndroid && !isWP8) pending('SKIP for iOS/macOS plugin (Cordova BUG: CB-9435)'); + if (isWebSql && !isWindows && isAndroid) pending('SKIP for Android Web SQL'); // TBD SKIP for Android Web for now // NOTE: since the above test shows the UNICODE line separator (\u2028) // is seen by the sqlite implementation OK, it is now concluded that From 45446898af64dc4c658f9d722f3b4575318cf168 Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Thu, 7 Dec 2017 18:01:22 -0500 Subject: [PATCH 21/47] legacy version branch test fixes - test fix needed for this version branch to pass on several Android versions - cordova-sqlite-spec name in lower case in this version branch - .gitignore test fixes in this version branch --- .gitignore | 3 +++ spec/config.xml | 2 +- spec/platforms/.gitignore | 4 ---- spec/plugins/.gitignore | 4 ---- spec/www/spec/db-tx-value-bindings-test.js | 2 ++ 5 files changed, 6 insertions(+), 9 deletions(-) delete mode 100644 spec/platforms/.gitignore delete mode 100644 spec/plugins/.gitignore diff --git a/.gitignore b/.gitignore index c262bb5f..8a83f574 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ .metadata/* *.swp *~ +spec/myplugin +spec/plugins +spec/platforms diff --git a/spec/config.xml b/spec/config.xml index 4177ccb8..bd3ab6fb 100644 --- a/spec/config.xml +++ b/spec/config.xml @@ -1,7 +1,7 @@ - Cordova-sqlite-spec + cordova-sqlite-spec Runs the unit tests suite for the Cordova SQLite plugin. diff --git a/spec/platforms/.gitignore b/spec/platforms/.gitignore deleted file mode 100644 index 5e7d2734..00000000 --- a/spec/platforms/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -# Ignore everything in this directory -* -# Except this file -!.gitignore diff --git a/spec/plugins/.gitignore b/spec/plugins/.gitignore deleted file mode 100644 index 5e7d2734..00000000 --- a/spec/plugins/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -# Ignore everything in this directory -* -# Except this file -!.gitignore diff --git a/spec/www/spec/db-tx-value-bindings-test.js b/spec/www/spec/db-tx-value-bindings-test.js index 23a760b5..273aa1fc 100755 --- a/spec/www/spec/db-tx-value-bindings-test.js +++ b/spec/www/spec/db-tx-value-bindings-test.js @@ -350,11 +350,13 @@ var mytests = function() { // we would like to know, so the test is coded to fail if it starts // working there. if(isWebSql) { + /* SKIP IN THIS VERSION BRANCH: ok(expected.indexOf(name) === -1, 'field value: ' + JSON.stringify(name) + ' should not be in this until a bug is fixed ' + JSON.stringify(expected)); equal(name.length, 0, 'length of field === 0'); + // */ start(); return; } From 35dc759569d77b983d87341d643ed290d8498192 Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Thu, 7 Dec 2017 18:01:22 -0500 Subject: [PATCH 22/47] cordova-sqlite-legacy-express-core package fix - style(package): add macOS Platform by Daniel Sogl in this legacy version branch --- CHANGES.md | 4 ++++ package.json | 6 ++++-- plugin.xml | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 968eb4fe..7c410120 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Changes +###### cordova-sqlite-legacy-express-core 1.0.3-pre0 + +TBD + ###### cordova-sqlite-legacy-express-core 1.0.2 - Use PSPDFThreadSafeMutableDictionary for iOS/macOS to avoid threading issue ref: litehelpers/Cordova-sqlite-storage#716 diff --git a/package.json b/package.json index 9950f0d7..3dbde39e 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,14 @@ { "name": "cordova-sqlite-legacy-express-core", - "version": "1.0.2", + "version": "1.0.3-pre0", "description": "Native interface to SQLite for PhoneGap/Cordova (legacy express core version)", "cordova": { "id": "cordova-sqlite-legacy-express-core", "platforms": [ "android", - "ios" + "ios", + "osx", + "xx" ] }, "repository": { diff --git a/plugin.xml b/plugin.xml index a0a87385..0bcd25e7 100644 --- a/plugin.xml +++ b/plugin.xml @@ -2,7 +2,7 @@ + version="1.0.3-pre0"> Cordova sqlite storage plugin - legacy express core version From 8efff355372ab87ceadf49707fe8697fe9845566 Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Fri, 8 Dec 2017 13:37:06 -0500 Subject: [PATCH 23/47] README.md document issue with cordova-android@7 ref: litehelpers/Cordova-sqlite-storage#729 --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 24c3df23..880846c8 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,7 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase - A recent version of the Cordova CLI (such as `6.5.0` or `7.1.0`) is recommended. Cordova versions older than `6.0.0` are missing the `cordova-ios@4.0.0` security fixes. In addition it is *required* to use `cordova prepare` in case of cordova-ios older than `4.3.0` (Cordova CLI `6.4.0`). - This version uses a `before_plugin_install` hook to install sqlite3 library dependencies from `cordova-sqlite-storage-dependencies` via npm. +- This plugin will NOT work on `cordova-android@7` due to issue with JAR and NDK library files as discussed in [litehelpers/Cordova-sqlite-storage#729](https://github.com/litehelpers/Cordova-sqlite-storage/issues/729). - SQLite `3.8.10.2` included when building (all platforms), with the following definitions for iOS/macOS/Windows: - `SQLITE_THREADSAFE=1` (`SQLITE_THREADSAFE=2` on iOS/macOS) - `SQLITE_DEFAULT_MEMSTATUS=0` @@ -192,6 +193,7 @@ As "strongly recommended" by [Web SQL Database API 8.5 SQL injection](https://ww ## Known issues +- This plugin will NOT work on `cordova-android@7` due to issue with JAR and NDK library files as discussed in [litehelpers/Cordova-sqlite-storage#729](https://github.com/litehelpers/Cordova-sqlite-storage/issues/729). - iOS/macOS platform version does not support certain rapidly repeated open-and-close or open-and-delete test scenarios due to how the implementation handles background processing - As described below, auto-vacuum is NOT enabled by default. - It is possible to request a SQL statement list such as "SELECT 1; SELECT 2" within a single SQL statement string, however the plugin will only execute the first statement and silently ignore the others ref: [litehelpers/Cordova-sqlite-storage#551](https://github.com/litehelpers/Cordova-sqlite-storage/issues/551) From f868edd291737fe47f5074ecb00148a42f716511 Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Sun, 10 Dec 2017 12:31:42 -0500 Subject: [PATCH 24/47] cordova-sqlite-legacy-express-core doc updates legacy version branch doc fixes & updates --- README.md | 121 +++++++++++++++++++++++++++++------------------------- 1 file changed, 66 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index 4e99f855..6487971e 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,20 @@ Native interface to sqlite in a Cordova/PhoneGap plugin for Android/iOS/macOS, with API similar to HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/). -License for Android platform version: MIT or Apache 2.0 +License terms for Android platform version: MIT or Apache 2.0 -License for iOS/macOS platform version: MIT only +License terms for iOS/macOS platform version: MIT only ## About this version branch This version branch contains the source code for the Android/iOS/macOS platforms (legacy express core version branch). This version does not contain any libraries or source code from www.sqlite.org. This version branch uses the built-in sqlite libraries on Android, iOS, and macOS. +This version branch is now used to develop Android platform version without dependency on JAR or NDK library artifacts for testing on `cordova-android@7`. + +**NOTICE:** Workaround solution for [BUG 666 (litehelpers/Cordova-sqlite-storage#666)](https://github.com/litehelpers/Cordova-sqlite-storage/issues/666) (possible transaction issue after window.location change change with possible data loss) is incorrect in this version branch as discussed in [this BUG 666 comment](https://github.com/litehelpers/Cordova-sqlite-storage/issues/666#issuecomment-343757398) and may continue to suffer from a possible data loss risk. New workaround solution described in [this newer BUG 666 comment](https://github.com/litehelpers/Cordova-sqlite-storage/issues/666#issuecomment-350159666) is available in newer version branches. New workaround solution to bug 666 will cause selfTest to fail on Android ref: + +NOTE: This version branch has some additional known [issues fixed in newer version branches](#issues-fixed-in-newer-version-branches). + ## BREAKING CHANGE: Database location parameter is now mandatory @@ -55,12 +61,12 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase - REGEXP (Android/iOS/macOS) - SELECT BLOB data in Base64 format (all platforms Android/iOS/macOS/Windows) - Pre-populated database (Android/iOS/macOS/Windows) -- Amazon Fire-OS is dropped due to lack of support by Cordova. Android version should be used to deploy to Fire-OS 5.0(+) devices. For reference: [cordova/cordova-discuss#32 (comment)](https://github.com/cordova/cordova-discuss/issues/32#issuecomment-167021676) +- Amazon Fire-OS is dropped due to lack of support by Cordova. Android platform version should be used to deploy to Fire-OS 5.0(+) devices. For reference: [cordova/cordova-discuss#32 (comment)](https://github.com/cordova/cordova-discuss/issues/32#issuecomment-167021676) - FTS3 and FTS4 are tested working OK in this version branch (for all target platforms in this version branch Android/iOS/macOS) - R-Tree is *not* tested or supported for Android in this version branch. -- Android is supported back to SDK 10 (a.k.a. Gingerbread, Android 2.3.3); support for older versions is available upon request. -- iOS versions supported: 8.x/9.x/10.x -- In case of memory issues please use smaller transactions or use the version (with GPL or commercial license options) at: [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) +- Android versions supported: 2.3.3 - 7.1.1 (API level 10 - 25), depending on Cordova version ref: +- iOS versions supported: 8.x / 9.x / 10.x / 11.x +- In case of memory issues please use smaller transactions or use the version at [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (GPL or commercial license terms). @@ -79,7 +85,6 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase - Self-test functions to verify proper installation and operation of this plugin - More explicit `openDatabase` and `deleteDatabase` `iosDatabaseLocation` option - Added simple sql batch function -- Published [brodybits / Cordova-quick-start-checklist](https://github.com/brodybits/Cordova-quick-start-checklist) and [brodybits / Cordova-troubleshooting-guide](https://github.com/brodybits/Cordova-troubleshooting-guide) - [MetaMemoryT / websql-promise](https://github.com/MetaMemoryT/websql-promise) now provides a Promises-based interface to both Web SQL and this plugin - [SQLCipher](https://www.zetetic.net/sqlcipher/) for Android/iOS/macOS/Windows is supported by [litehelpers / Cordova-sqlcipher-adapter](https://github.com/litehelpers/Cordova-sqlcipher-adapter) @@ -89,13 +94,13 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase - Drop-in replacement for HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/): the only change should be to replace the static `window.openDatabase()` factory call with `window.sqlitePlugin.openDatabase()`, with parameters as documented below. - Failure-safe nested transactions with batch processing optimizations (according to HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/)) -- API (based on HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/)) is designed to be as flexible as possible but does not allow the application to leave any transactions hanging open. +- Transaction API (based on HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/)) is designed for maximum flexiblibility, does not allow any transactions to be left hanging open. - As described in [this posting](http://brodyspark.blogspot.com/2012/12/cordovaphonegap-sqlite-plugins-offer.html): - - Keeps sqlite database in a user data location that is known; can be reconfigured (iOS/macOS platform version); and may be synchronized to iCloud (iOS platform version). - - No 5MB maximum, more information at: http://www.sqlite.org/limits.html -- Also tested for multi-page applications with window location changes + - Keeps sqlite database in known, platform specific user data location on all platforms (Android/iOS/macOS/Windows), which can be reconfigured on iOS/macOS. Whether or not the database on the iOS platform is synchronized to iCloud depends on the selected database location. + - No arbitrary size limit. SQLite limits described at: +- ~~Also tested for multi-page applications with window location changes~~ _(workaround solution for BUG 666 is incorrect in this version branch)_ - This project is self-contained: no dependencies on other plugins such as cordova-plugin-file -- Windows 10 UWP platform version available in [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) and Windows 8.1/Windows Phone 8.1/Windows 10 platform version available in [litehelpers / Cordova-sqlite-legacy](https://github.com/litehelpers/Cordova-sqlite-legacy) use the performant C++ [doo / SQLite3-WinRT](https://github.com/doo/SQLite3-WinRT) component. +- Windows 10 UWP platform version available in TBD uses the performant C++ [doo / SQLite3-WinRT](https://github.com/doo/SQLite3-WinRT) component. - [SQLCipher](https://www.zetetic.net/sqlcipher/) support for Android/iOS/macOS/Windows is available in: [litehelpers / Cordova-sqlcipher-adapter](https://github.com/litehelpers/Cordova-sqlcipher-adapter) - Intellectual property: - All source code is tracked to the original author in git @@ -112,7 +117,7 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase - [Trailforks Mountain Bike Trail Map App](http://www.trailforks.com/apps/map/) with a couple of nice videos at: - [Get It Done app](http://getitdoneapp.com/) by [marcucio.com](http://marcucio.com/) - [KAAHE Health Encyclopedia](http://www.kaahe.org/en/index.php?option=com_content&view=article&id=817): Official health app of the Kingdom of Saudi Arabia. -- [Larkwire](http://www.larkwire.com/) (iOS version): Learn bird songs the fun way +- [Larkwire](http://www.larkwire.com/) (iOS platform): Learn bird songs the fun way - [Tangorin](https://play.google.com/store/apps/details?id=com.tangorin.app) (Android) Japanese Dictionary at [tangorin.com](http://tangorin.com/) @@ -148,28 +153,24 @@ As "strongly recommended" by [Web SQL Database API 8.5 SQL injection](https://ww - iOS/macOS platform version does not support certain rapidly repeated open-and-close or open-and-delete test scenarios due to how the implementation handles background processing - As described below, auto-vacuum is NOT enabled by default. - It is possible to request a SQL statement list such as "SELECT 1; SELECT 2" within a single SQL statement string, however the plugin will only execute the first statement and silently ignore the others ref: [litehelpers/Cordova-sqlite-storage#551](https://github.com/litehelpers/Cordova-sqlite-storage/issues/551) -- INSERT statement that affects multiple rows (due to SELECT cause or using TRIGGER(s), for example) does not report proper rowsAffected on Android -- Memory issue observed when adding a large number of records due to the JSON implementation which is improved in [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (available with GPL or commercial license options) +- Execution of INSERT statement that affects multiple rows (due to SELECT cause or using TRIGGER(s), for example) does not report proper rowsAffected on Android +- Memory issue observed when adding a large number of records due to the JSON implementation which is improved in [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (GPL or commercial license terms) - Infinity (positive or negative) values are not supported on Android/iOS/macOS due to issues described above including a possible crash on iOS/macOS ref: [litehelpers/Cordova-sqlite-storage#405](https://github.com/litehelpers/Cordova-sqlite-storage/issues/405) -- A stability issue was reported on the iOS version when in use together with [SockJS](http://sockjs.org/) client such as [pusher-js](https://github.com/pusher/pusher-js) at the same time (see [litehelpers/Cordova-sqlite-storage#196](https://github.com/litehelpers/Cordova-sqlite-storage/issues/196)). The workaround is to call sqlite functions and [SockJS](http://sockjs.org/) client functions in separate ticks (using setTimeout with 0 timeout). -- If a sql statement fails for which there is no error handler or the error handler does not return `false` to signal transaction recovery, the plugin fires the remaining sql callbacks before aborting the transaction. +- A stability issue was reported on the iOS platform version when in use together with [SockJS](http://sockjs.org/) client such as [pusher-js](https://github.com/pusher/pusher-js) at the same time (see [litehelpers/Cordova-sqlite-storage#196](https://github.com/litehelpers/Cordova-sqlite-storage/issues/196)). The workaround is to call sqlite functions and [SockJS](http://sockjs.org/) client functions in separate ticks (using setTimeout with 0 timeout). - Possible crash on Android when using Unicode emoji and other 4-octet UTF-8 characters due to [Android bug 81341](https://code.google.com/p/android/issues/detail?id=81341), which *should* be fixed in Android 6.x - Close/delete database bugs described below. - When a database is opened and deleted without closing, the iOS/macOS platform version is known to leak resources. - It is NOT possible to open multiple databases with the same name but in different locations (iOS/macOS platform version). -- Incorrect or missing insertId/rowsAffected in results for INSERT/UPDATE/DELETE SQL statements with extra semicolon(s) in the beginning for Android (android.database implementation) -- readTransaction does *not* reject modification SQL statements with extra semicolon(s) in the beginning -- Problems reported with PhoneGap Build in the past: - - PhoneGap Build Hydration. - - Apparently FIXED: ~~PhoneGap Build may fail to build the iOS version unless the name of the app starts with an uppercase and contains no spaces (see [litehelpers/Cordova-sqlite-storage#243](https://github.com/litehelpers/Cordova-sqlite-storage/issues/243); [Wizcorp/phonegap-facebook-plugin#830](https://github.com/Wizcorp/phonegap-facebook-plugin/issues/830); [phonegap/build#431](https://github.com/phonegap/build/issues/431)).~~ -Issues fixed in some newer version branches: +### Issues fixed in newer version branches + +- Incorrect or missing insertId/rowsAffected in results for INSERT/UPDATE/DELETE SQL statements with extra semicolon(s) in the beginning for Android (android.database implementation) - In case of an error, the error `code` member is bogus on Android - iOS platform version generates extra logging in release version - iOS platform version may crash if deleteDatabase is called with an object in place of the database name - readTransaction does not reject ALTER, REINDEX, and REPLACE operations -- readTransaction does not reject modification statements with extra semicolon(s) in the beginning -- extra executeSql callbacks triggered in a transaction after a failure that was not recovered by an error callback that returns false +- readTransaction does *not* reject modification statements with extra semicolon(s) in the beginning +- extra executeSql callbacks triggered in a transaction after a failure that was not recovered by an error callback (by returning false) - does not signal an error in case of excess parameter argument values given on iOS/macOS @@ -179,17 +180,17 @@ Issues fixed in some newer version branches: - ~~The db version, display name, and size parameter values are not supported and will be ignored.~~ (No longer supported by the API) - Absolute and relative subdirectory path(s) are not tested or supported. - This plugin will not work before the callback for the 'deviceready' event has been fired, as described in **Usage**. (This is consistent with the other Cordova plugins.) -- Extremely large records are not supported by this plugin version. TBD: specify maximum record; FUTURE TBD: to be fixed in [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (available with GPL or commercial license options) -- This plugin version will not work within a web worker (not properly supported by the Cordova framework). Use within a web worker is supported for Android/iOS in: [litehelpers / Cordova-sqlite-evplus-legacy-workers-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-workers-free) (available with GPL or premium commercial license options) +- Extremely large records are not supported by this plugin. It is recommended to store images and similar binary data in separate files. TBD: specify maximum record. For future consideration: support in a version such as [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (available with GPL or commercial license options) +- This plugin version will not work within a web worker (not properly supported by the Cordova framework). Use within a web worker is supported for Android/iOS in [litehelpers / cordova-sqlite-evmax-ext-workers-legacy-build-free](https://github.com/litehelpers/cordova-sqlite-evmax-ext-workers-legacy-build-free) (GPL or premium commercial license terms). - In-memory database `db=window.sqlitePlugin.openDatabase({name: ':memory:', ...})` is currently not supported. -- The Android version cannot work with more than 100 open db files (due to the threading model used). -- UNICODE `\u2028` (line separator) and `\u2029` (paragraph separator) characters are currently not supported and known to be broken in iOS, macOS, and Android version due to JSON issues reported in [Cordova bug CB-9435](https://issues.apache.org/jira/browse/CB-9435) and [cordova/cordova-discuss#57](https://github.com/cordova/cordova-discuss/issues/57). There *may* be a similar issue with certain other UNICODE characters in the iOS/macOS version (needs further investigation). This is fixed for iOS in: [litehelpers / Cordova-sqlite-evplus-legacy-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-free) and [litehelpers / Cordova-sqlite-evplus-legacy-attach-detach-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-attach-detach-free) (available with GPL or special commercial license options) as well as [litehelpers / Cordova-sqlite-evplus-legacy-workers-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-workers-free) (available with GPL or premium commercial license options) +- The Android platform version cannot properly support more than 100 open database files due to the threading model used. +- UNICODE `\u2028` (line separator) and `\u2029` (paragraph separator) characters are currently not supported and known to be broken on iOS, macOS, and Android platform versions due to JSON issues reported in [Cordova bug CB-9435](https://issues.apache.org/jira/browse/CB-9435) and [cordova/cordova-discuss#57](https://github.com/cordova/cordova-discuss/issues/57). This is fixed with a workaround for iOS/macOS in: [litehelpers / Cordova-sqlite-evplus-legacy-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-free) and [litehelpers / Cordova-sqlite-evplus-legacy-attach-detach-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-attach-detach-free) (available with GPL or special commercial license options) as well as [litehelpers / Cordova-sqlite-evplus-legacy-workers-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-workers-free) (available with GPL or premium commercial license options) - The BLOB data type is not fully supported by this version branch. SELECT BLOB in Base64 format is supported by [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) (permissive license terms) and [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (GPL or commercial license options). - Truncation in case of UNICODE `\u0000` (same as `\0`) character on Android - Case-insensitive matching and other string manipulations on Unicode characters, which is provided by optional ICU integration in the sqlite source and working with recent versions of Android, is not supported for any target platforms. -- iOS/macOS platform version uses a thread pool but with only one thread working at a time due to "synchronized" database access +- The iOS/macOS platform version uses a thread pool but with only one thread working at a time due to "synchronized" database access. - Some large query results may be slow, also due to the JSON implementation. -- ATTACH to another database file is not supported by this version. Attach/detach is supported (along with the memory and iOS UNICODE `\u2028` line separator / `\u2029` paragraph separator fixes) in: [litehelpers / Cordova-sqlite-evplus-legacy-attach-detach-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-attach-detach-free) (available with GPL or special commercial license options) +- ATTACH to another database file is not supported by this version branch. Attach/detach is supported (along with the memory and iOS UNICODE `\u2028` line separator / `\u2029` paragraph separator fixes) in [litehelpers / Cordova-sqlite-evplus-legacy-attach-detach-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-attach-detach-free) (available with GPL or special commercial license terms). - UPDATE/DELETE with LIMIT or ORDER BY is not supported. - WITH clause is not supported by some older Android/iOS versions. - User-defined savepoints are not supported and not expected to be compatible with the transaction locking mechanism used by this plugin. In addition, the use of BEGIN/COMMIT/ROLLBACK statements is not supported. @@ -259,8 +260,14 @@ Documented in: [brodybits / Avoiding-some-Cordova-pitfalls](https://github.com/b ## Major TODOs -- Integrate with IndexedDBShim and some other libraries such as Sequelize, Squel.js, WebSqlSync, Persistence.js, Knex, etc. -- Version with proper BLOB support +- More formal documentation of API, especially for non-standard functions +- Browser platform +- IndexedDBShim adapter +- Further cleanup of [support](#support) section +- Resolve or document remaining [open Cordova-sqlite-storage bugs](https://github.com/litehelpers/Cordova-sqlite-storage/issues?q=is%3Aissue+is%3Aopen+label%3Abug-general) +- Resolve [cordova-sqlite-help doc-todo issues](https://github.com/litehelpers/Cordova-sqlite-help/issues?q=is%3Aissue%20label%3Adoc-todo) and [cordova-sqlite-storage doc-todo issues](https://github.com/litehelpers/Cordova-sqlite-storage/issues?q=is%3Aissue+is%3Aopen+label%3Adoc-todo) + + ## For future considertion @@ -269,19 +276,20 @@ Documented in: [brodybits / Avoiding-some-Cordova-pitfalls](https://github.com/b ## Alternatives -### Other versions +### Comparison of sqlite plugin versions -- [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) - version with REGEXP (Android/iOS/macOS), SELECT BLOB in Base64 format (all platforms Android/iOS/macOS/Windows), and pre-populated databases (all platforms Android/iOS/macOS/Windows) -- [litehelpers / Cordova-sqlite-legacy-build-support](https://github.com/litehelpers/Cordova-sqlite-legacy-build-support) - maintenance of WP8 version along with Windows 8.1/Windows Phone 8.1 and the other supported platforms Android/iOS/macOS/Windows 10; limited support for PhoneGap CLI/PhoneGap Build/plugman/Intel XDK; limited testing; limited updates +- [litehelpers / Cordova-sqlite-storage](https://github.com/litehelpers/Cordova-sqlite-storage) - core version for Android/iOS/macOS/Windows (permissive license terms) +- [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) - version with REGEXP (Android/iOS/macOS), SELECT BLOB in Base64 format (all platforms Android/iOS/macOS/Windows), and pre-populated databases (all platforms Android/iOS/macOS/Windows). Permissive license terms. +- [litehelpers / Cordova-sqlite-legacy-build-support](https://github.com/litehelpers/Cordova-sqlite-legacy-build-support) - maintenance of WP8 version along with Windows 8.1/Windows Phone 8.1 and the other supported platforms Android/iOS/macOS/Windows 10; limited support for PhoneGap CLI/PhoneGap Build/plugman/Intel XDK; limited testing; limited updates. Permissive license terms. - [litehelpers / Cordova-sqlcipher-adapter](https://github.com/litehelpers/Cordova-sqlcipher-adapter) - supports [SQLCipher](https://www.zetetic.net/sqlcipher/) for Android/iOS/macOS/Windows -- [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) - Enhancements for Android: JSON and SQL statement handling implemented in C, supports larger transactions and handles large SQL batches in less than half the time as this version. Supports arbitrary database location on Android. Support for build environments such as PhoneGap Build and Intel XDK. Available with GPL or commercial license options. Also includes REGEXP (Android/iOS/macOS) and SELECT BLOB in Base64 format (all platforms Android/iOS/macOS/Windows). -- [litehelpers / Cordova-sqlite-evplus-legacy-workers-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-workers-free) - version with support for web workers, includes internal memory improvements to support larger transactions (Android/iOS) and fix to support all Unicode characters (iOS) (with GPL or premium commercial license options) -- [litehelpers / Cordova-sqlite-evplus-legacy-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-free) - internal memory improvements to support larger transactions (Android/iOS) and fix to support all Unicode characters (iOS) - with GPL or special commercial license options -- [litehelpers / Cordova-sqlite-evplus-legacy-attach-detach-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-attach-detach-free) - version with support for ATTACH, includes internal memory improvements to support larger transactions (Android/iOS) and fix to support all Unicode characters (with GPL or special commercial license options) -- Adaptation for React Native Android and iOS: [andpor / react-native-sqlite-storage](https://github.com/andpor/react-native-sqlite-storage) -- Original version for iOS (with a slightly different transaction API): [davibe / Phonegap-SQLitePlugin](https://github.com/davibe/Phonegap-SQLitePlugin) +- [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) - Enhancements for Android: JSON and SQL statement handling implemented in C, supports larger transactions and handles large SQL batches in less than half the time as this version. Supports arbitrary database location on Android. Support for build environments such as PhoneGap Build and Intel XDK. Also includes REGEXP (Android/iOS/macOS) and SELECT BLOB in Base64 format (all platforms Android/iOS/macOS/Windows). GPL or commercial license terms. +- [litehelpers / cordova-sqlite-evplus-ext-legacy-build-free](https://github.com/litehelpers/cordova-sqlite-evplus-ext-legacy-build-free) - internal memory improvements to support larger transactions (Android/iOS) and fix to support all Unicode characters (iOS). (GPL or special commercial license terms). +- [litehelpers / Cordova-sqlite-evplus-legacy-attach-detach-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-attach-detach-free) - version with support for ATTACH, includes internal memory improvements to support larger transactions (Android/iOS) and fix to support all Unicode characters (GPL or special commercial license terms). +- [litehelpers / cordova-sqlite-evmax-ext-workers-legacy-build-free](https://github.com/litehelpers/cordova-sqlite-evmax-ext-workers-legacy-build-free) - version with support for web workers, includes internal memory improvements to support larger transactions (Android/iOS) and fix to support all Unicode characters (iOS). (GPL or premium commercial license terms). +- Adaptation for React Native Android and iOS: [andpor / react-native-sqlite-storage](https://github.com/andpor/react-native-sqlite-storage) (permissive license terms) +- Original version for iOS (with a non-standard, outdated transaction API): [davibe / Phonegap-SQLitePlugin](https://github.com/davibe/Phonegap-SQLitePlugin) (permissive license terms) - + ### Other SQLite adapter projects @@ -303,7 +311,6 @@ Documented in: [brodybits / Avoiding-some-Cordova-pitfalls](https://github.com/b - Use [phearme / cordova-ContentProviderPlugin](https://github.com/phearme/cordova-ContentProviderPlugin) to query content providers on Android devices - [ABB-Austin / cordova-plugin-indexeddb-async](https://github.com/ABB-Austin/cordova-plugin-indexeddb-async) - Asynchronous IndexedDB plugin for Cordova that uses [axemclion / IndexedDBShim](https://github.com/axemclion/IndexedDBShim) (Browser/iOS/Android/Windows) and [Thinkwise / cordova-plugin-websql](https://github.com/Thinkwise/cordova-plugin-websql) - (Windows) -- Another sqlite binding for React-Native (iOS version): [almost/react-native-sqlite](https://github.com/almost/react-native-sqlite) - Use [NativeScript](https://www.nativescript.org) with its web view and [NathanaelA / nativescript-sqlite](https://github.com/Natha naelA/nativescript-sqlite) (Android and/or iOS) - Standard HTML5 [local storage](https://en.wikipedia.org/wiki/Web_storage#localStorage) @@ -421,7 +428,7 @@ window.openDatabase = function(dbname, ignored1, ignored2, ignored3) { ### Workaround for Android db locking issue -[litehelpers/Cordova-sqlite-storage#193](https://github.com/litehelpers/Cordova-sqlite-storage/issues/193) was reported (as observed by several app developers) that on some newer versions of the Android database classes, if the app is stopped or aborted without closing the database then: +[litehelpers/Cordova-sqlite-storage#193](https://github.com/litehelpers/Cordova-sqlite-storage/issues/193) reported (as observed by a number of app developers in the past) that when using the `androidDatabaseImplementation: 2` setting on certain Android versions and if the app is stopped or aborted without closing the database then: - (sometimes) there is an unexpected database lock - the data that was inserted is lost. @@ -746,7 +753,7 @@ db.executeSql("SELECT LENGTH('tenletters') AS stringlength", [], function (res) }); ``` -**SECOND BUG:** When a database connection is closed, any queued transactions are left hanging. All pending transactions should be errored when a database connection is closed. +**SECOND BUG:** When a database connection is closed, any queued transactions are left hanging. TODO: All pending transactions should be errored whenever a database connection is closed. **NOTE:** As described above, if multiple database access handle objects are opened for the same database and one database handle access object is closed, the database is no longer available for the other database handle objects. Possible workarounds: - It is still possible to open one or more new database handle objects on a database that has been closed. @@ -806,7 +813,7 @@ Other resource (apparently for Ionic 1): -## Source tree - -- `SQLitePlugin.coffee.md`: platform-independent (Literate coffee-script, can be read by recent coffee-script compiler) -- `www`: `SQLitePlugin.js` platform-independent Javascript as generated from `SQLitePlugin.coffee.md` (and checked in!) -- `src`: platform-specific source code -- `spec`: test suite using Jasmine (2.2.0) -- `tests`: very simple Jasmine test suite that is run on Circle CI (Android version) and Travis CI (iOS version) (used as a placeholder) - ## Installation test ### Easy installation test @@ -970,6 +969,18 @@ To run from a windows powershell (here is a sample for android target): - [nolanlawson / pouchdb-adapter-cordova-sqlite](https://github.com/nolanlawson/pouchdb-adapter-cordova-sqlite) + + +## Source tree + +- `SQLitePlugin.coffee.md`: platform-independent (Literate CoffeeScript, can be compiled with a recent CoffeeScript compiler) +- `www`: platform-independent Javascript as generated from `SQLitePlugin.coffee.md` (and committed!) +- `src`: platform-specific source code +- `spec`: test suite using Jasmine +- `tests`: very simple Jasmine test suite that is run on Circle CI (Android platform) and Travis CI (iOS platform) (used as a placeholder) + + + # Contributing ## Community From 4e80bd334a30c37f5686c5210c4905ede87cc51c Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Fri, 8 Dec 2017 12:36:41 -0500 Subject: [PATCH 25/47] package.json style add "cordova-osx" to keywords --- CHANGES.md | 2 +- package.json | 6 ++++-- plugin.xml | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 7c410120..b21ae410 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,6 @@ # Changes -###### cordova-sqlite-legacy-express-core 1.0.3-pre0 +###### cordova-sqlite-legacy-express-core 1.0.3-pre01 TBD diff --git a/package.json b/package.json index 3dbde39e..a925020b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cordova-sqlite-legacy-express-core", - "version": "1.0.3-pre0", + "version": "1.0.3-pre01", "description": "Native interface to SQLite for PhoneGap/Cordova (legacy express core version)", "cordova": { "id": "cordova-sqlite-legacy-express-core", @@ -19,7 +19,9 @@ "sqlite", "ecosystem:cordova", "cordova-android", - "cordova-ios" + "cordova-ios", + "cordova-osx", + "xx" ], "author": "various", "license": "MIT", diff --git a/plugin.xml b/plugin.xml index 0bcd25e7..b196fa4c 100644 --- a/plugin.xml +++ b/plugin.xml @@ -2,7 +2,7 @@ + version="1.0.3-pre01"> Cordova sqlite storage plugin - legacy express core version From a61b96518bae93b9567cbbf5564916e6ae30d0a3 Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Sat, 9 Dec 2017 21:21:20 -0500 Subject: [PATCH 26/47] Update delete test (do not ignore delete error on any platform) --- spec/www/spec/db-open-close-delete-test.js | 48 ++++++++++++++-------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/spec/www/spec/db-open-close-delete-test.js b/spec/www/spec/db-open-close-delete-test.js index 31d710f8..35031693 100755 --- a/spec/www/spec/db-open-close-delete-test.js +++ b/spec/www/spec/db-open-close-delete-test.js @@ -143,8 +143,7 @@ var mytests = function() { } } - test_it(suiteName + ' test sqlitePlugin.deleteDatabase()', function () { - stop(); + it(suiteName + ' test sqlitePlugin.deleteDatabase()', function (done) { var db = openDatabase("DB-Deletable", "1.0", "Demo", DEFAULT_SIZE); function createAndInsertStuff() { @@ -155,9 +154,14 @@ var mytests = function() { tx.executeSql('INSERT INTO test VALUES (?)', ['foo']); }); }, function (err) { - ok(false, 'create and insert tx failed with ERROR: ' + JSON.stringify(err)); + // NOT EXPECTED: console.log('create and insert tx failed with ERROR: ' + JSON.stringify(err)); - start(); + expect(false).toBe(true); + expect(err).toBeDefined(); + expect(err.message).toBeDefined(); + expect(err.message).toBe('--'); + done(); + }, function () { // check that we can read it db.transaction(function(tx) { @@ -165,9 +169,13 @@ var mytests = function() { equal(res.rows.item(0).name, 'foo'); }); }, function (err) { - ok(false, 'SELECT tx failed with ERROR: ' + JSON.stringify(err)); + // NOT EXPECTED: console.log('SELECT tx failed with ERROR: ' + JSON.stringify(err)); - start(); + expect(false).toBe(true); + expect(err).toBeDefined(); + expect(err.message).toBeDefined(); + expect(err.message).toBe('--'); + done(); }, function () { deleteAndConfirmDeleted(); }); @@ -182,30 +190,38 @@ var mytests = function() { db.transaction(function (tx) { tx.executeSql('SELECT name FROM test', []); }, function (err) { - ok(true, 'got an expected transaction error'); + // EXPECTED RESULT: + expect(err).toBeDefined(); + expect(err.message).toBeDefined(); testDeleteError(); }, function () { + // SUCCESS CALLBACK NOT EXPECTED: console.log('UNEXPECTED SUCCESS: expected a transaction error'); - ok(false, 'expected a transaction error'); - start(); + expect(false).toBe(true); + done(); }); }, function (err) { + // NOT EXPECTED - DO NOT IGNORE ON ANY PLATFORM: console.log("ERROR: " + JSON.stringify(err)); - ok(false, 'error: ' + err); - start(); + expect(false).toBe(true); + expect(err).toBeDefined(); + expect(err.message).toBeDefined(); + expect(err.message).toBe('--'); + done(); }); } function testDeleteError() { // should throw an error if the db doesn't exist deleteDatabase("Foo-Doesnt-Exist", function () { + // SUCCESS CALLBACK NOT EXPECTED: console.log('UNEXPECTED SUCCESS: expected a delete error'); - ok(false, 'expected error'); - start(); + expect(false).toBe(true); + done(); }, function (err) { - ok(!!err, 'got error like we expected'); - - start(); + // EXPECTED RESULT: + expect(err).toBeDefined(); + done(); }); } From 452d3c9c01aa68f2ad1ce886bf10f13382cc36eb Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Sat, 9 Dec 2017 21:57:16 -0500 Subject: [PATCH 27/47] selfTest database cleanup fixes - cleanup (delete) database in separate step - do not ignore close or delete error on any platforms - fix logging & return statements --- CHANGES.md | 4 ++-- SQLitePlugin.coffee.md | 39 ++++++++++++++++++++++----------------- package.json | 2 +- plugin.xml | 2 +- www/SQLitePlugin.js | 37 ++++++++++++++----------------------- 5 files changed, 40 insertions(+), 44 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index b21ae410..b4ad1a9c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,8 +1,8 @@ # Changes -###### cordova-sqlite-legacy-express-core 1.0.3-pre01 +###### cordova-sqlite-legacy-express-core 1.0.3-pre02 -TBD +- selfTest database cleanup do not ignore close or delete error on any platforms ###### cordova-sqlite-legacy-express-core 1.0.2 diff --git a/SQLitePlugin.coffee.md b/SQLitePlugin.coffee.md index a3dc8c06..bb4312d0 100644 --- a/SQLitePlugin.coffee.md +++ b/SQLitePlugin.coffee.md @@ -881,24 +881,20 @@ , (tx2_err) -> SelfTest.finishWithError errorcb, "readTransaction error: #{tx2_err}" + , () -> - # CLEANUP & FINISH: - db.close () -> - SQLiteFactory.deleteDatabase {name: SelfTest.DBNAME, location: 'default'}, successcb, (cleanup_err)-> - # TBD IGNORE THIS ERROR on Windows (and WP8): - if /Windows /.test(navigator.userAgent) or /IEMobile/.test(navigator.userAgent) - console.log "IGNORE CLEANUP (DELETE) ERROR: #{JSON.stringify cleanup_err} (Windows/WP8)" - successcb() + + # CLEANUP & FINISH: + db.close () -> + SelfTest.cleanupAndFinish successcb, errorcb + return + + , (close_err) -> + # DO NOT IGNORE CLOSE ERROR ON ANY PLATFORM: + SelfTest.finishWithError errorcb, "close error: #{close_err}" return - SelfTest.finishWithError errorcb, "Cleanup error: #{cleanup_err}" - , (close_err) -> - # TBD IGNORE THIS ERROR on Windows (and WP8): - if /Windows /.test(navigator.userAgent) or /IEMobile/.test(navigator.userAgent) - console.log "IGNORE close ERROR: #{JSON.stringify close_err} (Windows/WP8)" - SQLiteFactory.deleteDatabase {name: SelfTest.DBNAME, location: 'default'}, successcb, successcb return - SelfTest.finishWithError errorcb, "close error: #{close_err}" , (select_err) -> SelfTest.finishWithError errorcb, "SELECT error: #{select_err}" @@ -910,13 +906,22 @@ SelfTest.finishWithError errorcb, "Open database error: #{open_err}" return + cleanupAndFinish: (successcb, errorcb) -> + SQLiteFactory.deleteDatabase {name: SelfTest.DBNAME, location: 'default'}, successcb, (cleanup_err)-> + # DO NOT IGNORE CLEANUP DELETE ERROR ON ANY PLATFORM: + SelfTest.finishWithError errorcb, "CLEANUP DELETE ERROR: #{cleanup_err}" + return + return + finishWithError: (errorcb, message) -> console.log "selfTest ERROR with message: #{message}" SQLiteFactory.deleteDatabase {name: SelfTest.DBNAME, location: 'default'}, -> errorcb newSQLError message - # FUTURE TODO: return - # FUTURE TODO log err2 - , (err2)-> errorcb newSQLError "Cleanup error: #{err2} for error: #{message}" + return + , (err2)-> + console.log "selfTest CLEANUP DELETE ERROR #{err2}" + errorcb newSQLError "CLEANUP DELETE ERROR: #{err2} for error: #{message}" + return return ## Exported API: diff --git a/package.json b/package.json index a925020b..aeea6363 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cordova-sqlite-legacy-express-core", - "version": "1.0.3-pre01", + "version": "1.0.3-pre02", "description": "Native interface to SQLite for PhoneGap/Cordova (legacy express core version)", "cordova": { "id": "cordova-sqlite-legacy-express-core", diff --git a/plugin.xml b/plugin.xml index b196fa4c..e7b3ecbd 100644 --- a/plugin.xml +++ b/plugin.xml @@ -2,7 +2,7 @@ + version="1.0.3-pre02"> Cordova sqlite storage plugin - legacy express core version diff --git a/www/SQLitePlugin.js b/www/SQLitePlugin.js index 6d612d44..03cb363d 100644 --- a/www/SQLitePlugin.js +++ b/www/SQLitePlugin.js @@ -762,28 +762,10 @@ }, function(tx2_err) { return SelfTest.finishWithError(errorcb, "readTransaction error: " + tx2_err); }, function() { - return db.close(function() { - return SQLiteFactory.deleteDatabase({ - name: SelfTest.DBNAME, - location: 'default' - }, successcb, function(cleanup_err) { - if (/Windows /.test(navigator.userAgent) || /IEMobile/.test(navigator.userAgent)) { - console.log("IGNORE CLEANUP (DELETE) ERROR: " + (JSON.stringify(cleanup_err)) + " (Windows/WP8)"); - successcb(); - return; - } - return SelfTest.finishWithError(errorcb, "Cleanup error: " + cleanup_err); - }); + db.close(function() { + SelfTest.cleanupAndFinish(successcb, errorcb); }, function(close_err) { - if (/Windows /.test(navigator.userAgent) || /IEMobile/.test(navigator.userAgent)) { - console.log("IGNORE close ERROR: " + (JSON.stringify(close_err)) + " (Windows/WP8)"); - SQLiteFactory.deleteDatabase({ - name: SelfTest.DBNAME, - location: 'default' - }, successcb, successcb); - return; - } - return SelfTest.finishWithError(errorcb, "close error: " + close_err); + SelfTest.finishWithError(errorcb, "close error: " + close_err); }); }); }); @@ -797,15 +779,24 @@ return SelfTest.finishWithError(errorcb, "Open database error: " + open_err); }); }, + cleanupAndFinish: function(successcb, errorcb) { + SQLiteFactory.deleteDatabase({ + name: SelfTest.DBNAME, + location: 'default' + }, successcb, function(cleanup_err) { + SelfTest.finishWithError(errorcb, "CLEANUP DELETE ERROR: " + cleanup_err); + }); + }, finishWithError: function(errorcb, message) { console.log("selfTest ERROR with message: " + message); SQLiteFactory.deleteDatabase({ name: SelfTest.DBNAME, location: 'default' }, function() { - return errorcb(newSQLError(message)); + errorcb(newSQLError(message)); }, function(err2) { - return errorcb(newSQLError("Cleanup error: " + err2 + " for error: " + message)); + console.log("selfTest CLEANUP DELETE ERROR " + err2); + errorcb(newSQLError("CLEANUP DELETE ERROR: " + err2 + " for error: " + message)); }); } }; From b656b89b0a56c4545298eb973d51977d4126d041 Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Sun, 10 Dec 2017 10:16:02 -0500 Subject: [PATCH 28/47] General doc fixes & updates --- README.md | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 6487971e..6c1367a8 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase - Published [brodybits / Cordova-quick-start-checklist](https://github.com/brodybits/Cordova-quick-start-checklist) and [brodybits / Avoiding-some-Cordova-pitfalls](https://github.com/brodybits/Avoiding-some-Cordova-pitfalls). - Self-test functions to verify proper installation and operation of this plugin - More explicit `openDatabase` and `deleteDatabase` `iosDatabaseLocation` option -- Added simple sql batch function +- Added straightforward sql batch function - [MetaMemoryT / websql-promise](https://github.com/MetaMemoryT/websql-promise) now provides a Promises-based interface to both Web SQL and this plugin - [SQLCipher](https://www.zetetic.net/sqlcipher/) for Android/iOS/macOS/Windows is supported by [litehelpers / Cordova-sqlcipher-adapter](https://github.com/litehelpers/Cordova-sqlcipher-adapter) @@ -150,7 +150,7 @@ As "strongly recommended" by [Web SQL Database API 8.5 SQL injection](https://ww ## Known issues -- iOS/macOS platform version does not support certain rapidly repeated open-and-close or open-and-delete test scenarios due to how the implementation handles background processing +- The iOS/macOS platform versions do not support certain rapidly repeated open-and-close or open-and-delete test scenarios due to how the implementation handles background processing - As described below, auto-vacuum is NOT enabled by default. - It is possible to request a SQL statement list such as "SELECT 1; SELECT 2" within a single SQL statement string, however the plugin will only execute the first statement and silently ignore the others ref: [litehelpers/Cordova-sqlite-storage#551](https://github.com/litehelpers/Cordova-sqlite-storage/issues/551) - Execution of INSERT statement that affects multiple rows (due to SELECT cause or using TRIGGER(s), for example) does not report proper rowsAffected on Android @@ -173,6 +173,8 @@ As "strongly recommended" by [Web SQL Database API 8.5 SQL injection](https://ww - extra executeSql callbacks triggered in a transaction after a failure that was not recovered by an error callback (by returning false) - does not signal an error in case of excess parameter argument values given on iOS/macOS +Some additional issues are tracked in [open Cordova-sqlite-storage bug-general issues](https://github.com/litehelpers/Cordova-sqlite-storage/issues?q=is%3Aissue+is%3Aopen+label%3Abug-general). + ## Other limitations @@ -180,23 +182,25 @@ As "strongly recommended" by [Web SQL Database API 8.5 SQL injection](https://ww - ~~The db version, display name, and size parameter values are not supported and will be ignored.~~ (No longer supported by the API) - Absolute and relative subdirectory path(s) are not tested or supported. - This plugin will not work before the callback for the 'deviceready' event has been fired, as described in **Usage**. (This is consistent with the other Cordova plugins.) -- Extremely large records are not supported by this plugin. It is recommended to store images and similar binary data in separate files. TBD: specify maximum record. For future consideration: support in a version such as [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (available with GPL or commercial license options) +- Extremely large records are not supported by this plugin. It is recommended to store images and similar binary data in separate files. TBD: specify maximum record. For future consideration: support in a version such as [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (GPL or commercial license terms). - This plugin version will not work within a web worker (not properly supported by the Cordova framework). Use within a web worker is supported for Android/iOS in [litehelpers / cordova-sqlite-evmax-ext-workers-legacy-build-free](https://github.com/litehelpers/cordova-sqlite-evmax-ext-workers-legacy-build-free) (GPL or premium commercial license terms). - In-memory database `db=window.sqlitePlugin.openDatabase({name: ':memory:', ...})` is currently not supported. - The Android platform version cannot properly support more than 100 open database files due to the threading model used. -- UNICODE `\u2028` (line separator) and `\u2029` (paragraph separator) characters are currently not supported and known to be broken on iOS, macOS, and Android platform versions due to JSON issues reported in [Cordova bug CB-9435](https://issues.apache.org/jira/browse/CB-9435) and [cordova/cordova-discuss#57](https://github.com/cordova/cordova-discuss/issues/57). This is fixed with a workaround for iOS/macOS in: [litehelpers / Cordova-sqlite-evplus-legacy-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-free) and [litehelpers / Cordova-sqlite-evplus-legacy-attach-detach-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-attach-detach-free) (available with GPL or special commercial license options) as well as [litehelpers / Cordova-sqlite-evplus-legacy-workers-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-workers-free) (available with GPL or premium commercial license options) +- UNICODE `\u2028` (line separator) and `\u2029` (paragraph separator) characters are currently not supported and known to be broken on iOS, macOS, and Android platform versions due to JSON issues reported in [Cordova bug CB-9435](https://issues.apache.org/jira/browse/CB-9435) and [cordova/cordova-discuss#57](https://github.com/cordova/cordova-discuss/issues/57). This is fixed with a workaround for iOS/macOS in: [litehelpers / Cordova-sqlite-evplus-legacy-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-free) and [litehelpers / Cordova-sqlite-evplus-legacy-attach-detach-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-attach-detach-free) (GPL or special commercial license terms) as well as [litehelpers / Cordova-sqlite-evplus-legacy-workers-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-workers-free) (GPL or premium commercial license terms). - The BLOB data type is not fully supported by this version branch. SELECT BLOB in Base64 format is supported by [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) (permissive license terms) and [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (GPL or commercial license options). - Truncation in case of UNICODE `\u0000` (same as `\0`) character on Android - Case-insensitive matching and other string manipulations on Unicode characters, which is provided by optional ICU integration in the sqlite source and working with recent versions of Android, is not supported for any target platforms. - The iOS/macOS platform version uses a thread pool but with only one thread working at a time due to "synchronized" database access. - Some large query results may be slow, also due to the JSON implementation. -- ATTACH to another database file is not supported by this version branch. Attach/detach is supported (along with the memory and iOS UNICODE `\u2028` line separator / `\u2029` paragraph separator fixes) in [litehelpers / Cordova-sqlite-evplus-legacy-attach-detach-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-attach-detach-free) (available with GPL or special commercial license terms). +- ATTACH to another database file is not supported by this version branch. Attach/detach is supported (along with the memory and iOS UNICODE `\u2028` line separator / `\u2029` paragraph separator fixes) in [litehelpers / Cordova-sqlite-evplus-legacy-attach-detach-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-attach-detach-free) (GPL or special commercial license terms). - UPDATE/DELETE with LIMIT or ORDER BY is not supported. - WITH clause is not supported by some older Android/iOS versions. - User-defined savepoints are not supported and not expected to be compatible with the transaction locking mechanism used by this plugin. In addition, the use of BEGIN/COMMIT/ROLLBACK statements is not supported. - Problems have been reported when using this plugin with Crosswalk (for Android). It may help to install Crosswalk as a plugin instead of using Crosswalk to create the project. - Does not work with [axemclion / react-native-cordova-plugin](https://github.com/axemclion/react-native-cordova-plugin) since the `window.sqlitePlugin` object is *not* properly exported (ES5 feature). It is recommended to use [andpor / react-native-sqlite-storage](https://github.com/andpor/react-native-sqlite-storage) for SQLite database access with React Native Android/iOS instead. +Additional limitations are tracked in [open Cordova-sqlite-storage doc-todo issues](https://github.com/litehelpers/Cordova-sqlite-storage/issues?q=is%3Aissue+is%3Aopen+label%3Adoc-todo). + ## Further testing needed @@ -209,7 +213,7 @@ As "strongly recommended" by [Web SQL Database API 8.5 SQL injection](https://ww - Actual behavior when using SAVEPOINT(s) - R-Tree is not fully tested with Android - UNICODE characters not fully tested -- Use with TRIGGER(s), JOIN and ORDER BY RANDOM +- ORDER BY RANDOM() (ref: [litehelpers/Cordova-sqlite-storage#334](https://github.com/litehelpers/Cordova-sqlite-storage/issues/334)) - UPDATE/DELETE with LIMIT or ORDER BY (newer Android/iOS versions) - Integration with JXCore for Cordova (must be built without sqlite(3) built-in) - Delete an open database inside a statement or transaction callback. @@ -262,7 +266,7 @@ Documented in: [brodybits / Avoiding-some-Cordova-pitfalls](https://github.com/b - More formal documentation of API, especially for non-standard functions - Browser platform -- IndexedDBShim adapter +- IndexedDBShim adapter (possibly based on IndexedDBShim) - Further cleanup of [support](#support) section - Resolve or document remaining [open Cordova-sqlite-storage bugs](https://github.com/litehelpers/Cordova-sqlite-storage/issues?q=is%3Aissue+is%3Aopen+label%3Abug-general) - Resolve [cordova-sqlite-help doc-todo issues](https://github.com/litehelpers/Cordova-sqlite-help/issues?q=is%3Aissue%20label%3Adoc-todo) and [cordova-sqlite-storage doc-todo issues](https://github.com/litehelpers/Cordova-sqlite-storage/issues?q=is%3Aissue+is%3Aopen+label%3Adoc-todo) @@ -272,7 +276,10 @@ Documented in: [brodybits / Avoiding-some-Cordova-pitfalls](https://github.com/b ## For future considertion - Auto-vacuum option -- Browser platform +- Support for extremely large records in a version such as [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (available with GPL or commercial license options) +- Integrate with some other libraries such as Sequelize, Squel.js, WebSqlSync, Persistence.js, Knex, etc. + + ## Alternatives @@ -875,7 +882,7 @@ Free support is provided on a best-effort basis and is only available in public Commercial support is available by contacting: -## Before asking for help +## Before seeking help First steps: - Verify that you have followed the steps in [brodybits / Cordova-quick-start-checklist](https://github.com/brodybits/Cordova-quick-start-checklist) @@ -961,6 +968,8 @@ To run from a windows powershell (here is a sample for android target): # Adapters +**GENERAL:** The adapters described here are community maintained. + ## Lawnchair Adapter - [litehelpers / cordova-sqlite-lawnchair-adapter](https://github.com/litehelpers/cordova-sqlite-lawnchair-adapter) @@ -969,12 +978,16 @@ To run from a windows powershell (here is a sample for android target): - [nolanlawson / pouchdb-adapter-cordova-sqlite](https://github.com/nolanlawson/pouchdb-adapter-cordova-sqlite) +## Adapters wanted + +- IndexedDBShim adapter (possibly based on IndexedDBShim) + ## Source tree -- `SQLitePlugin.coffee.md`: platform-independent (Literate CoffeeScript, can be compiled with a recent CoffeeScript compiler) -- `www`: platform-independent Javascript as generated from `SQLitePlugin.coffee.md` (and committed!) +- `SQLitePlugin.coffee.md`: platform-independent (Literate CoffeeScript, can be compiled with a recent CoffeeScript (1.x) compiler) +- `www`: platform-independent Javascript as generated from `SQLitePlugin.coffee.md` using `coffeescript@1` (and committed!) - `src`: platform-specific source code - `spec`: test suite using Jasmine - `tests`: very simple Jasmine test suite that is run on Circle CI (Android platform) and Travis CI (iOS platform) (used as a placeholder) From 4c7b59c085db405b191c2ef50e8de538dc4010cf Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Sun, 10 Dec 2017 18:32:38 -0500 Subject: [PATCH 29/47] Resolve Java 6/7/8 concurrent map compatibility & cleanup imports for Android MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FIXES litehelpers/Cordova-sqlite-storage#726 (keySet error) THANKS to @NeoLSN (Jason Yang/楊朝傑) for pointer to the solution in litehelpers/Cordova-sqlite-storage#727 --- CHANGES.md | 3 ++- README.md | 1 + package.json | 2 +- plugin.xml | 2 +- src/android/io/sqlc/SQLitePlugin.java | 28 +++++++++++++++++---------- 5 files changed, 23 insertions(+), 13 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index b4ad1a9c..e43f50c9 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,7 +1,8 @@ # Changes -###### cordova-sqlite-legacy-express-core 1.0.3-pre02 +###### cordova-sqlite-legacy-express-core 1.0.3 +- Resolve Java 6/7/8 concurrent map compatibility issue reported in litehelpers/Cordova-sqlite-storage#726, THANKS to pointer by @NeoLSN (Jason Yang/楊朝傑) in litehelpers/Cordova-sqlite-storage#727. - selfTest database cleanup do not ignore close or delete error on any platforms ###### cordova-sqlite-legacy-express-core 1.0.2 diff --git a/README.md b/README.md index 6c1367a8..cbf649a9 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,7 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase ## Announcements +- Resolved Java 6/7/8 concurrent map compatibility issue reported in [litehelpers/Cordova-sqlite-storage#726](https://github.com/litehelpers/Cordova-sqlite-storage/issues/726), THANKS to pointer by [@NeoLSN (Jason Yang/楊朝傑)](https://github.com/NeoLSN) in [litehelpers/Cordova-sqlite-storage#727](https://github.com/litehelpers/Cordova-sqlite-storage/issues/727). - Fixed iOS/macOS platform version to use [PSPDFThreadSafeMutableDictionary.m](https://gist.github.com/steipete/5928916) to avoid threading issue ref: [litehelpers/Cordova-sqlite-storage#716](https://github.com/litehelpers/Cordova-sqlite-storage/issues/716) - Resolved transaction problem after window.location (page) change with possible data loss ref: [litehelpers/Cordova-sqlite-storage#666](https://github.com/litehelpers/Cordova-sqlite-storage/issues/666) - [brodybits / cordova-sqlite-test-app](https://github.com/brodybits/cordova-sqlite-test-app) project is a CC0 (public domain) starting point (NOTE that this plugin must be added) and may also be used to reproduce issues with this plugin. diff --git a/package.json b/package.json index aeea6363..c6325124 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cordova-sqlite-legacy-express-core", - "version": "1.0.3-pre02", + "version": "1.0.3", "description": "Native interface to SQLite for PhoneGap/Cordova (legacy express core version)", "cordova": { "id": "cordova-sqlite-legacy-express-core", diff --git a/plugin.xml b/plugin.xml index e7b3ecbd..11689aa6 100644 --- a/plugin.xml +++ b/plugin.xml @@ -2,7 +2,7 @@ + version="1.0.3"> Cordova sqlite storage plugin - legacy express core version diff --git a/src/android/io/sqlc/SQLitePlugin.java b/src/android/io/sqlc/SQLitePlugin.java index 9ad6d49e..f4fb74cf 100755 --- a/src/android/io/sqlc/SQLitePlugin.java +++ b/src/android/io/sqlc/SQLitePlugin.java @@ -8,15 +8,18 @@ import android.annotation.SuppressLint; -import android.util.Base64; import android.util.Log; import java.io.File; + import java.lang.IllegalArgumentException; import java.lang.Number; -import java.util.concurrent.ConcurrentHashMap; + +import java.util.Map; import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.LinkedBlockingQueue; + import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -27,19 +30,24 @@ import org.json.JSONException; import org.json.JSONObject; -import java.io.FileOutputStream; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.IOException; - public class SQLitePlugin extends CordovaPlugin { /** * Multiple database runner map (static). - * NOTE: no public static accessor to db (runner) map since it would not work with db threading. - * FUTURE put DBRunner into a public class that can provide external accessor. + * + * NOTE: no public static accessor to db (runner) map since it is not + * expected to work properly with db threading. + * + * FUTURE TBD put DBRunner into a public class that can provide external accessor. + * + * ADDITIONAL NOTE: Storing as Map to avoid portabiity issue + * between Java 6/7/8 as discussed in: + * https://gist.github.com/AlainODea/1375759b8720a3f9f094 + * + * THANKS to @NeoLSN (Jason Yang/楊朝傑) for giving the pointer in: + * https://github.com/litehelpers/Cordova-sqlite-storage/issues/727 */ - static ConcurrentHashMap dbrmap = new ConcurrentHashMap(); + static Map dbrmap = new ConcurrentHashMap(); /** * NOTE: Using default constructor, no explicit constructor. From 7d897be5c950791537b3486776074056dfe3a0ec Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Sun, 10 Dec 2017 09:55:19 -0500 Subject: [PATCH 30/47] legacy branch doc fixes --- README.md | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 880846c8..2c93c203 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,10 @@ This version branch uses a `before_plugin_install` hook to install sqlite3 libra |[![Circle CI](https://circleci.com/gh/litehelpers/Cordova-sqlite-storage.svg?style=svg)](https://circleci.com/gh/litehelpers/Cordova-sqlite-storage)|[![Build Status](https://travis-ci.org/litehelpers/Cordova-sqlite-storage.svg)](https://travis-ci.org/litehelpers/Cordova-sqlite-storage)| --> +NOTE: This version branch has some additional known [issues fixed in newer version branches](#issues-fixed-in-newer-version-branches). + + + ## WARNING: Multiple SQLite problem on Android @@ -104,7 +108,6 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase - Windows platform version uses `UTF-16le` internal database encoding while the other platform versions use `UTF-8` internal encoding. (`UTF-8` internal encoding is preferred ref: [litehelpers/Cordova-sqlite-storage#652](https://github.com/litehelpers/Cordova-sqlite-storage/issues/652)) - FTS3, FTS4, and R-Tree support is tested working OK in this version (for all target platforms in this version branch Android/iOS/macOS/Windows) - Android versions supported: 2.3.3 - 7.1.1 (API level 10 - 25), depending on Cordova version ref: -minimum API level is supported back to SDK 10 (a.k.a. Gingerbread, Android 2.3.3); support for older versions is available upon request. - iOS versions supported: 8.x / 9.x / 10.x / 11.x - In case of memory issues please use smaller transactions or use the version at [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (GPL or commercial license terms). @@ -207,15 +210,15 @@ As "strongly recommended" by [Web SQL Database API 8.5 SQL injection](https://ww - Close/delete database bugs described below. - When a database is opened and deleted without closing, the iOS/macOS platform version is known to leak resources. - It is NOT possible to open multiple databases with the same name but in different locations (iOS/macOS platform version). -- Incorrect or missing insertId/rowsAffected in results for INSERT/UPDATE/DELETE SQL statements with extra semicolon(s) in the beginning for Android (android.database implementation) -- readTransaction does *not* reject modification SQL statements with extra semicolon(s) in the beginning -Issues fixed in some newer version branches: +### Issues fixed in newer version branches + +- Incorrect or missing insertId/rowsAffected in results for INSERT/UPDATE/DELETE SQL statements with extra semicolon(s) in the beginning for Android platform version in case the `androidDatabaseImplementation: 2` (built-in android.database implementation) option is used. - In case of an error, the error `code` member is bogus on Android - iOS platform version generates extra logging in release version - iOS platform version may crash if deleteDatabase is called with an object in place of the database name - readTransaction does not reject ALTER, REINDEX, and REPLACE operations -- readTransaction does not reject modification statements with extra semicolon(s) in the beginning +- readTransaction does *not* reject modification statements with extra semicolon(s) in the beginning - extra executeSql callbacks triggered in a transaction after a failure that was not recovered by an error callback that returns false - does not signal an error in case of excess parameter argument values given on iOS/macOS @@ -231,7 +234,6 @@ Issues fixed in some newer version branches: - In-memory database `db=window.sqlitePlugin.openDatabase({name: ':memory:', ...})` is currently not supported. - The Android platform version cannot properly support more than 100 open database files due to the threading model used. - SQL error messages reported by Windows platform version are not consistent with Android/iOS/macOS platform versions. -- UNICODE `\u2028` (line separator) and `\u2029` (paragraph separator) characters are not supported and known to be broken on iOS, macOS, and Android platform versions due to JSON issues reported in [Cordova bug CB-9435](https://issues.apache.org/jira/browse/CB-9435) and [cordova/cordova-discuss#57](https://github.com/cordova/cordova-discuss/issues/57). - UNICODE `\u2028` (line separator) and `\u2029` (paragraph separator) characters are currently not supported and known to be broken on iOS, macOS, and Android platform versions due to JSON issues reported in [Cordova bug CB-9435](https://issues.apache.org/jira/browse/CB-9435) and [cordova/cordova-discuss#57](https://github.com/cordova/cordova-discuss/issues/57). This is fixed with a workaround for iOS/macOS in: [litehelpers / Cordova-sqlite-evplus-legacy-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-free) and [litehelpers / Cordova-sqlite-evplus-legacy-attach-detach-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-attach-detach-free) (available with GPL or special commercial license options) as well as [litehelpers / Cordova-sqlite-evplus-legacy-workers-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-workers-free) (available with GPL or premium commercial license options) - The BLOB data type is not fully supported by this version branch. SELECT BLOB in Base64 format is supported by [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) (permissive license terms) and [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (GPL or commercial license options). - Truncation in case of UNICODE `\u0000` (same as `\0`) character on Android (default Android-sqlite-connector database implementation) and Windows. @@ -903,14 +905,6 @@ cordova platform add ios -## Source tree - -- `SQLitePlugin.coffee.md`: platform-independent (Literate coffee-script, can be read by recent coffee-script compiler) -- `www`: `SQLitePlugin.js` platform-independent Javascript as generated from `SQLitePlugin.coffee.md` (and checked in!) -- `src`: platform-specific source code -- `spec`: test suite using Jasmine (2.2.0) -- `tests`: very simple Jasmine test suite that is run on Circle CI (Android platform) and Travis CI (iOS platform) (used as a placeholder) - ## Installation test ### Easy installation test @@ -982,7 +976,7 @@ Free support for issues with Angular/"ngCordova"/Ionic will only be provided if ## What information is needed for help Please include the following: -- Which platform(s) _(Android/iOS/macOS/Windows 8.1/Windows Phone 8.1/Windows 10)_ +- Which platform(s) (Android/iOS/macOS/Windows 8.1/Windows Phone 8.1/Windows 10) - Clear description of the issue - A small, complete, self-contained program that demonstrates the problem, preferably as a Github project. ZIP/TGZ/BZ2 archive available from a public link is OK. No RAR or other such formats please! - A Cordova project is highly preferred. Intel, MS IDE, or similar project formats should be avoided. @@ -1035,6 +1029,20 @@ To run from a windows powershell (here is a sample for android target): - [nolanlawson / pouchdb-adapter-cordova-sqlite](https://github.com/nolanlawson/pouchdb-adapter-cordova-sqlite) + + +## Source tree + +- `SQLitePlugin.coffee.md`: platform-independent (Literate CoffeeScript, can be compiled with a recent CoffeeScript compiler) +- `www`: platform-independent Javascript as generated from `SQLitePlugin.coffee.md` (and committed!) +- `src`: platform-specific source code +- `node_modules`: placeholder for external dependencies +- `scripts`: installation hook script to fetch the external dependencies via `npm` +- `spec`: test suite using Jasmine +- `tests`: very simple Jasmine test suite that is run on Circle CI (Android platform) and Travis CI (iOS platform) (used as a placeholder) + + + # Contributing ## Community From d07611934ac49fa313ee078c9201ff55044de9e9 Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Sun, 10 Dec 2017 10:32:08 -0500 Subject: [PATCH 31/47] General doc updates --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2c93c203..31f8414a 100644 --- a/README.md +++ b/README.md @@ -106,9 +106,9 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase - INCORRECT error code and INCONSISTENT error message in error callbacks ref: [litehelpers/Cordova-sqlite-storage#539](https://github.com/litehelpers/Cordova-sqlite-storage/issues/539) - Not possible to read BLOB column values - Windows platform version uses `UTF-16le` internal database encoding while the other platform versions use `UTF-8` internal encoding. (`UTF-8` internal encoding is preferred ref: [litehelpers/Cordova-sqlite-storage#652](https://github.com/litehelpers/Cordova-sqlite-storage/issues/652)) -- FTS3, FTS4, and R-Tree support is tested working OK in this version (for all target platforms in this version branch Android/iOS/macOS/Windows) - Android versions supported: 2.3.3 - 7.1.1 (API level 10 - 25), depending on Cordova version ref: - iOS versions supported: 8.x / 9.x / 10.x / 11.x +- FTS3, FTS4, and R-Tree are fully tested and supported for all target platforms in this version branch. - In case of memory issues please use smaller transactions or use the version at [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (GPL or commercial license terms). @@ -144,7 +144,7 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase - As described in [this posting](http://brodyspark.blogspot.com/2012/12/cordovaphonegap-sqlite-plugins-offer.html): - Keeps sqlite database in known, platform specific user data location on all platforms (Android/iOS/macOS/Windows), which can be reconfigured on iOS/macOS. Whether or not the database on the iOS platform is synchronized to iCloud depends on the selected database location. - No arbitrary size limit. SQLite limits described at: -- Also tested for multi-page applications with window location changes +- Also validated for multi-page applications with window location changes by selfTest function. - This project is self-contained though with sqlite3 dependencies auto-fetched by npm. There are no dependencies on other plugins such as cordova-plugin-file. - Windows platform version uses a customized version of the performant [doo / SQLite3-WinRT](https://github.com/doo/SQLite3-WinRT) C++ component. - [SQLCipher](https://www.zetetic.net/sqlcipher/) support for Android/iOS/macOS/Windows is available in: [litehelpers / Cordova-sqlcipher-adapter](https://github.com/litehelpers/Cordova-sqlcipher-adapter) From f23012d9a27d03e94934362ea5d393496fdeb455 Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Tue, 12 Dec 2017 11:42:01 -0500 Subject: [PATCH 32/47] add sqlite-version-test.js to this legacy version branch --- CHANGES.md | 4 + package.json | 2 +- plugin.xml | 2 +- spec/www/index.html | 5 +- spec/www/spec/sqlite-version-test.js | 123 +++++++++++++++++++++++++++ 5 files changed, 133 insertions(+), 3 deletions(-) create mode 100755 spec/www/spec/sqlite-version-test.js diff --git a/CHANGES.md b/CHANGES.md index e43f50c9..6f750b34 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Changes +###### cordova-sqlite-legacy-express-core 1.0.4-devtest00 + +TBD + ###### cordova-sqlite-legacy-express-core 1.0.3 - Resolve Java 6/7/8 concurrent map compatibility issue reported in litehelpers/Cordova-sqlite-storage#726, THANKS to pointer by @NeoLSN (Jason Yang/楊朝傑) in litehelpers/Cordova-sqlite-storage#727. diff --git a/package.json b/package.json index c6325124..bf0368f3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cordova-sqlite-legacy-express-core", - "version": "1.0.3", + "version": "1.0.4-devtest00", "description": "Native interface to SQLite for PhoneGap/Cordova (legacy express core version)", "cordova": { "id": "cordova-sqlite-legacy-express-core", diff --git a/plugin.xml b/plugin.xml index 11689aa6..8380b945 100644 --- a/plugin.xml +++ b/plugin.xml @@ -2,7 +2,7 @@ + version="1.0.4-devtest00"> Cordova sqlite storage plugin - legacy express core version diff --git a/spec/www/index.html b/spec/www/index.html index d84a43e6..2a1ac74d 100644 --- a/spec/www/index.html +++ b/spec/www/index.html @@ -16,9 +16,12 @@ - + + + + diff --git a/spec/www/spec/sqlite-version-test.js b/spec/www/spec/sqlite-version-test.js new file mode 100755 index 00000000..b5b8c611 --- /dev/null +++ b/spec/www/spec/sqlite-version-test.js @@ -0,0 +1,123 @@ +/* 'use strict'; */ + +var MYTIMEOUT = 12000; + +var DEFAULT_SIZE = 5000000; // max to avoid popup in safari/ios + +var isWP8 = /IEMobile/.test(navigator.userAgent); // Matches WP(7/8/8.1) +var isWindows = /Windows /.test(navigator.userAgent); // Windows (8.1) +var isAndroid = !isWindows && /Android/.test(navigator.userAgent); + +// The following openDatabase settings are used for Plugin-implementation-2 +// on Android: +// - androidDatabaseImplementation: 2 +// - androidLockWorkaround: 1 +var scenarioList = [ + isAndroid ? 'Plugin-implementation-default' : 'Plugin', + 'HTML5', + 'Plugin-implementation-2' +]; + +var scenarioCount = (!!window.hasWebKitBrowser) ? (isAndroid ? 3 : 2) : 1; + +var mytests = function() { + + for (var i=0; i Date: Mon, 11 Dec 2017 19:00:31 -0500 Subject: [PATCH 33/47] sqlite version test fixes --- spec/www/spec/sqlite-version-test.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/spec/www/spec/sqlite-version-test.js b/spec/www/spec/sqlite-version-test.js index b5b8c611..3dc99933 100755 --- a/spec/www/spec/sqlite-version-test.js +++ b/spec/www/spec/sqlite-version-test.js @@ -30,8 +30,11 @@ var mytests = function() { var isWebSql = (i === 1); var isImpl2 = (i === 2); - // NOTE: MUST be defined in proper describe function scope, NOT outer scope: - var openDatabase = function(name, ignored1, ignored2, ignored3) { + // NOTE 1: MUST be defined in proper describe function scope, NOT outer scope. + // NOTE 2: Using same database name in this script to avoid creating extra, + // unneeded database files. + var openDatabase = function(name_ignored, ignored1, ignored2, ignored3) { + var name = 'sqlite-version-test.db'; if (isImpl2) { return window.sqlitePlugin.openDatabase({ // prevent reuse of database from default db implementation: @@ -50,7 +53,7 @@ var mytests = function() { describe(suiteName + 'basic sqlite version test(s)', function() { - it(suiteName + 'Check sqlite version (pattern ONLY for WebKit Web SQL & androidDatabaseImplementation: 2)', function(done) { + it(suiteName + 'Check sqlite version (check pattern ONLY for WebKit Web SQL & androidDatabaseImplementation: 2)', function(done) { var db = openDatabase("check-sqlite-version.db", "1.0", "Demo", DEFAULT_SIZE); expect(db).toBeDefined(); From a6c399721bbe17fe2014c0c2e1bc7cfbfc453c98 Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Tue, 12 Dec 2017 15:26:19 -0500 Subject: [PATCH 34/47] repeated open/close/delete tests also for androidDatabaseImplementation: 2 other minor test fixes --- spec/www/spec/db-open-close-delete-test.js | 51 ++++++++++++++++++---- 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/spec/www/spec/db-open-close-delete-test.js b/spec/www/spec/db-open-close-delete-test.js index bef8a4d5..59a92e56 100755 --- a/spec/www/spec/db-open-close-delete-test.js +++ b/spec/www/spec/db-open-close-delete-test.js @@ -447,16 +447,46 @@ var mytests = function() { }); } - describe('repeated open/close/delete test(s)', function() { - var scenarioName = isAndroid ? 'Plugin-implementation-default' : 'Plugin'; - var suiteName = scenarioName + ': '; + for (var i=0; i Date: Tue, 12 Dec 2017 16:53:45 -0500 Subject: [PATCH 35/47] README.md document UNSUPPORTED workaround for Visual Studio 2017 ref: litehelpers/cordova-sqlite-ext#60 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f9e42f5b..f9052f6c 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,7 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase - Pre-populated database (Android/iOS/macOS/Windows) - Amazon Fire-OS is dropped due to lack of support by Cordova. Android platform version should be used to deploy to Fire-OS 5.0(+) devices. For reference: [cordova/cordova-discuss#32 (comment)](https://github.com/cordova/cordova-discuss/issues/32#issuecomment-167021676) - Windows version (using a customized version of the performant [doo / SQLite3-WinRT](https://github.com/doo/SQLite3-WinRT) C++ component) has the following known limitations: - - This version branch with dependency on platform toolset libraries included by Visual Studio 2015 ref: [litehelpers/Cordova-sqlite-storage#580](https://github.com/litehelpers/Cordova-sqlite-storage/issues/580) + - This version branch has dependency on `v140` build toolset included by Visual Studio 2015 ref: [litehelpers/Cordova-sqlite-storage#580](https://github.com/litehelpers/Cordova-sqlite-storage/issues/580) (UNTESTED and UNSUPPORTED WORKAROUND for Visual Studio 2017 is described at: ) - It is **not** possible to use this plugin with the default "Any CPU" target. A specific target CPU type **must** be specified when building an app with this plugin. - Truncation issue with UNICODE `\u0000` character (same as `\0`) - No background processing @@ -127,7 +127,7 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase - macOS ("osx" platform) is now supported - New [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) version with Android JSON and SQL statement handling implemented in C, as well as support for PhoneGap Build, Intel XDK, etc. (GPL or commercial license terms). Handles large SQL batches in less than half the time as this version. Also supports arbitrary database location on Android. - Published [brodybits / Cordova-quick-start-checklist](https://github.com/brodybits/Cordova-quick-start-checklist) and [brodybits / Avoiding-some-Cordova-pitfalls](https://github.com/brodybits/Avoiding-some-Cordova-pitfalls). -- Windows 8.1/Windows Phone 8.1/Windows 10 version is available **here** (Visual Studio 2015 required, Visual Studio 2017 NOT supported by this version branch) as well as in [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) (with pre-populated database support) and [litehelpers / Cordova-sqlite-legacy](https://github.com/litehelpers/Cordova-sqlite-legacy) (with WP8 support). +- Windows 8.1/Windows Phone 8.1/Windows 10 version is available **here** (with dependency on `v140` build toolset included by Visual Studio 2015) as well as in [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) (with pre-populated database support) and [litehelpers / Cordova-sqlite-legacy](https://github.com/litehelpers/Cordova-sqlite-legacy) (with WP8 support). UNTESTED and UNSUPPORTED WORKAROUND for Visual Studio 2017 is described at: ) - Android version currently uses the lightweight [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector) by default configuration (may be changed as described below). - Self-test functions to verify proper installation and operation of this plugin - More explicit `openDatabase` and `deleteDatabase` `iosDatabaseLocation` option From e4846e131ce014ec9d979183f440f5c4b040d2b4 Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Mon, 11 Dec 2017 19:00:31 -0500 Subject: [PATCH 36/47] Test & document default PRAGMA journal_mode setting --- README.md | 3 +++ spec/www/spec/sqlite-version-test.js | 31 ++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/README.md b/README.md index cbf649a9..5b4c2456 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,9 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase - R-Tree is *not* tested or supported for Android in this version branch. - Android versions supported: 2.3.3 - 7.1.1 (API level 10 - 25), depending on Cordova version ref: - iOS versions supported: 8.x / 9.x / 10.x / 11.x +- Default `PRAGMA journal_mode` setting (*tested*): + - Android (builtin android.database implementation): `persist` + - otherwise: `delete` - In case of memory issues please use smaller transactions or use the version at [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (GPL or commercial license terms). diff --git a/spec/www/spec/sqlite-version-test.js b/spec/www/spec/sqlite-version-test.js index 3dc99933..0b5a5036 100755 --- a/spec/www/spec/sqlite-version-test.js +++ b/spec/www/spec/sqlite-version-test.js @@ -114,6 +114,37 @@ var mytests = function() { }); + describe(suiteName + 'additional sqlite check(s)', function() { + + it(suiteName + 'Check default PRAGMA journal_mode setting (plugin ONLY)', function(done) { + if (isWebSql) pending('SKIP: NOT SUPPORTED for (WebKit) Web SQL'); + + var db = openDatabase("Check-sqlite-PRAGMA-encoding.db", "1.0", "Demo", DEFAULT_SIZE); + + expect(db).toBeDefined(); + + db.executeSql('PRAGMA journal_mode', [], function(rs) { + expect(rs).toBeDefined(); + expect(rs.rows).toBeDefined(); + expect(rs.rows.length).toBe(1); + // TBD different for builtin android.database implementation: + if (!isWindows && isAndroid) // TBD ... + expect(rs.rows.item(0).journal_mode).toBe('persist'); + else + expect(rs.rows.item(0).journal_mode).toBe('delete'); + + // Close (plugin only) & finish: + (isWebSql) ? done() : db.close(done, done); + }, function(error) { + // NOT EXPECTED: + expect(false).toBe(true); + expect(error.message).toBe('--'); + done(); + }); + }, MYTIMEOUT); + + }); + }); } From fb1cede1bb5e14e4adb1648637b159085df9f0a6 Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Tue, 12 Dec 2017 16:56:54 -0500 Subject: [PATCH 37/47] general plugin doc fixes --- README.md | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 5b4c2456..d9686ffa 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,7 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase - SELECT BLOB data in Base64 format (all platforms Android/iOS/macOS/Windows) - Pre-populated database (Android/iOS/macOS/Windows) - Amazon Fire-OS is dropped due to lack of support by Cordova. Android platform version should be used to deploy to Fire-OS 5.0(+) devices. For reference: [cordova/cordova-discuss#32 (comment)](https://github.com/cordova/cordova-discuss/issues/32#issuecomment-167021676) +- The macOS platform version ("osx" platform) is not tested in a release build and should be considered pre-alpha. - FTS3 and FTS4 are tested working OK in this version branch (for all target platforms in this version branch Android/iOS/macOS) - R-Tree is *not* tested or supported for Android in this version branch. - Android versions supported: 2.3.3 - 7.1.1 (API level 10 - 25), depending on Cordova version ref: @@ -69,7 +70,7 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase - Default `PRAGMA journal_mode` setting (*tested*): - Android (builtin android.database implementation): `persist` - otherwise: `delete` -- In case of memory issues please use smaller transactions or use the version at [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (GPL or commercial license terms). +- In case of memory issues please use smaller transactions or use the plugin version at [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (GPL or commercial license terms). @@ -84,7 +85,7 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase - [brodybits / sql-promise-helper](https://github.com/brodybits/sql-promise-helper) provides a Promise-based API wrapper. - [nolanlawson / pouchdb-adapter-cordova-sqlite](https://github.com/nolanlawson/pouchdb-adapter-cordova-sqlite) supports this plugin along with other implementations such as [nolanlawson / sqlite-plugin-2](https://github.com/nolanlawson/sqlite-plugin-2) and [Microsoft / cordova-plugin-websql](https://github.com/Microsoft/cordova-plugin-websql). - macOS ("osx" platform) is now supported -- New [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) version with Android JSON and SQL statement handling implemented in C, as well as support for PhoneGap Build, Intel XDK, etc., available with GPL or commercial license options. Handles large SQL batches in less than half the time as this version. Also supports arbitrary database location on Android. +- New [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) plugin version with Android JSON and SQL statement handling implemented in C, as well as support for PhoneGap Build, Intel XDK, etc. (GPL or commercial license terms). Handles large SQL batches in less than half the time as this plugin version. Also supports arbitrary database location on Android. - Published [brodybits / Cordova-quick-start-checklist](https://github.com/brodybits/Cordova-quick-start-checklist) and [brodybits / Avoiding-some-Cordova-pitfalls](https://github.com/brodybits/Avoiding-some-Cordova-pitfalls). - Self-test functions to verify proper installation and operation of this plugin - More explicit `openDatabase` and `deleteDatabase` `iosDatabaseLocation` option @@ -186,7 +187,7 @@ Some additional issues are tracked in [open Cordova-sqlite-storage bug-general i - ~~The db version, display name, and size parameter values are not supported and will be ignored.~~ (No longer supported by the API) - Absolute and relative subdirectory path(s) are not tested or supported. - This plugin will not work before the callback for the 'deviceready' event has been fired, as described in **Usage**. (This is consistent with the other Cordova plugins.) -- Extremely large records are not supported by this plugin. It is recommended to store images and similar binary data in separate files. TBD: specify maximum record. For future consideration: support in a version such as [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (GPL or commercial license terms). +- Extremely large records are not supported by this plugin. It is recommended to store images and similar binary data in separate files. TBD: specify maximum record. For future consideration: support in a plugin version such as [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (GPL or commercial license terms). - This plugin version will not work within a web worker (not properly supported by the Cordova framework). Use within a web worker is supported for Android/iOS in [litehelpers / cordova-sqlite-evmax-ext-workers-legacy-build-free](https://github.com/litehelpers/cordova-sqlite-evmax-ext-workers-legacy-build-free) (GPL or premium commercial license terms). - In-memory database `db=window.sqlitePlugin.openDatabase({name: ':memory:', ...})` is currently not supported. - The Android platform version cannot properly support more than 100 open database files due to the threading model used. @@ -280,7 +281,7 @@ Documented in: [brodybits / Avoiding-some-Cordova-pitfalls](https://github.com/b ## For future considertion - Auto-vacuum option -- Support for extremely large records in a version such as [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (available with GPL or commercial license options) +- Support for extremely large records in a plugin version such as [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (available with GPL or commercial license options) - Integrate with some other libraries such as Sequelize, Squel.js, WebSqlSync, Persistence.js, Knex, etc. @@ -289,16 +290,16 @@ Documented in: [brodybits / Avoiding-some-Cordova-pitfalls](https://github.com/b ### Comparison of sqlite plugin versions -- [litehelpers / Cordova-sqlite-storage](https://github.com/litehelpers/Cordova-sqlite-storage) - core version for Android/iOS/macOS/Windows (permissive license terms) -- [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) - version with REGEXP (Android/iOS/macOS), SELECT BLOB in Base64 format (all platforms Android/iOS/macOS/Windows), and pre-populated databases (all platforms Android/iOS/macOS/Windows). Permissive license terms. -- [litehelpers / Cordova-sqlite-legacy-build-support](https://github.com/litehelpers/Cordova-sqlite-legacy-build-support) - maintenance of WP8 version along with Windows 8.1/Windows Phone 8.1 and the other supported platforms Android/iOS/macOS/Windows 10; limited support for PhoneGap CLI/PhoneGap Build/plugman/Intel XDK; limited testing; limited updates. Permissive license terms. +- [litehelpers / Cordova-sqlite-storage](https://github.com/litehelpers/Cordova-sqlite-storage) - core plugin version for Android/iOS/macOS/Windows (permissive license terms) +- [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) - plugin version with REGEXP (Android/iOS/macOS), SELECT BLOB in Base64 format (all platforms Android/iOS/macOS/Windows), and pre-populated databases (all platforms Android/iOS/macOS/Windows). Permissive license terms. +- [litehelpers / Cordova-sqlite-legacy-build-support](https://github.com/litehelpers/Cordova-sqlite-legacy-build-support) - maintenance of WP8 platform version along with Windows 8.1/Windows Phone 8.1 and the other supported platforms Android/iOS/macOS/Windows 10; limited support for PhoneGap CLI/PhoneGap Build/plugman/Intel XDK; limited testing; limited updates. Permissive license terms. - [litehelpers / Cordova-sqlcipher-adapter](https://github.com/litehelpers/Cordova-sqlcipher-adapter) - supports [SQLCipher](https://www.zetetic.net/sqlcipher/) for Android/iOS/macOS/Windows -- [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) - Enhancements for Android: JSON and SQL statement handling implemented in C, supports larger transactions and handles large SQL batches in less than half the time as this version. Supports arbitrary database location on Android. Support for build environments such as PhoneGap Build and Intel XDK. Also includes REGEXP (Android/iOS/macOS) and SELECT BLOB in Base64 format (all platforms Android/iOS/macOS/Windows). GPL or commercial license terms. +- [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) - Enhancements for Android: JSON and SQL statement handling implemented in C, supports larger transactions and handles large SQL batches in less than half the time as this plugin version. Supports arbitrary database location on Android. Support for build environments such as PhoneGap Build and Intel XDK. Also includes REGEXP (Android/iOS/macOS) and SELECT BLOB in Base64 format (all platforms Android/iOS/macOS/Windows). GPL or commercial license terms. - [litehelpers / cordova-sqlite-evplus-ext-legacy-build-free](https://github.com/litehelpers/cordova-sqlite-evplus-ext-legacy-build-free) - internal memory improvements to support larger transactions (Android/iOS) and fix to support all Unicode characters (iOS). (GPL or special commercial license terms). -- [litehelpers / Cordova-sqlite-evplus-legacy-attach-detach-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-attach-detach-free) - version with support for ATTACH, includes internal memory improvements to support larger transactions (Android/iOS) and fix to support all Unicode characters (GPL or special commercial license terms). -- [litehelpers / cordova-sqlite-evmax-ext-workers-legacy-build-free](https://github.com/litehelpers/cordova-sqlite-evmax-ext-workers-legacy-build-free) - version with support for web workers, includes internal memory improvements to support larger transactions (Android/iOS) and fix to support all Unicode characters (iOS). (GPL or premium commercial license terms). +- [litehelpers / Cordova-sqlite-evplus-legacy-attach-detach-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-attach-detach-free) - plugin version with support for ATTACH, includes internal memory improvements to support larger transactions (Android/iOS) and fix to support all Unicode characters (GPL or special commercial license terms). +- [litehelpers / cordova-sqlite-evmax-ext-workers-legacy-build-free](https://github.com/litehelpers/cordova-sqlite-evmax-ext-workers-legacy-build-free) - plugin version with support for web workers, includes internal memory improvements to support larger transactions (Android/iOS) and fix to support all Unicode characters (iOS). (GPL or premium commercial license terms). - Adaptation for React Native Android and iOS: [andpor / react-native-sqlite-storage](https://github.com/andpor/react-native-sqlite-storage) (permissive license terms) -- Original version for iOS (with a non-standard, outdated transaction API): [davibe / Phonegap-SQLitePlugin](https://github.com/davibe/Phonegap-SQLitePlugin) (permissive license terms) +- Original plugin version for iOS (with a non-standard, outdated transaction API): [davibe / Phonegap-SQLitePlugin](https://github.com/davibe/Phonegap-SQLitePlugin) (permissive license terms) @@ -462,7 +463,7 @@ var db = window.sqlitePlugin.openDatabase({ ## SQL transactions -The following types of SQL transactions are supported by this version: +The following types of SQL transactions are supported by this plugin version: - Single-statement transactions - SQL batch transactions - Standard asynchronous transactions @@ -617,7 +618,7 @@ db.readTransaction(function(tx) { ## Background processing -The threading model depends on which version is used: +The threading model depends on which platform version is used: - For Android, one background thread per db; - for iOS/macOS, background processing using a very limited thread pool (only one thread working at a time). From 58161739ca2116300457eab2682b7abafab03cdf Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Tue, 12 Dec 2017 16:56:54 -0500 Subject: [PATCH 38/47] general legacy doc fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d9686ffa..b33bd615 100644 --- a/README.md +++ b/README.md @@ -171,7 +171,7 @@ As "strongly recommended" by [Web SQL Database API 8.5 SQL injection](https://ww - Incorrect or missing insertId/rowsAffected in results for INSERT/UPDATE/DELETE SQL statements with extra semicolon(s) in the beginning for Android (android.database implementation) - In case of an error, the error `code` member is bogus on Android -- iOS platform version generates extra logging in release version +- iOS platform version generates extra logging in release build - iOS platform version may crash if deleteDatabase is called with an object in place of the database name - readTransaction does not reject ALTER, REINDEX, and REPLACE operations - readTransaction does *not* reject modification statements with extra semicolon(s) in the beginning From 9ce56b87aeed666e52022498118fef513997c8d5 Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Tue, 12 Dec 2017 16:56:54 -0500 Subject: [PATCH 39/47] general plugin doc updates --- README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index f9052f6c..03a6302a 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase ## Status - A recent version of the Cordova CLI (such as `6.5.0` or `7.1.0`) is recommended. Cordova versions older than `6.0.0` are missing the `cordova-ios@4.0.0` security fixes. In addition it is *required* to use `cordova prepare` in case of cordova-ios older than `4.3.0` (Cordova CLI `6.4.0`). -- This version uses a `before_plugin_install` hook to install sqlite3 library dependencies from `cordova-sqlite-storage-dependencies` via npm. +- This version branch uses a `before_plugin_install` hook to install sqlite3 library dependencies from `cordova-sqlite-storage-dependencies` via npm. - This plugin will NOT work on `cordova-android@7` due to issue with JAR and NDK library files as discussed in [litehelpers/Cordova-sqlite-storage#729](https://github.com/litehelpers/Cordova-sqlite-storage/issues/729). - SQLite `3.8.10.2` included when building (all platforms), with the following definitions for iOS/macOS/Windows: - `SQLITE_THREADSAFE=1` (`SQLITE_THREADSAFE=2` on iOS/macOS) @@ -89,7 +89,7 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase - `SQLITE_OS_WINRT` for Windows only - Use of other systems such as Cordova Plugman, PhoneGap CLI, PhoneGap Build, and Intel XDK is no longer supported since they do not honor the `before_plugin_install` hook. The supported solution is to use [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (available with GPL or commercial license terms) or [litehelpers / Cordova-sqlite-legacy-build-support](https://github.com/litehelpers/Cordova-sqlite-legacy-build-support) (limited testing, limited updates) - The iOS database location is now mandatory, as documented below. -- This version supports the use of two (2) possible Android sqlite database implementations: +- This version branch supports the use of two (2) possible Android sqlite database implementations: - default: lightweight [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector) - optional: built-in Android database classes (usage described below) - Support for WP8 is available in: [litehelpers / Cordova-sqlite-legacy-build-support](https://github.com/litehelpers/Cordova-sqlite-legacy-build-support) (along with Windows 8.1/Windows Phone 8.1/Windows 10 using Visual Studio 2015) @@ -98,7 +98,7 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase - SELECT BLOB data in Base64 format (all platforms Android/iOS/macOS/Windows) - Pre-populated database (Android/iOS/macOS/Windows) - Amazon Fire-OS is dropped due to lack of support by Cordova. Android platform version should be used to deploy to Fire-OS 5.0(+) devices. For reference: [cordova/cordova-discuss#32 (comment)](https://github.com/cordova/cordova-discuss/issues/32#issuecomment-167021676) -- Windows version (using a customized version of the performant [doo / SQLite3-WinRT](https://github.com/doo/SQLite3-WinRT) C++ component) has the following known limitations: +- Windows platform version (using a customized version of the performant [doo / SQLite3-WinRT](https://github.com/doo/SQLite3-WinRT) C++ component) has the following known limitations: - This version branch has dependency on `v140` build toolset included by Visual Studio 2015 ref: [litehelpers/Cordova-sqlite-storage#580](https://github.com/litehelpers/Cordova-sqlite-storage/issues/580) (UNTESTED and UNSUPPORTED WORKAROUND for Visual Studio 2017 is described at: ) - It is **not** possible to use this plugin with the default "Any CPU" target. A specific target CPU type **must** be specified when building an app with this plugin. - Truncation issue with UNICODE `\u0000` character (same as `\0`) @@ -128,7 +128,7 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase - New [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) version with Android JSON and SQL statement handling implemented in C, as well as support for PhoneGap Build, Intel XDK, etc. (GPL or commercial license terms). Handles large SQL batches in less than half the time as this version. Also supports arbitrary database location on Android. - Published [brodybits / Cordova-quick-start-checklist](https://github.com/brodybits/Cordova-quick-start-checklist) and [brodybits / Avoiding-some-Cordova-pitfalls](https://github.com/brodybits/Avoiding-some-Cordova-pitfalls). - Windows 8.1/Windows Phone 8.1/Windows 10 version is available **here** (with dependency on `v140` build toolset included by Visual Studio 2015) as well as in [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) (with pre-populated database support) and [litehelpers / Cordova-sqlite-legacy](https://github.com/litehelpers/Cordova-sqlite-legacy) (with WP8 support). UNTESTED and UNSUPPORTED WORKAROUND for Visual Studio 2017 is described at: ) -- Android version currently uses the lightweight [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector) by default configuration (may be changed as described below). +- Android platform version currently uses the lightweight [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector) by default configuration (may be changed as described below). - Self-test functions to verify proper installation and operation of this plugin - More explicit `openDatabase` and `deleteDatabase` `iosDatabaseLocation` option - Added straightforward sql batch function @@ -200,6 +200,7 @@ As "strongly recommended" by [Web SQL Database API 8.5 SQL injection](https://ww - This plugin will NOT work on `cordova-android@7` due to issue with JAR and NDK library files as discussed in [litehelpers/Cordova-sqlite-storage#729](https://github.com/litehelpers/Cordova-sqlite-storage/issues/729). - The iOS/macOS platform versions do not support certain rapidly repeated open-and-close or open-and-delete test scenarios due to how the implementation handles background processing - As described below, auto-vacuum is NOT enabled by default. +- The Android platform version does not always handle four-byte UTF-8 characters emoji characters such as `\u1F603` (SMILING FACE, MOUTH OPEN) correctly ref: [litehelpers/Cordova-sqlite-storage#564](https://github.com/litehelpers/Cordova-sqlite-storage/issues/564). It is sometimes possible to store and retrieve such characters but certain operations such as hex conversions do not work properly when using the default [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector) database implementation. It is suspected that such characters would be stored incorrectly by the default Android platform version. Note that this is not an issue in case the built-in Android database is used (using the `androidDatabaseImplementation: 2` setting in `window.sqlitePlugin.openDatabase`) - It is possible to request a SQL statement list such as "SELECT 1; SELECT 2" within a single SQL statement string, however the plugin will only execute the first statement and silently ignore the others ref: [litehelpers/Cordova-sqlite-storage#551](https://github.com/litehelpers/Cordova-sqlite-storage/issues/551) - Execution of INSERT statement that affects multiple rows (due to SELECT cause or using TRIGGER(s), for example) reports incorrect rowsAffected on Android in case the built-in Android database used (using the `androidDatabaseImplementation` option in `window.sqlitePlugin.openDatabase`) - Memory issue observed when adding a large number of records due to the JSON implementation which is improved in [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (GPL or commercial license terms) @@ -496,7 +497,7 @@ var db = window.sqlitePlugin.openDatabase({name: 'my.db', location: 'default', a **IMPORTANT:** - As described above: by default this plugin uses a non-standard [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector) implementation on Android. In case an application access the **same** database using multiple plugins there is a risk of data corruption ref: [litehelpers/Cordova-sqlite-storage#626](https://github.com/litehelpers/Cordova-sqlite-storage/issues/626)) as described in and . The workaround is to use the `androidDatabaseImplementation: 2` setting as described here. -- In case of the `androidDatabaseImplementation: 2` setting, [litehelpers/Cordova-sqlite-storage#193](https://github.com/litehelpers/Cordova-sqlite-storage/issues/193) reported that in certain Android versions, if the app is stopped or aborted without closing the database then there is an unexpected database lock and the data that was inserted is lost. The workaround is described below. +- In case of the `androidDatabaseImplementation: 2` setting, [litehelpers/Cordova-sqlite-storage#193](https://github.com/litehelpers/Cordova-sqlite-storage/issues/193) reported (as observed by a number of app developers in the past) that in certain Android versions, if the app is stopped or aborted without closing the database then there is an unexpected database lock and the data that was inserted is lost. The workaround is described below. ### Workaround for Android db locking issue From 7aa81a421c97264b8a3dfee7f606b2c7342eff4c Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Wed, 13 Dec 2017 10:08:32 -0500 Subject: [PATCH 40/47] cordova-sqlite-legacy-core legacy doc updates --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 03a6302a..685e37ff 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ License terms for iOS/macOS platform version: MIT only ## About this version branch -This version branch contains the source code for the Android/iOS/macOS platforms (legacy core version branch). +This version branch contains the source code for the Android/iOS/macOS/Windows platforms (legacy core version branch). This version branch uses a `before_plugin_install` hook to install sqlite3 library dependencies from `cordova-sqlite-storage-dependencies` via npm. @@ -103,7 +103,7 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase - It is **not** possible to use this plugin with the default "Any CPU" target. A specific target CPU type **must** be specified when building an app with this plugin. - Truncation issue with UNICODE `\u0000` character (same as `\0`) - No background processing - - INCORRECT error code and INCONSISTENT error message in error callbacks ref: [litehelpers/Cordova-sqlite-storage#539](https://github.com/litehelpers/Cordova-sqlite-storage/issues/539) + - NONCOMPLIANT error code (-1) and INCONSISTENT error message (missing actual error info) in error callbacks ref: [litehelpers/Cordova-sqlite-storage#539](https://github.com/litehelpers/Cordova-sqlite-storage/issues/539) - Not possible to read BLOB column values - Windows platform version uses `UTF-16le` internal database encoding while the other platform versions use `UTF-8` internal encoding. (`UTF-8` internal encoding is preferred ref: [litehelpers/Cordova-sqlite-storage#652](https://github.com/litehelpers/Cordova-sqlite-storage/issues/652)) - Android versions supported: 2.3.3 - 7.1.1 (API level 10 - 25), depending on Cordova version ref: @@ -206,7 +206,6 @@ As "strongly recommended" by [Web SQL Database API 8.5 SQL injection](https://ww - Memory issue observed when adding a large number of records due to the JSON implementation which is improved in [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (GPL or commercial license terms) - Infinity (positive or negative) values are not supported on Android/iOS/macOS due to issues described above including a possible crash on iOS/macOS ref: [litehelpers/Cordova-sqlite-storage#405](https://github.com/litehelpers/Cordova-sqlite-storage/issues/405) - A stability issue was reported on the iOS platform version when in use together with [SockJS](http://sockjs.org/) client such as [pusher-js](https://github.com/pusher/pusher-js) at the same time (see [litehelpers/Cordova-sqlite-storage#196](https://github.com/litehelpers/Cordova-sqlite-storage/issues/196)). The workaround is to call sqlite functions and [SockJS](http://sockjs.org/) client functions in separate ticks (using setTimeout with 0 timeout). -- If a sql statement fails for which there is no error handler or the error handler does not return `false` to signal transaction recovery, the plugin fires the remaining sql callbacks before aborting the transaction. - In case of an error, the error `code` member is bogus on _Android and Windows_. - Possible crash on Android when using Unicode emoji and other 4-octet UTF-8 characters due to [Android bug 81341](https://code.google.com/p/android/issues/detail?id=81341), which *should* be fixed in Android 6.x - Close/delete database bugs described below. @@ -217,11 +216,12 @@ As "strongly recommended" by [Web SQL Database API 8.5 SQL injection](https://ww - Incorrect or missing insertId/rowsAffected in results for INSERT/UPDATE/DELETE SQL statements with extra semicolon(s) in the beginning for Android platform version in case the `androidDatabaseImplementation: 2` (built-in android.database implementation) option is used. - In case of an error, the error `code` member is bogus on Android +- Windows platform version reports NONCOMPLIANT error code (-1) in case of an error - iOS platform version generates extra logging in release version - iOS platform version may crash if deleteDatabase is called with an object in place of the database name - readTransaction does not reject ALTER, REINDEX, and REPLACE operations - readTransaction does *not* reject modification statements with extra semicolon(s) in the beginning -- extra executeSql callbacks triggered in a transaction after a failure that was not recovered by an error callback (by returning false) +- extra executeSql callbacks: if a sql statement fails for which there is no error handler or the error handler does not return `false` to signal transaction recovery, the plugin fires the remaining sql callbacks before aborting the transaction. - does not signal an error in case of excess parameter argument values given on iOS/macOS Some additional issues are tracked in [open Cordova-sqlite-storage bug-general issues](https://github.com/litehelpers/Cordova-sqlite-storage/issues?q=is%3Aissue+is%3Aopen+label%3Abug-general). From 7051b1b93c000d6ec28b48798af9a95e77bebb59 Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Wed, 13 Dec 2017 11:09:06 -0500 Subject: [PATCH 41/47] additional plugin doc updates --- README.md | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index b33bd615..d26dd4bf 100644 --- a/README.md +++ b/README.md @@ -70,12 +70,15 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase - Default `PRAGMA journal_mode` setting (*tested*): - Android (builtin android.database implementation): `persist` - otherwise: `delete` +- AUTO-VACUUM is not enabled by default. If no form of `VACUUM` or `PRAGMA auto_vacuum` is used then sqlite will automatically reuse deleted data space for new data but the database file will never shrink. For reference: and [litehelpers/Cordova-sqlite-storage#646](https://github.com/litehelpers/Cordova-sqlite-storage/issues/646) - In case of memory issues please use smaller transactions or use the plugin version at [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (GPL or commercial license terms). ## Announcements +- Nice overview of this and other alternatives for storing local data at: +- New alternative solution for small data storage: [TheCocoaProject/ cordova-plugin-nativestorage](https://github.com/TheCocoaProject/cordova-plugin-nativestorage) - simpler "native storage of variables" for Android/iOS/Windows - Resolved Java 6/7/8 concurrent map compatibility issue reported in [litehelpers/Cordova-sqlite-storage#726](https://github.com/litehelpers/Cordova-sqlite-storage/issues/726), THANKS to pointer by [@NeoLSN (Jason Yang/楊朝傑)](https://github.com/NeoLSN) in [litehelpers/Cordova-sqlite-storage#727](https://github.com/litehelpers/Cordova-sqlite-storage/issues/727). - Fixed iOS/macOS platform version to use [PSPDFThreadSafeMutableDictionary.m](https://gist.github.com/steipete/5928916) to avoid threading issue ref: [litehelpers/Cordova-sqlite-storage#716](https://github.com/litehelpers/Cordova-sqlite-storage/issues/716) - Resolved transaction problem after window.location (page) change with possible data loss ref: [litehelpers/Cordova-sqlite-storage#666](https://github.com/litehelpers/Cordova-sqlite-storage/issues/666) @@ -101,7 +104,7 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase - Failure-safe nested transactions with batch processing optimizations (according to HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/)) - Transaction API (based on HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/)) is designed for maximum flexiblibility, does not allow any transactions to be left hanging open. - As described in [this posting](http://brodyspark.blogspot.com/2012/12/cordovaphonegap-sqlite-plugins-offer.html): - - Keeps sqlite database in known, platform specific user data location on all platforms (Android/iOS/macOS/Windows), which can be reconfigured on iOS/macOS. Whether or not the database on the iOS platform is synchronized to iCloud depends on the selected database location. + - Keeps sqlite database in known, platform specific user data location on all supported platforms (Android/iOS/macOS/Windows), which can be reconfigured on iOS/macOS. Whether or not the database on the iOS platform is synchronized to iCloud depends on the selected database location. - No arbitrary size limit. SQLite limits described at: - ~~Also tested for multi-page applications with window location changes~~ _(workaround solution for BUG 666 is incorrect in this version branch)_ - This project is self-contained: no dependencies on other plugins such as cordova-plugin-file @@ -156,7 +159,6 @@ As "strongly recommended" by [Web SQL Database API 8.5 SQL injection](https://ww ## Known issues - The iOS/macOS platform versions do not support certain rapidly repeated open-and-close or open-and-delete test scenarios due to how the implementation handles background processing -- As described below, auto-vacuum is NOT enabled by default. - It is possible to request a SQL statement list such as "SELECT 1; SELECT 2" within a single SQL statement string, however the plugin will only execute the first statement and silently ignore the others ref: [litehelpers/Cordova-sqlite-storage#551](https://github.com/litehelpers/Cordova-sqlite-storage/issues/551) - Execution of INSERT statement that affects multiple rows (due to SELECT cause or using TRIGGER(s), for example) does not report proper rowsAffected on Android - Memory issue observed when adding a large number of records due to the JSON implementation which is improved in [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (GPL or commercial license terms) @@ -248,7 +250,7 @@ Additional limitations are tracked in [open Cordova-sqlite-storage doc-todo issu - The plugin class name starts with "SQL" in capital letters, but in Javascript the `sqlitePlugin` object name starts with "sql" in small letters. - Attempting to open a database before receiving the 'deviceready' event callback. - Inserting STRING into ID field -- Auto-vacuum is NOT enabled by default. It is recommended to periodically VACUUM the database. +- Auto-vacuum is NOT enabled by default. It is recommended to periodically VACUUM the database. If no form of `VACUUM` or `PRAGMA auto_vacuum` is used then sqlite will automatically reuse deleted data space for new data but the database file will never shrink. For reference: and [litehelpers/Cordova-sqlite-storage#646](https://github.com/litehelpers/Cordova-sqlite-storage/issues/646) - Transactions on a database are run sequentially. A large transaction could block smaller transactions requested afterwards. ### Some weird pitfall(s) @@ -280,7 +282,7 @@ Documented in: [brodybits / Avoiding-some-Cordova-pitfalls](https://github.com/b ## For future considertion -- Auto-vacuum option +- Explicit auto-vacuum option ref: [litehelpers/Cordova-sqlite-storage#646](https://github.com/litehelpers/Cordova-sqlite-storage/issues/646) - Support for extremely large records in a plugin version such as [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (available with GPL or commercial license options) - Integrate with some other libraries such as Sequelize, Squel.js, WebSqlSync, Persistence.js, Knex, etc. @@ -303,7 +305,7 @@ Documented in: [brodybits / Avoiding-some-Cordova-pitfalls](https://github.com/b -### Other SQLite adapter projects +### Other SQLite access projects - [object-layer / AnySQL](https://github.com/object-layer/anysql) - Unified SQL API over multiple database engines - [samikrc / CordovaSQLite](https://github.com/samikrc/CordovaSQLite) - Simpler sqlite plugin with a simpler API and browser platform @@ -317,18 +319,23 @@ Documented in: [brodybits / Avoiding-some-Cordova-pitfalls](https://github.com/b - [Thinkwise / cordova-plugin-websql](https://github.com/Thinkwise/cordova-plugin-websql) - fork of [Microsoft / cordova-plugin-websql](https://github.com/Microsoft/cordova-plugin-websql) that supports asynchronous execution - [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. - + -### Alternative solutions +### Alternative storage solutions +- available on newer Android/iOS/Windows versions, please see best practices at +- [TheCocoaProject/ cordova-plugin-nativestorage](https://github.com/TheCocoaProject/cordova-plugin-nativestorage) - simpler "native storage of variables" (small data storage) for Android/iOS/macOS/Windows (TBD browser support?) - Use [phearme / cordova-ContentProviderPlugin](https://github.com/phearme/cordova-ContentProviderPlugin) to query content providers on Android devices - [ABB-Austin / cordova-plugin-indexeddb-async](https://github.com/ABB-Austin/cordova-plugin-indexeddb-async) - Asynchronous IndexedDB plugin for Cordova that uses [axemclion / IndexedDBShim](https://github.com/axemclion/IndexedDBShim) (Browser/iOS/Android/Windows) and [Thinkwise / cordova-plugin-websql](https://github.com/Thinkwise/cordova-plugin-websql) - (Windows) -- Use [NativeScript](https://www.nativescript.org) with its web view and [NathanaelA / nativescript-sqlite](https://github.com/Natha -naelA/nativescript-sqlite) (Android and/or iOS) +- Use [NativeScript](https://www.nativescript.org) with its web view and [NathanaelA / nativescript-sqlite](https://github.com/NathanaelA/nativescript-sqlite) (Android and/or iOS) - Standard HTML5 [local storage](https://en.wikipedia.org/wiki/Web_storage#localStorage) - [Realm.io](https://realm.io/) +- Other Cordova storage alternatives described at: + - + - + - - + # Usage From 51ced034c4d723a658a603bde2e5123fb9788348 Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Wed, 13 Dec 2017 18:33:44 -0500 Subject: [PATCH 42/47] legacy test workaround to pass on iOS 11 --- spec/www/spec/db-tx-sql-results.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/www/spec/db-tx-sql-results.js b/spec/www/spec/db-tx-sql-results.js index 71182a82..0425d809 100644 --- a/spec/www/spec/db-tx-sql-results.js +++ b/spec/www/spec/db-tx-sql-results.js @@ -331,8 +331,8 @@ var mytests = function() { if (isWebSql) { // Web SQL STANDARD: - // 1. this is a native object that is NOT affected by the change (SKIP for Android 5.x/+): - if (!isAndroid || /Android [1-4]/.test(navigator.userAgent)) + // 1. this is a native object that is NOT affected by the change (SKIP for Android 5.x/+ and iOS): + if (isAndroid && /Android [1-4]/.test(navigator.userAgent)) expect(temp1.data).toBe('test'); // 2. object returned by second resultSet.rows.item call not affected: expect(temp2.data).toBe('test'); From 9f1d0a7983de2936aac0de2eceb46e07017bd1b9 Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Mon, 11 Dec 2017 20:05:05 -0500 Subject: [PATCH 43/47] android.database end transaction if active before closing Needed for new BUG 666 workaround solution to pass selfTest in case of builtin android.database implementation (no Android-sqlite-connector / Android-sqlite-native-driver libraries). Ref: - litehelpers/Cordova-sqlite-storage#730 - litehelpers/Cordova-sqlite-storage#666 --- CHANGES.md | 4 ++-- package.json | 2 +- plugin.xml | 2 +- src/android/io/sqlc/SQLiteAndroidDatabase.java | 9 +++++++++ 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 6f750b34..4f63fec3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,8 +1,8 @@ # Changes -###### cordova-sqlite-legacy-express-core 1.0.4-devtest00 +###### cordova-sqlite-legacy-express-core 1.0.4-newdev01 -TBD +- android.database end transaction if active before closing (needed for new BUG 666 workaround solution to pass selfTest in case of builtin android.database implementation) ###### cordova-sqlite-legacy-express-core 1.0.3 diff --git a/package.json b/package.json index bf0368f3..de01ba1d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cordova-sqlite-legacy-express-core", - "version": "1.0.4-devtest00", + "version": "1.0.4-newdev01", "description": "Native interface to SQLite for PhoneGap/Cordova (legacy express core version)", "cordova": { "id": "cordova-sqlite-legacy-express-core", diff --git a/plugin.xml b/plugin.xml index 8380b945..d2e83367 100644 --- a/plugin.xml +++ b/plugin.xml @@ -2,7 +2,7 @@ + version="1.0.4-newdev01"> Cordova sqlite storage plugin - legacy express core version diff --git a/src/android/io/sqlc/SQLiteAndroidDatabase.java b/src/android/io/sqlc/SQLiteAndroidDatabase.java index a3870445..a1887184 100644 --- a/src/android/io/sqlc/SQLiteAndroidDatabase.java +++ b/src/android/io/sqlc/SQLiteAndroidDatabase.java @@ -51,6 +51,8 @@ class SQLiteAndroidDatabase SQLiteDatabase mydb; + boolean isTransactionActive = false; + /** * NOTE: Using default constructor, no explicit constructor. */ @@ -70,6 +72,10 @@ void open(File dbfile) throws Exception { */ void closeDatabaseNow() { if (mydb != null) { + if (isTransactionActive) { + mydb.endTransaction(); + isTransactionActive = false; + } mydb.close(); mydb = null; } @@ -195,6 +201,7 @@ void executeSqlBatch(String[] queryarr, JSONArray[] jsonparams, needRawQuery = false; try { mydb.beginTransaction(); + isTransactionActive = true; queryResult = new JSONObject(); queryResult.put("rowsAffected", 0); @@ -210,6 +217,7 @@ void executeSqlBatch(String[] queryarr, JSONArray[] jsonparams, try { mydb.setTransactionSuccessful(); mydb.endTransaction(); + isTransactionActive = false; queryResult = new JSONObject(); queryResult.put("rowsAffected", 0); @@ -224,6 +232,7 @@ void executeSqlBatch(String[] queryarr, JSONArray[] jsonparams, needRawQuery = false; try { mydb.endTransaction(); + isTransactionActive = false; queryResult = new JSONObject(); queryResult.put("rowsAffected", 0); From ec654b6e7a7758721e58204ce51b95627f81ce7c Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Wed, 13 Dec 2017 22:10:46 -0500 Subject: [PATCH 44/47] Cleaned up version of new BUG 666 WORKAROUND SOLUTION Close db before opening (ignore close error) Now tested on builtin android.database implementation (no Android-sqlite-connector / Android-sqlite-native-driver libs). Ref: - litehelpers/Cordova-sqlite-storage#666 - litehelpers/Cordova-sqlite-storage#730 --- CHANGES.md | 3 ++- README.md | 6 ++---- SQLitePlugin.coffee.md | 23 +++++++++------------- package.json | 2 +- plugin.xml | 2 +- spec/www/spec/db-open-close-delete-test.js | 17 ++++++++-------- www/SQLitePlugin.js | 20 +++++++++---------- 7 files changed, 33 insertions(+), 40 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 4f63fec3..eec63168 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,7 +1,8 @@ # Changes -###### cordova-sqlite-legacy-express-core 1.0.4-newdev01 +###### cordova-sqlite-legacy-express-core 1.0.4 +- Cleaned up workaround solution to BUG 666: close db before opening (ignore close error) - android.database end transaction if active before closing (needed for new BUG 666 workaround solution to pass selfTest in case of builtin android.database implementation) ###### cordova-sqlite-legacy-express-core 1.0.3 diff --git a/README.md b/README.md index d26dd4bf..fd4de49b 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,6 @@ This version branch contains the source code for the Android/iOS/macOS platforms This version branch is now used to develop Android platform version without dependency on JAR or NDK library artifacts for testing on `cordova-android@7`. -**NOTICE:** Workaround solution for [BUG 666 (litehelpers/Cordova-sqlite-storage#666)](https://github.com/litehelpers/Cordova-sqlite-storage/issues/666) (possible transaction issue after window.location change change with possible data loss) is incorrect in this version branch as discussed in [this BUG 666 comment](https://github.com/litehelpers/Cordova-sqlite-storage/issues/666#issuecomment-343757398) and may continue to suffer from a possible data loss risk. New workaround solution described in [this newer BUG 666 comment](https://github.com/litehelpers/Cordova-sqlite-storage/issues/666#issuecomment-350159666) is available in newer version branches. New workaround solution to bug 666 will cause selfTest to fail on Android ref: - NOTE: This version branch has some additional known [issues fixed in newer version branches](#issues-fixed-in-newer-version-branches). @@ -80,8 +78,8 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase - Nice overview of this and other alternatives for storing local data at: - New alternative solution for small data storage: [TheCocoaProject/ cordova-plugin-nativestorage](https://github.com/TheCocoaProject/cordova-plugin-nativestorage) - simpler "native storage of variables" for Android/iOS/Windows - Resolved Java 6/7/8 concurrent map compatibility issue reported in [litehelpers/Cordova-sqlite-storage#726](https://github.com/litehelpers/Cordova-sqlite-storage/issues/726), THANKS to pointer by [@NeoLSN (Jason Yang/楊朝傑)](https://github.com/NeoLSN) in [litehelpers/Cordova-sqlite-storage#727](https://github.com/litehelpers/Cordova-sqlite-storage/issues/727). +- Updated workaround solution to [BUG 666 (litehelpers/Cordova-sqlite-storage#666)](https://github.com/litehelpers/Cordova-sqlite-storage/issues/666) (possible transaction issue after window.location change with possible data loss): close database if already open before opening again - Fixed iOS/macOS platform version to use [PSPDFThreadSafeMutableDictionary.m](https://gist.github.com/steipete/5928916) to avoid threading issue ref: [litehelpers/Cordova-sqlite-storage#716](https://github.com/litehelpers/Cordova-sqlite-storage/issues/716) -- Resolved transaction problem after window.location (page) change with possible data loss ref: [litehelpers/Cordova-sqlite-storage#666](https://github.com/litehelpers/Cordova-sqlite-storage/issues/666) - [brodybits / cordova-sqlite-test-app](https://github.com/brodybits/cordova-sqlite-test-app) project is a CC0 (public domain) starting point (NOTE that this plugin must be added) and may also be used to reproduce issues with this plugin. - The Lawnchair adapter is now moved to [litehelpers / cordova-sqlite-lawnchair-adapter](https://github.com/litehelpers/cordova-sqlite-lawnchair-adapter). - [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) now supports SELECT BLOB data in Base64 format on all platforms in addition to REGEXP (Android/iOS/macOS) and pre-populated database (all platforms). @@ -106,7 +104,7 @@ Use the `location` or `iosDatabaseLocation` option in `sqlitePlugin.openDatabase - As described in [this posting](http://brodyspark.blogspot.com/2012/12/cordovaphonegap-sqlite-plugins-offer.html): - Keeps sqlite database in known, platform specific user data location on all supported platforms (Android/iOS/macOS/Windows), which can be reconfigured on iOS/macOS. Whether or not the database on the iOS platform is synchronized to iCloud depends on the selected database location. - No arbitrary size limit. SQLite limits described at: -- ~~Also tested for multi-page applications with window location changes~~ _(workaround solution for BUG 666 is incorrect in this version branch)_ +- Also validated for multi-page applications by internal test selfTest function. - This project is self-contained: no dependencies on other plugins such as cordova-plugin-file - Windows 10 UWP platform version available in TBD uses the performant C++ [doo / SQLite3-WinRT](https://github.com/doo/SQLite3-WinRT) component. - [SQLCipher](https://www.zetetic.net/sqlcipher/) support for Android/iOS/macOS/Windows is available in: [litehelpers / Cordova-sqlcipher-adapter](https://github.com/litehelpers/Cordova-sqlcipher-adapter) diff --git a/SQLitePlugin.coffee.md b/SQLitePlugin.coffee.md index bb4312d0..1a6235ee 100644 --- a/SQLitePlugin.coffee.md +++ b/SQLitePlugin.coffee.md @@ -250,21 +250,16 @@ # store initial DB state: @openDBs[@dbname] = DB_STATE_INIT - # As a WORKAROUND SOLUTION to BUG litehelpers/Cordova-sqlite-storage#666 - # (in the next event tick): - # If the database was never opened on the JavaScript side - # start an extra ROLLBACK statement to abort any pending transaction - # (does not matter whether it succeeds or fails here). - # FUTURE TBD a better solution would be to send a special signal or parameter - # if the database was never opened on the JavaScript side. - nextTick => - if not txLocks[@dbname] - myfn = (tx) -> - tx.addStatement 'ROLLBACK' - return - @addTransaction new SQLitePluginTransaction @, myfn, null, null, false, false - + # UPDATED WORKAROUND SOLUTION to cordova-sqlite-storage BUG 666: + # Request to native side to close existing database + # connection in case it is already open. + # Wait for callback before opening the database + # (ignore close error). + step2 = => cordova.exec opensuccesscb, openerrorcb, "SQLitePlugin", "open", [ @openargs ] + return + + cordova.exec step2, step2, 'SQLitePlugin', 'close', [ { path: @dbname } ] return diff --git a/package.json b/package.json index de01ba1d..6e3a1b0e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cordova-sqlite-legacy-express-core", - "version": "1.0.4-newdev01", + "version": "1.0.4", "description": "Native interface to SQLite for PhoneGap/Cordova (legacy express core version)", "cordova": { "id": "cordova-sqlite-legacy-express-core", diff --git a/plugin.xml b/plugin.xml index d2e83367..e76aae76 100644 --- a/plugin.xml +++ b/plugin.xml @@ -2,7 +2,7 @@ + version="1.0.4"> Cordova sqlite storage plugin - legacy express core version diff --git a/spec/www/spec/db-open-close-delete-test.js b/spec/www/spec/db-open-close-delete-test.js index 35031693..bb6644b7 100755 --- a/spec/www/spec/db-open-close-delete-test.js +++ b/spec/www/spec/db-open-close-delete-test.js @@ -593,10 +593,8 @@ var mytests = function() { // XXX SEE BELOW: repeat scenario but wait for open callback before close/delete/reopen // Needed to support some large-scale applications: test_it(suiteName + ' immediate close, then delete then re-open allows subsequent queries to run', function () { - // TBD POSSIBLY BROKEN on iOS/macOS ... - // if (!isAndroid && !isWindows && !isWP8) pending(...); - // TBD CURRENTLY BROKEN DUE TO BUG 666 WORKAROUND HACK - pending('CURRENTLY BROKEN DUE TO BUG 666 WORKAROUND HACK'); + // TBD ... + if (!isAndroid && !isWindows && !isWP8) pending('TBD TEST FAILURE on iOS/macOS (background processing implementation)'); var dbName = "Immediate-close-delete-Reopen.db"; var dbargs = {name: dbName, location: 'default'}; @@ -742,8 +740,11 @@ var mytests = function() { }); test_it(suiteName + ' repeatedly open and close database faster (5x)', function () { - // TBD CURRENTLY BROKEN on iOS/macOS due to current background processing implementation: - if (!isAndroid && !isWindows && !isWP8) pending('CURRENTLY BROKEN on iOS/macOS (background processing implementation)'); + // TBD BROKEN on iOS/macOS ... + // if (!isAndroid && !isWindows && !isWP8) pending(...); + // TBD SKIP FOR NOW + // (also seems to fail on Android in case of builtin android.database implementation) + pending('SKIP FOR NOW'); var dbName = 'repeatedly-open-and-close-faster-5x.db'; var dbargs = {name: dbName, location: 'default'}; @@ -866,8 +867,8 @@ var mytests = function() { test_it(suiteName + ' repeatedly open and delete database faster (5x)', function () { // TBD POSSIBLY BROKEN on iOS/macOS ... // if (!isAndroid && !isWindows && !isWP8) pending(...); - // TBD CURRENTLY BROKEN DUE TO BUG 666 WORKAROUND HACK - pending('CURRENTLY BROKEN DUE TO BUG 666 WORKAROUND HACK'); + // TBD CURRENTLY BROKEN DUE TO BUG 666 WORKAROUND SOLUTION + pending('CURRENTLY BROKEN DUE TO BUG 666 WORKAROUND SOLUTION'); var dbName = 'repeatedly-open-and-delete-faster-5x.db'; var dbargs = {name: dbName, location: 'default'}; diff --git a/www/SQLitePlugin.js b/www/SQLitePlugin.js index 03cb363d..7dad81eb 100644 --- a/www/SQLitePlugin.js +++ b/www/SQLitePlugin.js @@ -162,7 +162,7 @@ }; SQLitePlugin.prototype.open = function(success, error) { - var openerrorcb, opensuccesscb; + var openerrorcb, opensuccesscb, step2; if (this.dbname in this.openDBs) { console.log('database already open: ' + this.dbname); nextTick((function(_this) { @@ -202,18 +202,16 @@ }; })(this); this.openDBs[this.dbname] = DB_STATE_INIT; - nextTick((function(_this) { + step2 = (function(_this) { return function() { - var myfn; - if (!txLocks[_this.dbname]) { - myfn = function(tx) { - tx.addStatement('ROLLBACK'); - }; - _this.addTransaction(new SQLitePluginTransaction(_this, myfn, null, null, false, false)); - } - return cordova.exec(opensuccesscb, openerrorcb, "SQLitePlugin", "open", [_this.openargs]); + cordova.exec(opensuccesscb, openerrorcb, "SQLitePlugin", "open", [_this.openargs]); }; - })(this)); + })(this); + cordova.exec(step2, step2, 'SQLitePlugin', 'close', [ + { + path: this.dbname + } + ]); } }; From 0d5895fcc6e0ee5bc2bd4dcb2e01bfaaeef1c427 Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Thu, 28 Dec 2017 21:31:26 -0500 Subject: [PATCH 45/47] cordova-sqlcipher-adapter doc updates --- README.md | 172 +++++++++++++++++++++++++++++------------------------- 1 file changed, 93 insertions(+), 79 deletions(-) diff --git a/README.md b/README.md index 6a22fb37..f588432b 100644 --- a/README.md +++ b/README.md @@ -6,15 +6,21 @@ License terms for Android and Windows platform versions: MIT or Apache 2.0 License terms for iOS/macOS platform version: MIT only -## About this version branch +**PLEASE READ WARNING NOTICES BELOW.** -_Version with SQLCipher included_ +**Windows platform REMOVAL NOTICE:** Windows platform support will be removed in the near future ref: [litehelpers / Cordova-sqlcipher-adapter#63](https://github.com/litehelpers/Cordova-sqlcipher-adapter/issues/63) +## About this plugin version branch + +Plugin version with SQLCipher included + + - + ## IMPORTANT WARNING NOTICES @@ -22,11 +28,11 @@ _Version with SQLCipher included_ **IMPORTANT EXPORT REQUIREMENTS** described at: -_**WINDOWS ENCRYPTION WARNING NOTICE:** libTomCrypt may have inferior entropy (randomness) for encryption. It is desired to replace libTomCrypt with a recent build of the OpenSSL crypto library ref: [litehelpers/Cordova-sqlcipher-adapter#30](https://github.com/litehelpers/Cordova-sqlcipher-adapter/issues/30)_ +**WINDOWS ENCRYPTION WARNING NOTICE:** libTomCrypt may have inferior entropy (randomness) for encryption. It is desired to replace libTomCrypt with a recent build of the OpenSSL crypto library ref: [litehelpers/Cordova-sqlcipher-adapter#30](https://github.com/litehelpers/Cordova-sqlcipher-adapter/issues/30) ### Multiple SQLite problem on Android -This plugin uses a non-standard _SQLCipher for Android_ implementation on Android. In case an application access the **same** database using multiple plugins there is a risk of data corruption ref: [litehelpers/Cordova-sqlite-storage#626](https://github.com/litehelpers/Cordova-sqlite-storage/issues/626)) as described in and . +This plugin uses a non-standard SQLCipher for Android implementation on Android. In case an application access the **same** database using multiple plugins there is a risk of data corruption ref: [litehelpers/Cordova-sqlite-storage#626](https://github.com/litehelpers/Cordova-sqlite-storage/issues/626)) as described in and . @@ -65,7 +71,7 @@ document.addEventListener('deviceready', function() { **IMPORTANT:** Like with the other Cordova plugins your application must wait for the `deviceready` event. This is especially tricky in Angular/ngCordova/Ionic controller/factory/service callbacks which may be triggered before the `deviceready` event is fired. -To populate a database using the standard transaction API: +To populate a database using the "standard" transaction API: ```Javascript db.transaction(function(tx) { @@ -79,7 +85,7 @@ To populate a database using the standard transaction API: }); ``` -To check the data using the standard transaction API: +To check the data using the "standard" transaction API: ```Javascript db.transaction(function(tx) { @@ -91,6 +97,8 @@ To check the data using the standard transaction API: }); ``` +### Using recommended API calls + To populate a database using the SQL batch API: ```Javascript @@ -115,13 +123,14 @@ To check the data using the single SQL statement API: }); ``` -See the [Sample section](#sample) for a sample with a more detailed explanation. +See the [Sample section](#sample) for a sample with a more detailed explanation (using the "standard" transaction API). ## Status -- Alpha version +- Windows platform support will be removed in the near future ref: [litehelpers / Cordova-sqlcipher-adapter#63](https://github.com/litehelpers/Cordova-sqlcipher-adapter/issues/63) +- Alpha version, with previous version of SQLCipher (will upgrade to SQLCipher 3.4.2 / 3.5.8 along with other fixes as discussed in ) - SQLCipher `3.4.1` for iOS/macOS/Windows - SQLCipher `3.5.6` for Android built from [brodybits / android-database-sqlcipher-build-fix](https://github.com/brodybits/android-database-sqlcipher-build-fix), now with 64-bit CPU support - `SQLITE_DEFAULT_PAGE_SIZE=1024` and `SQLITE_DEFAULT_CACHE_SIZE=2000` to avoid "potentially distruptive change(s)" from SQLite 3.12.0 in unencrypted databases ref: @@ -130,63 +139,64 @@ See the [Sample section](#sample) for a sample with a more detailed explanation. - with LibTomCrypt (1.17) embedded for Windows - for future consideration: embed OpenSSL libcrypto for all target platforms - NOT supported by PhoneGap Developer App or PhoneGap Desktop App -- A recent version of the Cordova CLI (such as `6.5.0` or `7.1.0`) is recommended. Cordova versions older than `6.0.0` are missing the `cordova-ios@4.0.0` security fixes. In addition it is *required* to use `cordova prepare` in case of cordova-ios older than `4.3.0` (Cordova CLI `6.4.0`). -- _SQLCipher build options on **iOS/macOS/Windows** platforms_: - - _`SQLITE_HAS_CODEC`_ - - _`HAVE_USLEEP=1`_ - - _`SQLITE_TEMP_STORE=3`_ - - _`SQLCIPHER_CRYPTO_CC`_ - - _`SQLITE_LOCKING_STYLE=1`_ - - _`NDEBUG`_ +- This plugin will NOT work on `cordova-android@7` due to issue with JAR and NDK library files as discussed in [litehelpers / Cordova-sqlcipher-adapter/issues/64](https://github.com/litehelpers/Cordova-sqlcipher-adapter/issues/64) and [litehelpers/Cordova-sqlite-storage#729](https://github.com/litehelpers/Cordova-sqlite-storage/issues/729). +- A recent version of the Cordova CLI (such as `6.5.0` / `7.1.0`) is recommended. (Cordova CLI 8.x includes `cordova-android@7`, NOT supported by this plugin due to [litehelpers / Cordova-sqlcipher-adapter/issues/64](https://github.com/litehelpers/Cordova-sqlcipher-adapter/issues/64) / [litehelpers/Cordova-sqlite-storage#729](https://github.com/litehelpers/Cordova-sqlite-storage/issues/729).) Cordova versions older than `6.0.0` are missing the `cordova-ios@4.0.0` security fixes. In addition it is *required* to use `cordova prepare` in case of cordova-ios older than `4.3.0` (Cordova CLI `6.4.0`). +- SQLCipher build settings used: + - `SQLITE_HAS_CODEC` + - `SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT=1048576` (Android only) + - `HAVE_USLEEP=1` + - `SQLITE_TEMP_STORE=3` + - `SQLCIPHER_CRYPTO_CC` (iOS/macOS only) + - `SQLITE_LOCKING_STYLE=1` (iOS/macOS only) + - `NDEBUG` (`NDEBUG=1` on Android) - `SQLITE_THREADSAFE=1` (`SQLITE_THREADSAFE=2` on iOS/macOS) - `SQLITE_DEFAULT_MEMSTATUS=0` - `SQLITE_OMIT_DECLTYPE` - - ~~`SQLITE_OMIT_DEPRECATED`~~ - `SQLITE_OMIT_PROGRESS_CALLBACK` - `SQLITE_OMIT_SHARED_CACHE` - - ~~`SQLITE_TEMP_STORE=2`~~ - `SQLITE_OMIT_LOAD_EXTENSION` - - `SQLITE_ENABLE_FTS3` + - `SQLITE_ENABLE_FTS3` (iOS/macOS/Windows) - `SQLITE_ENABLE_FTS3_PARENTHESIS` - `SQLITE_ENABLE_FTS4` - `SQLITE_ENABLE_RTREE` - - _`SQLITE_ENABLE_FTS5`_ - - _`SQLITE_ENABLE_JSON1`_ - - `SQLITE_DEFAULT_PAGE_SIZE=1024` and `SQLITE_DEFAULT_CACHE_SIZE=2000` to avoid "potentially distruptive change(s)" from SQLite 3.12.0 ref: - - `SQLITE_OS_WINRT` for Windows only + - `SQLITE_ENABLE_FTS5` + - `SQLITE_ENABLE_JSON1` + - `SQLITE_DEFAULT_PAGE_SIZE=1024` (all platforms) and `SQLITE_DEFAULT_CACHE_SIZE=2000` (iOS/macOS/Windows) to avoid "potentially distruptive change(s)" from SQLite 3.12.0 ref: + - `SQLITE_OS_WINRT` (Windows only) + - `SQLCIPHER_CRYPTO_LIBTOMCRYPT` (Windows only) + - `SQLCIPHER_CRYPTO_OPENSSL` (Android only) - The iOS database location is now mandatory, as documented below. -- Windows 8.1 and Windows Phone 8.1 are ~~no longer~~ supported by this _plugin version, **now deprecated**_. +- Windows 8.1 and Windows Phone 8.1 are currently supported by this plugin version, now deprecated **and will be removed in the near future**. - Amazon Fire-OS is dropped due to lack of support by Cordova. Android version should be used to deploy to Fire-OS 5.0(+) devices. For reference: [cordova/cordova-discuss#32 (comment)](https://github.com/cordova/cordova-discuss/issues/32#issuecomment-167021676) -- Windows version (using a customized version of the performant [doo / SQLite3-WinRT](https://github.com/doo/SQLite3-WinRT) C++ component) has the following known limitations: - - This version branch with dependency on platform toolset libraries included by Visual Studio 2015 ref: [litehelpers/Cordova-sqlite-storage#580](https://github.com/litehelpers/Cordova-sqlite-storage/issues/580) +- Windows platform version (using a customized version of the performant [doo / SQLite3-WinRT](https://github.com/doo/SQLite3-WinRT) C++ component) **will be removed in the near future** ref: [litehelpers / Cordova-sqlcipher-adapter#63](https://github.com/litehelpers/Cordova-sqlcipher-adapter/issues/63), has the following known limitations: + - This plugin version branch has dependency on `v140` toolset libraries included by Visual Studio 2015 ref: [litehelpers/Cordova-sqlite-storage#580](https://github.com/litehelpers/Cordova-sqlite-storage/issues/580) (UNTESTED and UNSUPPORTED WORKAROUND for Visual Studio 2017 is described at: ) - It is **not** possible to use this plugin with the default "Any CPU" target. A specific target CPU type **must** be specified when building an app with this plugin. - Truncation issue with UNICODE `\u0000` character (same as `\0`) - No background processing - - INCORRECT error code and INCONSISTENT error message in error callbacks ref: [litehelpers/Cordova-sqlite-storage#539](https://github.com/litehelpers/Cordova-sqlite-storage/issues/539) - - Not possible to read BLOB column values + - INCORRECT error code (0) and INCONSISTENT error message (missing actual error info) in error callbacks ref: [litehelpers/Cordova-sqlite-storage#539](https://github.com/litehelpers/Cordova-sqlite-storage/issues/539) + - Not possible to select BLOB column values - Windows platform version uses `UTF-16le` internal database encoding while the other platform versions use `UTF-8` internal encoding. (`UTF-8` internal encoding is preferred ref: [litehelpers/Cordova-sqlite-storage#652](https://github.com/litehelpers/Cordova-sqlite-storage/issues/652)) - - _**IMPORTANT WARNING NOTICE:**_ libTomCrypt may have inferior entropy (randomness) for encryption _and may run much more slowly_. It is desired to replace libTomCrypt with a recent build of the OpenSSL crypto library _ref: [litehelpers/Cordova-sqlcipher-adapter#30](https://github.com/litehelpers/Cordova-sqlcipher-adapter/issues/30)_ - - _Test to attempt to open encrypted DB with INCORRECT password THEN OPEN & READ with CORRECT PASSWORD does not succeed on Debug build on ARM and x86 (Win32)._ -- Android version: - - Minimum SDK 10 (a.k.a. Gingerbread, Android 2.3.3); support for older versions is available upon request. + - **IMPORTANT WARNING NOTICE:** libTomCrypt may have inferior entropy (randomness) for encryption and may run much more slowly. It is desired to replace libTomCrypt with a recent build of the OpenSSL crypto library ref: [litehelpers/Cordova-sqlcipher-adapter#30](https://github.com/litehelpers/Cordova-sqlcipher-adapter/issues/30) +- The macOS platform version ("osx" platform) is not tested in a release build and should be considered pre-alpha. +- Android platform version: + - Android versions supported: 2.3.3 - 7.1.1 (API level 10 - 25), depending on Cordova version ref: - SQLCipher for Android build uses the OpenSSL crypto library for encryption - - ~~NOTE: 64-bit CPUs such as `x64_64`, ARM-64, and MIPS are currently not supported by the SQLCipher for Android build (support for these CPUs is for future consideration).~~ - ICU case-insensitive matching and other Unicode string manipulations is no longer supported for Android. -- FTS3, FTS4, FTS5, and R-Tree support is tested working OK for all target platforms in this version branch Android/iOS/macOS/Windows -- JSON1 support for Android/iOS/macOS/Windows -- Android versions supported: 2.3.3 - 7.1.1 (API level 10 - 25), depending on Cordova version ref: -- iOS version: - - iOS versions supported: _8.x/9.x/10.x/11.x_ see [deviations section](#deviations) below for differences in case of WKWebView) +- iOS platform version: + - iOS versions supported: 8.x / 9.x / 10.x / 11.x see [deviations section](#deviations) below for differences in case of WKWebView) - REGEXP is no longer supported for iOS. -- macOS version ("osx" platform) has not been tested in a release build and should be considered pre-alpha. +- FTS3, FTS4, FTS5, JSON1, and R-Tree are fully tested and supported for all target platforms in this version branch. +- AUTO-VACUUM is not enabled by default. If no form of `VACUUM` or `PRAGMA auto_vacuum` is used then sqlite will automatically reuse deleted data space for new data but the database file will never shrink. For reference: and [litehelpers/Cordova-sqlite-storage#646](https://github.com/litehelpers/Cordova-sqlite-storage/issues/646) - In case of memory issues please use smaller transactions. - Pre-populatd DB is NOT supported by this version. -- Lawnchair adapter has *not* been validated with this _plugin_ version *and is _TBD_ not expected to work (see below)*. +- Lawnchair adapter has *not* been validated with this plugin version *and is NOT guaranteed to work (see below)*. ## Announcements +- Nice overview of cordova-sqlite-storage and other alternatives for storing local data at: +- New alternative solution for small data storage (without SQLCipher): [TheCocoaProject/ cordova-plugin-nativestorage](https://github.com/TheCocoaProject/cordova-plugin-nativestorage) - simpler "native storage of variables" for Android/iOS/Windows - New workaround solution to [BUG 666 (litehelpers/Cordova-sqlite-storage#666)](https://github.com/litehelpers/Cordova-sqlite-storage/issues/666) (possible transaction issue after window.location change with possible data loss): attempt to close database before opening (ignore close error) - Windows 10 (UWP) build with /SAFESEH flag on Win32 (x86) target to specify "Image has Safe Exception Handlers" as described in - Fixed iOS/macOS platform version to use [PSPDFThreadSafeMutableDictionary.m](https://gist.github.com/steipete/5928916) to avoid threading issue ref: [litehelpers/Cordova-sqlite-storage#716](https://github.com/litehelpers/Cordova-sqlite-storage/issues/716) @@ -197,13 +207,9 @@ See the [Sample section](#sample) for a sample with a more detailed explanation. - [nolanlawson / pouchdb-adapter-cordova-sqlite](https://github.com/nolanlawson/pouchdb-adapter-cordova-sqlite) supports this plugin along with other implementations such as [nolanlawson / sqlite-plugin-2](https://github.com/nolanlawson/sqlite-plugin-2) and [Microsoft / cordova-plugin-websql](https://github.com/Microsoft/cordova-plugin-websql). - macOS ("osx" platform) is now supported - Published [brodybits / Cordova-quick-start-checklist](https://github.com/brodybits/Cordova-quick-start-checklist) and [brodybits / Avoiding-some-Cordova-pitfalls](https://github.com/brodybits/Avoiding-some-Cordova-pitfalls). -- Android version uses the lightweight [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector) by default configuration (may be changed as described below) -- Windows 10 UWP is now supported by this version. - Self-test functions to verify proper installation and operation of this plugin - More explicit `openDatabase` and `deleteDatabase` `iosDatabaseLocation` option -- Added simple sql batch query function -- Added echo test function to verify installation of this plugin -- All iOS/macOS operations are now using background processing (reported to resolve intermittent problems with cordova-ios@4.0.1) +- Added straightforward sql batch function - PhoneGap Build is now supported through the npm package: http://phonegap.com/blog/2015/05/26/npm-plugins-available/ - [MetaMemoryT / websql-promise](https://github.com/MetaMemoryT/websql-promise) now provides a Promises-based interface to both Web SQL and this plugin @@ -211,15 +217,15 @@ See the [Sample section](#sample) for a sample with a more detailed explanation. ## Highlights -- This _plugin_ version _is built with [SQLCipher](https://www.zetetic.net/sqlcipher/) included_. +- This plugin version is built with [SQLCipher](https://www.zetetic.net/sqlcipher/) included. - Drop-in replacement for HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/): the only change should be to replace the static `window.openDatabase()` factory call with `window.sqlitePlugin.openDatabase()`, with parameters as documented below. Known deviations are documented in the [deviations section](#deviations) below. - Failure-safe nested transactions with batch processing optimizations (according to HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/)) - Transaction API (based on HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/)) is designed for maximum flexiblibility, does not allow any transactions to be left hanging open. - As described in [this posting](http://brodyspark.blogspot.com/2012/12/cordovaphonegap-sqlite-plugins-offer.html): - - Keeps sqlite database in known, platform specific user data location on all platforms (Android/iOS/macOS/Windows), which can be reconfigured on iOS/macOS. Whether or not the database on the iOS platform is synchronized to iCloud depends on the selected database location. + - Keeps sqlite database in known, platform specific user data location on all supported platforms (Android/iOS/macOS/Windows), which can be reconfigured on iOS/macOS. Whether or not the database on the iOS platform is synchronized to iCloud depends on the selected database location. - No arbitrary size limit. SQLite limits described at: - Also tested for multi-page applications with window location changes -- This project is self-contained: no dependencies on other plugins such as cordova-plugin-file +- This project is self-contained. There are no dependencies on other plugins such as cordova-plugin-file. - Windows platform version uses a customized version of the performant [doo / SQLite3-WinRT](https://github.com/doo/SQLite3-WinRT) C++ component. - [SQLCipher](https://www.zetetic.net/sqlcipher/) support for Android/iOS/macOS/Windows is available in: [litehelpers / Cordova-sqlcipher-adapter](https://github.com/litehelpers/Cordova-sqlcipher-adapter) - Intellectual property: @@ -232,12 +238,6 @@ See the [Sample section](#sample) for a sample with a more detailed explanation. -## Some apps using Cordova SQLCipher adapter - -TBD *YOUR APP HERE* - - - ## Getting started ### Recommended prerequisites @@ -367,12 +367,20 @@ In case you get stuck with something please read through the [support](#support) ### Plugin usage examples +TBD WANTED: samples using this plugin version (with encryption) + +**WITHOUT SQLCIPHER:** + - [brodybits / cordova-sqlite-storage-starter-app](https://github.com/brodybits/cordova-sqlite-storage-starter-app) - ### Plugin tutorials -- +TBD WANTED: tutorials using this plugin version (with encryption) + +**WITHOUT SQLCIPHER:** + +- (using `cordova-sqlite-storage` plugin version) **NOTICE:** The above tutorial shows `cordova plugin add cordova-sqlite-storage` with the `--save` flag missing. Please be sure to use the `--save` flag to keep the plugins in `config.xml`. @@ -388,6 +396,12 @@ Other plugin tutorials wanted ref: [litehelpers/Cordova-sqlite-storage#609](http +## Some apps using Cordova SQLCipher adapter plugin version + +TBD *YOUR APP HERE* + + + ## Security ### Security of sensitive data @@ -443,7 +457,7 @@ As "strongly recommended" by [Web SQL Database API 8.5 SQL injection](https://ww - In certain cases such as `transaction.executeSql()` with no arguments (Android/iOS WebKit) Web SQL includes includes a code member with value of 0 (SQLError.UNKNOWN_ERR) in the exception while the plugin includes no such code member. - If the SQL arguments are passed in an `Array` subclass object where the `constructor` does not point to `Array` then the SQL arguments are ignored by the plugin. - The results data objects are not immutable as specified/implied by [Web SQL API section 4.5](https://www.w3.org/TR/webdatabase/#database-query-results). -- This version provides encryption which is *not* covered by the HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/). +- This plugin version provides encryption which is NOT covered by the HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/). ### Security of deleted data @@ -461,22 +475,21 @@ See **Security of sensitive data** in the [Security](#security) section above. ## Known issues -- iOS/macOS platform version does not support certain rapidly repeated open-and-close or open-and-delete test scenarios due to how the implementation handles background processing -- As described below, auto-vacuum is NOT enabled by default. +- This plugin will NOT work on `cordova-android@7` due to issue with JAR and NDK library files as discussed in [litehelpers / Cordova-sqlcipher-adapter/issues/64](https://github.com/litehelpers/Cordova-sqlcipher-adapter/issues/64) and [litehelpers/Cordova-sqlite-storage#729](https://github.com/litehelpers/Cordova-sqlite-storage/issues/729). +- The iOS/macOS platform versions do not support certain rapidly repeated open-and-close or open-and-delete test scenarios due to how the implementation handles background processing - Cannot read encrypted database with CORRECT password directly after attempt to open with INCORRECT password ref: [litehelpers/Cordova-sqlcipher-adapter#43](https://github.com/litehelpers/Cordova-sqlcipher-adapter/issues/43) - It is possible to request a SQL statement list such as "SELECT 1; SELECT 2" within a single SQL statement string, however the plugin will only execute the first statement and silently ignore the others ref: [litehelpers/Cordova-sqlite-storage#551](https://github.com/litehelpers/Cordova-sqlite-storage/issues/551) - Execution of INSERT statement that affects multiple rows (due to SELECT cause or using TRIGGER(s), for example) reports incorrect rowsAffected on Android in case the built-in Android database used (using the `androidDatabaseImplementation` option in `window.sqlitePlugin.openDatabase`) - Memory issue observed when adding a large number of records due to the JSON implementation which is improved in [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (GPL or commercial license terms) - Infinity (positive or negative) values are not supported on Android/iOS/macOS due to issues described above including a possible crash on iOS/macOS ref: [litehelpers/Cordova-sqlite-storage#405](https://github.com/litehelpers/Cordova-sqlite-storage/issues/405) - A stability issue was reported on the iOS platform version when in use together with [SockJS](http://sockjs.org/) client such as [pusher-js](https://github.com/pusher/pusher-js) at the same time (see [litehelpers/Cordova-sqlite-storage#196](https://github.com/litehelpers/Cordova-sqlite-storage/issues/196)). The workaround is to call sqlite functions and [SockJS](http://sockjs.org/) client functions in separate ticks (using setTimeout with 0 timeout). -- If a sql statement fails for which there is no error handler or the error handler does not return `false` to signal transaction recovery, the plugin fires the remaining sql callbacks before aborting the transaction. -- In case of an error, the error `code` member is bogus on _Android and Windows_. -- Possible crash on Android when using Unicode emoji and other 4-octet UTF-8 characters due to [Android bug 81341](https://code.google.com/p/android/issues/detail?id=81341), which *should* be fixed in Android 6.x +- In case of an error, the error `code` member is bogus on Android and Windows. +- Possible crash on Android 5 when using Unicode emoji and other 4-octet UTF-8 characters due to [Android bug 81341](https://code.google.com/p/android/issues/detail?id=81341), which *should* be fixed in newer releases starting with Android 6. - Close/delete database bugs described below. - When a database is opened and deleted without closing, the iOS/macOS platform version is known to leak resources. - It is NOT possible to open multiple databases with the same name but in different locations (iOS/macOS platform version). -_TODO: link to open bugs_ +Some additional issues are tracked in [open Cordova-sqlite-storage bug-general issues](https://github.com/litehelpers/Cordova-sqlite-storage/issues?q=is%3Aissue+is%3Aopen+label%3Abug-general) and [open Cordova-sqlcipher-adapter bug-general issues](https://github.com/litehelpers/Cordova-sqlcipher-adapter/issues?q=is%3Aissue+is%3Aopen+label%3Abug-general). @@ -486,26 +499,26 @@ _TODO: link to open bugs_ - Absolute and relative subdirectory path(s) are not tested or supported. - This plugin will not work before the callback for the 'deviceready' event has been fired, as described in **Usage**. (This is consistent with the other Cordova plugins.) - Extremely large records are not supported by this plugin. It is recommended to store images and similar binary data in separate files. TBD: specify maximum record. For future consideration: support in a version such as [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (available with GPL or commercial license options) -- _This plugin version_ will not work within a web worker (not properly supported by the Cordova framework). Use within a web worker is supported for Android and iOS (*without SQLCipher*) in [litehelpers / cordova-sqlite-evmax-ext-workers-legacy-build-free](https://github.com/litehelpers/cordova-sqlite-evmax-ext-workers-legacy-build-free) (GPL or premium commercial license terms). +- This plugin version will not work within a web worker (not properly supported by the Cordova framework). Use within a web worker is supported for Android and iOS (WITHOUT SQLCipher) in [litehelpers / cordova-sqlite-evmax-ext-workers-legacy-build-free](https://github.com/litehelpers/cordova-sqlite-evmax-ext-workers-legacy-build-free) (GPL or premium commercial license terms). - In-memory database `db=window.sqlitePlugin.openDatabase({name: ':memory:', ...})` is currently not supported. - The Android platform version cannot properly support more than 100 open database files due to the threading model used. - SQL error messages reported by Windows platform version are not consistent with Android/iOS/macOS platform versions. - UNICODE `\u2028` (line separator) and `\u2029` (paragraph separator) characters are not supported and known to be broken on iOS, macOS, and Android platform versions due to JSON issues reported in [Cordova bug CB-9435](https://issues.apache.org/jira/browse/CB-9435) and [cordova/cordova-discuss#57](https://github.com/cordova/cordova-discuss/issues/57). -- The BLOB data type is not fully supported by this version branch. SELECT BLOB in Base64 format _(without SQLCipher)_ is supported by [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) (permissive license terms) and [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (GPL or commercial license options). +- UNICODE `\u2028` (line separator) and `\u2029` (paragraph separator) characters are currently not supported and known to be broken on iOS, macOS, and Android platform versions due to JSON issues reported in [Cordova bug CB-9435](https://issues.apache.org/jira/browse/CB-9435) and [cordova/cordova-discuss#57](https://github.com/cordova/cordova-discuss/issues/57). This is fixed with a workaround for iOS/macOS (WITHOUT SQLCipher) in: [litehelpers / Cordova-sqlite-evplus-legacy-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-free) and [litehelpers / Cordova-sqlite-evplus-legacy-attach-detach-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-attach-detach-free) (GPL or special commercial license terms) as well as [litehelpers / Cordova-sqlite-evplus-legacy-workers-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-workers-free) (GPL or premium commercial license terms). +- The BLOB data type is not consistently supported for all platforms by this version branch. SELECT BLOB in Base64 format (WITHOUT SQLCipher) is supported by [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) (permissive license terms) and [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (GPL or commercial license terms). - Truncation in case of UNICODE `\u0000` (same as `\0`) character on Android (default Android-sqlite-connector database implementation) and Windows. - Case-insensitive matching and other string manipulations on Unicode characters, which is provided by optional ICU integration in the sqlite source and working with recent versions of Android, is not supported for any target platforms. -- The iOS/macOS platform version uses a thread pool but with only one thread working at a time due to "synchronized" database access +- The iOS/macOS platform version uses a thread pool but with only one thread working at a time due to "synchronized" database access. - Some large query results may be slow, also due to the JSON implementation. -- ATTACH to another database file is not supported by this version branch. Attach/detach is supported (along with the memory and iOS UNICODE `\u2028` line separator / `\u2029` paragraph separator fixes) in [litehelpers / Cordova-sqlite-evplus-legacy-attach-detach-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-attach-detach-free) (_without SQLCipher,_ available with GPL or special commercial license terms). +- ATTACH to another database file is not supported by this version branch. Attach/detach is supported (along with the memory and iOS UNICODE `\u2028` line separator / `\u2029` paragraph separator fixes, WITHOUT SQLCipher) in [litehelpers / Cordova-sqlite-evplus-legacy-attach-detach-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-attach-detach-free) (GPL or special commercial license terms). - UPDATE/DELETE with LIMIT or ORDER BY is not supported. -- ~~WITH clause is not supported on some older Android platform versions in case the `androidDatabaseImplementation: 2` (built-in android.database implementation) option is used.~~ - User-defined savepoints are not supported and not expected to be compatible with the transaction locking mechanism used by this plugin. In addition, the use of BEGIN/COMMIT/ROLLBACK statements is not supported. - Problems have been reported when using other version(s) of this plugin with Crosswalk (for Android). It may help to install Crosswalk as a plugin instead of using Crosswalk to create the project. - Does not work with [axemclion / react-native-cordova-plugin](https://github.com/axemclion/react-native-cordova-plugin) since the `window.sqlitePlugin` object exported (ES5 feature). Possible alternative solutions: - Follow the solution given in [this comment to axemclion/react-native-cordova-plugin#4](https://github.com/axemclion/react-native-cordova-plugin/issues/4#issuecomment-156751197) or [litehelpers/Cordova-sqlite-storage#382](https://github.com/litehelpers/Cordova-sqlite-storage/issues/382) - - Adapt [andpor / react-native-sqlite-storage](https://github.com/andpor/react-native-sqlite-storage) to work with SQLCipher + - Adapt [andpor / react-native-sqlite-storage](https://github.com/andpor/react-native-sqlite-storage) to work with SQLCipher as discussed in and -_TODO: link to open doc issues_ +Additional limitations are tracked in [cordova-sqlite-help doc-todo issues](https://github.com/litehelpers/Cordova-sqlite-help/issues?q=is%3Aissue%20label%3Adoc-todo), [cordova-sqlite-storage doc-todo issues](https://github.com/litehelpers/Cordova-sqlite-storage/issues?q=is%3Aissue+is%3Aopen+label%3Adoc-todo), and [Cordova-sqlcipher-adapter doc-todo issues](https://github.com/litehelpers/Cordova-sqlcipher-adapter/issues?q=is%3Aissue+label%3Adoc-todo). @@ -530,6 +543,7 @@ _TODO: link to open doc issues_ - More emojis and other 4-octet UTF-8 characters - `?NNN`/`:AAA`/`@AAAA`/`$AAAA` parameter placeholders ref: , ) - Single-statement and SQL batch transaction calls with invalid arguments (TBD behavior subject to change) +- Other [open Cordova-sqlite-storage testing issues](https://github.com/litehelpers/Cordova-sqlite-storage/issues?q=is%3Aissue+is%3Aopen+label%3Atesting) and [open Cordova-sqlcipher-adapter testing issues](https://github.com/litehelpers/Cordova-sqlcipher-adapter/issues?q=is%3Aissue+is%3Aopen+label%3Atesting) @@ -600,8 +614,8 @@ FUTURE TBD: Proper date/time handling will be further tested and documented at s - More formal documentation of API, especially for non-standard functions - IndexedDBShim adapter - Further cleanup of [support](#support) section -- Resolve or document remaining [open Cordova-sqlite-storage bugs](https://github.com/litehelpers/Cordova-sqlite-storage/issues?q=is%3Aissue+is%3Aopen+label%3Abug) -- Resolve [open Cordova-sqlite-storage documentation issues](https://github.com/litehelpers/Cordova-sqlite-storage/issues?q=is%3Aissue+is%3Aopen+label%3Adocumentation) +- Resolve or document remaining [open Cordova-sqlite-storage bug-general issues](https://github.com/litehelpers/Cordova-sqlite-storage/issues?q=is%3Aissue+is%3Aopen+label%3Abug-general) and [open Cordova-sqlcipher-adapter bug-general issues](https://github.com/litehelpers/Cordova-sqlcipher-adapter/issues?q=is%3Aissue+is%3Aopen+label%3Abug-general). +- Resolve [cordova-sqlite-help doc-todo issues](https://github.com/litehelpers/Cordova-sqlite-help/issues?q=is%3Aissue%20label%3Adoc-todo), [cordova-sqlite-storage doc-todo issues](https://github.com/litehelpers/Cordova-sqlite-storage/issues?q=is%3Aissue+is%3Aopen+label%3Adoc-todo), and [Cordova-sqlcipher-adapter doc-todo issues](https://github.com/litehelpers/Cordova-sqlcipher-adapter/issues?q=is%3Aissue+label%3Adoc-todo). ## For future considertion @@ -612,7 +626,7 @@ FUTURE TBD: Proper date/time handling will be further tested and documented at s ## Alternatives -**NOTE:** None of _the other alternatives_ currently support SQLCipher. +**NOTE:** None of the other alternatives currently support SQLCipher. ### Comparison of sqlite plugin versions @@ -677,7 +691,7 @@ window.sqlitePlugin.selfTest(successCallback, errorCallback); ## General -- Drop-in replacement for HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/): the only change should be to replace the static `window.openDatabase()` factory call with `window.sqlitePlugin.openDatabase()`, with parameters as documented below. Some known deviations are documented _above_. Reports of any additional deviations would be appreciated. +- Drop-in replacement for HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/): the only change should be to replace the static `window.openDatabase()` factory call with `window.sqlitePlugin.openDatabase()`, with parameters as documented below. Some other known deviations are _described_ throughout this document. Reports of any additional deviations would be appreciated. - Single-page application design is recommended. - In case of a multi-page application the JavaScript used by each page must use `sqlitePlugin.openDatabase` to open the database access handle object before it can access the data. @@ -695,7 +709,7 @@ var db = window.sqlitePlugin.openDatabase({name: 'my.db', key: 'your-password-he **WARNING:** The new "default" location value is *NOT* the same as the old default location and would break an upgrade for an app that was using the old default value (0) on iOS. -**WARNING 2:** As described above: by default this plugin uses a non-standard _SQLCipher database_ implementation on Android. In case an application access the **same** database using multiple plugins there is a risk of data corruption ref: [litehelpers/Cordova-sqlite-storage#626](https://github.com/litehelpers/Cordova-sqlite-storage/issues/626)) as described in and . The workaround is to use the `androidDatabaseImplementation: 2` setting as described in the **Android sqlite implementation** section below. +**WARNING 2:** As described above: by default this plugin uses a non-standard SQLCipher database implementation on Android. In case an application access the **same** database using multiple plugins there is a risk of data corruption ref: [litehelpers/Cordova-sqlite-storage#626](https://github.com/litehelpers/Cordova-sqlite-storage/issues/626)) as described in and . The workaround is to use the `androidDatabaseImplementation: 2` setting as described in the **Android sqlite implementation** section below. To specify a different location (affects iOS/macOS *only*): @@ -1296,7 +1310,7 @@ In case of a problem with a pre-populated database, please post your entire proj ## What information is needed for help Please include the following: -- Which platform(s) _(Android/iOS/macOS/Windows 8.1/Windows Phone 8.1/Windows 10)_ +- Which platform(s) (Android/iOS/macOS/Windows 8.1/Windows Phone 8.1/Windows 10) - Clear description of the issue - A small, complete, self-contained program that demonstrates the problem, preferably as a Github project, based on [brodybits / cordova-sqlite-test-app](https://github.com/brodybits/cordova-sqlite-test-app). ZIP/TGZ/BZ2 archive available from a public link is OK. No RAR or other such formats please. - In case of a Windows build problem please capture the entire compiler output. @@ -1323,7 +1337,7 @@ Unit testing is done in `spec`. ## running tests from shell -**TBD** `test.sh` ~~not~~ tested with sqlcipher version of this plugin: *does not auto-remove correct plugin id* +**TBD** `test.sh` testing limited with sqlcipher version of this plugin, *does not auto-remove correct plugin id* To run the tests from \*nix shell, simply do either: @@ -1345,7 +1359,7 @@ To run from a windows powershell (here is a sample for android target): - [litehelpers / cordova-sqlite-lawnchair-adapter](https://github.com/litehelpers/cordova-sqlite-lawnchair-adapter) -**_POSSIBLY_ BROKEN:** The Lawnchair adapter ~~does~~ _may_ not support _all_ `openDatabase` options such as `key`, `location` or `iosDatabaseLocation` options and is therefore *not* ~~expected~~ _guaranteed_ to work with this plugin. +**POSSIBLY BROKEN:** The Lawnchair adapter ~~does~~ *may* not support *all* `openDatabase` options such as `key`, `location` or `iosDatabaseLocation` options and is therefore *not* ~~expected~~ *guaranteed* to work with this plugin. ## PouchDB @@ -1556,7 +1570,7 @@ function closeDB() { ## Community - Testimonials of apps that are using this plugin would be especially helpful. -- Reporting issues at [litehelpers / Cordova-sqlcipher-adapter / issues](https://github.com/litehelpers/Cordova-sqlcipher-adapter/issues) can help improve the quality of this plugin. +- Reporting issues in [litehelpers / Cordova-sqlcipher-adapter / issues](https://github.com/litehelpers/Cordova-sqlcipher-adapter/issues) can help improve the quality of this plugin. ## Code From d0712351c05dc077c5b50053b1e92f2351b7aeaa Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Thu, 28 Dec 2017 22:32:21 -0500 Subject: [PATCH 46/47] cordova-sqlcipher-adapter general test fixes --- spec/config.xml | 2 +- spec/www/spec/db-tx-sql-select-value-test.js | 3 +-- spec/www/spec/db-tx-string-test.js | 18 +++--------------- 3 files changed, 5 insertions(+), 18 deletions(-) diff --git a/spec/config.xml b/spec/config.xml index 9863bc56..4177ccb8 100644 --- a/spec/config.xml +++ b/spec/config.xml @@ -1,5 +1,5 @@ - Cordova-sqlite-spec diff --git a/spec/www/spec/db-tx-sql-select-value-test.js b/spec/www/spec/db-tx-sql-select-value-test.js index d1ad4909..60d2d8d2 100644 --- a/spec/www/spec/db-tx-sql-select-value-test.js +++ b/spec/www/spec/db-tx-sql-select-value-test.js @@ -1830,9 +1830,8 @@ var mytests = function() { }); }, MYTIMEOUT); - it(suiteName + "SELECT X'FFD1FFD2' [TBD BROKEN androidDatabaseImplementation: 2 & Windows; missing result value iOS/macOS]", function(done) { + it(suiteName + "SELECT X'FFD1FFD2' [TBD BROKEN: ERROR on Android (SQLCipher plugin version) & Windows; missing result value on iOS/macOS]", function(done) { if (isWP8) pending('SKIP for WP8'); - if (!isWebSql && !isWindows && isAndroid && !isImpl2) pending('BROKEN: CRASH on Android 5.x (default sqlite-connector version)'); var db = openDatabase("Inline-SELECT-BLOB-FFD1FFD2-result-test.db", "1.0", "Demo", DEFAULT_SIZE); diff --git a/spec/www/spec/db-tx-string-test.js b/spec/www/spec/db-tx-string-test.js index 5e6d637e..88243a36 100755 --- a/spec/www/spec/db-tx-string-test.js +++ b/spec/www/spec/db-tx-string-test.js @@ -669,14 +669,9 @@ var mytests = function() { }); }, MYTIMEOUT); - // TBD NOTE: In case of the default Android database implementation - // (Android-sqlite-connector) it is possible to manipulate, - // store, and retrieve a text string with 4-octet UTF-8 characters such as emojis. - // However HEX manipulations do not work the same as Android/iOS WebKit Web SQL, - // iOS plugin, or Android plugin with androidDatabaseImplementation : 2 setting. - // This linkely indicates that such characters are stored differently [incorrectly] - // due to UTF-8 string handling limitations of Android-sqlite-connector - // and Android-sqlite-native-driver. ref: litehelpers/Cordova-sqlite-storage#564 + // NOTE: SQLCipher for Android database implementation shows consistent + // results in emoji string manipulation tests including HEX + // manipulation tests. it(suiteName + 'Inline emoji string manipulation test: SELECT UPPER("a\\uD83D\\uDE03.") [\\u1F603 SMILING FACE (MOUTH OPEN)]', function(done) { var db = openDatabase("Inline-emoji-hex-test.db", "1.0", "Demo", DEFAULT_SIZE); @@ -715,9 +710,6 @@ var mytests = function() { expect(rs.rows).toBeDefined(); expect(rs.rows.length).toBe(1); - // STOP HERE [HEX encoding BUG] for Android-sqlite-connector: - //if (!isWebSql && !isWindows && isAndroid && !isImpl2) return done(); - if (isWindows) expect(rs.rows.item(0).hexvalue).toBe('40003DD803DE2100'); // (UTF-16le) else @@ -737,8 +729,6 @@ var mytests = function() { it(suiteName + "Inline BLOB with emoji string manipulation test: SELECT LOWER(X'41F09F9883') [A\uD83D\uDE03] [\\u1F603 SMILING FACE (MOUTH OPEN)]", function(done) { if (isWP8) pending('BROKEN for WP8'); - // XXX TBD ???: - // if (!isWebSql && !isWindows && isAndroid && !isImpl2) pending('BROKEN: CRASH on Android 5.x (default sqlite-connector version)'); if (isWindows) pending('SKIP for Windows'); // FUTURE TBD var db = openDatabase("Inline-emoji-select-lower-result-test.db", "1.0", "Demo", DEFAULT_SIZE); @@ -767,8 +757,6 @@ var mytests = function() { it(suiteName + 'emoji SELECT HEX(?) parameter value test: "@\\uD83D\\uDE03!" [\\u1F603 SMILING FACE (MOUTH OPEN)]', function(done) { if (isWP8) pending('BROKEN for WP8'); - // XXX TBD ???: - //if (isAndroid && !isWebSql && !isImpl2) pending('BROKEN for Android (default sqlite-connector version)'); var db = openDatabase("String-emoji-parameter-value-test.db", "1.0", "Demo", DEFAULT_SIZE); expect(db).toBeDefined(); From 7c4404ed1ec5d3ab4001899b4c39cb31ee91b93d Mon Sep 17 00:00:00 2001 From: "Christopher J. Brody" Date: Fri, 29 Dec 2017 00:24:36 -0500 Subject: [PATCH 47/47] cipher test updates --- spec/www/spec/cipher.js | 356 +++++++++++++++++----------------------- 1 file changed, 154 insertions(+), 202 deletions(-) diff --git a/spec/www/spec/cipher.js b/spec/www/spec/cipher.js index f350ae25..e319bfe2 100644 --- a/spec/www/spec/cipher.js +++ b/spec/www/spec/cipher.js @@ -1,48 +1,13 @@ /* 'use strict'; */ -// Extra-long timeout needed for Windows 10 mobile device (is this due to libTomCrypt?) -//var MYTIMEOUT = 20000; +// TBD Extra-long timeout needed for Windows 10 mobile device (is this due to libTomCrypt?) +// var MYTIMEOUT = 20000; var MYTIMEOUT = 120000; var DEFAULT_SIZE = 5000000; // max to avoid popup in safari/ios -// XXX TODO replace in test(s): -function ok(test, desc) { expect(test).toBe(true); } -function equal(a, b, desc) { expect(a).toEqual(b); } // '==' -function strictEqual(a, b, desc) { expect(a).toBe(b); } // '===' - -// XXX TODO NEED TO BE FIXED: -var wait = 0; -var test_it_done = null; -function xtest_it(desc, fun) { xit(desc, fun); } -function test_it(desc, fun) { - wait = 0; - it(desc, function(done) { - test_it_done = done; - fun(); - }, MYTIMEOUT); -} -function stop(n) { - if (!!n) wait += n - else ++wait; -} -function start(n) { - if (!!n) wait -= n; - else --wait; - if (wait == 0) test_it_done(); -} - -var isAndroid = /Android/.test(navigator.userAgent); -var isWindows = /Windows NT/.test(navigator.userAgent); // Windows (...) -// TBD GONE: -// var isWP8 = /IEMobile/.test(navigator.userAgent); // WP(8) -//var isWindowsPhone = /Windows Phone 8.1/.test(navigator.userAgent); // Windows [NT] (8.1) -var isIE = isWindows || isWP8; -var isWebKit = !isIE; // TBD [Android or iOS] - -var scenarioList = [ 'Plugin', 'HTML5' ]; - -var scenarioCount = isWebKit ? 2 : 1; +var isWindows = /Windows /.test(navigator.userAgent); // Windows (...) +var isAndroid = !isWindows && /Android/.test(navigator.userAgent); describe('encryption test(s)', function() { var suiteName = "sqlcipher: "; @@ -56,134 +21,133 @@ describe('encryption test(s)', function() { window.sqlitePlugin.openDatabase({name: first.name, location: 'default'}, second, third); } - test_it(suiteName + "nested transaction test with encryption", function() { - + it(suiteName + "nested transaction test with encryption", function(done) { var db = openDatabase("nested-transaction-test-with-encryption.db", "1.0", "Demo", DEFAULT_SIZE); expect(db).toBeDefined(); - stop(); - db.transaction(function(tx) { expect(tx).toBeDefined(); tx.executeSql('DROP TABLE IF EXISTS test_table'); tx.executeSql('CREATE TABLE IF NOT EXISTS test_table (id integer primary key, data text, data_num integer)'); - tx.executeSql("INSERT INTO test_table (data, data_num) VALUES (?,?)", ["test", 100], function(tx, res) { - console.log("insertId: " + res.insertId + " -- probably 1"); - console.log("rowsAffected: " + res.rowsAffected + " -- should be 1"); - - expect(res).toBeDefined(); - // if (!isWindows) // ... - expect(res.insertId).toBeDefined(); - // if (!isWindows) // ... - expect(res.rowsAffected).toEqual(1); - - tx.executeSql("select count(id) as cnt from test_table;", [], function(tx, res) { - console.log("res.rows.length: " + res.rows.length + " -- should be 1"); - console.log("res.rows.item(0).cnt: " + res.rows.item(0).cnt + " -- should be 1"); - - equal(res.rows.length, 1, "res rows length"); - equal(res.rows.item(0).cnt, 1, "select count"); - - start(); + tx.executeSql("INSERT INTO test_table (data, data_num) VALUES (?,?)", ["test", 100], function(tx, rs1) { + expect(rs1).toBeDefined(); + expect(rs1.insertId).toBeDefined(); + expect(rs1.insertId).toBe(1); + expect(rs1.rowsAffected).toBe(1); + + tx.executeSql("select count(id) as cnt from test_table;", [], function(tx, rs2) { + expect(rs2).toBeDefined(); + expect(rs2.rows).toBeDefined(); + expect(rs2.rows.length).toBe(1); + expect(rs2.rows.item(0).cnt).toBe(1); + done(); }); }); }); - }); + }); - test_it(suiteName + ' Open & read encrypted DB with same password', function () { + it(suiteName + ' Open & read encrypted DB with same password', function (done) { var dbName = "Open-read-encrypted-DB-with-same-password.db"; var test_data = "Test string to be stored with encryption"; - // async test: - stop(2); - openDatabase({name: dbName, key: 'test-password'}, function (db) { - db.transaction(function(tx) { - tx.executeSql('DROP TABLE IF EXISTS tt'); - tx.executeSql('CREATE TABLE IF NOT EXISTS tt (test_data)'); - tx.executeSql('INSERT INTO tt (test_data) VALUES (?)', [test_data]); - }, function(error) { - console.log('ERROR: ' + error.message); - ok(false, error.message); - }, function() { - ok(true, 'DB populated OK'); + // CREATE TABLE & INSERT some contents into the DB: + db.sqlBatch([ + 'DROP TABLE IF EXISTS tt', + 'CREATE TABLE IF NOT EXISTS tt (test_data)', + ['INSERT INTO tt (test_data) VALUES (?)', [test_data]] + ], function() { db.close(function () { openDatabase({name: dbName, key: 'test-password'}, function (db) { - ok(!!db, 'reopen DB same test-password'); - start(1); - - db.executeSql('SELECT * FROM tt', null, function(result) { - ok(!!result, 'result from encrypted DB'); - ok(!!result.rows.item(0).test_data, 'got the test data from encrypted DB'); - strictEqual(result.rows.item(0).test_data, test_data, 'correct test data from encrypted DB'); - start(1); + expect(db).toBeDefined(); + + db.executeSql('SELECT * FROM tt', null, function(rs) { + expect(rs).toBeDefined(); + expect(rs.rows).toBeDefined(); + expect(rs.rows.length).toBe(1); + expect(rs.rows.item(0).test_data).toBe(test_data); + done(); }, function (error) { - ok(false, error.message); - start(1); + // NOT EXPECTED: + expect(false).toBe(true); + expect(error).toBeDefined(); + expect(error.message).toBeDefined(); + expect(error.message).toBe('--'); + done(); }); }, function (error) { - ok(false, error.message); - start(1); + // NOT EXPECTED: + expect(false).toBe(true); + expect(error).toBeDefined(); + expect(error.message).toBeDefined(); + expect(error.message).toBe('--'); + done(); }); }, function (error) { - ok(false, error.message); - start(1); + // NOT EXPECTED: + expect(false).toBe(true); + expect(error).toBeDefined(); + expect(error.message).toBeDefined(); + expect(error.message).toBe('--'); + done(); }); }); }, function (error) { - ok(false, error.message); - start(2); + // NOT EXPECTED: + expect(false).toBe(true); + expect(error).toBeDefined(); + expect(error.message).toBe('--'); + done(); }); - }); + }); - test_it(suiteName + ' Open encrypted DB with another password - should fail', function () { + it(suiteName + ' Open encrypted DB with another password - should fail', function (done) { var dbName = "Encrypted-DB-reopen-with-another-password.db"; - stop(); - openDatabase({name: dbName, key: 'test-password'}, function (db) { - // CREATE TABLE to put some contents into the DB: - // XXX cannot close within db.executeSql() callback due to BUG - //db.executeSql("CREATE TABLE IF NOT EXISTS tt (test_data)", [], function() { - db.transaction(function(tx) { - tx.executeSql("DROP TABLE IF EXISTS tt"); - tx.executeSql("CREATE TABLE IF NOT EXISTS tt (test_data)"); - }, function(error) { - console.log("ERROR: " + error.message); - ok(false, error.message); - }, function() { + db.sqlBatch([ + 'DROP TABLE IF EXISTS tt', + 'CREATE TABLE IF NOT EXISTS tt (test_data)', + ['INSERT INTO tt (test_data) VALUES (?)', ['test data']] + ], function() { db.close(function () { openDatabase({name: dbName, key: "another-password"}, function (db) { - ok(false, 'Open DB with another-password should not have succeeded'); - start(); + // NOT EXPECTED (Open DB with another-password should not have succeeded): + expect(false).toBe(true); + done(); }, function (error) { - // Expected/desired result: - ok(true, 'DB was succesfully encrypted with test-password'); - ok(!!error, 'valid error object'); - // XXX BROKEN: - //ok(!!error.message, 'should report a valid error message'); - start(); + // EXPECTED RESULT: + expect(error).toBeDefined(); + expect(error.message).toBeDefined(); + done(); }); }, function (error) { - ok(false, error.message); - start(); + // NOT EXPECTED: + expect(false).toBe(true); + expect(error).toBeDefined(); + expect(error.message).toBeDefined(); + expect(error.message).toBe('--'); + done(); }); }); - //}); }, function (error) { - ok(false, error.message); - start(); + // NOT EXPECTED: + expect(false).toBe(true); + expect(error).toBeDefined(); + expect(error.message).toBeDefined(); + expect(error.message).toBe('--'); + done(); }); - }); + }); - it(suiteName + ' Attempt to open encrypted DB with INCORRECT password THEN OPEN & READ with CORRECT PASSWORD [PLUGIN BROKEN: MUST CLOSE THEN TRY AGAIN]', function (done) { + it(suiteName + ' Attempt to open encrypted DB with INCORRECT password THEN OPEN & READ with CORRECT PASSWORD [PLUGIN BROKEN - BUG #43: MUST CLOSE THEN TRY AGAIN]', function (done) { // if (isWindows) pending('SKIP for Windows: CALLBACK NOT RECEIVED'); var dbName = 'Encrypted-DB-attempt-incorrect-password-then-correct-password.db'; @@ -191,17 +155,12 @@ describe('encryption test(s)', function() { window.sqlitePlugin.openDatabase({name: dbName, key: 'test-password', location: 'default'}, function (db1) { expect(db1).toBeDefined(); - // CREATE TABLE to put some contents into the DB: - db1.transaction(function(tx) { - tx.executeSql('DROP TABLE IF EXISTS tt'); - tx.executeSql('CREATE TABLE IF NOT EXISTS tt (test_data)'); - tx.executeSql('INSERT INTO tt (test_data) VALUES (?)', [test_data]); - }, function(error) { - // NOT EXPECTED: - expect(false).toBe(true); - expect(error.message).toBe('--'); - done(); - }, function() { + // CREATE TABLE & INSERT some contents into the DB: + db1.sqlBatch([ + 'DROP TABLE IF EXISTS tt', + 'CREATE TABLE IF NOT EXISTS tt (test_data)', + ['INSERT INTO tt (test_data) VALUES (?)', [test_data]] + ], function() { db1.close(function () { window.sqlitePlugin.openDatabase({name: dbName, key: 'another-password', location: 'default'}, function (db2) { @@ -216,9 +175,25 @@ describe('encryption test(s)', function() { window.sqlitePlugin.openDatabase({name: dbName, key: 'test-password', location: 'default'}, function (db3) { // EXPECTED RESULT: expect(db3).toBeDefined(); - //* ** ALT 1: + /* ** FUTURE TODO single SQL statement [BROKEN: NO SQL CALLBACK RECEIVED]: + db3.executeSql('SELECT * FROM tt', null, function(rs) { + expect(rs).toBeDefined(); + expect(rs.rows).toBeDefined(); + expect(rs.rows.length).toBe(1); + expect(rs.rows.item(0).test_data).toBe(test_data); + done(); + }, function (error) { + // NOT EXPECTED: + expect(false).toBe(true); + expect(error).toBeDefined(); + expect(error.message).toBe('--'); + done(); + }); + // */ + //* Use "standard" transaction mechanism instead: db3.transaction(function(tx) { tx.executeSql('SELECT * FROM tt', null, function(ignored, rs) { + // TBD CORRECT RESULT: expect('PLUGIN FIXED PLEASE UPDATE THIS TEST').toBe('--'); expect(rs).toBeDefined(); expect(rs.rows).toBeDefined(); @@ -228,66 +203,44 @@ describe('encryption test(s)', function() { }, function (ignored, error) { // NOT EXPECTED: expect(false).toBe(true); + expect(error).toBeDefined(); + expect(error.message).toBeDefined(); expect(error.message).toBe('--'); done(); }); }, function (error) { - // TEST GETS HERE [Android/iOS/macOS]: - //expect(false).toBe(true); - //expect(error.message).toBe('--'); + // TBD ACTUAL RESULT: + // expect(false).toBe(true); + // expect(error).toBeDefined(); + // expect(error.message).toBe('--'); expect(error).toBeDefined(); db3.close(function() { + // TBD CORRECT RESULT: expect('PLUGIN BEHAVIOR CHANGED PLEASE UPDATE THIS TEST AND CHECK STORED DATA HERE').toBe('--'); done(); }, function(error) { - // TBD ???: - //expect(error).toBeDefined(); + // TBD ACTUAL RESULT: + // expect(false).toBe(true); + // expect(error).toBeDefined(); + // expect(error.message).toBe('--'); expect(error).not.toBeDefined(); - // TRY AGAIN: + // TRY OPENING AGAIN: window.sqlitePlugin.openDatabase({name: dbName, key: 'test-password', location: 'default'}, function (db4) { // EXPECTED RESULT: expect(db4).toBeDefined(); - //* ** ALT 1: - db4.transaction(function(tx) { - tx.executeSql('SELECT * FROM tt', null, function(ignored, rs) { - expect(rs).toBeDefined(); - expect(rs.rows).toBeDefined(); - expect(rs.rows.length).toBe(1); - expect(rs.rows.item(0).test_data).toBe(test_data); - done(); - }, function (ignored, error) { - // NOT EXPECTED: - expect(false).toBe(true); - expect(error.message).toBe('--'); - done(); - }); - }, function (error) { - // NOT EXPECTED: - expect(false).toBe(true); - expect(error.message).toBe('--'); + // Single SQL statement should work now: + db4.executeSql('SELECT * FROM tt', null, function(rs) { + expect(rs).toBeDefined(); + expect(rs.rows).toBeDefined(); + expect(rs.rows.length).toBe(1); + expect(rs.rows.item(0).test_data).toBe(test_data); done(); }); }); }); }); // */ - /* ** FUTURE TBD ALT 2 [NO SQL CALLBACK RECEIVED]: - expect('check1').toBe('--'); // TEST ALT 2 GETS HERE - db3.executeSql('SELECT * FROM tt', null, function(rs) { - // NOT TRIGGERED Android/iOS: - expect(rs).toBeDefined(); - // FUTURE TBD CHECK rs - done(); - }, function (error) { - // NOT TRIGGERED Android/iOS: - // NOT EXPECTED: - expect(false).toBe(true); - expect(error.message).toBe('--'); - done(); - }); - expect('check2').toBe('--'); // TEST ALT 2 GETS HERE - // */ }); }); @@ -295,6 +248,7 @@ describe('encryption test(s)', function() { }, function (error) { // NOT EXPECTED: expect(false).toBe(true); + expect(error.message).toBeDefined(); expect(error.message).toBe('--'); done(); }); @@ -302,54 +256,52 @@ describe('encryption test(s)', function() { }, function (error) { // NOT EXPECTED: expect(false).toBe(true); + expect(error.message).toBeDefined(); expect(error.message).toBe('--'); done(); }); }); - test_it(suiteName + 'Attempt to open and read unencrypted DB with password key', function () { - + it(suiteName + 'Attempt to open and read unencrypted DB with password key', function (done) { var dbName = 'Open-and-read-unencrypted-DB-with-password.db'; - stop(); openDatabase({name: dbName}, function (db) { - ok(!!db, 'valid db handle object'); - db.transaction(function(tx) { - ok(!!tx, 'valid tx object'); - tx.executeSql('DROP TABLE IF EXISTS tt'); - tx.executeSql('CREATE TABLE IF NOT EXISTS tt (test_data)'); - tx.executeSql('INSERT INTO tt (test_data) VALUES (?)', ['test data']); - }, function(error) { - console.log('ERROR: ' + error.message); - ok(false, 'tx error: ' + error.message); - start(); - }, function() { - ok(true, 'ready to close db'); + expect(db).toBeDefined(); + db.sqlBatch([ + 'DROP TABLE IF EXISTS tt', + 'CREATE TABLE IF NOT EXISTS tt (test_data)', + ['INSERT INTO tt (test_data) VALUES (?)', ['test data']] + ], function() { db.close(function () { - ok(true, 'db is closed'); - var db1 = openDatabase({name: dbName, key: 'test-password'}); db1.transaction(function(tx) { - // not expected (ignored): - tx.executeSql('SELECT 1'); + // NOT EXPECTED: + expect(false).toBe(true); + done(); }, function(error) { - console.log('ERROR: ' + error.message); - ok(true, 'Attempted transaction on invalid db should fail'); - ok(!!error, 'valid error object'); - ok(!!error.message, 'should report a valid error message'); - start(); + // EXPECTED RESULT: + expect(error).toBeDefined(); + expect(error.message).toBeDefined(); + done(); }, function() { - ok(false, 'Attempted transaction on invalid db should not succeed'); - start(); + // NOT EXPECTED: + expect(false).toBe(true); + done(); }); }, function (error) { - ok(false, 'error closing: ' + error.message); - start(); + // NOT EXPECTED: + expect(false).toBe(true); + expect(error.message).toBeDefined(); + expect(error.message).toBe('--'); + done(); }); }); }, function (error) { - ok(false, 'open error: ' + error.message); - start(); + // NOT EXPECTED: + expect(false).toBe(true); + expect(error.message).toBeDefined(); + expect(error.message).toBe('--'); + done(); }); });