Cleanup project: remove old DroidGap, PGSQLite versions, iOS from @marcucio is PG1.4- (had no batching improvements)

This commit is contained in:
Chris Brody 2012-04-12 14:46:56 +02:00
parent fd3eaad9c2
commit be092c1d3f
7 changed files with 508 additions and 732 deletions

View File

@ -1,342 +0,0 @@
/*
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) 2005-2010, Nitobi Software Inc.
* Copyright (c) 2010-2011, IBM Corporation
*/
/*
* This is purely for the Android 1.5/1.6 HTML 5 Storage
* I was hoping that Android 2.0 would deprecate this, but given the fact that
* most manufacturers ship with Android 1.5 and do not do OTA Updates, this is required
*/
// XXX TODO: use function() { ... } () to encapsulate these declarations (except for Java callback)
/**
* SQL result set object
* PRIVATE METHOD
* @constructor
*/
var DDB_Rows = function() {
this.resultSet = []; // results array
this.length = 0; // number of rows
};
/**
* Get item from SQL result set
*
* @param row The row number to return
* @return The row object
*/
DDB_Rows.prototype.item = function(row) {
return this.resultSet[row];
};
/**
* SQL result set that is returned to user.
* PRIVATE METHOD
* @constructor
*/
var DDB_Result = function() {
this.rows = new DDB_Rows();
};
/**
* Storage object that is called by native code when performing queries.
* PRIVATE METHOD
* @constructor
*/
var DDB = function() {
this.queryQueue = {};
};
/**
* Callback from native code when query is complete.
* PRIVATE METHOD
*
* @param id Query id
*/
DDB.prototype.completeQuery = function(id, data) {
var query = this.queryQueue[id];
if (query) {
try {
delete this.queryQueue[id];
// Get transaction
var tx = query.tx;
// If transaction hasn't failed
// Note: We ignore all query results if previous query
// in the same transaction failed.
if (tx && tx.queryList[id]) {
// Save query results
var r = new DDB_Result();
r.rows.resultSet = data;
r.rows.length = data.length;
try {
if (typeof query.successCallback === 'function') {
query.successCallback(query.tx, r);
}
} catch (ex) {
console.log("executeSql error calling user success callback: "+ex);
}
tx.queryComplete(id);
}
} catch (e) {
console.log("executeSql error: "+e);
}
}
};
/**
* Callback from native code when query fails
* PRIVATE METHOD
*
* @param reason Error message
* @param id Query id
*/
DDB.prototype.fail = function(reason, id) {
var query = this.queryQueue[id];
if (query) {
try {
delete this.queryQueue[id];
// Get transaction
var tx = query.tx;
// If transaction hasn't failed
// Note: We ignore all query results if previous query
// in the same transaction failed.
if (tx && tx.queryList[id]) {
tx.queryList = {};
try {
if (typeof query.errorCallback === 'function') {
query.errorCallback(query.tx, reason);
}
} catch (ex) {
console.log("executeSql error calling user error callback: "+ex);
}
tx.queryFailed(id, reason);
}
} catch (e) {
console.log("executeSql error: "+e);
}
}
};
var mycreateUUID = function() {
return myUUIDcreatePart(4) + '-' +
myUUIDcreatePart(2) + '-' +
myUUIDcreatePart(2) + '-' +
myUUIDcreatePart(2) + '-' +
myUUIDcreatePart(6);
};
myUUIDcreatePart = function(length) {
var uuidpart = "";
var i, uuidchar;
for (i=0; i<length; i++) {
uuidchar = parseInt((Math.random() * 256),0).toString(16);
if (uuidchar.length === 1) {
uuidchar = "0" + uuidchar;
}
uuidpart += uuidchar;
}
return uuidpart;
};
/**
* SQL query object
* PRIVATE METHOD
*
* @constructor
* @param tx The transaction object that this query belongs to
*/
var DDB_Query = function(tx) {
// Set the id of the query
this.id = mycreateUUID();
// Add this query to the queue
dddb.queryQueue[this.id] = this;
// Init result
this.resultSet = [];
// Set transaction that this query belongs to
this.tx = tx;
// Add this query to transaction list
this.tx.queryList[this.id] = this;
// Callbacks
this.successCallback = null;
this.errorCallback = null;
};
/**
* Transaction object
* PRIVATE METHOD
* @constructor
*/
var DDB_Tx = function() {
// Set the id of the transaction
this.id = mycreateUUID();
// Callbacks
this.successCallback = null;
this.errorCallback = null;
// Query list
this.queryList = {};
};
/**
* Mark query in transaction as complete.
* If all queries are complete, call the user's transaction success callback.
*
* @param id Query id
*/
DDB_Tx.prototype.queryComplete = function(id) {
delete this.queryList[id];
// If no more outstanding queries, then fire transaction success
if (this.successCallback) {
var count = 0;
var i;
for (i in this.queryList) {
if (this.queryList.hasOwnProperty(i)) {
count++;
}
}
if (count === 0) {
try {
this.successCallback();
} catch(e) {
console.log("Transaction error calling user success callback: " + e);
}
}
}
};
/**
* Mark query in transaction as failed.
*
* @param id Query id
* @param reason Error message
*/
DDB_Tx.prototype.queryFailed = function(id, reason) {
// The sql queries in this transaction have already been run, since
// we really don't have a real transaction implemented in native code.
// However, the user callbacks for the remaining sql queries in transaction
// will not be called.
this.queryList = {};
if (this.errorCallback) {
try {
this.errorCallback(reason);
} catch(e) {
console.log("Transaction error calling user error callback: " + e);
}
}
};
/**
* Execute SQL statement
*
* @param sql SQL statement to execute
* @param params Statement parameters
* @param successCallback Success callback
* @param errorCallback Error callback
*/
DDB_Tx.prototype.executeSql = function(sql, params, successCallback, errorCallback) {
// Init params array
if (typeof params === 'undefined') {
params = [];
}
// Create query and add to queue
var query = new DDB_Query(this);
dddb.queryQueue[query.id] = query;
// Save callbacks
query.successCallback = successCallback;
query.errorCallback = errorCallback;
// Call native code
PhoneGap.exec(null, null, "SQLitePlugin", "executeSql", [sql, params, query.id]);
};
var DatabaseShell = function() {
};
/**
* Start a transaction.
* Does not support rollback in event of failure.
*
* @param process {Function} The transaction function
* @param successCallback {Function}
* @param errorCallback {Function}
*/
DatabaseShell.prototype.transaction = function(process, errorCallback, successCallback) {
var tx = new DDB_Tx();
tx.successCallback = successCallback;
tx.errorCallback = errorCallback;
try {
process(tx);
} catch (e) {
console.log("Transaction error: "+e);
if (tx.errorCallback) {
try {
tx.errorCallback(e);
} catch (ex) {
console.log("Transaction error calling user error callback: "+e);
}
}
}
};
/**
* Open database
*
* @param name Database name
* @param version Database version
* @param display_name Database display name
* @param size Database size in bytes
* @return Database object
*/
var DDB_openDatabase = function(name, version, display_name, size) {
PhoneGap.exec(null, null, "SQLitePlugin", "openDatabase", [name, version, display_name, size]);
var db = new DatabaseShell();
return db;
};
/**
* For browsers with no localStorage we emulate it with SQLite. Follows the w3c api.
* TODO: Do similar for sessionStorage.
*/
/**
* @constructor
*/
window.sqlitePlugin = {
openDatabase: function(name, version, desc, size) {
window.dddb = new DDB();
return DDB_openDatabase(name, version, desc, size);
}
};

