mirror of
https://github.com/moparisthebest/PhoneGap-SQLitePlugin-Android
synced 2024-11-28 11:22:15 -05:00
Merge branch 'master' into batch-merge
This commit is contained in:
commit
878208585d
259
Android/assets/www/SQLitePlugin.js
Executable file
259
Android/assets/www/SQLitePlugin.js
Executable file
@ -0,0 +1,259 @@
|
|||||||
|
(function() {
|
||||||
|
var root;
|
||||||
|
root = this;
|
||||||
|
root.SQLitePlugin = (function() {
|
||||||
|
console.log("root.SQLitePlugin");
|
||||||
|
SQLitePlugin.prototype.openDBs = {};
|
||||||
|
|
||||||
|
function SQLitePlugin(dbPath, openSuccess, openError)
|
||||||
|
{
|
||||||
|
console.log("SQLitePlugin");
|
||||||
|
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.prototype.transaction = function(fn, error, success)
|
||||||
|
{
|
||||||
|
console.log("SQLitePlugin.prototype.transaction");
|
||||||
|
var t;
|
||||||
|
t = new root.SQLitePluginTransaction(this.dbPath);
|
||||||
|
fn(t);
|
||||||
|
return t.complete(success, error);
|
||||||
|
};
|
||||||
|
|
||||||
|
SQLitePlugin.prototype.open = function(success, error)
|
||||||
|
{
|
||||||
|
console.log("SQLitePlugin.prototype.open");
|
||||||
|
var opts;
|
||||||
|
if (!(this.dbPath in this.openDBs)) {
|
||||||
|
this.openDBs[this.dbPath] = true;
|
||||||
|
PhoneGap.exec(success, error, "SQLitePlugin", "open", [this.dbPath]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
SQLitePlugin.prototype.close = function(success, error)
|
||||||
|
{
|
||||||
|
console.log("SQLitePlugin.prototype.close");
|
||||||
|
var opts;
|
||||||
|
if (this.dbPath in this.openDBs) {
|
||||||
|
delete this.openDBs[this.dbPath];
|
||||||
|
PhoneGap.exec(null, null, "SQLitePlugin", "close", [this.dbPath]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return SQLitePlugin;
|
||||||
|
})();
|
||||||
|
get_unique_id = function()
|
||||||
|
{
|
||||||
|
var id = new Date().getTime();
|
||||||
|
var id2 = new Date().getTime();
|
||||||
|
while(id === id2)
|
||||||
|
{
|
||||||
|
id2 = new Date().getTime();
|
||||||
|
}
|
||||||
|
return id2+'000';
|
||||||
|
}
|
||||||
|
transaction_queue = [];
|
||||||
|
transaction_callback_queue = new Object();
|
||||||
|
root.SQLitePluginTransaction = (function() {
|
||||||
|
console.log("root.SQLitePluginTransaction");
|
||||||
|
function SQLitePluginTransaction(dbPath)
|
||||||
|
{
|
||||||
|
console.log("root.SQLitePluginTransaction.SQLitePluginTransaction");
|
||||||
|
this.dbPath = dbPath;
|
||||||
|
this.executes = [];
|
||||||
|
this.trans_id = get_unique_id();
|
||||||
|
this.__completed = false;
|
||||||
|
this.__submitted = false;
|
||||||
|
this.optimization_no_nested_callbacks = true;//if set to true large batches of queries within a transaction will be much faster but you _MAY_ lose the ability to do deep multi level nesting of executeSQL callbacks
|
||||||
|
console.log("root.SQLitePluginTransaction - this.trans_id:"+this.trans_id);
|
||||||
|
transaction_queue[this.trans_id] = [];
|
||||||
|
transaction_callback_queue[this.trans_id] = new Object();
|
||||||
|
}
|
||||||
|
SQLitePluginTransaction.queryCompleteCallback = function(transId, queryId, result)
|
||||||
|
{
|
||||||
|
console.log("SQLitePluginTransaction.queryCompleteCallback");
|
||||||
|
var query = null;
|
||||||
|
for (var x in transaction_queue[transId])
|
||||||
|
{
|
||||||
|
if(transaction_queue[transId][x]['query_id'] == queryId)
|
||||||
|
{
|
||||||
|
query = transaction_queue[transId][x];
|
||||||
|
if(transaction_queue[transId].length == 1)
|
||||||
|
transaction_queue[transId] = [];
|
||||||
|
else
|
||||||
|
transaction_queue[transId].splice(x, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if(query)
|
||||||
|
// console.log("SQLitePluginTransaction.completeCallback---query:"+query['query']);
|
||||||
|
if(query && query['callback'])
|
||||||
|
{
|
||||||
|
query['callback'](result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SQLitePluginTransaction.queryErrorCallback = function(transId, queryId, result)
|
||||||
|
{
|
||||||
|
var query = null;
|
||||||
|
for (var x in transaction_queue[transId])
|
||||||
|
{
|
||||||
|
if(transaction_queue[transId][x]['query_id'] == queryId)
|
||||||
|
{
|
||||||
|
query = transaction_queue[transId][x];
|
||||||
|
if(transaction_queue[transId].length == 1)
|
||||||
|
transaction_queue[transId] = [];
|
||||||
|
else
|
||||||
|
transaction_queue[transId].splice(x, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//if(query)
|
||||||
|
// console.log("SQLitePluginTransaction.queryErrorCallback---query:"+query['query']);
|
||||||
|
if(query && query['err_callback'])
|
||||||
|
query['err_callback'](result)
|
||||||
|
}
|
||||||
|
SQLitePluginTransaction.txCompleteCallback = function(transId)
|
||||||
|
{
|
||||||
|
if(typeof transId != 'undefined')
|
||||||
|
{
|
||||||
|
if(transId && transaction_callback_queue[transId] && transaction_callback_queue[transId]['success'])
|
||||||
|
{
|
||||||
|
transaction_callback_queue[transId]['success']();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// delete transaction_queue[transId];
|
||||||
|
// delete transaction_callback_queue[transId];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
console.log("SQLitePluginTransaction.txCompleteCallback---transId = NULL");
|
||||||
|
}
|
||||||
|
SQLitePluginTransaction.txErrorCallback = function(transId, error)
|
||||||
|
{
|
||||||
|
if(typeof transId != 'undefined')
|
||||||
|
{
|
||||||
|
console.log("SQLitePluginTransaction.txErrorCallback---transId:"+transId);
|
||||||
|
if(transId && transaction_callback_queue[transId]['error'])
|
||||||
|
transaction_callback_queue[transId]['error'](error);
|
||||||
|
delete transaction_queue[transId];
|
||||||
|
delete transaction_callback_queue[transId];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
console.log("SQLitePluginTransaction.txErrorCallback---transId = NULL");
|
||||||
|
}
|
||||||
|
SQLitePluginTransaction.prototype.add_to_transaction = function(trans_id, query, params, callback, err_callback)
|
||||||
|
{
|
||||||
|
var new_query = new Object();;
|
||||||
|
new_query['trans_id'] = trans_id;
|
||||||
|
if(callback || !this.optimization_no_nested_callbacks)
|
||||||
|
new_query['query_id'] = get_unique_id();
|
||||||
|
else if(this.optimization_no_nested_callbacks)
|
||||||
|
new_query['query_id'] = "";
|
||||||
|
new_query['query'] = query;
|
||||||
|
if(params)
|
||||||
|
new_query['params'] = params;
|
||||||
|
else
|
||||||
|
new_query['params'] = [];
|
||||||
|
new_query['callback'] = callback;
|
||||||
|
new_query['err_callback'] = err_callback;
|
||||||
|
if(!transaction_queue[trans_id])
|
||||||
|
transaction_queue[trans_id] = [];
|
||||||
|
transaction_queue[trans_id].push(new_query);
|
||||||
|
}
|
||||||
|
|
||||||
|
SQLitePluginTransaction.prototype.executeSql = function(sql, values, success, error) {
|
||||||
|
console.log("SQLitePluginTransaction.prototype.executeSql");
|
||||||
|
var errorcb, successcb, txself;
|
||||||
|
txself = this;
|
||||||
|
successcb = null;
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
console.log("success not null:"+sql);
|
||||||
|
successcb = function(execres)
|
||||||
|
{
|
||||||
|
console.log("executeSql callback:"+JSON.stringify(execres));
|
||||||
|
var res, saveres;
|
||||||
|
saveres = execres;
|
||||||
|
res = {
|
||||||
|
rows: {
|
||||||
|
item: function(i) {
|
||||||
|
return saveres[i];
|
||||||
|
},
|
||||||
|
length: saveres.length
|
||||||
|
},
|
||||||
|
rowsAffected: saveres.rowsAffected,
|
||||||
|
insertId: saveres.insertId || null
|
||||||
|
};
|
||||||
|
return success(txself, res);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
console.log("success NULL:"+sql);
|
||||||
|
|
||||||
|
errorcb = null;
|
||||||
|
if (error) {
|
||||||
|
errorcb = function(res) {
|
||||||
|
return error(txself, res);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
this.add_to_transaction(this.trans_id, sql, values, successcb, errorcb);
|
||||||
|
console.log("executeSql - add_to_transaction"+sql);
|
||||||
|
};
|
||||||
|
|
||||||
|
SQLitePluginTransaction.prototype.complete = function(success, error) {
|
||||||
|
console.log("SQLitePluginTransaction.prototype.complete");
|
||||||
|
var begin_opts, commit_opts, errorcb, executes, opts, successcb, txself;
|
||||||
|
if (this.__completed) throw new Error("Transaction already run");
|
||||||
|
if (this.__submitted) throw new Error("Transaction already submitted");
|
||||||
|
this.__submitted = true;
|
||||||
|
txself = this;
|
||||||
|
successcb = function()
|
||||||
|
{
|
||||||
|
if(transaction_queue[txself.trans_id].length > 0 && !txself.optimization_no_nested_callbacks)
|
||||||
|
{
|
||||||
|
txself.__submitted = false;
|
||||||
|
txself.complete(success, error);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.__completed = true;
|
||||||
|
if(success)
|
||||||
|
return success(txself);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
errorcb = function(res) {};
|
||||||
|
if (error) {
|
||||||
|
errorcb = function(res) {
|
||||||
|
return error(txself, res);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
transaction_callback_queue[this.trans_id]['success'] = successcb;
|
||||||
|
transaction_callback_queue[this.trans_id]['error'] = errorcb;
|
||||||
|
PhoneGap.exec(null, null, "SQLitePlugin", "executeSqlBatch", transaction_queue[this.trans_id]);
|
||||||
|
};
|
||||||
|
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);
|
272
Android/src/com/phonegap/plugin/sqlitePlugin/SQLitePlugin.java
Executable file
272
Android/src/com/phonegap/plugin/sqlitePlugin/SQLitePlugin.java
Executable file
@ -0,0 +1,272 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
public class SQLitePlugin extends Plugin {
|
||||||
|
|
||||||
|
// Data Definition Language
|
||||||
|
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("open")) {
|
||||||
|
this.openDatabase(args.getString(0), "1",
|
||||||
|
"database", 5000000);
|
||||||
|
//this.openDatabase(args.getString(0), args.getString(1),
|
||||||
|
// args.getString(2), args.getLong(3));
|
||||||
|
}
|
||||||
|
else if (action.equals("executeSqlBatch"))
|
||||||
|
{
|
||||||
|
String[] queries = null;
|
||||||
|
String[] queryIDs = null;
|
||||||
|
String[][] params = null;
|
||||||
|
String trans_id = null;
|
||||||
|
JSONObject a = null;
|
||||||
|
JSONArray jsonArr = null;
|
||||||
|
int paramLen = 0;
|
||||||
|
|
||||||
|
if (args.isNull(0)) {
|
||||||
|
queries = new String[0];
|
||||||
|
} else {
|
||||||
|
int len = args.length();
|
||||||
|
queries = new String[len];
|
||||||
|
queryIDs = new String[len];
|
||||||
|
params = new String[len][1];
|
||||||
|
for (int i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
a = args.getJSONObject(i);
|
||||||
|
queries[i] = a.getString("query");
|
||||||
|
queryIDs[i] = a.getString("query_id");
|
||||||
|
trans_id = a.getString("trans_id");
|
||||||
|
jsonArr = a.getJSONArray("params");
|
||||||
|
paramLen = jsonArr.length();
|
||||||
|
params[i] = new String[paramLen];
|
||||||
|
|
||||||
|
for (int j = 0; j < paramLen; j++) {
|
||||||
|
params[i][j] = jsonArr.getString(j);
|
||||||
|
if(params[i][j] == "null")
|
||||||
|
params[i][j] = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(trans_id != null)
|
||||||
|
this.executeSqlBatch(queries, params, queryIDs, trans_id);
|
||||||
|
else
|
||||||
|
Log.v("error", "null trans_id");
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void executeSqlBatch(String[] queryarr, String[][] paramsarr, String[] queryIDs, String tx_id) {
|
||||||
|
try {
|
||||||
|
this.myDb.beginTransaction();
|
||||||
|
String query = "";
|
||||||
|
String query_id = "";
|
||||||
|
String[] params;
|
||||||
|
int len = queryarr.length;
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
query = queryarr[i];
|
||||||
|
params = paramsarr[i];
|
||||||
|
query_id = queryIDs[i];
|
||||||
|
Cursor myCursor = this.myDb.rawQuery(query, params);
|
||||||
|
|
||||||
|
this.processResults(myCursor, query_id, tx_id);
|
||||||
|
myCursor.close();
|
||||||
|
}
|
||||||
|
this.myDb.setTransactionSuccessful();
|
||||||
|
}
|
||||||
|
catch (SQLiteException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
Log.v("executeSqlBatch", "SQLitePlugin.executeSql(): Error=" + ex.getMessage());
|
||||||
|
this.sendJavascript("SQLitePluginTransaction.txErrorCallback('" + tx_id + "', '"+ex.getMessage()+"');");
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
this.myDb.endTransaction();
|
||||||
|
Log.v("executeSqlBatch", tx_id);
|
||||||
|
this.sendJavascript("SQLitePluginTransaction.txCompleteCallback('" + tx_id + "');");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process query results.
|
||||||
|
*
|
||||||
|
* @param cur
|
||||||
|
* Cursor into query results
|
||||||
|
* @param tx_id
|
||||||
|
* Transaction id
|
||||||
|
*/
|
||||||
|
public void processResults(Cursor cur, String query_id, String tx_id) {
|
||||||
|
|
||||||
|
String result = "[]";
|
||||||
|
// If query result has rows
|
||||||
|
|
||||||
|
if (cur.moveToFirst()) {
|
||||||
|
JSONArray fullresult = new JSONArray();
|
||||||
|
String key = "";
|
||||||
|
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);
|
||||||
|
// for old Android SDK remove lines from HERE:
|
||||||
|
if(android.os.Build.VERSION.SDK_INT >= 11)
|
||||||
|
{
|
||||||
|
switch(cur.getType (i))
|
||||||
|
{
|
||||||
|
case Cursor.FIELD_TYPE_NULL:
|
||||||
|
row.put(key, null);
|
||||||
|
break;
|
||||||
|
case Cursor.FIELD_TYPE_INTEGER:
|
||||||
|
row.put(key, cur.getInt(i));
|
||||||
|
break;
|
||||||
|
case Cursor.FIELD_TYPE_FLOAT:
|
||||||
|
row.put(key, cur.getFloat(i));
|
||||||
|
break;
|
||||||
|
case Cursor.FIELD_TYPE_STRING:
|
||||||
|
row.put(key, cur.getString(i));
|
||||||
|
break;
|
||||||
|
case Cursor.FIELD_TYPE_BLOB:
|
||||||
|
row.put(key, cur.getBlob(i));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // to HERE.
|
||||||
|
{
|
||||||
|
row.put(key, cur.getString(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fullresult.put(row);
|
||||||
|
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
} while (cur.moveToNext());
|
||||||
|
|
||||||
|
result = fullresult.toString();
|
||||||
|
}
|
||||||
|
if(query_id.length() > 0)
|
||||||
|
this.sendJavascript(" SQLitePluginTransaction.queryCompleteCallback('" + tx_id + "','" + query_id + "', " + result + ");");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
310
README.md
Normal file
310
README.md
Normal file
@ -0,0 +1,310 @@
|
|||||||
|
Cordova/PhoneGap SQLitePlugin
|
||||||
|
=============================
|
||||||
|
|
||||||
|
Native interface to sqlite in a Cordova/PhoneGap plugin, working to follow the HTML5 Web SQL API as close as possible. **NOTE** that the API is now different from https://github.com/davibe/Phonegap-SQLitePlugin.
|
||||||
|
|
||||||
|
Created by @joenoon and @davibe
|
||||||
|
|
||||||
|
Adapted to Cordova 1.5+ by @coomsie, Cordova 1.6 bugfix by @mineshaftgap
|
||||||
|
|
||||||
|
Android version by @marcucio and @chbrody
|
||||||
|
|
||||||
|
API changes by @chbrody
|
||||||
|
|
||||||
|
Highlights
|
||||||
|
----------
|
||||||
|
|
||||||
|
- Keeps sqlite database in a known user data location that will be backed up by iCloud on iOS
|
||||||
|
- Drop-in replacement for HTML5 SQL API, the only change is window.openDatabase() --> sqlitePlugin.openDatabase()
|
||||||
|
- Both Android and iOS versions are designed with batch processing optimizations
|
||||||
|
- Future: API to configure the desired database location
|
||||||
|
|
||||||
|
Usage
|
||||||
|
=====
|
||||||
|
|
||||||
|
The idea is to emulate the HTML5 SQL API as closely as possible. The only major change is to use window.sqlitePlugin.openDatabase() (or sqlitePlugin.openDatabase()) instead of window.openDatabase(). If you see any other major change please report it, it is probably a bug.
|
||||||
|
|
||||||
|
Sample in Javascript:
|
||||||
|
|
||||||
|
// Wait for Cordova/PhoneGap to load
|
||||||
|
//
|
||||||
|
document.addEventListener("deviceready", onDeviceReady, false);
|
||||||
|
|
||||||
|
// Cordova/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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Installing
|
||||||
|
==========
|
||||||
|
|
||||||
|
**NOTE:** There are now the following trees:
|
||||||
|
|
||||||
|
- `iOS` for Cordova 1.5/1.6 iOS
|
||||||
|
- `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
|
||||||
|
|
||||||
|
PhoneGap 1.3.0
|
||||||
|
--------------
|
||||||
|
|
||||||
|
For installing with PhoneGap 1.3.0:
|
||||||
|
in iOS-legacy-phonegap/SQLitePlugin.h file change for PhoneGap's JSONKit.h implementation.
|
||||||
|
|
||||||
|
#ifdef PHONEGAP_FRAMEWORK
|
||||||
|
#import <PhoneGap/PGPlugin.h>
|
||||||
|
#import <PhoneGap/JSONKit.h>
|
||||||
|
#import <PhoneGap/PhoneGapDelegate.h>
|
||||||
|
#import <PhoneGap/File.h>
|
||||||
|
#import<PhoneGap/FileTransfer.h>
|
||||||
|
#else
|
||||||
|
#import "PGPlugin.h"
|
||||||
|
#import "JSON.h"
|
||||||
|
#import "PhoneGapDelegate.h"
|
||||||
|
#import "File.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
and in iOS-legacy-phonegap/SQLitePlugin.m JSONRepresentation must be changed to JSONString:
|
||||||
|
|
||||||
|
@@ -219,7 +219,7 @@
|
||||||
|
if (hasInsertId) {
|
||||||
|
[resultSet setObject:insertId forKey:@"insertId"];
|
||||||
|
}
|
||||||
|
- [self respond:callback withString:[resultSet JSONRepresentation] withType:@"success"];
|
||||||
|
+ [self respond:callback withString:[resultSet JSONString] withType:@"success"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SQLite library
|
||||||
|
--------------
|
||||||
|
|
||||||
|
In the Project "Build Phases" tab, select the _first_ "Link Binary with Libraries" dropdown menu and add the library `libsqlite3.dylib` or `libsqlite3.0.dylib`.
|
||||||
|
|
||||||
|
**NOTE:** In the "Build Phases" there can be multiple "Link Binary with Libraries" dropdown menus. Please select the first one otherwise it will not work.
|
||||||
|
|
||||||
|
Native SQLite Plugin
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Drag .h and .m files into your project's Plugins folder (in xcode) -- I always
|
||||||
|
just have "Create references" as the option selected.
|
||||||
|
|
||||||
|
Take the precompiled javascript file from build/, or compile the coffeescript
|
||||||
|
file in src/ to javascript WITH the top-level function wrapper option (default).
|
||||||
|
|
||||||
|
Use the resulting javascript file in your HTML.
|
||||||
|
|
||||||
|
Look for the following to your project's Cordova.plist or PhoneGap.plist:
|
||||||
|
|
||||||
|
<key>Plugins</key>
|
||||||
|
<dict>
|
||||||
|
...
|
||||||
|
</dict>
|
||||||
|
|
||||||
|
Insert this in there:
|
||||||
|
|
||||||
|
<key>SQLitePlugin</key>
|
||||||
|
<string>SQLitePlugin</string>
|
||||||
|
|
||||||
|
Extra Usage
|
||||||
|
===========
|
||||||
|
|
||||||
|
Cordova iOS
|
||||||
|
-----------
|
||||||
|
|
||||||
|
**NOTE:** These are from old samples, old API which is hereby deprecated.
|
||||||
|
|
||||||
|
## Coffee Script
|
||||||
|
|
||||||
|
db = sqlitePlugin.openDatabase("my_sqlite_database.sqlite3")
|
||||||
|
db.executeSql('DROP TABLE IF EXISTS test_table')
|
||||||
|
db.executeSql('CREATE TABLE IF NOT EXISTS test_table (id integer primary key, data text, data_num integer)')
|
||||||
|
|
||||||
|
db.transaction (tx) ->
|
||||||
|
|
||||||
|
tx.executeSql "INSERT INTO test_table (data, data_num) VALUES (?,?)", ["test", 100], (res) ->
|
||||||
|
|
||||||
|
# success callback
|
||||||
|
|
||||||
|
console.log "insertId: #{res.insertId} -- probably 1"
|
||||||
|
console.log "rowsAffected: #{res.rowsAffected} -- should be 1"
|
||||||
|
|
||||||
|
# check the count (not a part of the transaction)
|
||||||
|
db.executeSql "select count(id) as cnt from test_table;", [], (res) ->
|
||||||
|
console.log "rows.length: #{res.rows.length} -- should be 1"
|
||||||
|
console.log "rows[0].cnt: #{res.rows[0].cnt} -- should be 1"
|
||||||
|
|
||||||
|
, (e) ->
|
||||||
|
|
||||||
|
# error callback
|
||||||
|
|
||||||
|
console.log "ERROR: #{e.message}"
|
||||||
|
|
||||||
|
## Plain Javascript
|
||||||
|
|
||||||
|
var db = sqlitePlugin.openDatabase("my_sqlite_database.sqlite3");
|
||||||
|
|
||||||
|
db.executeSql('DROP TABLE IF EXISTS test_table');
|
||||||
|
db.executeSql('CREATE TABLE IF NOT EXISTS test_table (id integer primary key, data text, data_num integer)');
|
||||||
|
db.transaction(function(tx) {
|
||||||
|
return tx.executeSql("INSERT INTO test_table (data, data_num) VALUES (?,?)", ["test", 100], function(res) {
|
||||||
|
console.log("insertId: " + res.insertId + " -- probably 1");
|
||||||
|
console.log("rowsAffected: " + res.rowsAffected + " -- should be 1");
|
||||||
|
return db.executeSql("select count(id) as cnt from test_table;", [], function(res) {
|
||||||
|
console.log("rows.length: " + res.rows.length + " -- should be 1");
|
||||||
|
return console.log("rows[0].cnt: " + res.rows[0].cnt + " -- should be 1");
|
||||||
|
});
|
||||||
|
}, function(e) {
|
||||||
|
return console.log("ERROR: " + e.message);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
## Changes in tx.executeSql() success callback
|
||||||
|
|
||||||
|
var db = sqlitePlugin.openDatabase("my_sqlite_database.sqlite3");
|
||||||
|
db.executeSql('DROP TABLE IF EXISTS test_table');
|
||||||
|
db.executeSql('CREATE TABLE IF NOT EXISTS test_table (id integer primary key, data text, data_num integer)');
|
||||||
|
db.executeSql("INSERT INTO test_table (data, data_num) VALUES (?,?)", ["test", 100], function(res) {
|
||||||
|
console.log("insertId: " + res.insertId + " -- probably 1");
|
||||||
|
console.log("rowsAffected: " + res.rowsAffected + " -- should be 1");
|
||||||
|
db.transaction(function(tx) {
|
||||||
|
return 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.item(0).cnt: " + res.rows.item(0).cnt + " -- should be 1");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
iOS Legacy PhoneGap
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
## Coffee Script
|
||||||
|
|
||||||
|
db = new PGSQLitePlugin("my_sqlite_database.sqlite3")
|
||||||
|
db.executeSql('DROP TABLE IF EXISTS test_table')
|
||||||
|
db.executeSql('CREATE TABLE IF NOT EXISTS test_table (id integer primary key, data text, data_num integer)')
|
||||||
|
|
||||||
|
db.transaction (tx) ->
|
||||||
|
|
||||||
|
tx.executeSql "INSERT INTO test_table (data, data_num) VALUES (?,?)", ["test", 100], (res) ->
|
||||||
|
|
||||||
|
# success callback
|
||||||
|
|
||||||
|
console.log "insertId: #{res.insertId} -- probably 1"
|
||||||
|
console.log "rowsAffected: #{res.rowsAffected} -- should be 1"
|
||||||
|
|
||||||
|
# check the count (not a part of the transaction)
|
||||||
|
db.executeSql "select count(id) as cnt from test_table;", (res) ->
|
||||||
|
console.log "rows.length: #{res.rows.length} -- should be 1"
|
||||||
|
console.log "rows[0].cnt: #{res.rows[0].cnt} -- should be 1"
|
||||||
|
|
||||||
|
, (e) ->
|
||||||
|
|
||||||
|
# error callback
|
||||||
|
|
||||||
|
console.log "ERROR: #{e.message}"
|
||||||
|
|
||||||
|
## Plain Javascript
|
||||||
|
|
||||||
|
var db;
|
||||||
|
db = new PGSQLitePlugin("my_sqlite_database.sqlite3");
|
||||||
|
db.executeSql('DROP TABLE IF EXISTS test_table');
|
||||||
|
db.executeSql('CREATE TABLE IF NOT EXISTS test_table (id integer primary key, data text, data_num integer)');
|
||||||
|
db.transaction(function(tx) {
|
||||||
|
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");
|
||||||
|
return db.executeSql("select count(id) as cnt from test_table;", [], function(res) {
|
||||||
|
console.log("rows.length: " + res.rows.length + " -- should be 1");
|
||||||
|
return console.log("rows[0].cnt: " + res.rows[0].cnt + " -- should be 1");
|
||||||
|
});
|
||||||
|
}, function(e) {
|
||||||
|
return console.log("ERROR: " + e.message);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
## Changes in tx.executeSql() success callback
|
||||||
|
|
||||||
|
var db;
|
||||||
|
db = new PGSQLitePlugin("my_sqlite_database.sqlite3");
|
||||||
|
db.executeSql('DROP TABLE IF EXISTS test_table');
|
||||||
|
db.executeSql('CREATE TABLE IF NOT EXISTS test_table (id integer primary key, data text, data_num integer)');
|
||||||
|
db.executeSql("INSERT INTO test_table (data, data_num) VALUES (?,?)", ["test", 100], function(res) {
|
||||||
|
console.log("insertId: " + res.insertId + " -- probably 1");
|
||||||
|
console.log("rowsAffected: " + res.rowsAffected + " -- should be 1");
|
||||||
|
db.transaction(function(tx) {
|
||||||
|
return 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");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Lawnchair Adapter Usage
|
||||||
|
=======================
|
||||||
|
|
||||||
|
Common adapter
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Please look at the `Lawnchair-adapter` tree that contains a common adapter, working for both Android and iOS, along with a test-www directory.
|
||||||
|
|
||||||
|
|
||||||
|
Legacy: iOS/iPhone only
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Include the following js files in your html:
|
||||||
|
|
||||||
|
- lawnchair.js (you provide)
|
||||||
|
- SQLitePlugin.js [pgsqlite_plugin.js in Legacy-PhoneGap-iPhone]
|
||||||
|
- Lawnchair-sqlitePlugin.js (must come after SQLitePlugin.js)
|
||||||
|
|
||||||
|
|
||||||
|
The `name` option will determine the sqlite filename. Optionally, you can change it using the `db` option.
|
||||||
|
|
||||||
|
In this example, you would be using/creating the database at: *Documents/kvstore.sqlite3* (all db's in SQLitePlugin are in the Documents folder)
|
||||||
|
|
||||||
|
kvstore = new Lawnchair { name: "kvstore", adapter: SQLitePlugin.lawnchair_adapter }, () ->
|
||||||
|
# do stuff
|
||||||
|
|
||||||
|
Using the `db` option you can create multiple stores in one sqlite file. (There will be one table per store.)
|
||||||
|
|
||||||
|
recipes = new Lawnchair {db: "cookbook", name: "recipes", ...}
|
||||||
|
ingredients = new Lawnchair {db: "cookbook", name: "ingredients", ...}
|
||||||
|
|
||||||
|
|
||||||
|
Legacy iOS Lawnchair test
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
*For cleanup this is to be removed*: In the lawnchair-test subdirectory of `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
|
||||||
|
-----------
|
||||||
|
|
||||||
|
### Other notes from @Joenoon - iOS batching:
|
||||||
|
|
||||||
|
I played with the idea of batching responses into larger sets of
|
||||||
|
writeJavascript on a timer, however there was only a barely noticeable
|
||||||
|
performance gain. So I took it out, not worth it. However there is a
|
||||||
|
massive performance gain by batching on the client-side to minimize
|
||||||
|
PhoneGap.exec calls using the transaction support.
|
45
iOS-legacy-phonegap/SQLitePlugin.h
Executable file
45
iOS-legacy-phonegap/SQLitePlugin.h
Executable 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
|
185
iOS-legacy-phonegap/SQLitePlugin.js
Executable file
185
iOS-legacy-phonegap/SQLitePlugin.js
Executable 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);
|
265
iOS-legacy-phonegap/SQLitePlugin.m
Executable file
265
iOS-legacy-phonegap/SQLitePlugin.m
Executable 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
|
Loading…
Reference in New Issue
Block a user