View File

@ -1,111 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<meta name="viewport" content="width=320; user-scalable=no" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>PhoneGap</title>
<link rel="stylesheet" href="master.css" type="text/css" media="screen" title="no title" charset="utf-8">
<script type="text/javascript" charset="utf-8" src="cordova-1.5.0.js"></script>
<script type="text/javascript" charset="utf-8" src="SQLitePlugin.js"></script>
<script type="text/javascript" charset="utf-8" src="main.js"></script>
<script type="text/javascript" charset="utf-8" >
function init1test() {
//alert("alert 0");
}
// Wait for PhoneGap to load
//
document.addEventListener("deviceready", onDeviceReady, false);
// PhoneGap is ready
//
function onDeviceReady() {
var db = window.sqlitePlugin.openDatabase("Database", "1.0", "PhoneGap Demo", 200000);
db.transaction(function(tx) {
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)');
return 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");
tx.executeSql("select count(id) as cnt from test_table;", [], function(tx, res) {
console.log("rows.length: " + res.rows.length + " -- should be 1");
return console.log("rows[0].cnt: " + res.rows.item(0).cnt + " -- should be 1");
});
}, function(e) {
return console.log("ERROR: " + e.message);
});
});
}
// Populate the database
//
function populateDB(tx) {
alert("a 3");
tx.executeSql('DROP TABLE IF EXISTS DEMO');
alert("a 4");
tx.executeSql('CREATE TABLE IF NOT EXISTS DEMO (id unique, data)');
alert("a 5");
tx.executeSql('INSERT INTO DEMO (id, data) VALUES (1, "First row")');
alert("a 6");
tx.executeSql('INSERT INTO DEMO (id, data) VALUES (2, "Second row")');
alert("a 7");
}
// Transaction error callback
//
function errorCB(tx, err) {
alert("Error processing SQL: "+err);
}
// Transaction success callback
//
function successCB() {
alert("success!");
}
</script>
</head>
<body onload="init1test();" id="stage" class="theme">
<h1>Welcome to PhoneGap!</h1>
<h2>this file is located at assets/www/index.html</h2>
<div id="info">
<h4>Platform: <span id="platform"> &nbsp;</span>, Version: <span id="version">&nbsp;</span></h4>
<h4>UUID: <span id="uuid"> &nbsp;</span>, Name: <span id="name">&nbsp;</span></h4>
<h4>Width: <span id="width"> &nbsp;</span>, Height: <span id="height">&nbsp;
</span>, Color Depth: <span id="colorDepth"></span></h4>
</div>
<dl id="accel-data">
<dt>X:</dt><dd id="x">&nbsp;</dd>
<dt>Y:</dt><dd id="y">&nbsp;</dd>
<dt>Z:</dt><dd id="z">&nbsp;</dd>
</dl>
<a href="#" class="btn large" onclick="toggleAccel();">Toggle Accelerometer</a>
<a href="#" class="btn large" onclick="getLocation();">Get Location</a>
<a href="tel:411" class="btn large">Call 411</a>
<a href="#" class="btn large" onclick="beep();">Beep</a>
<a href="#" class="btn large" onclick="vibrate();">Vibrate</a>
<a href="#" class="btn large" onclick="show_pic();">Get a Picture</a>
<a href="#" class="btn large" onclick="get_contacts();">Get Phone's Contacts</a>
<a href="#" class="btn large" onclick="check_network();">Check Network</a>
<div id="viewport" class="viewport" style="display: none;">
<img style="width:60px;height:60px" id="test_img" src="" />
</div>
</body>
</html>

View File

@ -1,248 +0,0 @@
/*
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) 2005-2010, Nitobi Software Inc.
* Copyright (c) 2010, IBM Corporation
*/
package com.phonegap.plugin.sqlitePlugin;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import com.phonegap.api.Plugin;
import com.phonegap.api.PluginResult;
import android.database.Cursor;
import android.database.sqlite.*;
import android.util.Log;
/**
* This class implements the HTML5 database support for Android 1.X devices. It
* is not used for Android 2.X, since HTML5 database is built in to the browser.
*/
public class SQLitePlugin extends Plugin {
// Data Definition Language
private static final String ALTER = "alter";
private static final String CREATE = "create";
private static final String DROP = "drop";
private static final String TRUNCATE = "truncate";
SQLiteDatabase myDb = null; // Database object
String path = null; // Database path
String dbName = null; // Database name
/**
* Constructor.
*/
public SQLitePlugin() {
}
/**
* Executes the request and returns PluginResult.
*
* @param action
* The action to execute.
* @param args
* JSONArry of arguments for the plugin.
* @param callbackId
* The callback id used when calling back into JavaScript.
* @return A PluginResult object with a status and message.
*/
public PluginResult execute(String action, JSONArray args, String callbackId) {
PluginResult.Status status = PluginResult.Status.OK;
String result = "";
try {
// TODO: Do we want to allow a user to do this, since they could get
// to other app databases?
if (action.equals("setStorage")) {
this.setStorage(args.getString(0));
} else if (action.equals("openDatabase")) {
this.openDatabase(args.getString(0), args.getString(1),
args.getString(2), args.getLong(3));
} else if (action.equals("executeSql")) {
String[] s = null;
if (args.isNull(1)) {
s = new String[0];
} else {
JSONArray a = args.getJSONArray(1);
int len = a.length();
s = new String[len];
for (int i = 0; i < len; i++) {
s[i] = a.getString(i);
}
}
this.executeSql(args.getString(0), s, args.getString(2));
}
return new PluginResult(status, result);
} catch (JSONException e) {
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
}
}
/**
* Identifies if action to be executed returns a value and should be run
* synchronously.
*
* @param action
* The action to execute
* @return T=returns value
*/
public boolean isSynch(String action) {
return true;
}
/**
* Clean up and close database.
*/
@Override
public void onDestroy() {
if (this.myDb != null) {
this.myDb.close();
this.myDb = null;
}
}
// --------------------------------------------------------------------------
// LOCAL METHODS
// --------------------------------------------------------------------------
/**
* Set the application package for the database. Each application saves its
* database files in a directory with the application package as part of the
* file name.
*
* For example, application "com.phonegap.demo.Demo" would save its database
* files in "/data/data/com.phonegap.demo/databases/" directory.
*
* @param appPackage
* The application package.
*/
public void setStorage(String appPackage) {
this.path = "/data/data/" + appPackage + "/databases/";
}
/**
* Open database.
*
* @param db
* The name of the database
* @param version
* The version
* @param display_name
* The display name
* @param size
* The size in bytes
*/
public void openDatabase(String db, String version, String display_name,
long size) {
// If database is open, then close it
if (this.myDb != null) {
this.myDb.close();
}
// If no database path, generate from application package
if (this.path == null) {
Package pack = this.ctx.getClass().getPackage();
String appPackage = pack.getName();
this.setStorage(appPackage);
}
this.dbName = this.path + db + ".db";
this.myDb = SQLiteDatabase.openOrCreateDatabase(this.dbName, null);
}
/**
* Execute SQL statement.
*
* @param query
* The SQL query
* @param params
* Parameters for the query
* @param tx_id
* Transaction id
*/
public void executeSql(String query, String[] params, String tx_id) {
try {
if (isDDL(query)) {
this.myDb.execSQL(query);
this.sendJavascript("dddb.completeQuery('" + tx_id + "', '');");
}
else {
Cursor myCursor = this.myDb.rawQuery(query, params);
this.processResults(myCursor, tx_id);
myCursor.close();
}
}
catch (SQLiteException ex) {
ex.printStackTrace();
System.out.println("SQLitePlugin.executeSql(): Error=" + ex.getMessage());
// Send error message back to JavaScript
this.sendJavascript("dddb.fail('" + ex.getMessage() + "','" + tx_id + "');");
}
}
/**
* Checks to see the the query is a Data Definintion command
*
* @param query to be executed
* @return true if it is a DDL command, false otherwise
*/
private boolean isDDL(String query) {
String cmd = query.toLowerCase();
if (cmd.startsWith(DROP) || cmd.startsWith(CREATE) || cmd.startsWith(ALTER) || cmd.startsWith(TRUNCATE)) {
return true;
}
return false;
}
/**
* Process query results.
*
* @param cur
* Cursor into query results
* @param tx_id
* Transaction id
*/
public void processResults(Cursor cur, String tx_id) {
String result = "[]";
// If query result has rows
if (cur.moveToFirst()) {
JSONArray fullresult = new JSONArray();
String key = "";
String value = "";
int colCount = cur.getColumnCount();
// Build up JSON result object for each row
do {
JSONObject row = new JSONObject();
try {
for (int i = 0; i < colCount; ++i) {
key = cur.getColumnName(i);
value = cur.getString(i);
row.put(key, value);
}
fullresult.put(row);
} catch (JSONException e) {
e.printStackTrace();
}
} while (cur.moveToNext());
result = fullresult.toString();
}
// Let JavaScript know that there are no more rows
this.sendJavascript("dddb.completeQuery('" + tx_id + "', " + result
+ ");");
}
}

View File

@ -9,10 +9,10 @@ Created by @Joenoon:
Adapted to 1.5 by @coomsie
Major improvements for batch processing by @marcucio
Android version by @marcucio and @chbrody
Major improvements for batch processing by @marcucio (Android version)
API changes by @chbrody
DISCLAIMER:
@ -61,11 +61,9 @@ Installing
**NOTE:** There are now the following trees:
- `iOS`: new version by @marcucio, with improvements for batch transaction processing, testing seems OK, **TBD** needs to be ported to Cordova 1.5/1.6
- `Cordova-iOS` for Cordova 1.5/1.6 iOS
- `Legacy-PhoneGap-iPhone` for PhoneGap (tested 1.3 and earlier).
- `iOS-legacy-phonegap` to support new API for PhoneGap 1.4- (cleanups by @marcucio)
- `Android`: new version by @marcucio, with improvements for batch transaction processing, testing seems OK
- `DroidGap-old`: initial adaptation for Android, expected to go away pretty soon.
Cordova 1.6 (RC)
----------------
@ -91,7 +89,7 @@ PhoneGap 1.3.0
--------------
For installing with PhoneGap 1.3.0:
in PGSQLitePlugin.h file change for PhoneGaps JSONKit.h implementation.
in iOS-legacy-phonegap/SQLitePlugin.h file change for PhoneGap's JSONKit.h implementation.
#ifdef PHONEGAP_FRAMEWORK
#import <PhoneGap/PGPlugin.h>
@ -106,10 +104,8 @@ in PGSQLitePlugin.h file change for PhoneGaps JSONKit.h implementation.
#import "File.h"
#endif
and in PGSQLitePlugin.m JSONRepresentation must be changed to JSONString:
and in iOS-legacy-phonegap/SQLitePlugin.m JSONRepresentation must be changed to JSONString:
--- a/Plugins/PGSQLitePlugin.m
+++ b/Plugins/PGSQLitePlugin.m
@@ -219,7 +219,7 @@
if (hasInsertId) {
[resultSet setObject:insertId forKey:@"insertId"];
@ -149,25 +145,13 @@ Insert this in there:
<key>SQLitePlugin</key>
<string>SQLitePlugin</string>
**NOTE:** For `Legacy-PhoneGap-iPhone` the plugin name is `PGSQLitePlugin`, no fix is expected in this project.
Extra Usage
===========
General Usage
=============
Cordova iOS
-----------
Android
-------
Tested OK using the Lawnchair test suite. Old DroidGap version is expected to go away very soon, yes I did repeat myself!
New iOS version
---------------
Tested OK using the sample above and the Lawnchair test suite. Needs to be ported for Cordova 1.5/1.6+. Old version will go away if there are no major issues.
Cordova iOS (stable version)
----------------------------
**NOTE:** Please use sqlitePlugin.openDatabase() to open a database, the parameters are different from the old SQLitePlugin() constructor which is now gone. This should a closer resemblance to the HTML5/W3 SQL API.
**NOTE:** These are from old samples, old API which is hereby deprecated.
## Coffee Script
@ -231,7 +215,7 @@ Cordova iOS (stable version)
});
Legacy PhoneGap (old version)
iOS Legacy PhoneGap
-----------------------------
## Coffee Script
@ -333,14 +317,12 @@ Using the `db` option you can create multiple stores in one sqlite file. (There
Legacy Lawnchair test
---------------------
In the lawnchair-test subdirectory of Cordova-iOS or Legacy-PhoneGap-iPhone you can copy the contents of the www subdirectory into a Cordova/PhoneGap project and see the behavior of the Lawnchair test suite.
In the lawnchair-test subdirectory of Cordova-iOS you can copy the contents of the www subdirectory into a Cordova/PhoneGap project and see the behavior of the Lawnchair test suite.
Extra notes
-----------
@marcucio has also made some improvements for batching in the iOS version in https://github.com/marcucio/Cordova-Plugins but I do not want to take these until I know it is working OK with multi-level transaction API.
Old baching notes, will go away:
Old baching notes for iOS version:
### Other notes from @Joenoon:

View File

@ -0,0 +1,45 @@
/*
* Copyright (C) 2011 Davide Bertola
*
* Authors:
* Davide Bertola <dade@dadeb.it>
* Joe Noon <joenoon@gmail.com>
*
* This library is available under the terms of the MIT License (2008).
* See http://opensource.org/licenses/alphabetical for full text.
*/
#import <Foundation/Foundation.h>
#import "sqlite3.h"
#ifdef PHONEGAP_FRAMEWORK
#import <PhoneGap/PGPlugin.h>
#import <PhoneGap/JSON.h>
#import <PhoneGap/PhoneGapDelegate.h>
#import <PhoneGap/File.h>
#else
#import "PGPlugin.h"
#import "JSON.h"
#import "PhoneGapDelegate.h"
#import "File.h"
#endif
@interface SQLitePlugin : PGPlugin {
NSMutableDictionary *openDBs;
}
@property (nonatomic, copy) NSMutableDictionary *openDBs;
@property (nonatomic, retain) NSString *appDocsPath;
-(void) open:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
-(void) backgroundExecuteSqlBatch:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
-(void) backgroundExecuteSql:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
-(void) executeSqlBatch:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
-(void) executeSql:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
-(void) _executeSqlBatch:(NSMutableDictionary*)options;
-(void) _executeSql:(NSMutableDictionary*)options;
-(void) close: (NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
-(void) respond: (id)cb withString:(NSString *)str withType:(NSString *)type;
-(id) getDBPath:(id)dbFile;
@end

View File

@ -0,0 +1,185 @@
(function() {
var callbacks, cbref, counter, getOptions, root;
root = this;
callbacks = {};
counter = 0;
cbref = function(hash) {
var f;
f = "cb" + (counter += 1);
callbacks[f] = hash;
return f;
};
getOptions = function(opts, success, error) {
var cb, has_cbs;
cb = {};
has_cbs = false;
if (typeof success === "function") {
has_cbs = true;
cb.success = success;
}
if (typeof error === "function") {
has_cbs = true;
cb.error = error;
}
if (has_cbs) opts.callback = cbref(cb);
return opts;
};
root.SQLitePlugin = (function() {
SQLitePlugin.prototype.openDBs = {};
function SQLitePlugin(dbPath, openSuccess, openError) {
this.dbPath = dbPath;
this.openSuccess = openSuccess;
this.openError = openError;
if (!dbPath) {
throw new Error("Cannot create a SQLitePlugin instance without a dbPath");
}
this.openSuccess || (this.openSuccess = function() {
console.log("DB opened: " + dbPath);
});
this.openError || (this.openError = function(e) {
console.log(e.message);
});
this.open(this.openSuccess, this.openError);
}
SQLitePlugin.handleCallback = function(ref, type, obj) {
var _ref;
if ((_ref = callbacks[ref]) != null) {
if (typeof _ref[type] === "function") _ref[type](obj);
}
callbacks[ref] = null;
delete callbacks[ref];
};
SQLitePlugin.prototype.executeSql = function(sql, values, success, error) {
var opts;
if (!sql) throw new Error("Cannot executeSql without a query");
opts = getOptions({
query: [sql].concat(values || []),
path: this.dbPath
}, success, error);
PhoneGap.exec("SQLitePlugin.backgroundExecuteSql", opts);
};
SQLitePlugin.prototype.transaction = function(fn, error, success) {
var t;
t = new root.SQLitePluginTransaction(this.dbPath);
fn(t);
return t.complete(success, error);
};
SQLitePlugin.prototype.open = function(success, error) {
var opts;
if (!(this.dbPath in this.openDBs)) {
this.openDBs[this.dbPath] = true;
opts = getOptions({
path: this.dbPath
}, success, error);
PhoneGap.exec("SQLitePlugin.open", opts);
}
};
SQLitePlugin.prototype.close = function(success, error) {
var opts;
if (this.dbPath in this.openDBs) {
delete this.openDBs[this.dbPath];
opts = getOptions({
path: this.dbPath
}, success, error);
PhoneGap.exec("SQLitePlugin.close", opts);
}
};
return SQLitePlugin;
})();
root.SQLitePluginTransaction = (function() {
function SQLitePluginTransaction(dbPath) {
this.dbPath = dbPath;
this.executes = [];
}
SQLitePluginTransaction.prototype.executeSql = function(sql, values, success, error) {
var errorcb, successcb, txself;
txself = this;
successcb = null;
if (success) {
successcb = function(execres) {
var res, saveres;
saveres = execres;
res = {
rows: {
item: function(i) {
return saveres.rows[i];
},
length: saveres.rows.length
},
rowsAffected: saveres.rowsAffected,
insertId: saveres.insertId || null
};
return success(txself, res);
};
}
errorcb = null;
if (error) {
errorcb = function(res) {
return error(txself, res);
};
}
this.executes.push(getOptions({
query: [sql].concat(values || []),
path: this.dbPath
}, successcb, errorcb));
};
SQLitePluginTransaction.prototype.complete = function(success, error) {
var begin_opts, commit_opts, errorcb, executes, opts, successcb, txself;
if (this.__completed) throw new Error("Transaction already run");
this.__completed = true;
txself = this;
successcb = function(res) {
return success(txself, res);
};
errorcb = function(res) {
return error(txself, res);
};
begin_opts = getOptions({
query: ["BEGIN;"],
path: this.dbPath
});
commit_opts = getOptions({
query: ["COMMIT;"],
path: this.dbPath
}, successcb, errorcb);
executes = [begin_opts].concat(this.executes).concat([commit_opts]);
opts = {
executes: executes
};
PhoneGap.exec("SQLitePlugin.backgroundExecuteSqlBatch", opts);
this.executes = [];
};
return SQLitePluginTransaction;
})();
root.sqlitePlugin = {
openDatabase: function(dbPath, version, displayName, estimatedSize, creationCallback, errorCallback) {
if (version == null) version = null;
if (displayName == null) displayName = null;
if (estimatedSize == null) estimatedSize = 0;
if (creationCallback == null) creationCallback = null;
if (errorCallback == null) errorCallback = null;
return new SQLitePlugin(dbPath, creationCallback, errorCallback);
}
};
}).call(this);

View File

@ -0,0 +1,265 @@
/*
* Copyright (C) 2011 Davide Bertola
*
* Authors:
* Davide Bertola <dade@dadeb.it>
* Joe Noon <joenoon@gmail.com>
*
* This library is available under the terms of the MIT License (2008).
* See http://opensource.org/licenses/alphabetical for full text.
*/
#import "SQLitePlugin.h"
@implementation SQLitePlugin
@synthesize openDBs;
@synthesize appDocsPath;
-(PGPlugin*) initWithWebView:(UIWebView*)theWebView
{
self = (SQLitePlugin*)[super initWithWebView:theWebView];
if (self) {
openDBs = [NSMutableDictionary dictionaryWithCapacity:0];
[openDBs retain];
PGFile* pgFile = [[self appDelegate] getCommandInstance: @"com.phonegap.file"];
NSString *docs = [pgFile appDocsPath];
[self setAppDocsPath:docs];
}
return self;
}
-(void) respond: (id)cb withString:(NSString *)str withType:(NSString *)type {
if (cb != NULL) {
NSString* jsString = [NSString stringWithFormat:@"SQLitePlugin.handleCallback('%@', '%@', %@);", cb, type, str ];
[self writeJavascript:jsString];
}
}
-(id) getDBPath:(id)dbFile {
if (dbFile == NULL) {
return NULL;
}
NSString *dbPath = [NSString stringWithFormat:@"%@/%@", appDocsPath, dbFile];
return dbPath;
}
-(void) open: (NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
{
NSString *callback = [options objectForKey:@"callback"];
NSString *dbPath = [self getDBPath:[options objectForKey:@"path"]];
if (dbPath == NULL) {
[self respond:callback withString:@"{ message: 'You must specify database path' }" withType:@"error"];
return;
}
sqlite3 *db;
const char *path = [dbPath UTF8String];
if (sqlite3_open(path, &db) != SQLITE_OK) {
[self respond:callback withString:@"{ message: 'Unable to open DB' }" withType:@"error"];
return;
}
NSValue *dbPointer = [NSValue valueWithPointer:db];
[openDBs setObject:dbPointer forKey: dbPath];
[self respond:callback withString: @"{ message: 'Database opened' }" withType:@"success"];
}
-(void) backgroundExecuteSqlBatch: (NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
{
[self performSelector:@selector(_executeSqlBatch:) withObject:options afterDelay:0.001];
}
-(void) backgroundExecuteSql: (NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
{
[self performSelector:@selector(_executeSql:) withObject:options afterDelay:0.001];
}
-(void) _executeSqlBatch:(NSMutableDictionary*)options
{
[self executeSqlBatch:NULL withDict:options];
}
-(void) _executeSql:(NSMutableDictionary*)options
{
[self executeSql:NULL withDict:options];
}
-(void) executeSqlBatch: (NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
{
NSMutableArray *executes = [options objectForKey:@"executes"];
for (NSMutableDictionary *dict in executes) {
[self executeSql:NULL withDict:dict];
}
}
-(void) executeSql: (NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
{
NSString *callback = [options objectForKey:@"callback"];
NSString *dbPath = [self getDBPath:[options objectForKey:@"path"]];
NSMutableArray *query_parts = [options objectForKey:@"query"];
NSString *query = [query_parts objectAtIndex:0];
if (dbPath == NULL) {
[self respond:callback withString:@"{ message: 'You must specify database path' }" withType:@"error"];
return;
}
if (query == NULL) {
[self respond:callback withString:@"{ message: 'You must specify a query to execute' }" withType:@"error"];
return;
}
NSValue *dbPointer = [openDBs objectForKey:dbPath];
if (dbPointer == NULL) {
[self respond:callback withString:@"{ message: 'No such database, you must open it first' }" withType:@"error"];
return;
}
sqlite3 *db = [dbPointer pointerValue];
const char *sql_stmt = [query UTF8String];
char *errMsg = NULL;
sqlite3_stmt *statement;
int result, i, column_type, count;
int previousRowsAffected, nowRowsAffected, diffRowsAffected;
long long previousInsertId, nowInsertId;
BOOL keepGoing = YES;
BOOL hasInsertId;
NSMutableDictionary *resultSet = [NSMutableDictionary dictionaryWithCapacity:0];
NSMutableArray *resultRows = [NSMutableArray arrayWithCapacity:0];
NSMutableDictionary *entry;
NSObject *columnValue;
NSString *columnName;
NSString *bindval;
NSObject *insertId;
NSObject *rowsAffected;
hasInsertId = NO;
previousRowsAffected = sqlite3_total_changes(db);
previousInsertId = sqlite3_last_insert_rowid(db);
if (sqlite3_prepare_v2(db, sql_stmt, -1, &statement, NULL) != SQLITE_OK) {
errMsg = (char *) sqlite3_errmsg (db);
keepGoing = NO;
} else {
for (int b = 1; b < query_parts.count; b++) {
bindval = [NSString stringWithFormat:@"%@", [query_parts objectAtIndex:b]];
sqlite3_bind_text(statement, b, [bindval UTF8String], -1, SQLITE_TRANSIENT);
}
}
while (keepGoing) {
result = sqlite3_step (statement);
switch (result) {
case SQLITE_ROW:
i = 0;
entry = [NSMutableDictionary dictionaryWithCapacity:0];
count = sqlite3_column_count(statement);
while (i < count) {
column_type = sqlite3_column_type(statement, i);
switch (column_type) {
case SQLITE_INTEGER:
columnValue = [NSNumber numberWithDouble: sqlite3_column_double(statement, i)];
columnName = [NSString stringWithFormat:@"%s", sqlite3_column_name(statement, i)];
[entry setObject:columnValue forKey:columnName];
break;
case SQLITE_TEXT:
columnValue = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, i)];
columnName = [NSString stringWithFormat:@"%s", sqlite3_column_name(statement, i)];
[entry setObject:columnValue forKey:columnName];
break;
case SQLITE_BLOB:
break;
case SQLITE_FLOAT:
columnValue = [NSNumber numberWithFloat: sqlite3_column_double(statement, i)];
columnName = [NSString stringWithFormat:@"%s", sqlite3_column_name(statement, i)];
[entry setObject:columnValue forKey:columnName];
break;
case SQLITE_NULL:
break;
}
i++;
}
[resultRows addObject:entry];
break;
case SQLITE_DONE:
nowRowsAffected = sqlite3_total_changes(db);
diffRowsAffected = nowRowsAffected - previousRowsAffected;
rowsAffected = [NSNumber numberWithInt:diffRowsAffected];
nowInsertId = sqlite3_last_insert_rowid(db);
if (previousInsertId != nowInsertId) {
hasInsertId = YES;
insertId = [NSNumber numberWithLongLong:sqlite3_last_insert_rowid(db)];
}
keepGoing = NO;
break;
default:
errMsg = "SQL statement error";
keepGoing = NO;
}
}
sqlite3_finalize (statement);
if (errMsg != NULL) {
[self respond:callback withString:[NSString stringWithFormat:@"{ message: 'SQL statement error : %s' }", errMsg] withType:@"error"];
} else {
[resultSet setObject:resultRows forKey:@"rows"];
[resultSet setObject:rowsAffected forKey:@"rowsAffected"];
if (hasInsertId) {
[resultSet setObject:insertId forKey:@"insertId"];
}
[self respond:callback withString:[resultSet JSONRepresentation] withType:@"success"];
}
}
-(void) close: (NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
{
NSString *callback = [options objectForKey:@"callback"];
NSString *dbPath = [self getDBPath:[options objectForKey:@"path"]];
if (dbPath == NULL) {
[self respond:callback withString:@"{ message: 'You must specify database path' }" withType:@"error"];
return;
}
NSValue *val = [openDBs objectForKey:dbPath];
sqlite3 *db = [val pointerValue];
if (db == NULL) {
[self respond:callback withString: @"{ message: 'Specified db was not open' }" withType:@"error"];
}
sqlite3_close (db);
[self respond:callback withString: @"{ message: 'db closed' }" withType:@"success"];
}
-(void)dealloc
{
int i;
NSArray *keys = [openDBs allKeys];
NSValue *pointer;
NSString *key;
sqlite3 *db;
/* close db the user forgot */
for (i=0; i<[keys count]; i++) {
key = [keys objectAtIndex:i];
pointer = [openDBs objectForKey:key];
db = [pointer pointerValue];
sqlite3_close (db);
}
[openDBs release];
[appDocsPath release];
[super dealloc];
}
@end