Sqlite-Plugin, DB-Copy plugin added und integriert
This commit is contained in:
@@ -37,6 +37,12 @@
|
||||
},
|
||||
"cordova-plugin-file-transfer": {
|
||||
"PACKAGE_NAME": "com.raataar.wolle_rosen_kaufen"
|
||||
},
|
||||
"cordova-sqlite-storage": {
|
||||
"PACKAGE_NAME": "com.raataar.wolle_rosen_kaufen"
|
||||
},
|
||||
"me.rahul.plugins.sqlDB": {
|
||||
"PACKAGE_NAME": "com.raataar.wolle_rosen_kaufen"
|
||||
}
|
||||
},
|
||||
"dependent_plugins": {}
|
||||
|
||||
35
plugins/cordova-sqlite-storage/AUTHORS.md
Normal file
35
plugins/cordova-sqlite-storage/AUTHORS.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Authors
|
||||
|
||||
## Common Javascript
|
||||
|
||||
- Extracted from DroidGap by @brodybits (Christopher J. Brody aka Chris Brody)
|
||||
- Fail-safe nested transaction support by @ef4 (Edward Faulkner)
|
||||
- Translated to Coffee-Script using js2coffee tool by @brodybits (Chris Brody)
|
||||
- API changes by @brodybits (Chris Brody)
|
||||
- Transaction timing fixes to support PouchDB by @nolanlawson
|
||||
|
||||
## Android version
|
||||
|
||||
- Extracted from DroidGap by @brodybits (Chris Brody)
|
||||
- Transaction batch processing of Android version by @marcucio
|
||||
- Maintained by @brodybits (Chris Brody)
|
||||
- Fixes to support old Android versions by @nolanlawson
|
||||
- Thanks to Mark Oppenheim <mark.oppenheim@mnetics.co.uk> for fixes to open/close callbacks and repeated open/close/delete operations
|
||||
|
||||
## iOS version
|
||||
|
||||
- Original authors: @davibe (Davide Bertola <dade@dadeb.it>) and @joenoon (Joe Noon <joenoon@gmail.com>)
|
||||
- Cordova 2.7+ port with background processing by @j3k0 (Jean-Christophe Hoelt <hoelt@fovea.cc>)
|
||||
- Maintained by @brodybits (Chris Brody)
|
||||
|
||||
## Windows (8.1) version
|
||||
|
||||
- SQLiteProxy.js by @vldmrrr (Vladimir Avdonin) and @brodybits (Chris Brody)
|
||||
- SQLite3-WinRT C++ classes and SQLite3JS (Javascript part) by @doo (doo GmbH)
|
||||
|
||||
## WP(7/8) version
|
||||
|
||||
- Original author: @marcucio (Mike Arcucio <mike@marcucio.com>)
|
||||
- Enhancements for background processing & improved transaction support by @Gillardo (Darren Gillard <darren.gillard81@gmail.com>)
|
||||
- DB threading and open/close/delete fixes by Mark Oppenheim <mark.oppenheim@mnetics.co.uk>
|
||||
- Uses csharp-sqlite library by Noah Hart and others (MIT license)
|
||||
125
plugins/cordova-sqlite-storage/CHANGES.md
Normal file
125
plugins/cordova-sqlite-storage/CHANGES.md
Normal file
@@ -0,0 +1,125 @@
|
||||
# Changes
|
||||
|
||||
## 0.7.15-pre
|
||||
|
||||
- All iOS operations are now using background processing (reported to resolve intermittent problems with cordova-ios@4.0.1)
|
||||
|
||||
## 0.7.14
|
||||
|
||||
- REGEXP support removed from this version branch
|
||||
- Remove src/android/libs/.gitignore (inadvertently added in 0.7.13)
|
||||
|
||||
## 0.7.13
|
||||
|
||||
- REGEXP support removed from this version branch
|
||||
- Rename Windows C++ Database close function to closedb to resolve conflict for Windows Store certification
|
||||
- Android version with sqlite `3.8.10.2` embedded (with error messages fixed)
|
||||
- Pre-populated database support removed from this version branch
|
||||
- Amazon Fire-OS support removed
|
||||
- Fix conversion warnings in iOS version
|
||||
|
||||
## 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
|
||||
|
||||
- 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 due to https://issues.apache.org/jira/browse/CB-9638
|
||||
|
||||
## 0.7.10
|
||||
|
||||
- Use Android-sqlite-connector instead of sqlite4java
|
||||
|
||||
## 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
|
||||
|
||||
- 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
|
||||
|
||||
- include build of sqlite4java for Android x86_64 and arm-64
|
||||
- clean publish to plugins.cordova.io
|
||||
|
||||
## 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
|
||||
|
||||
- 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
|
||||
|
||||
- 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
|
||||
|
||||
- insertId & rowsAffected implemented for Windows (8.1)
|
||||
- plugin id changed
|
||||
|
||||
## 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
|
||||
|
||||
- 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
|
||||
|
||||
- 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
|
||||
|
||||
- Workaround for Android db locking/closing issue
|
||||
- Fix double-precision REAL values in result (iOS version)
|
||||
- Fix query result truncation in case of NULL character (\0 or \u0000) (iOS version)
|
||||
- Convert array SQL parameters to string, according to match Web SQL spec
|
||||
- Fix closing of Android database
|
||||
- Some fixes for SQL API error handling to be consistent with Web SQL
|
||||
|
||||
## 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
|
||||
|
||||
- Fixed issue with multi-page apps on Android (due to problem when closing & re-opening app)
|
||||
|
||||
## 1.0.2
|
||||
|
||||
- Workaround for issue with multiple UPDATE statements WP(8) (#128)
|
||||
|
||||
## 1.0.1
|
||||
|
||||
- Support Cordova 3.3.0/3.4.0 to support Amazon-FireOS
|
||||
- Fixes for WP(8):
|
||||
- use one thread per db to solve open/close/delete issues
|
||||
- fix integer data binding
|
||||
- Fix open/close callbacks Android & WP(8)
|
||||
- Resolve issue with INSERT OR IGNORE (Android)
|
||||
|
||||
44
plugins/cordova-sqlite-storage/LICENSE.md
Normal file
44
plugins/cordova-sqlite-storage/LICENSE.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# Licenses
|
||||
|
||||
## Common Javascript
|
||||
|
||||
MIT or Apache 2.0
|
||||
|
||||
## Android version
|
||||
|
||||
MIT or Apache 2.0
|
||||
|
||||
## iOS version
|
||||
|
||||
MIT only
|
||||
|
||||
## Windows (8.1) version
|
||||
|
||||
MIT or Apache 2.0
|
||||
|
||||
### SQLite3-WinRT [used by Windows (8.1) version]
|
||||
|
||||
by @doo (doo GmbH)
|
||||
|
||||
## WP(7/8) version
|
||||
|
||||
MIT or Apache 2.0
|
||||
|
||||
## Class lib(s) used by WP(7/8) version
|
||||
|
||||
### C#-SQLite
|
||||
|
||||
MIT License
|
||||
|
||||
C#-SQLite is an independent reimplementation of the SQLite software library.
|
||||
SQLite® is a registered trademark of Hipp, Wyrick & Company, Inc.
|
||||
|
||||
The overall C#-SQLite package is distributed under the terms of the MIT License, given below. Some parts are distributed under more permissive licenses, see individual source files for details.
|
||||
|
||||
Copyright (c) 2009-2012 Noah Hart and others, see individual source files for details.
|
||||
|
||||
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.
|
||||
198
plugins/cordova-sqlite-storage/Lawnchair-adapter/Lawnchair-sqlitePlugin.js
vendored
Normal file
198
plugins/cordova-sqlite-storage/Lawnchair-adapter/Lawnchair-sqlitePlugin.js
vendored
Normal file
@@ -0,0 +1,198 @@
|
||||
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
|
||||
}
|
||||
//////
|
||||
}})())
|
||||
198
plugins/cordova-sqlite-storage/Lawnchair-adapter/test-www/Lawnchair-sqlitePlugin.js
vendored
Normal file
198
plugins/cordova-sqlite-storage/Lawnchair-adapter/test-www/Lawnchair-sqlitePlugin.js
vendored
Normal file
@@ -0,0 +1,198 @@
|
||||
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
|
||||
}
|
||||
//////
|
||||
}})())
|
||||
@@ -0,0 +1,76 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Lawnchair Spec</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no;" />
|
||||
<meta charset="utf-8">
|
||||
|
||||
<link rel="stylesheet" href="lib/qunit.css" type="text/css" media="screen">
|
||||
|
||||
<!-- If your application is targeting iOS BEFORE 4.0 you MUST put json2.js from http://www.JSON.org/json2.js into your www directory and include it here -->
|
||||
<script type="text/javascript" charset="utf-8" src="cordova-1.5.0.js"></script>
|
||||
|
||||
<script src="lib/qunit.js"></script>
|
||||
<script src="lib/json2.js"></script>
|
||||
<script src="lib/lawnchair.js"></script>
|
||||
<script src="SQLitePlugin.js"></script>
|
||||
<script src="Lawnchair-sqlitePlugin.js"></script>
|
||||
<script src="lawnchair-spec.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
// autostart seems to be a little eager
|
||||
QUnit.config.autostart = false
|
||||
|
||||
// kill qunit saves
|
||||
try{ sessionStorage.clear(); } catch(e){}
|
||||
|
||||
function onBodyLoad()
|
||||
{
|
||||
document.addEventListener("deviceready", startTests, false);
|
||||
}
|
||||
|
||||
var store;
|
||||
|
||||
function startTests() {
|
||||
|
||||
// allow restriction of adapter in URL (e.g., ?adapter=ie-userdata)
|
||||
var allowed = /adapter=([-\w]+)/.exec(window.location.href),
|
||||
adapters = Lawnchair.adapters,
|
||||
i;
|
||||
|
||||
if (allowed) {
|
||||
for (i = 0; i < adapters.length; ) {
|
||||
if (adapters[i].adapter == allowed[1]) {
|
||||
i++;
|
||||
} else {
|
||||
adapters.splice(i, 1)
|
||||
}
|
||||
}
|
||||
|
||||
if (adapters.length == 0) {
|
||||
alert("no such adapter: " + allowed[1]);
|
||||
}
|
||||
|
||||
if (!adapters[0].valid()) {
|
||||
alert("adapter " + allowed[1] + " is not valid in this environment");
|
||||
}
|
||||
}
|
||||
|
||||
// kickup the chair
|
||||
store = new Lawnchair({name:'tests'}, function(){
|
||||
QUnit.start()
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="onBodyLoad()">
|
||||
<h1 id="qunit-header">Lawnchair Spec</h1>
|
||||
<h2 id="qunit-banner"></h2>
|
||||
<h2 id="qunit-userAgent"></h2>
|
||||
<ol id="qunit-tests"></ol>
|
||||
</body>
|
||||
</html>
|
||||
431
plugins/cordova-sqlite-storage/Lawnchair-adapter/test-www/lawnchair-spec.js
vendored
Normal file
431
plugins/cordova-sqlite-storage/Lawnchair-adapter/test-www/lawnchair-spec.js
vendored
Normal file
@@ -0,0 +1,431 @@
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
//});
|
||||
});
|
||||
});
|
||||
482
plugins/cordova-sqlite-storage/Lawnchair-adapter/test-www/lib/json2.js
vendored
Normal file
482
plugins/cordova-sqlite-storage/Lawnchair-adapter/test-www/lib/json2.js
vendored
Normal file
@@ -0,0 +1,482 @@
|
||||
/*
|
||||
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');
|
||||
};
|
||||
}
|
||||
}());
|
||||
151
plugins/cordova-sqlite-storage/Lawnchair-adapter/test-www/lib/lawnchair.js
vendored
Normal file
151
plugins/cordova-sqlite-storage/Lawnchair-adapter/test-www/lib/lawnchair.js
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
// --
|
||||
};
|
||||
@@ -0,0 +1,225 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
1448
plugins/cordova-sqlite-storage/Lawnchair-adapter/test-www/lib/qunit.js
vendored
Normal file
1448
plugins/cordova-sqlite-storage/Lawnchair-adapter/test-www/lib/qunit.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
957
plugins/cordova-sqlite-storage/README.md
Normal file
957
plugins/cordova-sqlite-storage/README.md
Normal file
@@ -0,0 +1,957 @@
|
||||
# Cordova/PhoneGap sqlite storage adapter
|
||||
|
||||
Native interface to sqlite in a Cordova/PhoneGap plugin for Android, iOS, Windows "Universal" (8.1), and WP(7/8) with API similar to HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/).
|
||||
|
||||
License for Android, Windows "Universal" (8.1), and WP(7/8) versions: MIT or Apache 2.0
|
||||
|
||||
License for iOS version: MIT only
|
||||
|
||||
|Android CI (~~full~~ limited suite)|iOS CI (limited suite)|
|
||||
|-----------------------|----------------------|
|
||||
|[](https://circleci.com/gh/litehelpers/Cordova-sqlite-storage)|[](https://travis-ci.org/litehelpers/Cordova-sqlite-storage)|
|
||||
|
||||
## IMPORTANT: iCloud backup of SQLite database is NOT allowed
|
||||
|
||||
As documented in the "**A User’s iCloud Storage Is Limited**" section of [iCloudFundamentals in Mac Developer Library iCloud Design Guide](https://developer.apple.com/library/mac/documentation/General/Conceptual/iCloudDesignGuide/Chapters/iCloudFundametals.html) (near the beginning):
|
||||
|
||||
<blockquote>
|
||||
<ul>
|
||||
<li><b>DO</b> store the following in iCloud:
|
||||
<ul>
|
||||
<li>[<i>other items omitted</i>]</li>
|
||||
<li>Change log files for a SQLite database (a SQLite database’s store file must never be stored in iCloud)</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><b>DO NOT</b> store the following in iCloud:
|
||||
<ul>
|
||||
<li>[<i>items omitted</i>]</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
- <cite><a href="https://developer.apple.com/library/mac/documentation/General/Conceptual/iCloudDesignGuide/Chapters/iCloudFundametals.html">iCloudFundamentals in Mac Developer Library iCloud Design Guide</a>
|
||||
</blockquote>
|
||||
|
||||
### More information about iCloud backup
|
||||
|
||||
There are two ways iCloud backup is configured:
|
||||
- For each app, iCloud backup is configured in `config.xml` **and is enabled by default** (which I think is wrong) as documented at: https://cordova.apache.org/docs/en/5.1.1/guide/platforms/ios/config.html
|
||||
- In this plugin, the database is stored in the `Documents` subdirectory by default, which is backed up to iCloud. You can use the `location` option in `sqlitePlugin.openDatabase()` to store the database in a subdirectory that is *NOT* backed up to iCloud.
|
||||
|
||||
Unless you want your app to use iCloud backup for some reason, it is recommended to turn it off as documented in: https://cordova.apache.org/docs/en/5.1.1/guide/platforms/ios/config.html
|
||||
|
||||
I raised [Cordova bug CB-9830](https://issues.apache.org/jira/browse/CB-9830) to disable iCloud backup by default in `config.xml`.
|
||||
|
||||
## Available for hire
|
||||
|
||||
The primary author and maintainer [@brodybits (Chris Brody)](https://github.com/brodybits) is available for contracting assignments. Part-time contracting assignments would really help keep this project alive. [@brodybits (Chris Brody)](https://github.com/brodybits) can be contacted at: <brodybits@litehelpers.net>
|
||||
|
||||
LinkedIn: https://www.linkedin.com/in/chrisbrody
|
||||
|
||||
PhoneGap developer page: http://people.phonegap.com/developer/chris-brody
|
||||
|
||||
Other projects:
|
||||
- [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 - *under development and currently extremely limited*
|
||||
- [brodybits / java-node](https://github.com/brodybits/java-node) - two-way binding interface between Java and Node.js (Javascript) - *under development and currently extremely limited*
|
||||
|
||||
## Status
|
||||
|
||||
- The following features are moved to [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) and removed from this project:
|
||||
- REGEXP support
|
||||
- Pre-populated database
|
||||
- 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 ref: [cordova/cordova-discuss#32 (comment)](https://github.com/cordova/cordova-discuss/issues/32#issuecomment-167021676)
|
||||
- Windows "Universal" for Windows 8.0/8.1(+) and Windows Phone 8.1(+) version is in an alpha state with sqlite `3.8.10.2` embedded:
|
||||
- Issue with UNICODE `\u0000` character (same as `\0`)
|
||||
- No background processing (for future consideration)
|
||||
- You *may* encounter issues with Cordova CLI due to [CB-8866](https://issues.apache.org/jira/browse/CB-8866). *Old workaround:* you can install using [litehelpers / cordova-windows-nufix](https://github.com/litehelpers/cordova-windows-nufix) and `plugman` as described below.
|
||||
- In addition, problems with the Windows "Universal" version have been reported in case of a Cordova project using a Visual Studio template/extension instead of Cordova/PhoneGap CLI or `plugman`
|
||||
- Not tested with a Windows 10 (or Windows Phone 10) target; Windows 10 build is not expected to work with Windows Phone
|
||||
- FTS3, FTS4, and R-Tree support is tested working OK in this version (for target platforms Android/iOS/Windows "Universal")
|
||||
- Status for the other target platforms:
|
||||
- Android: now using [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector) (with sqlite `3.8.10.2` embedded in [Android-sqlite-native-driver](https://github.com/liteglue/Android-sqlite-native-driver)), with support for FTS3/FTS4 and R-Tree
|
||||
- iOS: sqlite `3.8.10.2` embedded
|
||||
- WP7: possible to build from C#, as specified by `plugin.xml` - **NOT TESTED**
|
||||
- WP8: performance/stability issues have been reported with the CSharp-SQLite library. Windows ("Universal") platform is recommended for the future. FTS3/FTS4/R-Tree are NOT supported for WP(7/8).
|
||||
- Android is supported back to SDK 10 (a.k.a. Gingerbread, Android 2.3.3); support for older versions is available upon request.
|
||||
- API to open the database may be changed somewhat to be more streamlined. Transaction and single-statement query API will NOT be changed.
|
||||
- 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) or [litehelpers / cordova-sqlite-workers-evfree](https://github.com/litehelpers/cordova-sqlite-workers-evfree)
|
||||
|
||||
## Announcements
|
||||
|
||||
- All iOS operations are now using background processing (reported to resolve intermittent problems with cordova-ios@4.0.1)
|
||||
- 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)
|
||||
- A version with pre-populated database support added for Windows "Universal" and REGEXP support added for Android is available at: [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext)
|
||||
- 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
|
||||
- Android version is now using the lightweight [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector) by default configuration (may be changed as described below)
|
||||
- iOS version is now fixed to override the correct pluginInitialize method and should work with recent versions of iOS
|
||||
- Project has been renamed to prevent confusion with [davibe / Phonegap-SQLitePlugin](https://github.com/davibe/Phonegap-SQLitePlugin) (original version for iOS, with a different API)
|
||||
- New project location (should redirect)
|
||||
- The test suite is completely ported to Jasmine (2.2.0) and was used to verify the functionality of the new Windows version
|
||||
- [SQLCipher](https://www.zetetic.net/sqlcipher/) for Windows (8.1) in addition to Android & iOS is now supported by [litehelpers / Cordova-sqlcipher-adapter](https://github.com/litehelpers/Cordova-sqlcipher-adapter)
|
||||
- New `openDatabase` and `deleteDatabase` `location` option to select database location (iOS *only*) and disable iCloud backup
|
||||
- Fixes to work with PouchDB by [@nolanlawson](https://github.com/nolanlawson)
|
||||
|
||||
## Highlights
|
||||
|
||||
- Drop-in replacement for HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/), the only change should be `window.openDatabase()` --> `sqlitePlugin.openDatabase()`
|
||||
- Failure-safe nested transactions with batch processing optimizations
|
||||
- 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).
|
||||
- No 5MB maximum, more information at: http://www.sqlite.org/limits.html
|
||||
|
||||
## Some apps using this plugin
|
||||
|
||||
- [Traiforks Mountain Bike Trail Map App](http://www.trailforks.com/apps/map/) with a couple of nice videos at: <http://www.pinkbike.com/news/trailforks-app-released.html>
|
||||
- [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
|
||||
- [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
|
||||
- As described below, auto-vacuum is NOT enabled by default.
|
||||
- INSERT statement that affects multiple rows (due to SELECT cause or using TRIGGER(s), for example) does not report proper rowsAffected on Android in case [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector) is disabled (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-enterprise-free](https://github.com/litehelpers/Cordova-sqlite-enterprise-free) (available with a different licensing scheme)
|
||||
- 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, Windows, and WP(7/8) (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
|
||||
- In-memory database `db=window.sqlitePlugin.openDatabase({name: ":memory:"})` is currently not supported.
|
||||
- Close 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).
|
||||
- DROP table does not actually delete it in WP(7/8) version, due to limitations of CSharp-SQLite.
|
||||
- On WP(7/8), very large integer values will be stored as negative numbers (see [#195](https://github.com/litehelpers/Cordova-sqlite-storage/issues/195) - fix is available and needs testing)
|
||||
- Problems reported with PhoneGap Build in the past:
|
||||
- PhoneGap Build Hydration.
|
||||
- For some reason, PhoneGap Build may fail to build the iOS version unless the name of the app starts with an uppercase and contains no spaces (see [#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)).
|
||||
|
||||
## Other limitations
|
||||
|
||||
- The db version, display name, and size parameter values are not supported and will be ignored.
|
||||
- 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).
|
||||
- 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 line separator (`\u2028`) and paragraph separator (`\u2029`) 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 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)
|
||||
- UNICODE characters not working in WP(7/8) version
|
||||
- Blob type is currently not supported and known to be broken on multiple platforms.
|
||||
- UNICODE `\u0000` (same as `\0`) character not working in Android (default implementation), Windows "Universal" (8.1/XX) or WP(7/8) versions
|
||||
- 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 another database file is not supported (due to path specifications, which work differently depending on the target platform)
|
||||
- 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). A couple of things you can try:
|
||||
- Install Crosswalk as a plugin instead of using Crosswalk to create the project.
|
||||
- Use `androidDatabaseImplementation: 2` in the openDatabase options as described below.
|
||||
- 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). 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
|
||||
|
||||
- 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))
|
||||
- SAVEPOINT(s)
|
||||
- On WP(7/8), very large integer values will be stored as negative numbers (see [#195](https://github.com/litehelpers/Cordova-sqlite-storage/issues/195) - fix is available for very large INTEGER values on WP(7/8)
|
||||
- FTS3/FTS4 is not tested or supported for WP(7/8)
|
||||
- R-Tree is not tested for Android (in case [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector) is disabled), or WP(7/8)
|
||||
- UNICODE characters not fully tested
|
||||
- Use with TRIGGER(s), JOIN and ORDER BY RANDOM
|
||||
- Integration with JXCore for Cordova (must be built without sqlite(3) built-in)
|
||||
- Delete an open database inside a statement or transaction callback.
|
||||
|
||||
## 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 your 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 shema version number. It is possible to add it later. The recommended schema update procedure is described below.
|
||||
|
||||
## Common pitfall(s)
|
||||
|
||||
- It is NOT allowed to execute sql statements on a transaction following the HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/), as described below.
|
||||
- It is possible to make a Windows Phone 8.1 project using either the `windows` platform or the `wp8` platform. The `windows` platform is highly recommended over `wp8` whenever possible. Also, some plugins only support `windows` and some plugins support only `wp8`.
|
||||
- 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.
|
||||
|
||||
## 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 controller/factory/service callbacks may be triggered before the 'deviceready' event is fired
|
||||
- As discussed in [#355](https://github.com/litehelpers/Cordova-sqlite-storage/issues/355), it may be necessary to install ionic-plugin-keyboard
|
||||
|
||||
## Major TODOs
|
||||
|
||||
- Integrate with IndexedDBShim and some other libraries such as Sequelize, Squel.js, WebSqlSync, Persistence.js, Knex, etc.
|
||||
|
||||
## Alternatives
|
||||
|
||||
### Other versions
|
||||
|
||||
- [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-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 support *added* for Android and pre-populated database support *added* for Windows "Universal" as well
|
||||
- [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)
|
||||
- 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
|
||||
- Simpler sqlite plugin with a simpler API: [samikrc / CordovaSQLite](https://github.com/samikrc/CordovaSQLite)
|
||||
- [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
|
||||
- [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
|
||||
|
||||
- [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)
|
||||
- [Realm.io](https://realm.io/)
|
||||
|
||||
# Usage
|
||||
|
||||
The idea is to emulate the HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/) as closely as possible. The only major change is to use `window.sqlitePlugin.openDatabase()` (or `sqlitePlugin.openDatabase()`) instead of `window.openDatabase()`. If you see any other major change please report it, it is probably a bug.
|
||||
|
||||
**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.
|
||||
|
||||
## Opening a database
|
||||
|
||||
There are two options to open a database access object:
|
||||
- **Recommended:** `var db = window.sqlitePlugin.openDatabase({name: "my.db", location: 1}, successcb, errorcb);`
|
||||
- **Classical:** `var db = window.sqlitePlugin.openDatabase("myDatabase.db", "1.0", "Demo", -1);`
|
||||
|
||||
The `location` option is used to select the database subdirectory location (iOS *only*) with the following choices:
|
||||
- `0` (default): `Documents` - visible to iTunes and backed up by iCloud
|
||||
- `1`: `Library` - backed up by iCloud, *NOT* visible to iTunes
|
||||
- `2`: `Library/LocalDatabase` - *NOT* visible to iTunes and *NOT* backed up by iCloud
|
||||
|
||||
**IMPORTANT:** Please wait for the 'deviceready' event, as in the following example:
|
||||
|
||||
```js
|
||||
// Wait for Cordova to load
|
||||
document.addEventListener('deviceready', onDeviceReady, false);
|
||||
|
||||
// Cordova is ready
|
||||
function onDeviceReady() {
|
||||
var db = window.sqlitePlugin.openDatabase({name: "my.db"});
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
The successcb and errorcb callback parameters are optional but can be extremely helpful in case anything goes wrong. For example:
|
||||
|
||||
```js
|
||||
window.sqlitePlugin.openDatabase({name: "my.db"}, function(db) {
|
||||
db.transaction(function(tx) {
|
||||
// ...
|
||||
}, function(err) {
|
||||
console.log('Open database ERROR: ' + JSON.stringify(err));
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
**OTHER NOTES:**
|
||||
- The database file name should include the extension, if desired.
|
||||
- It is possible to open multiple database access objects for the same database.
|
||||
- The database access object can be closed as described below.
|
||||
|
||||
### Android sqlite implementation
|
||||
|
||||
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});
|
||||
```
|
||||
|
||||
### Workaround for Android db locking issue
|
||||
|
||||
[Issue #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:
|
||||
- (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
|
||||
|
||||
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", androidDatabaseImplementation: 2, androidLockWorkaround: 1});
|
||||
```
|
||||
|
||||
This option is ignored if `androidDatabaseImplementation: 2` is not specified.
|
||||
|
||||
**IMPORTANT NOTE:** This workaround is *only* applied when using `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
|
||||
- Standard asynchronous transactions
|
||||
|
||||
### Single-statement transactions
|
||||
|
||||
Sample:
|
||||
|
||||
```Javascript
|
||||
db.executeSql("SELECT LENGTH('tenletters') AS stringlength", [], function (res) {
|
||||
console.log('got stringlength: ' + res.rows.item(0).stringlength);
|
||||
}, function(error) {
|
||||
console.log('SELECT error: ' + error.message);
|
||||
});
|
||||
```
|
||||
|
||||
## 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 very simple example from the test suite:
|
||||
|
||||
```Javascript
|
||||
db.transaction(function(tx) {
|
||||
tx.executeSql("SELECT UPPER('Some US-ASCII text') AS uppertext", [], function(tx, res) {
|
||||
console.log("res.rows.item(0).uppertext: " + res.rows.item(0).uppertext);
|
||||
}, function(error) {
|
||||
console.log('SELECT error: ' + error.message);
|
||||
});
|
||||
}, function(error) {
|
||||
console.log('transaction error: ' + error.message);
|
||||
}, function() {
|
||||
console.log('transaction ok');
|
||||
});
|
||||
```
|
||||
|
||||
In case of a read-only transaction, it is possible to use `readTransaction` which will not use BEGIN, COMMIT, or ROLLBACK:
|
||||
|
||||
```Javascript
|
||||
db.readTransaction(function(tx) {
|
||||
tx.executeSql("SELECT UPPER('Some US-ASCII text') AS uppertext", [], function(tx, res) {
|
||||
console.log("res.rows.item(0).uppertext: " + res.rows.item(0).uppertext);
|
||||
}, function(error) {
|
||||
console.log('SELECT error: ' + error.message);
|
||||
});
|
||||
}, function(error) {
|
||||
console.log('transaction error: ' + error.message);
|
||||
}, function() {
|
||||
console.log('transaction ok');
|
||||
});
|
||||
```
|
||||
|
||||
**WARNING:** It is NOT allowed to execute sql statements on a transaction after it has finished. Here is an example from my [Populating Cordova SQLite storage with the JQuery API post](http://www.brodybits.com/cordova/sqlite/api/jquery/2015/10/26/populating-cordova-sqlite-storage-with-the-jquery-api.html):
|
||||
|
||||
```Javascript
|
||||
// BROKEN SAMPLE:
|
||||
var db = window.sqlitePlugin.openDatabase({name: "test.db"});
|
||||
db.executeSql("DROP TABLE IF EXISTS tt");
|
||||
db.executeSql("CREATE TABLE tt (data)");
|
||||
|
||||
db.transaction(function(tx) {
|
||||
$.ajax({
|
||||
url: 'https://api.github.com/users/litehelpers/repos',
|
||||
dataType: 'json',
|
||||
success: function(res) {
|
||||
console.log('Got AJAX response: ' + JSON.stringify(res));
|
||||
$.each(res, function(i, item) {
|
||||
console.log('REPO NAME: ' + item.name);
|
||||
tx.executeSql("INSERT INTO tt values (?)", JSON.stringify(item.name));
|
||||
});
|
||||
}
|
||||
});
|
||||
}, function(e) {
|
||||
console.log('Transaction error: ' + e.message);
|
||||
}, function() {
|
||||
// Check results:
|
||||
db.executeSql('SELECT COUNT(*) FROM tt', [], function(res) {
|
||||
console.log('Check SELECT result: ' + JSON.stringify(res.rows.item(0)));
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
You can find more details and a step-by-step description how to do this right in the [Populating Cordova SQLite storage with the JQuery API post](http://www.brodybits.com/cordova/sqlite/api/jquery/2015/10/26/populating-cordova-sqlite-storage-with-the-jquery-api.html):
|
||||
|
||||
## Background processing
|
||||
|
||||
The threading model depends on which version is used:
|
||||
- For Android and WP(7/8), one background thread per db;
|
||||
- for iOS, background processing using a very limited thread pool (only one thread working at a time);
|
||||
- for Windows "Universal" (8.1), no background processing (for future consideration).
|
||||
|
||||
# Sample with PRAGMA feature
|
||||
|
||||
This is a pretty strong test: first we create a table and add a single entry, then query the count to check if the item was inserted as expected. Note that a new transaction is created in the middle of the first callback.
|
||||
|
||||
```js
|
||||
// Wait for Cordova to load
|
||||
document.addEventListener('deviceready', onDeviceReady, false);
|
||||
|
||||
// Cordova is ready
|
||||
function onDeviceReady() {
|
||||
var db = window.sqlitePlugin.openDatabase({name: "my.db"});
|
||||
|
||||
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)');
|
||||
|
||||
// demonstrate PRAGMA:
|
||||
db.executeSql("pragma table_info (test_table);", [], function(res) {
|
||||
console.log("PRAGMA res: " + JSON.stringify(res));
|
||||
});
|
||||
|
||||
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");
|
||||
|
||||
db.transaction(function(tx) {
|
||||
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");
|
||||
});
|
||||
});
|
||||
|
||||
}, function(e) {
|
||||
console.log("ERROR: " + e.message);
|
||||
});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**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.
|
||||
|
||||
```js
|
||||
// Wait for Cordova to load
|
||||
document.addEventListener('deviceready', onDeviceReady, false);
|
||||
|
||||
// Cordova is ready
|
||||
function onDeviceReady() {
|
||||
var db = window.sqlitePlugin.openDatabase("Database", "1.0", "Demo", -1);
|
||||
|
||||
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)');
|
||||
|
||||
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("res.rows.length: " + res.rows.length + " -- should be 1");
|
||||
console.log("res.rows.item(0).cnt: " + res.rows.item(0).cnt + " -- should be 1");
|
||||
});
|
||||
|
||||
}, function(e) {
|
||||
console.log("ERROR: " + e.message);
|
||||
});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
This case will also works with Safari (WebKit), assuming you replace `window.sqlitePlugin.openDatabase` with `window.openDatabase`.
|
||||
|
||||
## Close a database object
|
||||
|
||||
```js
|
||||
db.close(successcb, errorcb);
|
||||
```
|
||||
|
||||
It is OK to close the database within a transaction callback but *NOT* within a statement callback. The following example is OK:
|
||||
|
||||
```Javascript
|
||||
db.transaction(function(tx) {
|
||||
tx.executeSql("SELECT LENGTH('tenletters') AS stringlength", [], function(tx, res) {
|
||||
console.log('got stringlength: ' + res.rows.item(0).stringlength);
|
||||
});
|
||||
}, function(error) {
|
||||
// OK to close here:
|
||||
console.log('transaction error: ' + error.message);
|
||||
db.close();
|
||||
}, function() {
|
||||
// OK to close here:
|
||||
console.log('transaction ok');
|
||||
db.close(function() {
|
||||
console.log('database is closed ok');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
The following example is NOT OK:
|
||||
|
||||
```Javascript
|
||||
// BROKEN:
|
||||
db.transaction(function(tx) {
|
||||
tx.executeSql("SELECT LENGTH('tenletters') AS stringlength", [], function(tx, res) {
|
||||
console.log('got stringlength: ' + res.rows.item(0).stringlength);
|
||||
// BROKEN - this will trigger the error callback:
|
||||
db.close(function() {
|
||||
console.log('database is closed ok');
|
||||
}, function(error) {
|
||||
console.log('ERROR closing database');
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
**BUG 1:** It is currently NOT possible to close a database in a `db.executeSql` callback. For example:
|
||||
|
||||
```Javascript
|
||||
// BROKEN DUE TO BUG:
|
||||
db.executeSql("SELECT LENGTH('tenletters') AS stringlength", [], function (res) {
|
||||
var stringlength = res.rows.item(0).stringlength;
|
||||
console.log('got stringlength: ' + res.rows.item(0).stringlength);
|
||||
|
||||
// BROKEN - this will trigger the error callback DUE TO BUG:
|
||||
db.close(function() {
|
||||
console.log('database is closed ok');
|
||||
}, function(error) {
|
||||
console.log('ERROR closing database');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
**BUG 2:** If multiple database access objects are opened for the same database and one database access object is closed, the database is no longer available for the other database access objects. Possible workarounds:
|
||||
- It is still possible to open one or more new database access objects on a database that has been closed.
|
||||
- It *should* be OK not to explicitly close a database handle since database transactions are [ACID](https://en.wikipedia.org/wiki/ACID) compliant and the app's memory resources are cleaned up by the system upon termination.
|
||||
|
||||
## Delete a database
|
||||
|
||||
```js
|
||||
window.sqlitePlugin.deleteDatabase({name: "my.db", location: 1}, successcb, errorcb);
|
||||
```
|
||||
|
||||
`location` as described above for `openDatabase` (iOS *only*)
|
||||
|
||||
**NOTE:** not implemented for Windows "Universal" (8.1) version.
|
||||
|
||||
# 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)
|
||||
- If your database needs to be upgraded, do the following *within a single transaction* to be failure-safe:
|
||||
- Create your database schema version table (single row single column) if it does not exist (you can check the `sqlite_master` table as described at: http://stackoverflow.com/questions/1601151/how-do-i-check-in-sqlite-whether-a-table-exists)
|
||||
- Add any missing columns and tables, and apply any other changes necessary
|
||||
|
||||
**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/
|
||||
|
||||
Documentation at: http://ngcordova.com/docs/plugins/sqlite/
|
||||
|
||||
# Installing
|
||||
|
||||
## Windows "Universal" target platform
|
||||
|
||||
**IMPORTANT:** There are issues supporing certain Windows target platforms due to [CB-8866](https://issues.apache.org/jira/browse/CB-8866):
|
||||
- When using Visual Studio, the default target ("Mixed Platforms") will not work
|
||||
- Problems have been with the Windows "Universal" version case of a Cordova project using a Visual Studio template/extension instead of Cordova/PhoneGap CLI or `plugman`
|
||||
|
||||
*Old workaround:* As an alternative, which will support the ("Mixed Platforms") target, you can use `plugman` instead with [litehelpers / cordova-windows-nufix](https://github.com/litehelpers/cordova-windows-nufix), as described here.
|
||||
|
||||
### Old workaround - Using plugman to support "Mixed Platforms"
|
||||
|
||||
- make sure you have the latest version of `plugman` installed: `npm install -g plugman`
|
||||
- Download the [cordova-windows-nufix 3.9.0-nufixpre-01 zipball](https://github.com/litehelpers/cordova-windows-nufix/archive/3.9.0-nufixpre-01.zip) (or you can clone [litehelpers / cordova-windows-nufix](https://github.com/litehelpers/cordova-windows-nufix) instead)
|
||||
- Create your Windows "Universal" (8.1) project using [litehelpers / cordova-windows-nufix](https://github.com/litehelpers/cordova-windows-nufix):
|
||||
- `path.to.cordova-windows-nufix/bin/create.bat your_app_path your.app.id YourAppName`
|
||||
- `cd your_app_path` and install plugin using `plugman`:
|
||||
- `plugman install --platform windows --project . --plugin cordova-sqlite-storage`
|
||||
- Put your sql program in your project `www` (don't forget to reference it from `www\index.html` and wait for `deviceready` event)
|
||||
|
||||
Then your project in `CordovaApp.sln` should work with "Mixed Platforms" on both Windows 8.1 and Windows Phone 8.1.
|
||||
|
||||
## Easy install with Cordova CLI tool
|
||||
|
||||
npm install -g cordova # if you don't have cordova
|
||||
cordova create MyProjectFolder com.my.project MyProject && cd MyProjectFolder # if you are just starting
|
||||
cordova plugin add cordova-sqlite-storage
|
||||
|
||||
You can find more details at [this writeup](http://iphonedevlog.wordpress.com/2014/04/07/installing-chris-brodys-sqlite-database-with-cordova-cli-android/).
|
||||
|
||||
**WARNING:** as stated above, there are issues using Cordova CLI with Windows ("Universal") target platform due to [CB-8866](https://issues.apache.org/jira/browse/CB-8866). It is recommended to use `plugman` instead, as described above.
|
||||
|
||||
**IMPORTANT:** sometimes you have to update the version for a platform before you can build, like: `cordova prepare ios`
|
||||
|
||||
**NOTE:** If you cannot build for a platform after `cordova prepare`, you may have to remove the platform and add it again, such as:
|
||||
|
||||
cordova platform rm ios
|
||||
cordova platform add ios
|
||||
|
||||
**EXTRA NOTE:** You can use https://github.com/litehelpers/Cordova-sqlite-storage instead of `cordova-sqlite-storage` to get the latest version directly from github.
|
||||
|
||||
## Easy install with plugman tool
|
||||
|
||||
```shell
|
||||
plugman install --platform MYPLATFORM --project path.to.my.project.folder --plugin cordova-sqlite-storage
|
||||
```
|
||||
|
||||
where MYPLATFORM is `android`, `ios`, `windows`, or `wp8`.
|
||||
|
||||
A posting how to get started developing on Windows host without the Cordova CLI tool (for Android target only) is available [here](http://brodybits.blogspot.com/2015/03/trying-cordova-for-android-on-windows-without-cordova-cli.html).
|
||||
|
||||
**EXTRA NOTE:** You can use https://github.com/litehelpers/Cordova-sqlite-storage instead of `cordova-sqlite-storage` to get the latest version directly from github.
|
||||
|
||||
## 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:
|
||||
- `external` - placeholder for external dependencies - *not required in this version*
|
||||
- `android` - Java plugin code for Android (along with [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector) and [Android-sqlite-native-driver](https://github.com/liteglue/Android-sqlite-native-driver) library jars)
|
||||
- `ios` - Objective-C plugin code for iOS;
|
||||
- `windows` - Javascript proxy code and SQLite3-WinRT project for Windows "Universal" (8.1);
|
||||
- `wp` - C-sharp code for WP(7/8)
|
||||
- `spec`: test suite using Jasmine (2.2.0), ported from QUnit `test-www` test suite, working on all platforms
|
||||
- `tests`: very simple Jasmine test suite that is run on Circle CI (Android version) and Travis CI (iOS version)
|
||||
- `Lawnchair-adapter`: Lawnchair adaptor, based on the version from the Lawnchair repository, with the basic Lawnchair test suite in `test-www` subdirectory
|
||||
|
||||
## Manual installation - Android version
|
||||
|
||||
These installation instructions are based on the Android example project from Cordova/PhoneGap 2.7.0, using the `lib/android/example` subdirectory from the PhoneGap 2.7 zipball.
|
||||
|
||||
- Install `SQLitePlugin.js` from `www` into `assets/www`
|
||||
- Install `SQLitePlugin.java` from `src/android/io/liteglue` into `src/io/liteglue` subdirectory
|
||||
- Install the `libs` subtree with 2 jars from `src/android/libs` into your Android project
|
||||
- Add the plugin element `<plugin name="SQLitePlugin" value="io.liteglue.SQLitePlugin"/>` to `res/xml/config.xml`
|
||||
|
||||
Sample change to `res/xml/config.xml` for Cordova/PhoneGap 2.x:
|
||||
|
||||
```diff
|
||||
--- config.xml.orig 2015-04-14 14:03:05.000000000 +0200
|
||||
+++ res/xml/config.xml 2015-04-14 14:08:08.000000000 +0200
|
||||
@@ -36,6 +36,7 @@
|
||||
<preference name="useBrowserHistory" value="true" />
|
||||
<preference name="exit-on-suspend" value="false" />
|
||||
<plugins>
|
||||
+ <plugin name="SQLitePlugin" value="io.liteglue.SQLitePlugin"/>
|
||||
<plugin name="App" value="org.apache.cordova.App"/>
|
||||
<plugin name="Geolocation" value="org.apache.cordova.GeoBroker"/>
|
||||
<plugin name="Device" value="org.apache.cordova.Device"/>
|
||||
```
|
||||
|
||||
Before building for the first time, you have to update the project with the desired version of the Android SDK with a command like:
|
||||
|
||||
android update project --path $(pwd) --target android-19
|
||||
|
||||
(assuming Android SDK 19, use the correct desired Android SDK number here)
|
||||
|
||||
**NOTE:** using this plugin on Cordova pre-3.0 requires the following changes to `SQLiteAndroidDatabase.java` and `SQLitePlugin.java`:
|
||||
|
||||
```diff
|
||||
--- Cordova-sqlite-storage/src/android/io/liteglue/SQLiteAndroidDatabase.java 2015-04-14 14:05:01.000000000 +0200
|
||||
+++ src/io/liteglue/SQLiteAndroidDatabase.java 2015-04-14 14:15:23.000000000 +0200
|
||||
@@ -24,7 +24,7 @@
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
-import org.apache.cordova.CallbackContext;
|
||||
+import org.apache.cordova.api.CallbackContext;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
diff -u Cordova-sqlite-storage/src/android/io/liteglue/SQLitePlugin.java src/io/liteglue/SQLitePlugin.java
|
||||
--- Cordova-sqlite-storage/src/android/io/liteglue/SQLitePlugin.java 2015-04-14 14:05:01.000000000 +0200
|
||||
+++ src/io/liteglue/SQLitePlugin.java 2015-04-14 14:10:44.000000000 +0200
|
||||
@@ -22,8 +22,8 @@
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
-import org.apache.cordova.CallbackContext;
|
||||
-import org.apache.cordova.CordovaPlugin;
|
||||
+import org.apache.cordova.api.CallbackContext;
|
||||
+import org.apache.cordova.api.CordovaPlugin;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
```
|
||||
|
||||
## Manual installation - iOS version
|
||||
|
||||
### 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.
|
||||
|
||||
### SQLite Plugin
|
||||
|
||||
- Copy `SQLitePlugin.[hm]` from `src/ios` into your project Plugins folder and add them in XCode (I always just have "Create references" as the option selected).
|
||||
- Copy `SQLitePlugin.js` from `www` into your project `www` folder
|
||||
- Enable the SQLitePlugin in `config.xml`
|
||||
|
||||
Sample change to `config.xml` for Cordova/PhoneGap 2.x:
|
||||
|
||||
```diff
|
||||
--- config.xml.old 2013-05-17 13:18:39.000000000 +0200
|
||||
+++ config.xml 2013-05-17 13:18:49.000000000 +0200
|
||||
@@ -39,6 +39,7 @@
|
||||
<content src="index.html" />
|
||||
|
||||
<plugins>
|
||||
+ <plugin name="SQLitePlugin" value="SQLitePlugin" />
|
||||
<plugin name="Device" value="CDVDevice" />
|
||||
<plugin name="Logger" value="CDVLogger" />
|
||||
<plugin name="Compass" value="CDVLocation" />
|
||||
```
|
||||
|
||||
## Manual installation - Windows "Universal" (8.1) version
|
||||
|
||||
Described above.
|
||||
|
||||
## Manual installation - WP(7/8) version
|
||||
|
||||
TBD
|
||||
|
||||
## Quick installation test
|
||||
|
||||
Assuming your app has a recent template as used by the Cordova create script, add the following code to the `onDeviceReady` function, after `app.receivedEvent('deviceready');`:
|
||||
|
||||
```Javascript
|
||||
window.sqlitePlugin.openDatabase({ name: 'hello-world.db' }, function (db) {
|
||||
db.executeSql("select length('tenletters') as stringlength", [], function (res) {
|
||||
var stringlength = res.rows.item(0).stringlength;
|
||||
console.log('got stringlength: ' + stringlength);
|
||||
document.getElementById('deviceready').querySelector('.received').innerHTML = 'stringlength: ' + stringlength;
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Old installation test
|
||||
|
||||
Make a change like this to index.html (or use the sample code) verify proper installation:
|
||||
|
||||
```diff
|
||||
--- index.html.old 2012-08-04 14:40:07.000000000 +0200
|
||||
+++ assets/www/index.html 2012-08-04 14:36:05.000000000 +0200
|
||||
@@ -24,7 +24,35 @@
|
||||
<title>PhoneGap</title>
|
||||
<link rel="stylesheet" href="master.css" type="text/css" media="screen" title="no title">
|
||||
<script type="text/javascript" charset="utf-8" src="cordova-2.0.0.js"></script>
|
||||
- <script type="text/javascript" charset="utf-8" src="main.js"></script>
|
||||
+ <script type="text/javascript" charset="utf-8" src="SQLitePlugin.js"></script>
|
||||
+
|
||||
+
|
||||
+ <script type="text/javascript" charset="utf-8">
|
||||
+ document.addEventListener("deviceready", onDeviceReady, false);
|
||||
+ function onDeviceReady() {
|
||||
+ var db = window.sqlitePlugin.openDatabase("Database", "1.0", "Demo", -1);
|
||||
+
|
||||
+ 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)');
|
||||
+
|
||||
+ tx.executeSql("INSERT INTO test_table (data, data_num) VALUES (?,?)", ["test", 100], function(tx, res) {
|
||||
+ console.log("insertId: " + res.insertId + " -- probably 1"); // check #18/#38 is fixed
|
||||
+ alert("insertId: " + res.insertId + " -- should be valid");
|
||||
+
|
||||
+ db.transaction(function(tx) {
|
||||
+ tx.executeSql("SELECT data_num from test_table;", [], function(tx, res) {
|
||||
+ console.log("res.rows.length: " + res.rows.length + " -- should be 1");
|
||||
+ alert("res.rows.item(0).data_num: " + res.rows.item(0).data_num + " -- should be 100");
|
||||
+ });
|
||||
+ });
|
||||
+
|
||||
+ }, function(e) {
|
||||
+ console.log("ERROR: " + e.message);
|
||||
+ });
|
||||
+ });
|
||||
+ }
|
||||
+ </script>
|
||||
|
||||
</head>
|
||||
<body onload="init();" id="stage" class="theme">
|
||||
```
|
||||
|
||||
# Support
|
||||
|
||||
## Policy
|
||||
|
||||
As described above, free support will be provided on a very limited basis due to some changing priorities. In addition, free support is only available in public forums. Please follow the steps below to be sure you have done your best before requesting help.
|
||||
|
||||
Commercial support is available by contacting: <info@litehelpers.net>
|
||||
|
||||
## Before asking for help
|
||||
|
||||
First steps:
|
||||
- Verify that you have followed the steps in [brodybits / Cordova-quick-start-checklist](https://github.com/brodybits/Cordova-quick-start-checklist)
|
||||
- Check the troubleshooting steps and pitfalls in [brodybits / Cordova-troubleshooting-guide](https://github.com/brodybits/Cordova-troubleshooting-guide)
|
||||
|
||||
and check the following:
|
||||
- You are using the latest version of the Plugin Javascript & platform-specific Java or Objective-C source from this repository.
|
||||
- You have installed the Javascript & platform-specific Java or Objective-C correctly.
|
||||
- You have included the correct version of the cordova Javascript and SQLitePlugin.js and got the path right.
|
||||
- You have registered the plugin properly in `config.xml`.
|
||||
|
||||
If you still cannot get something to work:
|
||||
- create a fresh, clean Cordova project;
|
||||
- add this plugin according to the instructions above;
|
||||
- double-check that you follwed the steps in [brodybits / Cordova-quick-start-checklist](https://github.com/brodybits/Cordova-quick-start-checklist);
|
||||
- try a simple test program;
|
||||
- double-check the troubleshooting steps and pitfalls in [brodybits / Cordova-troubleshooting-guide](https://github.com/brodybits/Cordova-troubleshooting-guide)
|
||||
|
||||
If you continue to see the issue in the fresh, clean Cordova project:
|
||||
- Make the simplest test program you can to demonstrate the issue, including the following characteristics:
|
||||
- it completely self-contained, i.e. it is using no extra libraries beyond cordova & SQLitePlugin.js;
|
||||
- if the issue is with *adding* data to a table, that the test program includes the statements you used to open the database and create the table;
|
||||
- if the issue is with *retrieving* data from a table, that the test program includes the statements you used to open the database, create the table, and enter the data you are trying to retrieve.
|
||||
|
||||
## What will be supported for free
|
||||
|
||||
Please make a small, self-contained test program that can demonstrate your problem and post it. Please do not use any other plugins or frameworks than are absolutely necessary to demonstrate your problem.
|
||||
|
||||
In case of a problem with a pre-populated database, please post your entire project.
|
||||
|
||||
## Support for issues with Angular/"ngCordova"/Ionic
|
||||
|
||||
Free support for issues with Angular/"ngCordova"/Ionic will only be provided if you can demonstrate that you can do the same thing without such a framework.
|
||||
- Make a fresh, clean ngCordova or Ionic project with a test program that demonstrates the issue and post it. Please do not use any other plugins or frameworks unless absolutely necessary to demonstrate your issue.
|
||||
- Make another project without any form of Angular including ngCordova or Ionic, with the same test program to show that it will work outside Angular/"ngCordova"/Ionic.
|
||||
|
||||
## What information is needed for help
|
||||
|
||||
Please include the following:
|
||||
- Which platform(s) Android/iOS/WP8/Windows 8.1/Windows Phone 8.1
|
||||
- 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.
|
||||
|
||||
## Please do NOT use any of these formats
|
||||
|
||||
- screen casts or videos
|
||||
- RAR or similar archive formats
|
||||
- Intel, MS IDE, or similar project formats unless absolutely necessary
|
||||
|
||||
## Where to ask for help
|
||||
|
||||
Once you have followed the directions above, you may request free support in the following location(s):
|
||||
- [litehelpers / Cordova-sqlite-storage / issues](https://github.com/litehelpers/Cordova-sqlite-storage/issues)
|
||||
- TBD ???: ~~[litehelpers / Cordova-sqlite-help](https://github.com/litehelpers/Cordova-sqlite-help)~~
|
||||
|
||||
Please include the information described above otherwise.
|
||||
|
||||
## Professional support
|
||||
|
||||
Professional support is available, please contact: <info@litehelpers.net>
|
||||
|
||||
# Unit tests
|
||||
|
||||
Unit testing is done in `spec`.
|
||||
|
||||
## running tests from shell
|
||||
|
||||
To run the tests from \*nix shell, simply do either:
|
||||
|
||||
./bin/test.sh ios
|
||||
|
||||
or for Android:
|
||||
|
||||
./bin/test.sh android
|
||||
|
||||
To run then from a windows powershell do either
|
||||
|
||||
.\bin\test.ps1 android
|
||||
|
||||
or for Windows (8.1):
|
||||
|
||||
.\bin\test.ps1 windows
|
||||
|
||||
or for Windows Phone (7/8):
|
||||
|
||||
.\bin\test.ps1 wp8
|
||||
|
||||
# Adapters
|
||||
|
||||
## 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()`.
|
||||
|
||||
## PouchDB
|
||||
|
||||
The adapter is now part of [PouchDB](http://pouchdb.com/) thanks to [@nolanlawson](https://github.com/nolanlawson), see [PouchDB FAQ](http://pouchdb.com/faq.html).
|
||||
|
||||
**NOTE:** The PouchDB adapter has not been tested with the new [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector). You may need to include `androidDatabaseImplementation: 2` in the `sqlitePlugin.openDatabase()` options as described above.
|
||||
|
||||
# Contributing
|
||||
|
||||
## Community
|
||||
|
||||
- Testimonials of apps that are using this plugin would be especially helpful.
|
||||
- Reporting issues at [litehelpers / Cordova-sqlite-storage / issues](https://github.com/litehelpers/Cordova-sqlite-storage/issues) can help improve the quality of this plugin.
|
||||
|
||||
## Code
|
||||
|
||||
**WARNING:** Please do NOT propose changes from your default branch. In general, contributions are rebased using `git rebase` or `git cherry-pick` and not merged.
|
||||
|
||||
- Patches with bug fixes are helpful, especially when submitted with test code.
|
||||
- Other enhancements welcome for consideration, when submitted with test code and are working for all supported platforms. Increase of complexity should be avoided.
|
||||
- All contributions may be reused by [@brodybits (Chris Brody)](https://github.com/brodybits) under another license in the future. Efforts will be taken to give credit for major contributions but it will not be guaranteed.
|
||||
- Project restructuring, i.e. moving files and/or directories around, should be avoided if possible.
|
||||
- If you see a need for restructuring, it is better to discuss it first in a [new issue](https://github.com/litehelpers/Cordova-sqlite-storage/issues/new) where alternatives can be discussed before reaching a conclusion. If you want to propose a change to the project structure:
|
||||
- Remember to make (and use) a special branch within your fork from which you can send the proposed restructuring;
|
||||
- Always use `git mv` to move files & directories;
|
||||
- Never mix a move/rename operation with any other changes in the same commit.
|
||||
|
||||
## Other
|
||||
|
||||
[@brodybits (Chris Brody)](https://github.com/brodybits) and others contribute their valuable time and expertise to maintain this project for the benefit of the mobile app community. Small consulting relationships can help strengthen the business viability of this project (see contact below).
|
||||
|
||||
## Major branches
|
||||
|
||||
- `common-src` - source for Android, iOS, and Windows (8.1) versions (shared with [litehelpers / Cordova-sqlcipher-adapter](https://github.com/litehelpers/Cordova-sqlcipher-adapter))
|
||||
- `new-common-rc` - pre-release version for Android/iOS/Windows (8.1), including library dependencies for Android and Windows (8.1)
|
||||
- `wp-src` - source for Android (*not* using [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector)), iOS, and WP(7/8) versions
|
||||
- `wp-master-rc` - pre-release version for Android(*not* using [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector))/iOS/WP(7/8), including source for CSharp-SQLite (C#) library classes
|
||||
- `master-public` - release version for all supported platforms, including library dependencies for Android, Windows (8.1), and WP(7/8)
|
||||
- [FUTURE TBD] ~~`master` - version for release, to be included in PhoneGap build.~~
|
||||
|
||||
## Contact
|
||||
|
||||
<info@litehelpers.net>
|
||||
602
plugins/cordova-sqlite-storage/SQLitePlugin.coffee.md
Normal file
602
plugins/cordova-sqlite-storage/SQLitePlugin.coffee.md
Normal file
@@ -0,0 +1,602 @@
|
||||
# SQLite plugin in Markdown (litcoffee)
|
||||
|
||||
#### Use coffee compiler to compile this directly into Javascript
|
||||
|
||||
#### License for common script: MIT or Apache
|
||||
|
||||
# Top-level SQLite plugin objects
|
||||
|
||||
## root window object:
|
||||
|
||||
root = @
|
||||
|
||||
## constant(s):
|
||||
|
||||
READ_ONLY_REGEX = /^\s*(?:drop|delete|insert|update|create)\s/i
|
||||
|
||||
# per-db state
|
||||
DB_STATE_INIT = "INIT"
|
||||
DB_STATE_OPEN = "OPEN"
|
||||
|
||||
## global(s):
|
||||
|
||||
# per-db map of locking and queueing
|
||||
# XXX NOTE: This is NOT cleaned up when a db is closed and/or deleted.
|
||||
# If the record is simply removed when a db is closed or deleted,
|
||||
# it will cause some test failures and may break large-scale
|
||||
# applications that repeatedly open and close the database.
|
||||
# [BUG #210] TODO: better to abort and clean up the pending transaction state.
|
||||
# XXX TBD this will be renamed and include some more per-db state.
|
||||
txLocks = {}
|
||||
|
||||
## utility functions:
|
||||
|
||||
# Errors returned to callbacks must conform to `SqlError` with a code and message.
|
||||
# Some errors are of type `Error` or `string` and must be converted.
|
||||
newSQLError = (error, code) ->
|
||||
sqlError = error
|
||||
code = 0 if !code # unknown by default
|
||||
|
||||
if !sqlError
|
||||
sqlError = new Error "a plugin had an error but provided no response"
|
||||
sqlError.code = code
|
||||
|
||||
if typeof sqlError is "string"
|
||||
sqlError = new Error error
|
||||
sqlError.code = code
|
||||
|
||||
if !sqlError.code && sqlError.message
|
||||
sqlError.code = code
|
||||
|
||||
if !sqlError.code && !sqlError.message
|
||||
sqlError = new Error "an unknown error was returned: " + JSON.stringify(sqlError)
|
||||
sqlError.code = code
|
||||
|
||||
return sqlError
|
||||
|
||||
nextTick = window.setImmediate || (fun) ->
|
||||
window.setTimeout(fun, 0)
|
||||
return
|
||||
|
||||
###
|
||||
Utility that avoids leaking the arguments object. See
|
||||
https://www.npmjs.org/package/argsarray
|
||||
###
|
||||
argsArray = (fun) ->
|
||||
return ->
|
||||
len = arguments.length
|
||||
if len
|
||||
args = []
|
||||
i = -1
|
||||
while ++i < len
|
||||
args[i] = arguments[i]
|
||||
return fun.call this, args
|
||||
else
|
||||
return fun.call this, []
|
||||
|
||||
## SQLite plugin db-connection handle
|
||||
|
||||
#### NOTE: there can be multipe SQLitePlugin db-connection handles per open db.
|
||||
|
||||
#### SQLite plugin db connection handle object is defined by a constructor function and prototype member functions:
|
||||
|
||||
SQLitePlugin = (openargs, openSuccess, openError) ->
|
||||
# console.log "SQLitePlugin openargs: #{JSON.stringify openargs}"
|
||||
|
||||
if !(openargs and openargs['name'])
|
||||
throw newSQLError "Cannot create a SQLitePlugin db instance without a db name"
|
||||
|
||||
dbname = openargs.name
|
||||
|
||||
if typeof dbname != 'string'
|
||||
throw newSQLError 'sqlite plugin database name must be a string'
|
||||
|
||||
@openargs = openargs
|
||||
@dbname = dbname
|
||||
|
||||
@openSuccess = openSuccess
|
||||
@openError = openError
|
||||
|
||||
@openSuccess or
|
||||
@openSuccess = ->
|
||||
console.log "DB opened: " + dbname
|
||||
return
|
||||
|
||||
@openError or
|
||||
@openError = (e) ->
|
||||
console.log e.message
|
||||
return
|
||||
|
||||
@open @openSuccess, @openError
|
||||
return
|
||||
|
||||
SQLitePlugin::databaseFeatures = isSQLitePluginDatabase: true
|
||||
|
||||
# Keep track of state of open db connections
|
||||
# XXX TBD this will be moved and renamed or
|
||||
# combined with txLocks.
|
||||
SQLitePlugin::openDBs = {}
|
||||
|
||||
SQLitePlugin::addTransaction = (t) ->
|
||||
if !txLocks[@dbname]
|
||||
txLocks[@dbname] = {
|
||||
queue: []
|
||||
inProgress: false
|
||||
}
|
||||
txLocks[@dbname].queue.push t
|
||||
if @dbname of @openDBs && @openDBs[@dbname] isnt DB_STATE_INIT
|
||||
# XXX TODO: only when queue has length of 1 [and test it!!]
|
||||
@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.
|
||||
console.log 'database is closed, new transaction is [stuck] waiting until db is opened again!'
|
||||
return
|
||||
|
||||
SQLitePlugin::transaction = (fn, error, success) ->
|
||||
# FUTURE TBD check for valid fn here
|
||||
if !@openDBs[@dbname]
|
||||
error newSQLError 'database not open'
|
||||
return
|
||||
|
||||
@addTransaction new SQLitePluginTransaction(this, fn, error, success, true, false)
|
||||
return
|
||||
|
||||
SQLitePlugin::readTransaction = (fn, error, success) ->
|
||||
# FUTURE TBD check for valid fn here (and add test for this)
|
||||
if !@openDBs[@dbname]
|
||||
error newSQLError 'database not open'
|
||||
return
|
||||
|
||||
@addTransaction new SQLitePluginTransaction(this, fn, error, success, false, true)
|
||||
return
|
||||
|
||||
SQLitePlugin::startNextTransaction = ->
|
||||
self = @
|
||||
|
||||
nextTick =>
|
||||
if !(@dbname of @openDBs) || @openDBs[@dbname] isnt DB_STATE_OPEN
|
||||
console.log 'cannot start next transaction: database not open'
|
||||
return
|
||||
|
||||
txLock = txLocks[self.dbname]
|
||||
if !txLock
|
||||
console.log 'cannot start next transaction: database connection is lost'
|
||||
# XXX TBD TODO (BUG #210/??): abort all pending transactions with error cb [and test!!]
|
||||
# @abortAllPendingTransactions()
|
||||
return
|
||||
|
||||
else if txLock.queue.length > 0 && !txLock.inProgress
|
||||
# start next transaction in q
|
||||
txLock.inProgress = true
|
||||
txLock.queue.shift().start()
|
||||
return
|
||||
|
||||
return
|
||||
|
||||
SQLitePlugin::abortAllPendingTransactions = ->
|
||||
# extra debug info:
|
||||
# if txLocks[@dbname] then console.log 'abortAllPendingTransactions with transaction queue length: ' + txLocks[@dbname].queue.length
|
||||
# else console.log 'abortAllPendingTransactions with no transaction lock state'
|
||||
|
||||
txLock = txLocks[@dbname]
|
||||
if !!txLock && txLock.queue.length > 0
|
||||
# XXX TODO: what to do in case there is a (stray) transaction in progress?
|
||||
#console.log 'abortAllPendingTransactions - cleanup old transaction(s)'
|
||||
for tx in txLock.queue
|
||||
tx.abortFromQ newSQLError 'Invalid database handle'
|
||||
|
||||
# XXX TODO: consider cleaning up (delete) txLocks[@dbname] resource,
|
||||
# in case it is known there are no more pending transactions
|
||||
txLock.queue = []
|
||||
txLock.inProgress = false
|
||||
|
||||
return
|
||||
|
||||
SQLitePlugin::open = (success, error) ->
|
||||
if @dbname of @openDBs
|
||||
console.log 'database already open: ' + @dbname
|
||||
|
||||
# for a re-open run the success cb async so that the openDatabase return value
|
||||
# can be used in the success handler as an alternative to the handler's
|
||||
# db argument
|
||||
nextTick =>
|
||||
success @
|
||||
return
|
||||
|
||||
else
|
||||
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'
|
||||
|
||||
#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!!):
|
||||
# if !!error then error newSQLError 'database closed during open operation'
|
||||
# @abortAllPendingTransactions()
|
||||
|
||||
if @dbname of @openDBs
|
||||
@openDBs[@dbname] = DB_STATE_OPEN
|
||||
|
||||
if !!success then success @
|
||||
|
||||
txLock = txLocks[@dbname]
|
||||
if !!txLock && txLock.queue.length > 0 && !txLock.inProgress
|
||||
@startNextTransaction()
|
||||
return
|
||||
|
||||
openerrorcb = =>
|
||||
console.log 'OPEN database: ' + @dbname + ' failed, aborting any pending transactions'
|
||||
# XXX TODO: newSQLError missing the message part!
|
||||
if !!error then error newSQLError 'Could not open database'
|
||||
delete @openDBs[@dbname]
|
||||
@abortAllPendingTransactions()
|
||||
return
|
||||
|
||||
# store initial DB state:
|
||||
@openDBs[@dbname] = DB_STATE_INIT
|
||||
|
||||
cordova.exec opensuccesscb, openerrorcb, "SQLitePlugin", "open", [ @openargs ]
|
||||
|
||||
return
|
||||
|
||||
SQLitePlugin::close = (success, error) ->
|
||||
if @dbname of @openDBs
|
||||
if txLocks[@dbname] && txLocks[@dbname].inProgress
|
||||
# XXX TBD: wait for current tx then close (??)
|
||||
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
|
||||
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!!]
|
||||
|
||||
cordova.exec success, error, "SQLitePlugin", "close", [ { path: @dbname } ]
|
||||
|
||||
else
|
||||
console.log 'cannot close: database is not open'
|
||||
if error then nextTick -> error()
|
||||
|
||||
return
|
||||
|
||||
SQLitePlugin::executeSql = (statement, params, success, error) ->
|
||||
# XXX TODO: better to capture the result, and report it once
|
||||
# the transaction has completely finished.
|
||||
# This would fix BUG #204 (cannot close db in db.executeSql() callback).
|
||||
mysuccess = (t, r) -> if !!success then success r
|
||||
myerror = (t, e) -> if !!error then error e
|
||||
|
||||
myfn = (tx) ->
|
||||
tx.addStatement(statement, params, mysuccess, myerror)
|
||||
return
|
||||
|
||||
@addTransaction new SQLitePluginTransaction(this, myfn, null, null, false, false)
|
||||
return
|
||||
|
||||
## SQLite plugin transaction object for batching:
|
||||
|
||||
SQLitePluginTransaction = (db, fn, error, success, txlock, readOnly) ->
|
||||
# FUTURE TBD check this earlier:
|
||||
if typeof(fn) != "function"
|
||||
###
|
||||
This is consistent with the implementation in Chrome -- it
|
||||
throws if you pass anything other than a function. This also
|
||||
prevents us from stalling our txQueue if somebody passes a
|
||||
false value for fn.
|
||||
###
|
||||
throw newSQLError "transaction expected a function"
|
||||
|
||||
@db = db
|
||||
@fn = fn
|
||||
@error = error
|
||||
@success = success
|
||||
@txlock = txlock
|
||||
@readOnly = readOnly
|
||||
@executes = []
|
||||
|
||||
if txlock
|
||||
@addStatement "BEGIN", [], null, (tx, err) ->
|
||||
throw newSQLError "unable to begin transaction: " + err.message, err.code
|
||||
|
||||
return
|
||||
|
||||
SQLitePluginTransaction::start = ->
|
||||
try
|
||||
@fn this
|
||||
@run()
|
||||
catch err
|
||||
# If "fn" throws, we must report the whole transaction as failed.
|
||||
txLocks[@db.dbname].inProgress = false
|
||||
@db.startNextTransaction()
|
||||
if @error
|
||||
@error newSQLError err
|
||||
return
|
||||
|
||||
SQLitePluginTransaction::executeSql = (sql, values, success, error) ->
|
||||
|
||||
if @finalized
|
||||
throw {message: 'InvalidStateError: DOM Exception 11: This transaction is already finalized. Transactions are committed after its success or failure handlers are called. If you are using a Promise to handle callbacks, be aware that implementations following the A+ standard adhere to run-to-completion semantics and so Promise resolution occurs on a subsequent tick and therefore after the transaction commits.', code: 11}
|
||||
return
|
||||
|
||||
if @readOnly && READ_ONLY_REGEX.test(sql)
|
||||
@handleStatementFailure(error, {message: 'invalid sql for a read-only transaction'})
|
||||
return
|
||||
|
||||
@addStatement(sql, values, success, error)
|
||||
return
|
||||
|
||||
# This method adds the SQL statement to the transaction queue but does not check for
|
||||
# finalization since it is used to execute COMMIT and ROLLBACK.
|
||||
SQLitePluginTransaction::addStatement = (sql, values, success, error) ->
|
||||
|
||||
params = []
|
||||
if !!values && values.constructor == Array
|
||||
for v in values
|
||||
t = typeof v
|
||||
params.push (
|
||||
if v == null || v == undefined || t == 'number' || t == 'string' then v
|
||||
else if v instanceof Blob then v.valueOf()
|
||||
else v.toString()
|
||||
)
|
||||
|
||||
@executes.push
|
||||
success: success
|
||||
error: error
|
||||
|
||||
sql: sql
|
||||
params: params
|
||||
|
||||
return
|
||||
|
||||
SQLitePluginTransaction::handleStatementSuccess = (handler, response) ->
|
||||
if !handler
|
||||
return
|
||||
|
||||
rows = response.rows || []
|
||||
payload =
|
||||
rows:
|
||||
item: (i) ->
|
||||
rows[i]
|
||||
|
||||
length: rows.length
|
||||
|
||||
rowsAffected: response.rowsAffected or 0
|
||||
insertId: response.insertId or undefined
|
||||
|
||||
handler this, payload
|
||||
|
||||
return
|
||||
|
||||
SQLitePluginTransaction::handleStatementFailure = (handler, response) ->
|
||||
if !handler
|
||||
throw newSQLError "a statement with no error handler failed: " + response.message, response.code
|
||||
if handler(this, response) isnt false
|
||||
throw newSQLError "a statement error callback did not return false: " + response.message, response.code
|
||||
return
|
||||
|
||||
SQLitePluginTransaction::run = ->
|
||||
txFailure = null
|
||||
|
||||
tropts = []
|
||||
batchExecutes = @executes
|
||||
waiting = batchExecutes.length
|
||||
@executes = []
|
||||
tx = this
|
||||
|
||||
handlerFor = (index, didSucceed) ->
|
||||
(response) ->
|
||||
try
|
||||
if didSucceed
|
||||
tx.handleStatementSuccess batchExecutes[index].success, response
|
||||
else
|
||||
tx.handleStatementFailure batchExecutes[index].error, newSQLError(response)
|
||||
catch err
|
||||
if !txFailure
|
||||
txFailure = newSQLError(err)
|
||||
|
||||
if --waiting == 0
|
||||
if txFailure
|
||||
tx.abort txFailure
|
||||
else if tx.executes.length > 0
|
||||
# new requests have been issued by the callback
|
||||
# handlers, so run another batch.
|
||||
tx.run()
|
||||
else
|
||||
tx.finish()
|
||||
|
||||
return
|
||||
|
||||
i = 0
|
||||
|
||||
mycbmap = {}
|
||||
|
||||
while i < batchExecutes.length
|
||||
request = batchExecutes[i]
|
||||
|
||||
mycbmap[i] =
|
||||
success: handlerFor(i, true)
|
||||
error: handlerFor(i, false)
|
||||
|
||||
tropts.push
|
||||
qid: 1111
|
||||
sql: request.sql
|
||||
params: request.params
|
||||
|
||||
i++
|
||||
|
||||
mycb = (result) ->
|
||||
#console.log "mycb result #{JSON.stringify result}"
|
||||
|
||||
last = result.length-1
|
||||
for i in [0..last]
|
||||
r = result[i]
|
||||
type = r.type
|
||||
# NOTE: r.qid can be ignored
|
||||
res = r.result
|
||||
|
||||
q = mycbmap[i]
|
||||
|
||||
if q
|
||||
if q[type]
|
||||
q[type] res
|
||||
|
||||
return
|
||||
|
||||
cordova.exec mycb, null, "SQLitePlugin", "backgroundExecuteSqlBatch", [{dbargs: {dbname: @db.dbname}, executes: tropts}]
|
||||
|
||||
return
|
||||
|
||||
SQLitePluginTransaction::abort = (txFailure) ->
|
||||
if @finalized then return
|
||||
tx = @
|
||||
|
||||
succeeded = (tx) ->
|
||||
txLocks[tx.db.dbname].inProgress = false
|
||||
tx.db.startNextTransaction()
|
||||
if tx.error then tx.error txFailure
|
||||
return
|
||||
|
||||
failed = (tx, err) ->
|
||||
txLocks[tx.db.dbname].inProgress = false
|
||||
tx.db.startNextTransaction()
|
||||
if tx.error then tx.error newSQLError("error while trying to roll back: " + err.message, err.code)
|
||||
return
|
||||
|
||||
@finalized = true
|
||||
|
||||
if @txlock
|
||||
@addStatement "ROLLBACK", [], succeeded, failed
|
||||
@run()
|
||||
else
|
||||
succeeded(tx)
|
||||
|
||||
return
|
||||
|
||||
SQLitePluginTransaction::finish = ->
|
||||
if @finalized then return
|
||||
tx = @
|
||||
|
||||
succeeded = (tx) ->
|
||||
txLocks[tx.db.dbname].inProgress = false
|
||||
tx.db.startNextTransaction()
|
||||
if tx.success then tx.success()
|
||||
return
|
||||
|
||||
failed = (tx, err) ->
|
||||
txLocks[tx.db.dbname].inProgress = false
|
||||
tx.db.startNextTransaction()
|
||||
if tx.error then tx.error newSQLError("error while trying to commit: " + err.message, err.code)
|
||||
return
|
||||
|
||||
@finalized = true
|
||||
|
||||
if @txlock
|
||||
@addStatement "COMMIT", [], succeeded, failed
|
||||
@run()
|
||||
else
|
||||
succeeded(tx)
|
||||
|
||||
return
|
||||
|
||||
SQLitePluginTransaction::abortFromQ = (sqlerror) ->
|
||||
# NOTE: since the transaction is waiting in the queue,
|
||||
# the transaction function containing the SQL statements
|
||||
# would not be run yet. Simply report the transaction error.
|
||||
if @error
|
||||
@error sqlerror
|
||||
|
||||
return
|
||||
|
||||
## SQLite plugin object factory:
|
||||
|
||||
dblocations = [ "docs", "libs", "nosync" ]
|
||||
|
||||
SQLiteFactory =
|
||||
###
|
||||
NOTE: this function should NOT be translated from Javascript
|
||||
back to CoffeeScript by js2coffee.
|
||||
If this function is edited in Javascript then someone will
|
||||
have to translate it back to CoffeeScript by hand.
|
||||
###
|
||||
opendb: argsArray (args) ->
|
||||
if args.length < 1 then return null
|
||||
|
||||
first = args[0]
|
||||
openargs = null
|
||||
okcb = null
|
||||
errorcb = null
|
||||
|
||||
if first.constructor == String
|
||||
openargs = {name: first}
|
||||
|
||||
if args.length >= 5
|
||||
okcb = args[4]
|
||||
if args.length > 5 then errorcb = args[5]
|
||||
|
||||
else
|
||||
openargs = first
|
||||
|
||||
if args.length >= 2
|
||||
okcb = args[1]
|
||||
if args.length > 2 then errorcb = args[2]
|
||||
|
||||
dblocation = if !!openargs.location then dblocations[openargs.location] else null
|
||||
openargs.dblocation = dblocation || dblocations[0]
|
||||
|
||||
if !!openargs.createFromLocation and openargs.createFromLocation == 1
|
||||
openargs.createFromResource = "1"
|
||||
|
||||
if !!openargs.androidDatabaseImplementation and openargs.androidDatabaseImplementation == 2
|
||||
openargs.androidOldDatabaseImplementation = 1
|
||||
|
||||
if !!openargs.androidLockWorkaround and openargs.androidLockWorkaround == 1
|
||||
openargs.androidBugWorkaround = 1
|
||||
|
||||
new SQLitePlugin openargs, okcb, errorcb
|
||||
|
||||
deleteDb: (first, success, error) ->
|
||||
args = {}
|
||||
|
||||
if first.constructor == String
|
||||
#console.log "delete db name: #{first}"
|
||||
args.path = first
|
||||
args.dblocation = dblocations[0]
|
||||
|
||||
else
|
||||
#console.log "delete db args: #{JSON.stringify first}"
|
||||
if !(first and first['name']) then throw new Error "Please specify db name"
|
||||
args.path = first.name
|
||||
dblocation = if !!first.location then dblocations[first.location] else null
|
||||
args.dblocation = dblocation || dblocations[0]
|
||||
|
||||
# XXX [BUG #210] TODO: when closing or deleting a db, abort any pending transactions (with error callback)
|
||||
delete SQLitePlugin::openDBs[args.path]
|
||||
cordova.exec success, error, "SQLitePlugin", "delete", [ args ]
|
||||
|
||||
## Exported API:
|
||||
|
||||
root.sqlitePlugin =
|
||||
sqliteFeatures:
|
||||
isSQLitePlugin: true
|
||||
|
||||
openDatabase: SQLiteFactory.opendb
|
||||
deleteDatabase: SQLiteFactory.deleteDb
|
||||
|
||||
## vim directives
|
||||
|
||||
#### vim: set filetype=coffee :
|
||||
#### vim: set expandtab :
|
||||
|
||||
58
plugins/cordova-sqlite-storage/bin/test.ps1
Normal file
58
plugins/cordova-sqlite-storage/bin/test.ps1
Normal file
@@ -0,0 +1,58 @@
|
||||
# Automated cordova tests. Installs the correct cordova platform,
|
||||
# installs the plugin, installs the test app, and then runs it on
|
||||
# a device or emulator.
|
||||
#
|
||||
# usage: .\bin\test.ps1 [android|ios|windows|wp8]
|
||||
#
|
||||
# N.B. if you functionally change this script you _must_ change .\bin\test.sh too.
|
||||
#
|
||||
|
||||
param([string]$platform)
|
||||
|
||||
if (! $platform) {
|
||||
echo "usage: .\bin\test.sh [android|ios|windows|wp8]"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if (! (get-command coffee) ) {
|
||||
echo "you need coffeescript. please install with:"
|
||||
echo "npm install -g coffee-script"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if (! (get-command cordova) ) {
|
||||
echo "you need cordova. please install with:"
|
||||
echo "npm install -g cordova"
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
||||
pushd spec
|
||||
if (!$?) { # run from the bin/ directory
|
||||
echo "re-pushing"
|
||||
pushd ../spec
|
||||
}
|
||||
try {
|
||||
# compile coffeescript
|
||||
coffee --no-header -cl -o ../www ../SQLitePlugin.coffee.md
|
||||
if (!$?) {
|
||||
echo "coffeescript compilation failed"
|
||||
exit 1
|
||||
}
|
||||
echo "compiled coffeescript to javascript"
|
||||
|
||||
# move everything to a temp folder to avoid infinite recursion errors
|
||||
if (test-path ../.plugin) {
|
||||
rm -force -recurse ../.plugin -ErrorAction ignore
|
||||
}
|
||||
mkdir -ErrorAction ignore ../.plugin | out-null
|
||||
cp -recurse ../src, ../plugin.xml, ../www ../.plugin
|
||||
|
||||
# update the plugin, run the test app
|
||||
cordova platform add $platform
|
||||
cordova plugin rm com.phonegap.plugins.sqlite
|
||||
cordova plugin add ../.plugin
|
||||
cordova run $platform
|
||||
} finally {
|
||||
popd
|
||||
}
|
||||
56
plugins/cordova-sqlite-storage/bin/test.sh
Normal file
56
plugins/cordova-sqlite-storage/bin/test.sh
Normal file
@@ -0,0 +1,56 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Automated cordova tests. Installs the correct cordova platform,
|
||||
# installs the plugin, installs the test app, and then runs it on
|
||||
# a device or emulator.
|
||||
#
|
||||
# usage: ./bin/test.sh [android|ios]
|
||||
#
|
||||
# N.B. if you functionally change this script you _must_ change .\bin\test.sh too.
|
||||
#
|
||||
|
||||
# N.B. if you functionally change this script you _must_ change ./bin/test.ps1 too.
|
||||
|
||||
platform=$1
|
||||
|
||||
if [[ -z $platform ]]; then
|
||||
echo "usage: ./bin/test.sh [android|ios]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -x $(which coffee) ]]; then
|
||||
echo "you need coffeescript. please install with:"
|
||||
echo "npm install -g coffee-script"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -x $(which cordova) ]]; then
|
||||
echo "you need cordova. please install with:"
|
||||
echo "npm install -g cordova"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd spec
|
||||
if [[ $? != 0 ]]; then # run from the bin/ directory
|
||||
cd ../spec
|
||||
fi
|
||||
|
||||
# compile coffeescript
|
||||
coffee --no-header -cl -o ../www ../SQLitePlugin.coffee.md
|
||||
|
||||
if [[ $? != 0 ]]; then
|
||||
echo "coffeescript compilation failed"
|
||||
exit 1
|
||||
fi
|
||||
echo "compiled coffeescript to javascript"
|
||||
|
||||
# move everything to a temp folder to avoid infinite recursion errors
|
||||
rm -fr ../.plugin
|
||||
mkdir -p ../.plugin
|
||||
cp -r ../src ../plugin.xml ../www ../.plugin
|
||||
|
||||
# update the plugin, run the test app
|
||||
cordova platform add $platform
|
||||
cordova plugin rm com.brodysoft.sqlitePlugin
|
||||
cordova plugin add ../.plugin
|
||||
cordova run $platform
|
||||
21
plugins/cordova-sqlite-storage/circle.yml
Normal file
21
plugins/cordova-sqlite-storage/circle.yml
Normal file
@@ -0,0 +1,21 @@
|
||||
machine:
|
||||
environment:
|
||||
ANDROID_NDK_HOME: $ANDROID_NDK
|
||||
|
||||
dependencies:
|
||||
pre:
|
||||
- npm install -g cordova-paramedic
|
||||
- npm install -g cordova
|
||||
|
||||
test:
|
||||
pre:
|
||||
- emulator -avd circleci-android21 -no-audio -no-window:
|
||||
background: true
|
||||
parallel: true
|
||||
- circle-android wait-for-boot
|
||||
override:
|
||||
- cordova-paramedic --platform android --plugin .
|
||||
- cp spec/www/spec/simple-test.js tests/tests.js
|
||||
- cordova-paramedic --platform android --plugin . --timeout 3600000
|
||||
- cp spec/www/spec/legacy.js tests/tests.js
|
||||
- cordova-paramedic --platform android --plugin . --timeout 7200000
|
||||
42
plugins/cordova-sqlite-storage/package.json
Normal file
42
plugins/cordova-sqlite-storage/package.json
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "cordova-sqlite-storage",
|
||||
"version": "0.7.15-pre",
|
||||
"description": "Native interface to SQLite for PhoneGap/Cordova",
|
||||
"cordova": {
|
||||
"id": "cordova-sqlite-storage",
|
||||
"platforms": [
|
||||
"android",
|
||||
"ios",
|
||||
"windows",
|
||||
"wp8",
|
||||
"wp7",
|
||||
"amazon-fireos"
|
||||
]
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/litehelpers/Cordova-sqlite-storage.git"
|
||||
},
|
||||
"keywords": [
|
||||
"sqlite",
|
||||
"ecosystem:cordova",
|
||||
"cordova-android",
|
||||
"cordova-ios",
|
||||
"cordova-windows",
|
||||
"cordova-wp8",
|
||||
"cordova-wp7",
|
||||
"cordova-amazon-fireos"
|
||||
],
|
||||
"engines": [
|
||||
{
|
||||
"name": "cordova",
|
||||
"version": ">=3.3.0"
|
||||
}
|
||||
],
|
||||
"author": "various",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/litehelpers/Cordova-sqlite-storage/issues"
|
||||
},
|
||||
"homepage": "https://github.com/litehelpers/Cordova-sqlite-storage"
|
||||
}
|
||||
298
plugins/cordova-sqlite-storage/plugin.xml
Normal file
298
plugins/cordova-sqlite-storage/plugin.xml
Normal file
@@ -0,0 +1,298 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<plugin xmlns="http://www.phonegap.com/ns/plugins/1.0"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
id="cordova-sqlite-storage"
|
||||
version="0.7.15-pre">
|
||||
|
||||
<name>Cordova sqlite storage plugin</name>
|
||||
|
||||
<license>MIT</license>
|
||||
|
||||
<keywords>sqlite</keywords>
|
||||
|
||||
<description>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).</description>
|
||||
<author>litehelpers (various)</author>
|
||||
|
||||
<engines>
|
||||
<engine name="cordova" version=">=3.3.0" />
|
||||
</engines>
|
||||
|
||||
<js-module src="www/SQLitePlugin.js" name="SQLitePlugin">
|
||||
<clobbers target="SQLitePlugin" />
|
||||
</js-module>
|
||||
|
||||
<!-- android -->
|
||||
<platform name="android">
|
||||
<!-- Cordova >= 3.0.0 -->
|
||||
<config-file target="res/xml/config.xml" parent="/*">
|
||||
<feature name="SQLitePlugin">
|
||||
<param name="android-package" value="io.liteglue.SQLitePlugin"/>
|
||||
</feature>
|
||||
</config-file>
|
||||
|
||||
<source-file src="src/android/io/liteglue/SQLitePlugin.java" target-dir="src/io/liteglue"/>
|
||||
<source-file src="src/android/io/liteglue/SQLiteAndroidDatabase.java" target-dir="src/io/liteglue"/>
|
||||
|
||||
<!-- Android-sqlite-connector: -->
|
||||
<source-file src="src/android/libs/sqlite-connector.jar" target-dir="libs"/>
|
||||
<!-- Android-sqlite-connector native driver libs: -->
|
||||
<source-file src="src/android/libs/arm64-v8a/libsqlc-native-driver.so" target-dir="libs/arm64-v8a"/>
|
||||
<source-file src="src/android/libs/armeabi/libsqlc-native-driver.so" target-dir="libs/armeabi"/>
|
||||
<source-file src="src/android/libs/armeabi-v7a/libsqlc-native-driver.so" target-dir="libs/armeabi-v7a"/>
|
||||
<source-file src="src/android/libs/x86/libsqlc-native-driver.so" target-dir="libs/x86"/>
|
||||
<source-file src="src/android/libs/x86_64/libsqlc-native-driver.so" target-dir="libs/x86_64"/>
|
||||
|
||||
</platform>
|
||||
|
||||
<!-- ios -->
|
||||
<platform name="ios">
|
||||
<config-file target="config.xml" parent="/*">
|
||||
<feature name="SQLitePlugin">
|
||||
<param name="ios-package" value="SQLitePlugin" />
|
||||
</feature>
|
||||
</config-file>
|
||||
|
||||
<header-file src="src/ios/SQLitePlugin.h" />
|
||||
<source-file src="src/ios/SQLitePlugin.m" />
|
||||
|
||||
<header-file src="src/common/sqlite3.h" />
|
||||
<source-file src="src/common/sqlite3.c" />
|
||||
|
||||
</platform>
|
||||
|
||||
<!-- windows -->
|
||||
<platform name="windows">
|
||||
<js-module src="src/windows/SQLiteProxy.js" name="SQLiteProxy">
|
||||
<merges target="" />
|
||||
</js-module>
|
||||
|
||||
<!-- SQLite3 JS module from SQLite3-WinRT/SQLite3JS: -->
|
||||
<js-module src="src/windows/SQLite3-WinRT/SQLite3JS/js/SQLite3.js" name="SQLite3">
|
||||
<merges target="" />
|
||||
</js-module>
|
||||
|
||||
<!-- Thanks to AllJoyn-Cordova / cordova-plugin-alljoyn: -->
|
||||
<framework src="src/windows/SQLite3-WinRT/SQLite3/SQLite3.Windows/SQLite3.Windows.vcxproj" custom="true" type="projectReference" target="windows" />
|
||||
<framework src="src/windows/SQLite3-WinRT/SQLite3/SQLite3.WindowsPhone/SQLite3.WindowsPhone.vcxproj" custom="true" type="projectReference" target="phone" />
|
||||
|
||||
</platform>
|
||||
|
||||
<!-- wp8 using dlls [should work but not tested on wp7]
|
||||
NOTE: SQLiteWPNative.dll includes the classes from SQLite.cs
|
||||
(which is not desired).
|
||||
<platform name="wp8">
|
||||
<config-file target="config.xml" parent="/*">
|
||||
<feature name="SQLitePlugin">
|
||||
<param name="wp-package" value="SQLitePlugin" />
|
||||
</feature>
|
||||
</config-file>
|
||||
|
||||
<source-file src="src/wp/SQLitePlugin.cs" />
|
||||
<framework src="src/wp/cs-sqlite-dll/SQLiteWPNative.dll" custom="true" />
|
||||
|
||||
</platform>
|
||||
-->
|
||||
|
||||
<platform name="wp8">
|
||||
<config-file target="config.xml" parent="/*">
|
||||
<feature name="SQLitePlugin">
|
||||
<param name="wp-package" value="SQLitePlugin" />
|
||||
</feature>
|
||||
</config-file>
|
||||
|
||||
<source-file src="src/wp/SQLitePlugin.cs" />
|
||||
<source-file src="src/wp/SQLite.cs" />
|
||||
|
||||
<source-file src="src/wp/csharp-sqlite-src/_Custom.cs" />
|
||||
|
||||
<source-file src="src/wp/csharp-sqlite-src/BtreeInt_h.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/Btree_h.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/Delegates.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/Hash_h.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/VdbeInt_h.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/Vdbe_h.cs" />
|
||||
|
||||
<source-file src="src/wp/csharp-sqlite-src/alter_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/analyze_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/attach_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/auth_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/backup_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/bitvec_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/btmutex_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/btree_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/build_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/callback_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/complete_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/crypto.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/ctime_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/date_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/delete_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/expr_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/fault_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/fkey_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/func_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/global_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/hash_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/hwtime_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/insert_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/journal_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/keywordhash_h.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/legacy_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/loadext_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/main_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/malloc_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/mem_Pool.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/memjournal_c.cs" />
|
||||
|
||||
<source-file src="src/wp/csharp-sqlite-src/mutex_h.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/mutex_noop_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/notify_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/opcodes_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/opcodes_h.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/os_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/os_common_h.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/os_h.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/os_win_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/pager_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/pager_h.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/parse_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/parse_h.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/pcache1_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/pcache_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/pcache_h.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/pragma_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/prepare_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/printf_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/random_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/resolve_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/rowset_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/select_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/sqlite3_h.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/sqliteInt_h.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/sqliteLimit_h.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/status_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/table_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/tokenize_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/trigger_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/update_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/utf_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/util_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/vacuum_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/vdbe_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/vdbeapi_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/vdbeaux_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/vdbeblob_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/vdbemem_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/vdbetrace_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/vtab_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/wal_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/wal_h.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/walker_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/where_c.cs" />
|
||||
|
||||
</platform>
|
||||
|
||||
<platform name="wp7">
|
||||
<config-file target="config.xml" parent="/*">
|
||||
<feature name="SQLitePlugin">
|
||||
<param name="wp-package" value="SQLitePlugin" />
|
||||
</feature>
|
||||
</config-file>
|
||||
|
||||
<source-file src="src/wp/SQLitePlugin.cs" />
|
||||
<source-file src="src/wp/SQLite.cs" />
|
||||
|
||||
<source-file src="src/wp/csharp-sqlite-src/_Custom.cs" />
|
||||
|
||||
<source-file src="src/wp/csharp-sqlite-src/BtreeInt_h.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/Btree_h.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/Delegates.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/Hash_h.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/VdbeInt_h.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/Vdbe_h.cs" />
|
||||
|
||||
<source-file src="src/wp/csharp-sqlite-src/alter_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/analyze_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/attach_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/auth_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/backup_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/bitvec_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/btmutex_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/btree_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/build_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/callback_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/complete_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/crypto.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/ctime_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/date_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/delete_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/expr_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/fault_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/fkey_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/func_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/global_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/hash_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/hwtime_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/insert_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/journal_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/keywordhash_h.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/legacy_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/loadext_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/main_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/malloc_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/mem_Pool.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/memjournal_c.cs" />
|
||||
|
||||
<source-file src="src/wp/csharp-sqlite-src/mutex_h.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/mutex_noop_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/notify_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/opcodes_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/opcodes_h.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/os_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/os_common_h.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/os_h.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/os_win_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/pager_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/pager_h.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/parse_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/parse_h.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/pcache1_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/pcache_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/pcache_h.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/pragma_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/prepare_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/printf_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/random_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/resolve_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/rowset_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/select_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/sqlite3_h.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/sqliteInt_h.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/sqliteLimit_h.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/status_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/table_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/tokenize_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/trigger_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/update_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/utf_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/util_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/vacuum_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/vdbe_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/vdbeapi_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/vdbeaux_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/vdbeblob_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/vdbemem_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/vdbetrace_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/vtab_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/wal_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/wal_h.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/walker_c.cs" />
|
||||
<source-file src="src/wp/csharp-sqlite-src/where_c.cs" />
|
||||
|
||||
</platform>
|
||||
|
||||
<!-- FUTURE TBD: fix and test for amazon-fireos -->
|
||||
|
||||
</plugin>
|
||||
|
||||
<!-- vim: set expandtab : -->
|
||||
4
plugins/cordova-sqlite-storage/spec/.cordova/config.json
Normal file
4
plugins/cordova-sqlite-storage/spec/.cordova/config.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"id": "com.phonegap.plugins.sqlite.tests",
|
||||
"name": "Cordova SQLite Plugin Test Runner"
|
||||
}
|
||||
13
plugins/cordova-sqlite-storage/spec/config.xml
Normal file
13
plugins/cordova-sqlite-storage/spec/config.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<widget id="com.phonegap.plugins.sqlite.tests" version="0.0.1"
|
||||
xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
|
||||
<name>Cordova SQLite Plugin Test Runner</name>
|
||||
<description>
|
||||
Runs the unit tests suite for the Cordova SQLite plugin.
|
||||
</description>
|
||||
<author email="nolan@nolanlawson.com" href="http://nolanlawson.com">
|
||||
Nolan Lawson
|
||||
</author>
|
||||
<content src="index.html" />
|
||||
<access origin="*" />
|
||||
</widget>
|
||||
4
plugins/cordova-sqlite-storage/spec/platforms/.gitignore
vendored
Normal file
4
plugins/cordova-sqlite-storage/spec/platforms/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
# Ignore everything in this directory
|
||||
*
|
||||
# Except this file
|
||||
!.gitignore
|
||||
4
plugins/cordova-sqlite-storage/spec/plugins/.gitignore
vendored
Normal file
4
plugins/cordova-sqlite-storage/spec/plugins/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
# Ignore everything in this directory
|
||||
*
|
||||
# Except this file
|
||||
!.gitignore
|
||||
33
plugins/cordova-sqlite-storage/spec/www/index.html
Normal file
33
plugins/cordova-sqlite-storage/spec/www/index.html
Normal file
@@ -0,0 +1,33 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="user-scalable=no" />
|
||||
|
||||
<title>SQLitePlugin Jasmine Spec Runner</title>
|
||||
|
||||
<link rel="shortcut icon" type="image/png" href="lib/jasmine-2.2.0/jasmine_favicon.png">
|
||||
<link rel="stylesheet" href="lib/jasmine-2.2.0/jasmine.css">
|
||||
|
||||
<script src="lib/jasmine-2.2.0/jasmine.js"></script>
|
||||
<script src="lib/jasmine-2.2.0/jasmine-html.js"></script>
|
||||
<script src="lib/jasmine-2.2.0/boot.js"></script>
|
||||
|
||||
<!-- [Cordova] source file(s): -->
|
||||
<script src="cordova.js"></script>
|
||||
<!-- Needed for Cordova pre-3.0: -->
|
||||
<script src="SQLitePlugin.js"></script>
|
||||
|
||||
<!-- spec file(s): -->
|
||||
<script src="spec/browser-check-startup.js"></script>
|
||||
<script src="spec/simple-test.js"></script>
|
||||
<script src="spec/legacy.js"></script>
|
||||
<!-- Slow test(s) disabled by default:
|
||||
<script src="spec/big-memory-test.js"></script>
|
||||
-->
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
</body>
|
||||
</html> <!-- vim: set expandtab : -->
|
||||
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
Starting with version 2.0, this file "boots" Jasmine, performing all of the necessary initialization before executing the loaded environment and all of a project's specs. This file should be loaded after `jasmine.js` and `jasmine_html.js`, but before any project source files or spec files are loaded. Thus this file can also be used to customize Jasmine for a project.
|
||||
|
||||
If a project is using Jasmine via the standalone distribution, this file can be customized directly. If a project is using Jasmine via the [Ruby gem][jasmine-gem], this file can be copied into the support directory via `jasmine copy_boot_js`. Other environments (e.g., Python) will have different mechanisms.
|
||||
|
||||
The location of `boot.js` can be specified and/or overridden in `jasmine.yml`.
|
||||
|
||||
[jasmine-gem]: http://github.com/pivotal/jasmine-gem
|
||||
*/
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* ## Require & Instantiate
|
||||
*
|
||||
* Require Jasmine's core files. Specifically, this requires and attaches all of Jasmine's code to the `jasmine` reference.
|
||||
*/
|
||||
window.jasmine = jasmineRequire.core(jasmineRequire);
|
||||
|
||||
/**
|
||||
* Since this is being run in a browser and the results should populate to an HTML page, require the HTML-specific Jasmine code, injecting the same reference.
|
||||
*/
|
||||
jasmineRequire.html(jasmine);
|
||||
|
||||
/**
|
||||
* Create the Jasmine environment. This is used to run all specs in a project.
|
||||
*/
|
||||
var env = jasmine.getEnv();
|
||||
|
||||
/**
|
||||
* ## The Global Interface
|
||||
*
|
||||
* Build up the functions that will be exposed as the Jasmine public interface. A project can customize, rename or alias any of these functions as desired, provided the implementation remains unchanged.
|
||||
*/
|
||||
var jasmineInterface = jasmineRequire.interface(jasmine, env);
|
||||
|
||||
/**
|
||||
* Add all of the Jasmine global/public interface to the proper global, so a project can use the public interface directly. For example, calling `describe` in specs instead of `jasmine.getEnv().describe`.
|
||||
*/
|
||||
if (typeof window == "undefined" && typeof exports == "object") {
|
||||
extend(exports, jasmineInterface);
|
||||
} else {
|
||||
extend(window, jasmineInterface);
|
||||
}
|
||||
|
||||
/**
|
||||
* ## Runner Parameters
|
||||
*
|
||||
* More browser specific code - wrap the query string in an object and to allow for getting/setting parameters from the runner user interface.
|
||||
*/
|
||||
|
||||
var queryString = new jasmine.QueryString({
|
||||
getWindowLocation: function() { return window.location; }
|
||||
});
|
||||
|
||||
var catchingExceptions = queryString.getParam("catch");
|
||||
env.catchExceptions(typeof catchingExceptions === "undefined" ? true : catchingExceptions);
|
||||
|
||||
/**
|
||||
* ## Reporters
|
||||
* The `HtmlReporter` builds all of the HTML UI for the runner page. This reporter paints the dots, stars, and x's for specs, as well as all spec names and all failures (if any).
|
||||
*/
|
||||
var htmlReporter = new jasmine.HtmlReporter({
|
||||
env: env,
|
||||
onRaiseExceptionsClick: function() { queryString.navigateWithNewParam("catch", !env.catchingExceptions()); },
|
||||
addToExistingQueryString: function(key, value) { return queryString.fullStringWithNewParam(key, value); },
|
||||
getContainer: function() { return document.body; },
|
||||
createElement: function() { return document.createElement.apply(document, arguments); },
|
||||
createTextNode: function() { return document.createTextNode.apply(document, arguments); },
|
||||
timer: new jasmine.Timer()
|
||||
});
|
||||
|
||||
/**
|
||||
* The `jsApiReporter` also receives spec results, and is used by any environment that needs to extract the results from JavaScript.
|
||||
*/
|
||||
env.addReporter(jasmineInterface.jsApiReporter);
|
||||
env.addReporter(htmlReporter);
|
||||
|
||||
/**
|
||||
* Filter which specs will be run by matching the start of the full name against the `spec` query param.
|
||||
*/
|
||||
var specFilter = new jasmine.HtmlSpecFilter({
|
||||
filterString: function() { return queryString.getParam("spec"); }
|
||||
});
|
||||
|
||||
env.specFilter = function(spec) {
|
||||
return specFilter.matches(spec.getFullName());
|
||||
};
|
||||
|
||||
/**
|
||||
* Setting up timing functions to be able to be overridden. Certain browsers (Safari, IE 8, phantomjs) require this hack.
|
||||
*/
|
||||
window.setTimeout = window.setTimeout;
|
||||
window.setInterval = window.setInterval;
|
||||
window.clearTimeout = window.clearTimeout;
|
||||
window.clearInterval = window.clearInterval;
|
||||
|
||||
/**
|
||||
* ## Execution
|
||||
*
|
||||
* Replace the browser window's `onload`, ensure it's called, and then run all of the loaded specs. This includes initializing the `HtmlReporter` instance and then executing the loaded Jasmine environment. All of this will happen after all of the specs are loaded.
|
||||
*/
|
||||
var currentWindowOnload = window.onload;
|
||||
|
||||
window.onload = function() {
|
||||
if (currentWindowOnload) {
|
||||
currentWindowOnload();
|
||||
}
|
||||
htmlReporter.initialize();
|
||||
env.execute();
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function for readability above.
|
||||
*/
|
||||
function extend(destination, source) {
|
||||
for (var property in source) destination[property] = source[property];
|
||||
return destination;
|
||||
}
|
||||
|
||||
}());
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
Copyright (c) 2008-2015 Pivotal Labs
|
||||
|
||||
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.
|
||||
*/
|
||||
function getJasmineRequireObj() {
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
return exports;
|
||||
} else {
|
||||
window.jasmineRequire = window.jasmineRequire || {};
|
||||
return window.jasmineRequire;
|
||||
}
|
||||
}
|
||||
|
||||
getJasmineRequireObj().console = function(jRequire, j$) {
|
||||
j$.ConsoleReporter = jRequire.ConsoleReporter();
|
||||
};
|
||||
|
||||
getJasmineRequireObj().ConsoleReporter = function() {
|
||||
|
||||
var noopTimer = {
|
||||
start: function(){},
|
||||
elapsed: function(){ return 0; }
|
||||
};
|
||||
|
||||
function ConsoleReporter(options) {
|
||||
var print = options.print,
|
||||
showColors = options.showColors || false,
|
||||
onComplete = options.onComplete || function() {},
|
||||
timer = options.timer || noopTimer,
|
||||
specCount,
|
||||
failureCount,
|
||||
failedSpecs = [],
|
||||
pendingCount,
|
||||
ansi = {
|
||||
green: '\x1B[32m',
|
||||
red: '\x1B[31m',
|
||||
yellow: '\x1B[33m',
|
||||
none: '\x1B[0m'
|
||||
},
|
||||
failedSuites = [];
|
||||
|
||||
print('ConsoleReporter is deprecated and will be removed in a future version.');
|
||||
|
||||
this.jasmineStarted = function() {
|
||||
specCount = 0;
|
||||
failureCount = 0;
|
||||
pendingCount = 0;
|
||||
print('Started');
|
||||
printNewline();
|
||||
timer.start();
|
||||
};
|
||||
|
||||
this.jasmineDone = function() {
|
||||
printNewline();
|
||||
for (var i = 0; i < failedSpecs.length; i++) {
|
||||
specFailureDetails(failedSpecs[i]);
|
||||
}
|
||||
|
||||
if(specCount > 0) {
|
||||
printNewline();
|
||||
|
||||
var specCounts = specCount + ' ' + plural('spec', specCount) + ', ' +
|
||||
failureCount + ' ' + plural('failure', failureCount);
|
||||
|
||||
if (pendingCount) {
|
||||
specCounts += ', ' + pendingCount + ' pending ' + plural('spec', pendingCount);
|
||||
}
|
||||
|
||||
print(specCounts);
|
||||
} else {
|
||||
print('No specs found');
|
||||
}
|
||||
|
||||
printNewline();
|
||||
var seconds = timer.elapsed() / 1000;
|
||||
print('Finished in ' + seconds + ' ' + plural('second', seconds));
|
||||
printNewline();
|
||||
|
||||
for(i = 0; i < failedSuites.length; i++) {
|
||||
suiteFailureDetails(failedSuites[i]);
|
||||
}
|
||||
|
||||
onComplete(failureCount === 0);
|
||||
};
|
||||
|
||||
this.specDone = function(result) {
|
||||
specCount++;
|
||||
|
||||
if (result.status == 'pending') {
|
||||
pendingCount++;
|
||||
print(colored('yellow', '*'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.status == 'passed') {
|
||||
print(colored('green', '.'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.status == 'failed') {
|
||||
failureCount++;
|
||||
failedSpecs.push(result);
|
||||
print(colored('red', 'F'));
|
||||
}
|
||||
};
|
||||
|
||||
this.suiteDone = function(result) {
|
||||
if (result.failedExpectations && result.failedExpectations.length > 0) {
|
||||
failureCount++;
|
||||
failedSuites.push(result);
|
||||
}
|
||||
};
|
||||
|
||||
return this;
|
||||
|
||||
function printNewline() {
|
||||
print('\n');
|
||||
}
|
||||
|
||||
function colored(color, str) {
|
||||
return showColors ? (ansi[color] + str + ansi.none) : str;
|
||||
}
|
||||
|
||||
function plural(str, count) {
|
||||
return count == 1 ? str : str + 's';
|
||||
}
|
||||
|
||||
function repeat(thing, times) {
|
||||
var arr = [];
|
||||
for (var i = 0; i < times; i++) {
|
||||
arr.push(thing);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
function indent(str, spaces) {
|
||||
var lines = (str || '').split('\n');
|
||||
var newArr = [];
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
newArr.push(repeat(' ', spaces).join('') + lines[i]);
|
||||
}
|
||||
return newArr.join('\n');
|
||||
}
|
||||
|
||||
function specFailureDetails(result) {
|
||||
printNewline();
|
||||
print(result.fullName);
|
||||
|
||||
for (var i = 0; i < result.failedExpectations.length; i++) {
|
||||
var failedExpectation = result.failedExpectations[i];
|
||||
printNewline();
|
||||
print(indent(failedExpectation.message, 2));
|
||||
print(indent(failedExpectation.stack, 2));
|
||||
}
|
||||
|
||||
printNewline();
|
||||
}
|
||||
|
||||
function suiteFailureDetails(result) {
|
||||
for (var i = 0; i < result.failedExpectations.length; i++) {
|
||||
printNewline();
|
||||
print(colored('red', 'An error was thrown in an afterAll'));
|
||||
printNewline();
|
||||
print(colored('red', 'AfterAll ' + result.failedExpectations[i].message));
|
||||
|
||||
}
|
||||
printNewline();
|
||||
}
|
||||
}
|
||||
|
||||
return ConsoleReporter;
|
||||
};
|
||||
@@ -0,0 +1,416 @@
|
||||
/*
|
||||
Copyright (c) 2008-2015 Pivotal Labs
|
||||
|
||||
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.
|
||||
*/
|
||||
jasmineRequire.html = function(j$) {
|
||||
j$.ResultsNode = jasmineRequire.ResultsNode();
|
||||
j$.HtmlReporter = jasmineRequire.HtmlReporter(j$);
|
||||
j$.QueryString = jasmineRequire.QueryString();
|
||||
j$.HtmlSpecFilter = jasmineRequire.HtmlSpecFilter();
|
||||
};
|
||||
|
||||
jasmineRequire.HtmlReporter = function(j$) {
|
||||
|
||||
var noopTimer = {
|
||||
start: function() {},
|
||||
elapsed: function() { return 0; }
|
||||
};
|
||||
|
||||
function HtmlReporter(options) {
|
||||
var env = options.env || {},
|
||||
getContainer = options.getContainer,
|
||||
createElement = options.createElement,
|
||||
createTextNode = options.createTextNode,
|
||||
onRaiseExceptionsClick = options.onRaiseExceptionsClick || function() {},
|
||||
addToExistingQueryString = options.addToExistingQueryString || defaultQueryString,
|
||||
timer = options.timer || noopTimer,
|
||||
results = [],
|
||||
specsExecuted = 0,
|
||||
failureCount = 0,
|
||||
pendingSpecCount = 0,
|
||||
htmlReporterMain,
|
||||
symbols,
|
||||
failedSuites = [];
|
||||
|
||||
this.initialize = function() {
|
||||
clearPrior();
|
||||
htmlReporterMain = createDom('div', {className: 'jasmine_html-reporter'},
|
||||
createDom('div', {className: 'banner'},
|
||||
createDom('a', {className: 'title', href: 'http://jasmine.github.io/', target: '_blank'}),
|
||||
createDom('span', {className: 'version'}, j$.version)
|
||||
),
|
||||
createDom('ul', {className: 'symbol-summary'}),
|
||||
createDom('div', {className: 'alert'}),
|
||||
createDom('div', {className: 'results'},
|
||||
createDom('div', {className: 'failures'})
|
||||
)
|
||||
);
|
||||
getContainer().appendChild(htmlReporterMain);
|
||||
|
||||
symbols = find('.symbol-summary');
|
||||
};
|
||||
|
||||
var totalSpecsDefined;
|
||||
this.jasmineStarted = function(options) {
|
||||
totalSpecsDefined = options.totalSpecsDefined || 0;
|
||||
timer.start();
|
||||
};
|
||||
|
||||
var summary = createDom('div', {className: 'summary'});
|
||||
|
||||
var topResults = new j$.ResultsNode({}, '', null),
|
||||
currentParent = topResults;
|
||||
|
||||
this.suiteStarted = function(result) {
|
||||
currentParent.addChild(result, 'suite');
|
||||
currentParent = currentParent.last();
|
||||
};
|
||||
|
||||
this.suiteDone = function(result) {
|
||||
if (result.status == 'failed') {
|
||||
failedSuites.push(result);
|
||||
}
|
||||
|
||||
if (currentParent == topResults) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentParent = currentParent.parent;
|
||||
};
|
||||
|
||||
this.specStarted = function(result) {
|
||||
currentParent.addChild(result, 'spec');
|
||||
};
|
||||
|
||||
var failures = [];
|
||||
this.specDone = function(result) {
|
||||
if(noExpectations(result) && typeof console !== 'undefined' && typeof console.error !== 'undefined') {
|
||||
console.error('Spec \'' + result.fullName + '\' has no expectations.');
|
||||
}
|
||||
|
||||
if (result.status != 'disabled') {
|
||||
specsExecuted++;
|
||||
}
|
||||
|
||||
symbols.appendChild(createDom('li', {
|
||||
className: noExpectations(result) ? 'empty' : result.status,
|
||||
id: 'spec_' + result.id,
|
||||
title: result.fullName
|
||||
}
|
||||
));
|
||||
|
||||
if (result.status == 'failed') {
|
||||
failureCount++;
|
||||
|
||||
var failure =
|
||||
createDom('div', {className: 'spec-detail failed'},
|
||||
createDom('div', {className: 'description'},
|
||||
createDom('a', {title: result.fullName, href: specHref(result)}, result.fullName)
|
||||
),
|
||||
createDom('div', {className: 'messages'})
|
||||
);
|
||||
var messages = failure.childNodes[1];
|
||||
|
||||
for (var i = 0; i < result.failedExpectations.length; i++) {
|
||||
var expectation = result.failedExpectations[i];
|
||||
messages.appendChild(createDom('div', {className: 'result-message'}, expectation.message));
|
||||
messages.appendChild(createDom('div', {className: 'stack-trace'}, expectation.stack));
|
||||
}
|
||||
|
||||
failures.push(failure);
|
||||
}
|
||||
|
||||
if (result.status == 'pending') {
|
||||
pendingSpecCount++;
|
||||
}
|
||||
};
|
||||
|
||||
this.jasmineDone = function() {
|
||||
var banner = find('.banner');
|
||||
banner.appendChild(createDom('span', {className: 'duration'}, 'finished in ' + timer.elapsed() / 1000 + 's'));
|
||||
|
||||
var alert = find('.alert');
|
||||
|
||||
alert.appendChild(createDom('span', { className: 'exceptions' },
|
||||
createDom('label', { className: 'label', 'for': 'raise-exceptions' }, 'raise exceptions'),
|
||||
createDom('input', {
|
||||
className: 'raise',
|
||||
id: 'raise-exceptions',
|
||||
type: 'checkbox'
|
||||
})
|
||||
));
|
||||
var checkbox = find('#raise-exceptions');
|
||||
|
||||
checkbox.checked = !env.catchingExceptions();
|
||||
checkbox.onclick = onRaiseExceptionsClick;
|
||||
|
||||
if (specsExecuted < totalSpecsDefined) {
|
||||
var skippedMessage = 'Ran ' + specsExecuted + ' of ' + totalSpecsDefined + ' specs - run all';
|
||||
alert.appendChild(
|
||||
createDom('span', {className: 'bar skipped'},
|
||||
createDom('a', {href: '?', title: 'Run all specs'}, skippedMessage)
|
||||
)
|
||||
);
|
||||
}
|
||||
var statusBarMessage = '';
|
||||
var statusBarClassName = 'bar ';
|
||||
|
||||
if (totalSpecsDefined > 0) {
|
||||
statusBarMessage += pluralize('spec', specsExecuted) + ', ' + pluralize('failure', failureCount);
|
||||
if (pendingSpecCount) { statusBarMessage += ', ' + pluralize('pending spec', pendingSpecCount); }
|
||||
statusBarClassName += (failureCount > 0) ? 'failed' : 'passed';
|
||||
} else {
|
||||
statusBarClassName += 'skipped';
|
||||
statusBarMessage += 'No specs found';
|
||||
}
|
||||
|
||||
alert.appendChild(createDom('span', {className: statusBarClassName}, statusBarMessage));
|
||||
|
||||
for(i = 0; i < failedSuites.length; i++) {
|
||||
var failedSuite = failedSuites[i];
|
||||
for(var j = 0; j < failedSuite.failedExpectations.length; j++) {
|
||||
var errorBarMessage = 'AfterAll ' + failedSuite.failedExpectations[j].message;
|
||||
var errorBarClassName = 'bar errored';
|
||||
alert.appendChild(createDom('span', {className: errorBarClassName}, errorBarMessage));
|
||||
}
|
||||
}
|
||||
|
||||
var results = find('.results');
|
||||
results.appendChild(summary);
|
||||
|
||||
summaryList(topResults, summary);
|
||||
|
||||
function summaryList(resultsTree, domParent) {
|
||||
var specListNode;
|
||||
for (var i = 0; i < resultsTree.children.length; i++) {
|
||||
var resultNode = resultsTree.children[i];
|
||||
if (resultNode.type == 'suite') {
|
||||
var suiteListNode = createDom('ul', {className: 'suite', id: 'suite-' + resultNode.result.id},
|
||||
createDom('li', {className: 'suite-detail'},
|
||||
createDom('a', {href: specHref(resultNode.result)}, resultNode.result.description)
|
||||
)
|
||||
);
|
||||
|
||||
summaryList(resultNode, suiteListNode);
|
||||
domParent.appendChild(suiteListNode);
|
||||
}
|
||||
if (resultNode.type == 'spec') {
|
||||
if (domParent.getAttribute('class') != 'specs') {
|
||||
specListNode = createDom('ul', {className: 'specs'});
|
||||
domParent.appendChild(specListNode);
|
||||
}
|
||||
var specDescription = resultNode.result.description;
|
||||
if(noExpectations(resultNode.result)) {
|
||||
specDescription = 'SPEC HAS NO EXPECTATIONS ' + specDescription;
|
||||
}
|
||||
if(resultNode.result.status === 'pending' && resultNode.result.pendingReason !== '') {
|
||||
specDescription = specDescription + ' PENDING WITH MESSAGE: ' + resultNode.result.pendingReason;
|
||||
}
|
||||
specListNode.appendChild(
|
||||
createDom('li', {
|
||||
className: resultNode.result.status,
|
||||
id: 'spec-' + resultNode.result.id
|
||||
},
|
||||
createDom('a', {href: specHref(resultNode.result)}, specDescription)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (failures.length) {
|
||||
alert.appendChild(
|
||||
createDom('span', {className: 'menu bar spec-list'},
|
||||
createDom('span', {}, 'Spec List | '),
|
||||
createDom('a', {className: 'failures-menu', href: '#'}, 'Failures')));
|
||||
alert.appendChild(
|
||||
createDom('span', {className: 'menu bar failure-list'},
|
||||
createDom('a', {className: 'spec-list-menu', href: '#'}, 'Spec List'),
|
||||
createDom('span', {}, ' | Failures ')));
|
||||
|
||||
find('.failures-menu').onclick = function() {
|
||||
setMenuModeTo('failure-list');
|
||||
};
|
||||
find('.spec-list-menu').onclick = function() {
|
||||
setMenuModeTo('spec-list');
|
||||
};
|
||||
|
||||
setMenuModeTo('failure-list');
|
||||
|
||||
var failureNode = find('.failures');
|
||||
for (var i = 0; i < failures.length; i++) {
|
||||
failureNode.appendChild(failures[i]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return this;
|
||||
|
||||
function find(selector) {
|
||||
return getContainer().querySelector('.jasmine_html-reporter ' + selector);
|
||||
}
|
||||
|
||||
function clearPrior() {
|
||||
// return the reporter
|
||||
var oldReporter = find('');
|
||||
|
||||
if(oldReporter) {
|
||||
getContainer().removeChild(oldReporter);
|
||||
}
|
||||
}
|
||||
|
||||
function createDom(type, attrs, childrenVarArgs) {
|
||||
var el = createElement(type);
|
||||
|
||||
for (var i = 2; i < arguments.length; i++) {
|
||||
var child = arguments[i];
|
||||
|
||||
if (typeof child === 'string') {
|
||||
el.appendChild(createTextNode(child));
|
||||
} else {
|
||||
if (child) {
|
||||
el.appendChild(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var attr in attrs) {
|
||||
if (attr == 'className') {
|
||||
el[attr] = attrs[attr];
|
||||
} else {
|
||||
el.setAttribute(attr, attrs[attr]);
|
||||
}
|
||||
}
|
||||
|
||||
return el;
|
||||
}
|
||||
|
||||
function pluralize(singular, count) {
|
||||
var word = (count == 1 ? singular : singular + 's');
|
||||
|
||||
return '' + count + ' ' + word;
|
||||
}
|
||||
|
||||
function specHref(result) {
|
||||
return addToExistingQueryString('spec', result.fullName);
|
||||
}
|
||||
|
||||
function defaultQueryString(key, value) {
|
||||
return '?' + key + '=' + value;
|
||||
}
|
||||
|
||||
function setMenuModeTo(mode) {
|
||||
htmlReporterMain.setAttribute('class', 'jasmine_html-reporter ' + mode);
|
||||
}
|
||||
|
||||
function noExpectations(result) {
|
||||
return (result.failedExpectations.length + result.passedExpectations.length) === 0 &&
|
||||
result.status === 'passed';
|
||||
}
|
||||
}
|
||||
|
||||
return HtmlReporter;
|
||||
};
|
||||
|
||||
jasmineRequire.HtmlSpecFilter = function() {
|
||||
function HtmlSpecFilter(options) {
|
||||
var filterString = options && options.filterString() && options.filterString().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
|
||||
var filterPattern = new RegExp(filterString);
|
||||
|
||||
this.matches = function(specName) {
|
||||
return filterPattern.test(specName);
|
||||
};
|
||||
}
|
||||
|
||||
return HtmlSpecFilter;
|
||||
};
|
||||
|
||||
jasmineRequire.ResultsNode = function() {
|
||||
function ResultsNode(result, type, parent) {
|
||||
this.result = result;
|
||||
this.type = type;
|
||||
this.parent = parent;
|
||||
|
||||
this.children = [];
|
||||
|
||||
this.addChild = function(result, type) {
|
||||
this.children.push(new ResultsNode(result, type, this));
|
||||
};
|
||||
|
||||
this.last = function() {
|
||||
return this.children[this.children.length - 1];
|
||||
};
|
||||
}
|
||||
|
||||
return ResultsNode;
|
||||
};
|
||||
|
||||
jasmineRequire.QueryString = function() {
|
||||
function QueryString(options) {
|
||||
|
||||
this.navigateWithNewParam = function(key, value) {
|
||||
options.getWindowLocation().search = this.fullStringWithNewParam(key, value);
|
||||
};
|
||||
|
||||
this.fullStringWithNewParam = function(key, value) {
|
||||
var paramMap = queryStringToParamMap();
|
||||
paramMap[key] = value;
|
||||
return toQueryString(paramMap);
|
||||
};
|
||||
|
||||
this.getParam = function(key) {
|
||||
return queryStringToParamMap()[key];
|
||||
};
|
||||
|
||||
return this;
|
||||
|
||||
function toQueryString(paramMap) {
|
||||
var qStrPairs = [];
|
||||
for (var prop in paramMap) {
|
||||
qStrPairs.push(encodeURIComponent(prop) + '=' + encodeURIComponent(paramMap[prop]));
|
||||
}
|
||||
return '?' + qStrPairs.join('&');
|
||||
}
|
||||
|
||||
function queryStringToParamMap() {
|
||||
var paramStr = options.getWindowLocation().search.substring(1),
|
||||
params = [],
|
||||
paramMap = {};
|
||||
|
||||
if (paramStr.length > 0) {
|
||||
params = paramStr.split('&');
|
||||
for (var i = 0; i < params.length; i++) {
|
||||
var p = params[i].split('=');
|
||||
var value = decodeURIComponent(p[1]);
|
||||
if (value === 'true' || value === 'false') {
|
||||
value = JSON.parse(value);
|
||||
}
|
||||
paramMap[decodeURIComponent(p[0])] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return paramMap;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return QueryString;
|
||||
};
|
||||
File diff suppressed because one or more lines are too long
3048
plugins/cordova-sqlite-storage/spec/www/lib/jasmine-2.2.0/jasmine.js
Normal file
3048
plugins/cordova-sqlite-storage/spec/www/lib/jasmine-2.2.0/jasmine.js
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
99
plugins/cordova-sqlite-storage/spec/www/spec/big-memory-test.js
vendored
Normal file
99
plugins/cordova-sqlite-storage/spec/www/spec/big-memory-test.js
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
/* 'use strict'; */
|
||||
|
||||
// increased timeout for these tests:
|
||||
var MYTIMEOUT = 120000;
|
||||
|
||||
var DEFAULT_SIZE = 5000000; // max to avoid popup in safari/ios
|
||||
|
||||
// FUTURE TBD 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); } // '==='
|
||||
|
||||
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 scenarioList = [ 'Plugin', 'HTML5' ];
|
||||
|
||||
var scenarioCount = 1;
|
||||
// FUTURE:
|
||||
//var scenarioCount = (!!window.hasWebKitBrowser) ? 2 : 1;
|
||||
|
||||
// big memory test(s):
|
||||
var mytests = function() {
|
||||
|
||||
for (var i=0; i<scenarioCount; ++i) {
|
||||
|
||||
describe(scenarioList[i] + ': big memory test(s)', function() {
|
||||
var scenarioName = scenarioList[i];
|
||||
var suiteName = scenarioName + ': ';
|
||||
var isWebSql = (i !== 0);
|
||||
|
||||
// NOTE: MUST be defined in function scope, NOT outer scope:
|
||||
var openDatabase = function(name, ignored1, ignored2, ignored3) {
|
||||
if (isWebSql) {
|
||||
return window.openDatabase(name, "1.0", "Demo", DEFAULT_SIZE);
|
||||
} else {
|
||||
return window.sqlitePlugin.openDatabase(name, "1.0", "Demo", DEFAULT_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// litehelpers/Cordova-sqlite-storage#18 - thanks to @sonalk:
|
||||
it(suiteName + 'adding a large number of records', function(done) {
|
||||
|
||||
if (isWP8) pending('BROKEN for WP(7/8)'); // Hangs on wp8 platform
|
||||
|
||||
// remove next line to reproduce crash on Android version:
|
||||
if (isAndroid && !isWebSql) pending('BROKEN-crashing on Android version of plugin');
|
||||
|
||||
var db = openDatabase("add-large-number-of-records.db", "1.0", "Demo", DEFAULT_SIZE);
|
||||
|
||||
expect(db).toBeDefined()
|
||||
|
||||
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('DROP TABLE IF EXISTS db');
|
||||
tx.executeSql('CREATE TABLE IF NOT EXISTS db (idd integer primary key, dataa text, data_numm integer)');
|
||||
tx.executeSql('DROP TABLE IF EXISTS abc');
|
||||
tx.executeSql('CREATE TABLE IF NOT EXISTS abc (iddd integer primary key, dataaa text, data_nummm integer)');
|
||||
tx.executeSql('DROP TABLE IF EXISTS abcd');
|
||||
|
||||
for (var k = 0; k < 80000; k++) { //loop to add 80000 records
|
||||
tx.executeSql("INSERT INTO test_table (data, data_num) VALUES (?,?)", ["test", 100]);
|
||||
tx.executeSql("INSERT INTO db (dataa, data_numm) VALUES (?,?)", ["abc", 100]);
|
||||
|
||||
tx.executeSql("INSERT INTO abc (dataaa, data_nummm) VALUES (?,?)", ["abc", 100]);
|
||||
} ////-for loop ends
|
||||
|
||||
//db.transaction ends
|
||||
}, function (e) {
|
||||
// not expected:
|
||||
expect(false).toBe(true);
|
||||
}, function () {
|
||||
// expected ok:
|
||||
expect(true).toBe(true);
|
||||
done();
|
||||
});
|
||||
|
||||
}, MYTIMEOUT);
|
||||
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
if (window.hasBrowser) mytests();
|
||||
else exports.defineAutoTests = mytests;
|
||||
|
||||
/* vim: set expandtab : */
|
||||
33
plugins/cordova-sqlite-storage/spec/www/spec/browser-check-startup.js
vendored
Normal file
33
plugins/cordova-sqlite-storage/spec/www/spec/browser-check-startup.js
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
/* 'use strict'; */
|
||||
|
||||
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]
|
||||
|
||||
window.hasBrowser = true;
|
||||
window.hasWebKitBrowser = isWebKit;
|
||||
|
||||
describe('check startup', function() {
|
||||
it('receives deviceready event', function(done) {
|
||||
expect(true).toBe(true);
|
||||
document.addEventListener("deviceready", function() {
|
||||
done();
|
||||
});
|
||||
}, MYTIMEOUT);
|
||||
|
||||
it('has openDatabase', function() {
|
||||
if (isWebKit) expect(window.openDatabase).toBeDefined();
|
||||
expect(window.sqlitePlugin).toBeDefined();
|
||||
expect(window.sqlitePlugin.openDatabase).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
/* vim: set expandtab : */
|
||||
2243
plugins/cordova-sqlite-storage/spec/www/spec/legacy.js
vendored
Normal file
2243
plugins/cordova-sqlite-storage/spec/www/spec/legacy.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
558
plugins/cordova-sqlite-storage/spec/www/spec/simple-test.js
vendored
Normal file
558
plugins/cordova-sqlite-storage/spec/www/spec/simple-test.js
vendored
Normal file
@@ -0,0 +1,558 @@
|
||||
/* 'use strict'; */
|
||||
|
||||
var MYTIMEOUT = 12000;
|
||||
|
||||
var DEFAULT_SIZE = 5000000; // max to avoid popup in safari/ios
|
||||
|
||||
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 scenarioList = [ isAndroid ? 'Plugin-sqlite-connector' : 'Plugin', 'HTML5', 'Plugin-android.database' ];
|
||||
|
||||
//var scenarioCount = isAndroid ? 3 : (isIE ? 1 : 2);
|
||||
//var scenarioCount = (!!window.hasWebKitBrowser) ? 2 : 1;
|
||||
var hasAndroidWebKitBrowser = isAndroid && (!!window.hasWebKitBrowser);
|
||||
var scenarioCount = hasAndroidWebKitBrowser ? 3 : ((!!window.hasWebKitBrowser) ? 2 : 1);
|
||||
|
||||
// simple tests:
|
||||
var mytests = function() {
|
||||
|
||||
for (var i=0; i<scenarioCount; ++i) {
|
||||
|
||||
describe(scenarioList[i] + ': SIMPLE SQL test(s)', function() {
|
||||
var scenarioName = scenarioList[i];
|
||||
var suiteName = scenarioName + ': ';
|
||||
var isWebSql = (i === 1);
|
||||
var isOldImpl = (i === 2);
|
||||
|
||||
// NOTE: MUST be defined in function scope, NOT outer scope:
|
||||
var openDatabase = function(name, ignored1, ignored2, ignored3) {
|
||||
if (isOldImpl) {
|
||||
return window.sqlitePlugin.openDatabase({name: name, androidDatabaseImplementation: 2});
|
||||
}
|
||||
if (isWebSql) {
|
||||
return window.openDatabase(name, '1.0', 'Test', DEFAULT_SIZE);
|
||||
} else {
|
||||
return window.sqlitePlugin.openDatabase(name, '1.0', 'Test', DEFAULT_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
it(suiteName + 'US-ASCII String manipulation test',
|
||||
function(done) {
|
||||
var db = openDatabase('ASCII-string-test.db', '1.0', 'Test', DEFAULT_SIZE);
|
||||
|
||||
expect(db).toBeDefined();
|
||||
|
||||
db.transaction(function(tx) {
|
||||
|
||||
expect(tx).toBeDefined();
|
||||
|
||||
tx.executeSql("SELECT UPPER('Some US-ASCII text') AS uppertext", [], function(tx, res) {
|
||||
console.log('res.rows.item(0).uppertext: ' + res.rows.item(0).uppertext);
|
||||
expect(res.rows.item(0).uppertext).toEqual('SOME US-ASCII TEXT');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
}, MYTIMEOUT);
|
||||
|
||||
// Only test ICU-UNICODE with Android 5.0(+) (Web SQL):
|
||||
if (isWebSql && /Android [5-9]/.test(navigator.userAgent))
|
||||
it(suiteName + 'ICU-UNICODE string manipulation test', function(done) {
|
||||
if ((!isWebSql) && isAndroid) pending('BROKEN for Android version of plugin [with sqlite-connector]');
|
||||
|
||||
var db = openDatabase('UNICODE-string-test.db', '1.0', 'Test', DEFAULT_SIZE);
|
||||
|
||||
expect(db).toBeDefined();
|
||||
|
||||
db.transaction(function(tx) {
|
||||
|
||||
expect(tx).toBeDefined();
|
||||
|
||||
// 'Some Cyrillic text'
|
||||
tx.executeSql("SELECT UPPER('Какой-то кириллический текст') AS uppertext", [], function (tx, res) {
|
||||
console.log('res.rows.item(0).uppertext: ' + res.rows.item(0).uppertext);
|
||||
expect(res.rows.item(0).uppertext).toEqual('КАКОЙ-ТО КИРИЛЛИЧЕСКИЙ ТЕКСТ');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it(suiteName + 'Simple INSERT test: check insertId & rowsAffected in result', function(done) {
|
||||
|
||||
var db = openDatabase('INSERT-test.db', '1.0', 'Test', DEFAULT_SIZE);
|
||||
|
||||
expect(db).toBeDefined();
|
||||
|
||||
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) {
|
||||
expect(res).toBeDefined();
|
||||
expect(res.insertId).toBeDefined();
|
||||
expect(res.rowsAffected).toBe(1);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
}, MYTIMEOUT);
|
||||
|
||||
it(suiteName + 'db transaction test',
|
||||
function(done) {
|
||||
var db = openDatabase('db-trx-test.db', '1.0', 'Test', DEFAULT_SIZE);
|
||||
|
||||
expect(db).toBeDefined();
|
||||
|
||||
var check = 0;
|
||||
|
||||
db.transaction(function(tx) {
|
||||
// first tx object:
|
||||
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) {
|
||||
// check tx & res object parameters:
|
||||
expect(tx).toBeDefined();
|
||||
expect(res).toBeDefined();
|
||||
|
||||
expect(res.insertId).toBeDefined();
|
||||
expect(res.rowsAffected).toBe(1);
|
||||
|
||||
db.transaction(function(tx) {
|
||||
// second tx object:
|
||||
expect(tx).toBeDefined();
|
||||
|
||||
tx.executeSql('SELECT COUNT(id) AS cnt FROM test_table;', [], function(tx, res) {
|
||||
++check;
|
||||
|
||||
expect(res.rows.length).toBe(1);
|
||||
expect(res.rows.item(0).cnt).toBe(1);
|
||||
});
|
||||
|
||||
tx.executeSql('SELECT data_num FROM test_table;', [], function(tx, res) {
|
||||
++check;
|
||||
|
||||
expect(res.rows.length).toBe(1);
|
||||
expect(res.rows.item(0).data_num).toBe(100);
|
||||
});
|
||||
|
||||
tx.executeSql('UPDATE test_table SET data_num = ? WHERE data_num = 100', [101], function(tx, res) {
|
||||
++check;
|
||||
|
||||
expect(res.rowsAffected).toBe(1);
|
||||
});
|
||||
|
||||
tx.executeSql('SELECT data_num FROM test_table;', [], function(tx, res) {
|
||||
++check;
|
||||
|
||||
expect(res.rows.length).toBe(1);
|
||||
expect(res.rows.item(0).data_num).toBe(101);
|
||||
});
|
||||
|
||||
tx.executeSql("DELETE FROM test_table WHERE data LIKE 'tes%'", [], function(tx, res) {
|
||||
++check;
|
||||
|
||||
expect(res.rowsAffected).toBe(1);
|
||||
});
|
||||
|
||||
tx.executeSql('SELECT data_num FROM test_table;', [], function(tx, res) {
|
||||
++check;
|
||||
|
||||
expect(res.rows.length).toBe(0);
|
||||
});
|
||||
|
||||
}, function(e) {
|
||||
// not expected:
|
||||
expect(false).toBe(true);
|
||||
console.log('ERROR: ' + e.message);
|
||||
}, function() {
|
||||
console.log('second tx ok success cb');
|
||||
expect(check).toBe(6);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
}, function(e) {
|
||||
// not expected:
|
||||
expect(false).toBe(true);
|
||||
console.log('ERROR: ' + e.message);
|
||||
});
|
||||
}, function(e) {
|
||||
// not expected:
|
||||
expect(false).toBe(true);
|
||||
console.log('ERROR: ' + e.message);
|
||||
done();
|
||||
}, function() {
|
||||
console.log('first tx success cb OK');
|
||||
});
|
||||
|
||||
}, MYTIMEOUT);
|
||||
|
||||
it(suiteName + 'number values inserted using number bindings',
|
||||
function(done) {
|
||||
var db = openDatabase('Value-binding-test.db', '1.0', 'Test', DEFAULT_SIZE);
|
||||
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_text1, data_text2, data_int, data_real)');
|
||||
}, function(err) {
|
||||
// went wrong:
|
||||
expect(false).toBe(true);
|
||||
}, function() {
|
||||
db.transaction(function(tx) {
|
||||
// create columns with no type affinity
|
||||
tx.executeSql('INSERT INTO test_table (data_text1, data_text2, data_int, data_real) VALUES (?,?,?,?)', ['314159', '3.14159', 314159, 3.14159], function(tx, res) {
|
||||
expect(res.rowsAffected).toBe(1);
|
||||
tx.executeSql('SELECT * FROM test_table', [], function(tx, res) {
|
||||
var row = res.rows.item(0);
|
||||
expect(row.data_text1).toBe('314159'); // data_text1 should have inserted data as text
|
||||
if (!isWP8) // JSON issue in WP(8) version
|
||||
expect(row.data_text2).toBe('3.14159'); // data_text2 should have inserted data as text
|
||||
expect(row.data_int).toBe(314159); // data_int should have inserted data as an integer
|
||||
expect(Math.abs(row.data_real - 3.14159) < 0.000001).toBe(true); // data_real should have inserted data as a real
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}, MYTIMEOUT);
|
||||
|
||||
/* THANKS to @calebeaires: */
|
||||
it(suiteName + 'create virtual table using FTS3', function(done) {
|
||||
if (isWP8) pending('NOT IMPLEMENTED for WP(8)'); // NOT IMPLEMENTED in CSharp-SQLite
|
||||
|
||||
var db = openDatabase('virtual-table-using-fts3.db', '1.0', 'Test', DEFAULT_SIZE);
|
||||
expect(db).toBeDefined();
|
||||
|
||||
db.transaction(function(tx) {
|
||||
expect(tx).toBeDefined();
|
||||
|
||||
tx.executeSql('CREATE INDEX liv_index ON book (liv, cap);');
|
||||
tx.executeSql('DROP TABLE IF EXISTS virtual_book');
|
||||
tx.executeSql('CREATE VIRTUAL TABLE IF NOT EXISTS virtual_book USING FTS3 (liv, cap, ver, tex, tes);', [], function(tx, res) {
|
||||
// ok:
|
||||
expect(true).toBe(true);
|
||||
}, function(err) {
|
||||
// went wrong:
|
||||
expect(false).toBe(true);
|
||||
});
|
||||
}, function(err) {
|
||||
// [ignored here]:
|
||||
//expect(false).toBe(true);
|
||||
expect(true).toBe(true);
|
||||
done();
|
||||
}, function() {
|
||||
// verify tx was ok:
|
||||
expect(true).toBe(true);
|
||||
done();
|
||||
});
|
||||
}, MYTIMEOUT);
|
||||
|
||||
// NOTE: looking at sqlite3.c, if FTS3 is enabled, FTS4 seems to be working as well!
|
||||
// (thanks again to @calebeaires for this scenario)
|
||||
it(suiteName + 'create virtual table using FTS4', function(done) {
|
||||
if (isWP8) pending('NOT IMPLEMENTED for WP(8)'); // NOT IMPLEMENTED in CSharp-SQLite
|
||||
|
||||
var db = openDatabase('virtual-table-using-fts4.db', '1.0', 'Test', DEFAULT_SIZE);
|
||||
expect(db).toBeDefined();
|
||||
|
||||
db.transaction(function(tx) {
|
||||
expect(tx).toBeDefined();
|
||||
|
||||
tx.executeSql('CREATE INDEX liv_index ON book (liv, cap);');
|
||||
tx.executeSql('DROP TABLE IF EXISTS virtual_book');
|
||||
tx.executeSql('CREATE VIRTUAL TABLE IF NOT EXISTS virtual_book USING FTS4 (liv, cap, ver, tex, tes);', [], function(tx, res) {
|
||||
// ok:
|
||||
expect(true).toBe(true);
|
||||
}, function(err) {
|
||||
// went wrong:
|
||||
expect(false).toBe(true);
|
||||
});
|
||||
}, function(err) {
|
||||
// [ignored here]:
|
||||
//expect(false).toBe(true);
|
||||
expect(true).toBe(true);
|
||||
done();
|
||||
}, function() {
|
||||
// verify tx was ok:
|
||||
expect(true).toBe(true);
|
||||
done();
|
||||
});
|
||||
}, MYTIMEOUT);
|
||||
|
||||
if (!isWebSql) {
|
||||
it(suiteName + 'create virtual table using R-Tree', function(done) {
|
||||
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)
|
||||
|
||||
var db = openDatabase('virtual-table-using-r-tree.db', '1.0', 'Test', DEFAULT_SIZE);
|
||||
expect(db).toBeDefined();
|
||||
|
||||
db.transaction(function(tx) {
|
||||
expect(tx).toBeDefined();
|
||||
|
||||
tx.executeSql('DROP TABLE IF EXISTS demo_index');
|
||||
// from https://www.sqlite.org/rtree.html
|
||||
tx.executeSql('CREATE VIRTUAL TABLE IF NOT EXISTS demo_index USING rtree (id, minX, maxX, minY, maxY);', [], function(tx, res) {
|
||||
// ok:
|
||||
expect(true).toBe(true);
|
||||
}, function(err) {
|
||||
// went wrong:
|
||||
expect(false).toBe(true);
|
||||
});
|
||||
}, function(err) {
|
||||
// [ignored here]:
|
||||
//expect(false).toBe(true);
|
||||
expect(true).toBe(true);
|
||||
done();
|
||||
}, function() {
|
||||
// verify tx was ok:
|
||||
expect(true).toBe(true);
|
||||
done();
|
||||
});
|
||||
}, MYTIMEOUT);
|
||||
}
|
||||
|
||||
/* found due to investigation of litehelpers/Cordova-sqlite-storage#226: */
|
||||
it(suiteName + 'Skip callbacks after syntax error with no handler', function(done) {
|
||||
if (!isWebSql) pending('Plugin BROKEN'); // XXX TODO
|
||||
|
||||
var db = openDatabase('first-syntax-error-with-no-handler.db', '1.0', 'Test', DEFAULT_SIZE);
|
||||
expect(db).toBeDefined();
|
||||
|
||||
db.transaction(function(tx) {
|
||||
expect(tx).toBeDefined();
|
||||
tx.executeSql('DROP TABLE IF EXISTS tt');
|
||||
tx.executeSql('CREATE TABLE IF NOT EXISTS tt (data unique)');
|
||||
|
||||
// This insertion has a sql syntax error-which is not handled:
|
||||
tx.executeSql('insert into tt (data) VALUES ', [123]);
|
||||
|
||||
// second insertion with syntax error in transaction ["skipped" by Web SQL]:
|
||||
tx.executeSql('insert into tt (data) VALUES ', [456], function(tx, res) {
|
||||
// not expected:
|
||||
expect(false).toBe(true);
|
||||
}, function(err) {
|
||||
// expected, but then it shows the handling this sql statement is NOT skipped (by the plugin):
|
||||
expect(false).toBe(true);
|
||||
return false;
|
||||
});
|
||||
}, function(err) {
|
||||
// transaction expected to fail:
|
||||
expect(true).toBe(true);
|
||||
done();
|
||||
}, function() {
|
||||
// not expected [ignored for now]:
|
||||
//expect(false).toBe(true);
|
||||
expect(true).toBe(true);
|
||||
done();
|
||||
});
|
||||
}, MYTIMEOUT);
|
||||
|
||||
/* found due to investigation of litehelpers/Cordova-sqlite-storage#226: */
|
||||
it(suiteName + 'Skip callbacks after syntax error handler returns true', function(done) {
|
||||
if (!isWebSql) pending('Plugin BROKEN'); // XXX TODO
|
||||
|
||||
var db = openDatabase('first-syntax-error-handler-returns-true.db', '1.0', 'Test', DEFAULT_SIZE);
|
||||
expect(db).toBeDefined();
|
||||
|
||||
var isFirstErrorHandlerCalled = false; // poor man's spy
|
||||
var isSecondErrorHandlerCalled = false;
|
||||
|
||||
db.transaction(function(tx) {
|
||||
expect(tx).toBeDefined();
|
||||
tx.executeSql('DROP TABLE IF EXISTS tt');
|
||||
tx.executeSql('CREATE TABLE IF NOT EXISTS tt (data unique)');
|
||||
|
||||
// first sql syntax error with handler that returns undefined [nothing]:
|
||||
tx.executeSql('insert into tt (data) VALUES ', [456], function(tx, res) {
|
||||
// not expected:
|
||||
expect(false).toBe(true);
|
||||
}, function(err) {
|
||||
// expected ok:
|
||||
expect(true).toBe(true);
|
||||
isFirstErrorHandlerCalled = true;
|
||||
// (should) completely stop transaction:
|
||||
return true;
|
||||
});
|
||||
|
||||
// second insertion with syntax error with handler that signals explicit recovery [SKIPPED by Web SQL]:
|
||||
tx.executeSql('insert into tt (data) VALUES ', [456], function(tx, res) {
|
||||
// not expected:
|
||||
expect(false).toBe(true);
|
||||
}, function(err) {
|
||||
// expected, but then it shows the handling this sql statement is NOT skipped (by the plugin):
|
||||
expect(false).toBe(true);
|
||||
isSecondErrorHandlerCalled = true;
|
||||
// explicit recovery:
|
||||
return false;
|
||||
});
|
||||
}, function(err) {
|
||||
// transaction expected to fail:
|
||||
expect(true).toBe(true);
|
||||
expect(isFirstErrorHandlerCalled).toBe(true);
|
||||
// [ignored for now]:
|
||||
//expect(isSecondErrorHandlerCalled).toBe(false);
|
||||
done();
|
||||
}, function() {
|
||||
// not expected [ignored for now]:
|
||||
//expect(false).toBe(true);
|
||||
expect(true).toBe(true);
|
||||
expect(isFirstErrorHandlerCalled).toBe(true);
|
||||
// [ignored for now]:
|
||||
//expect(isSecondErrorHandlerCalled).toBe(false);
|
||||
done();
|
||||
});
|
||||
}, MYTIMEOUT);
|
||||
|
||||
// ref: litehelpers/Cordova-sqlite-storage#232
|
||||
// according to the spec at http://www.w3.org/TR/webdatabase/ the transaction should be
|
||||
// recovered *only* if the sql error handler returns false.
|
||||
it(suiteName + 'Recover transaction with callbacks after syntax error handler returns false', function(done) {
|
||||
var db = openDatabase('recover-if-syntax-error-handler-returns-false.db', '1.0', 'Test', DEFAULT_SIZE);
|
||||
expect(db).toBeDefined();
|
||||
|
||||
var isFirstErrorHandlerCalled = false; // poor man's spy
|
||||
//var isFirstSuccessHandlerCalled = false; // (not expected)
|
||||
var isSecondSuccessHandlerCalled = false; // expected ok
|
||||
//var isSecondErrorHandlerCalled = false; // (not expected)
|
||||
|
||||
db.transaction(function(tx) {
|
||||
expect(tx).toBeDefined();
|
||||
tx.executeSql('DROP TABLE IF EXISTS tt');
|
||||
tx.executeSql('CREATE TABLE IF NOT EXISTS tt (data unique)');
|
||||
|
||||
// first sql syntax error with handler that returns undefined [nothing]:
|
||||
tx.executeSql('insert into tt (data) VALUES ', [456], function(tx, res) {
|
||||
// not expected:
|
||||
expect(false).toBe(true);
|
||||
}, function(err) {
|
||||
// expected ok:
|
||||
expect(true).toBe(true);
|
||||
isFirstErrorHandlerCalled = true;
|
||||
// [should] recover this transaction:
|
||||
return false;
|
||||
});
|
||||
|
||||
// second sql ok [NOT SKIPPED by Web SQL]:
|
||||
tx.executeSql('SELECT 1', [], function(tx, res) {
|
||||
// expected ok:
|
||||
isSecondSuccessHandlerCalled = true;
|
||||
expect(true).toBe(true);
|
||||
}, function(err) {
|
||||
// not expected:
|
||||
expect(false).toBe(true);
|
||||
//isSecondErrorHandlerCalled = true;
|
||||
return false;
|
||||
});
|
||||
}, function(err) {
|
||||
// not expected:
|
||||
expect(false).toBe(true);
|
||||
expect(isFirstErrorHandlerCalled).toBe(true);
|
||||
done();
|
||||
}, function() {
|
||||
// expected ok:
|
||||
expect(true).toBe(true);
|
||||
expect(isFirstErrorHandlerCalled).toBe(true);
|
||||
expect(isSecondSuccessHandlerCalled).toBe(true);
|
||||
done();
|
||||
});
|
||||
}, MYTIMEOUT);
|
||||
|
||||
// NOTE: as discussed in litehelpers/Cordova-sqlite-storage#232 this plugin is correct
|
||||
// according to the spec at http://www.w3.org/TR/webdatabase/
|
||||
it(suiteName + 'syntax error handler returns undefined', function(done) {
|
||||
var db = openDatabase('syntax-error-handler-returns-undefined.db', '1.0', 'Test', DEFAULT_SIZE);
|
||||
expect(db).toBeDefined();
|
||||
|
||||
var isFirstErrorHandlerCalled = false; // poor man's spy
|
||||
|
||||
db.transaction(function(tx) {
|
||||
expect(tx).toBeDefined();
|
||||
tx.executeSql('DROP TABLE IF EXISTS tt');
|
||||
tx.executeSql('CREATE TABLE IF NOT EXISTS tt (data unique)');
|
||||
|
||||
// first sql syntax error with handler that returns undefined [nothing]:
|
||||
tx.executeSql('insert into tt (data) VALUES ', [456], function(tx, res) {
|
||||
// not expected:
|
||||
expect(false).toBe(true);
|
||||
}, function(err) {
|
||||
// expected ok:
|
||||
expect(true).toBe(true);
|
||||
isFirstErrorHandlerCalled = true;
|
||||
// [should] recover this transaction:
|
||||
//return false;
|
||||
return undefined;
|
||||
});
|
||||
|
||||
// skip second sql [not relevant for this test, difference plugin vs. Web SQL]:
|
||||
|
||||
}, function(err) {
|
||||
// transaction expected to fail [plugin only]:
|
||||
expect(true).toBe(true);
|
||||
expect(isFirstErrorHandlerCalled).toBe(true);
|
||||
done();
|
||||
}, function() {
|
||||
// expected ok for Web SQL ONLY:
|
||||
if (isWebSql)
|
||||
expect(true).toBe(true);
|
||||
else
|
||||
expect(false).toBe(true);
|
||||
expect(isFirstErrorHandlerCalled).toBe(true);
|
||||
done();
|
||||
});
|
||||
}, MYTIMEOUT);
|
||||
|
||||
// FUTURE TODO ref: litehelpers/Cordova-sqlite-storage#232
|
||||
// test case of sql error handler returning values such as "true" (string), 1, 0, null
|
||||
|
||||
if (!isWebSql) {
|
||||
// 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
|
||||
// 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 };
|
||||
try {
|
||||
window.sqlitePlugin.openDatabase({ name: p1 }, function(db) {
|
||||
// not expected:
|
||||
expect(false).toBe(true);
|
||||
done();
|
||||
}, function(error) {
|
||||
// OK but NOT EXPECTED:
|
||||
expect(true).toBe(true);
|
||||
done();
|
||||
});
|
||||
} catch (e) {
|
||||
// stopped by the implementation:
|
||||
expect(true).toBe(true);
|
||||
done();
|
||||
}
|
||||
}, MYTIMEOUT);
|
||||
}
|
||||
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
if (window.hasBrowser) mytests();
|
||||
else exports.defineAutoTests = mytests;
|
||||
|
||||
/* vim: set expandtab : */
|
||||
@@ -0,0 +1,509 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2015: Christopher J. Brody (aka Chris Brody)
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
|
||||
package io.liteglue;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.database.CursorWindow;
|
||||
import android.database.sqlite.SQLiteCursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteException;
|
||||
import android.database.sqlite.SQLiteStatement;
|
||||
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.IllegalArgumentException;
|
||||
import java.lang.Number;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Android Database helper class
|
||||
*/
|
||||
class SQLiteAndroidDatabase
|
||||
{
|
||||
private static final Pattern FIRST_WORD = Pattern.compile("^\\s*(\\S+)",
|
||||
Pattern.CASE_INSENSITIVE);
|
||||
|
||||
private static final Pattern WHERE_CLAUSE = Pattern.compile("\\s+WHERE\\s+(.+)$",
|
||||
Pattern.CASE_INSENSITIVE);
|
||||
|
||||
private static final Pattern UPDATE_TABLE_NAME = Pattern.compile("^\\s*UPDATE\\s+(\\S+)",
|
||||
Pattern.CASE_INSENSITIVE);
|
||||
|
||||
private static final Pattern DELETE_TABLE_NAME = Pattern.compile("^\\s*DELETE\\s+FROM\\s+(\\S+)",
|
||||
Pattern.CASE_INSENSITIVE);
|
||||
|
||||
File dbFile;
|
||||
|
||||
SQLiteDatabase mydb;
|
||||
|
||||
/**
|
||||
* NOTE: Using default constructor, no explicit constructor.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Open a database.
|
||||
*
|
||||
* @param dbfile The database File specification
|
||||
*/
|
||||
void open(File dbfile) throws Exception {
|
||||
dbFile = dbfile; // for possible bug workaround
|
||||
mydb = SQLiteDatabase.openOrCreateDatabase(dbfile, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close a database (in the current thread).
|
||||
*/
|
||||
void closeDatabaseNow() {
|
||||
if (mydb != null) {
|
||||
mydb.close();
|
||||
mydb = null;
|
||||
}
|
||||
}
|
||||
|
||||
void bugWorkaround() throws Exception {
|
||||
this.closeDatabaseNow();
|
||||
this.open(dbFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a batch request and sends the results via cbc.
|
||||
*
|
||||
* @param dbname The name of the database.
|
||||
* @param queryarr Array of query strings
|
||||
* @param jsonparams Array of JSON query parameters
|
||||
* @param queryIDs Array of query ids
|
||||
* @param cbc Callback context from Cordova API
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
void executeSqlBatch(String[] queryarr, JSONArray[] jsonparams,
|
||||
String[] queryIDs, CallbackContext cbc) {
|
||||
|
||||
if (mydb == null) {
|
||||
// not allowed - can only happen if someone has closed (and possibly deleted) a database and then re-used the database
|
||||
cbc.error("database has been closed");
|
||||
return;
|
||||
}
|
||||
|
||||
String query = "";
|
||||
String query_id = "";
|
||||
int len = queryarr.length;
|
||||
JSONArray batchResults = new JSONArray();
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
int rowsAffectedCompat = 0;
|
||||
boolean needRowsAffectedCompat = false;
|
||||
query_id = queryIDs[i];
|
||||
|
||||
JSONObject queryResult = null;
|
||||
String errorMessage = "unknown";
|
||||
|
||||
try {
|
||||
boolean needRawQuery = true;
|
||||
|
||||
query = queryarr[i];
|
||||
|
||||
QueryType queryType = getQueryType(query);
|
||||
|
||||
if (queryType == QueryType.update || queryType == queryType.delete) {
|
||||
if (android.os.Build.VERSION.SDK_INT >= 11) {
|
||||
SQLiteStatement myStatement = mydb.compileStatement(query);
|
||||
|
||||
if (jsonparams != null) {
|
||||
bindArgsToStatement(myStatement, jsonparams[i]);
|
||||
}
|
||||
|
||||
int rowsAffected = -1; // (assuming invalid)
|
||||
|
||||
// Use try & catch just in case android.os.Build.VERSION.SDK_INT >= 11 is lying:
|
||||
try {
|
||||
rowsAffected = myStatement.executeUpdateDelete();
|
||||
// Indicate valid results:
|
||||
needRawQuery = false;
|
||||
} catch (SQLiteException ex) {
|
||||
// Indicate problem & stop this query:
|
||||
ex.printStackTrace();
|
||||
errorMessage = ex.getMessage();
|
||||
Log.v("executeSqlBatch", "SQLiteStatement.executeUpdateDelete(): Error=" + errorMessage);
|
||||
needRawQuery = false;
|
||||
} catch (Exception ex) {
|
||||
// Assuming SDK_INT was lying & method not found:
|
||||
// do nothing here & try again with raw query.
|
||||
}
|
||||
|
||||
if (rowsAffected != -1) {
|
||||
queryResult = new JSONObject();
|
||||
queryResult.put("rowsAffected", rowsAffected);
|
||||
}
|
||||
} else { // pre-honeycomb
|
||||
rowsAffectedCompat = countRowsAffectedCompat(queryType, query, jsonparams, mydb, i);
|
||||
needRowsAffectedCompat = true;
|
||||
}
|
||||
}
|
||||
|
||||
// INSERT:
|
||||
if (queryType == QueryType.insert && jsonparams != null) {
|
||||
needRawQuery = false;
|
||||
|
||||
SQLiteStatement myStatement = mydb.compileStatement(query);
|
||||
|
||||
bindArgsToStatement(myStatement, jsonparams[i]);
|
||||
|
||||
long insertId = -1; // (invalid)
|
||||
|
||||
try {
|
||||
insertId = myStatement.executeInsert();
|
||||
|
||||
// statement has finished with no constraint violation:
|
||||
queryResult = new JSONObject();
|
||||
if (insertId != -1) {
|
||||
queryResult.put("insertId", insertId);
|
||||
queryResult.put("rowsAffected", 1);
|
||||
} else {
|
||||
queryResult.put("rowsAffected", 0);
|
||||
}
|
||||
} catch (SQLiteException ex) {
|
||||
// report error result with the error message
|
||||
// could be constraint violation or some other error
|
||||
ex.printStackTrace();
|
||||
errorMessage = ex.getMessage();
|
||||
Log.v("executeSqlBatch", "SQLiteDatabase.executeInsert(): Error=" + errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
if (queryType == QueryType.begin) {
|
||||
needRawQuery = false;
|
||||
try {
|
||||
mydb.beginTransaction();
|
||||
|
||||
queryResult = new JSONObject();
|
||||
queryResult.put("rowsAffected", 0);
|
||||
} catch (SQLiteException ex) {
|
||||
ex.printStackTrace();
|
||||
errorMessage = ex.getMessage();
|
||||
Log.v("executeSqlBatch", "SQLiteDatabase.beginTransaction(): Error=" + errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
if (queryType == QueryType.commit) {
|
||||
needRawQuery = false;
|
||||
try {
|
||||
mydb.setTransactionSuccessful();
|
||||
mydb.endTransaction();
|
||||
|
||||
queryResult = new JSONObject();
|
||||
queryResult.put("rowsAffected", 0);
|
||||
} catch (SQLiteException ex) {
|
||||
ex.printStackTrace();
|
||||
errorMessage = ex.getMessage();
|
||||
Log.v("executeSqlBatch", "SQLiteDatabase.setTransactionSuccessful/endTransaction(): Error=" + errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
if (queryType == QueryType.rollback) {
|
||||
needRawQuery = false;
|
||||
try {
|
||||
mydb.endTransaction();
|
||||
|
||||
queryResult = new JSONObject();
|
||||
queryResult.put("rowsAffected", 0);
|
||||
} catch (SQLiteException ex) {
|
||||
ex.printStackTrace();
|
||||
errorMessage = ex.getMessage();
|
||||
Log.v("executeSqlBatch", "SQLiteDatabase.endTransaction(): Error=" + errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
// raw query for other statements:
|
||||
if (needRawQuery) {
|
||||
queryResult = this.executeSqlStatementQuery(mydb, query, jsonparams[i], cbc);
|
||||
|
||||
if (needRowsAffectedCompat) {
|
||||
queryResult.put("rowsAffected", rowsAffectedCompat);
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
errorMessage = ex.getMessage();
|
||||
Log.v("executeSqlBatch", "SQLiteAndroidDatabase.executeSql[Batch](): Error=" + errorMessage);
|
||||
}
|
||||
|
||||
try {
|
||||
if (queryResult != null) {
|
||||
JSONObject r = new JSONObject();
|
||||
r.put("qid", query_id);
|
||||
|
||||
r.put("type", "success");
|
||||
r.put("result", queryResult);
|
||||
|
||||
batchResults.put(r);
|
||||
} else {
|
||||
JSONObject r = new JSONObject();
|
||||
r.put("qid", query_id);
|
||||
r.put("type", "error");
|
||||
|
||||
JSONObject er = new JSONObject();
|
||||
er.put("message", errorMessage);
|
||||
r.put("result", er);
|
||||
|
||||
batchResults.put(r);
|
||||
}
|
||||
} catch (JSONException ex) {
|
||||
ex.printStackTrace();
|
||||
Log.v("executeSqlBatch", "SQLiteAndroidDatabase.executeSql[Batch](): Error=" + ex.getMessage());
|
||||
// TODO what to do?
|
||||
}
|
||||
}
|
||||
|
||||
cbc.success(batchResults);
|
||||
}
|
||||
|
||||
private int countRowsAffectedCompat(QueryType queryType, String query, JSONArray[] jsonparams,
|
||||
SQLiteDatabase mydb, int i) throws JSONException {
|
||||
// quick and dirty way to calculate the rowsAffected in pre-Honeycomb. just do a SELECT
|
||||
// beforehand using the same WHERE clause. might not be perfect, but it's better than nothing
|
||||
Matcher whereMatcher = WHERE_CLAUSE.matcher(query);
|
||||
|
||||
String where = "";
|
||||
|
||||
int pos = 0;
|
||||
while (whereMatcher.find(pos)) {
|
||||
where = " WHERE " + whereMatcher.group(1);
|
||||
pos = whereMatcher.start(1);
|
||||
}
|
||||
// WHERE clause may be omitted, and also be sure to find the last one,
|
||||
// e.g. for cases where there's a subquery
|
||||
|
||||
// bindings may be in the update clause, so only take the last n
|
||||
int numQuestionMarks = 0;
|
||||
for (int j = 0; j < where.length(); j++) {
|
||||
if (where.charAt(j) == '?') {
|
||||
numQuestionMarks++;
|
||||
}
|
||||
}
|
||||
|
||||
JSONArray subParams = null;
|
||||
|
||||
if (jsonparams != null) {
|
||||
// only take the last n of every array of sqlArgs
|
||||
JSONArray origArray = jsonparams[i];
|
||||
subParams = new JSONArray();
|
||||
int startPos = origArray.length() - numQuestionMarks;
|
||||
for (int j = startPos; j < origArray.length(); j++) {
|
||||
subParams.put(j - startPos, origArray.get(j));
|
||||
}
|
||||
}
|
||||
|
||||
if (queryType == QueryType.update) {
|
||||
Matcher tableMatcher = UPDATE_TABLE_NAME.matcher(query);
|
||||
if (tableMatcher.find()) {
|
||||
String table = tableMatcher.group(1);
|
||||
try {
|
||||
SQLiteStatement statement = mydb.compileStatement(
|
||||
"SELECT count(*) FROM " + table + where);
|
||||
|
||||
if (subParams != null) {
|
||||
bindArgsToStatement(statement, subParams);
|
||||
}
|
||||
|
||||
return (int)statement.simpleQueryForLong();
|
||||
} catch (Exception e) {
|
||||
// assume we couldn't count for whatever reason, keep going
|
||||
Log.e(SQLiteAndroidDatabase.class.getSimpleName(), "uncaught", e);
|
||||
}
|
||||
}
|
||||
} else { // delete
|
||||
Matcher tableMatcher = DELETE_TABLE_NAME.matcher(query);
|
||||
if (tableMatcher.find()) {
|
||||
String table = tableMatcher.group(1);
|
||||
try {
|
||||
SQLiteStatement statement = mydb.compileStatement(
|
||||
"SELECT count(*) FROM " + table + where);
|
||||
bindArgsToStatement(statement, subParams);
|
||||
|
||||
return (int)statement.simpleQueryForLong();
|
||||
} catch (Exception e) {
|
||||
// assume we couldn't count for whatever reason, keep going
|
||||
Log.e(SQLiteAndroidDatabase.class.getSimpleName(), "uncaught", e);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void bindArgsToStatement(SQLiteStatement myStatement, JSONArray sqlArgs) throws JSONException {
|
||||
for (int i = 0; i < sqlArgs.length(); i++) {
|
||||
if (sqlArgs.get(i) instanceof Float || sqlArgs.get(i) instanceof Double) {
|
||||
myStatement.bindDouble(i + 1, sqlArgs.getDouble(i));
|
||||
} else if (sqlArgs.get(i) instanceof Number) {
|
||||
myStatement.bindLong(i + 1, sqlArgs.getLong(i));
|
||||
} else if (sqlArgs.isNull(i)) {
|
||||
myStatement.bindNull(i + 1);
|
||||
} else {
|
||||
myStatement.bindString(i + 1, sqlArgs.getString(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get rows results from query cursor.
|
||||
*
|
||||
* @param cur Cursor into query results
|
||||
* @return results in string form
|
||||
*/
|
||||
private JSONObject executeSqlStatementQuery(SQLiteDatabase mydb,
|
||||
String query, JSONArray paramsAsJson,
|
||||
CallbackContext cbc) throws Exception {
|
||||
JSONObject rowsResult = new JSONObject();
|
||||
|
||||
Cursor cur = null;
|
||||
try {
|
||||
String[] params = null;
|
||||
|
||||
params = new String[paramsAsJson.length()];
|
||||
|
||||
for (int j = 0; j < paramsAsJson.length(); j++) {
|
||||
if (paramsAsJson.isNull(j))
|
||||
params[j] = "";
|
||||
else
|
||||
params[j] = paramsAsJson.getString(j);
|
||||
}
|
||||
|
||||
cur = mydb.rawQuery(query, params);
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
String errorMessage = ex.getMessage();
|
||||
Log.v("executeSqlBatch", "SQLiteAndroidDatabase.executeSql[Batch](): Error=" + errorMessage);
|
||||
throw ex;
|
||||
}
|
||||
|
||||
// If query result has rows
|
||||
if (cur != null && cur.moveToFirst()) {
|
||||
JSONArray rowsArrayResult = 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);
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT >= 11) {
|
||||
|
||||
// Use try & catch just in case android.os.Build.VERSION.SDK_INT >= 11 is lying:
|
||||
try {
|
||||
bindPostHoneycomb(row, key, cur, i);
|
||||
} catch (Exception ex) {
|
||||
bindPreHoneycomb(row, key, cur, i);
|
||||
}
|
||||
} else {
|
||||
bindPreHoneycomb(row, key, cur, i);
|
||||
}
|
||||
}
|
||||
|
||||
rowsArrayResult.put(row);
|
||||
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} while (cur.moveToNext());
|
||||
|
||||
try {
|
||||
rowsResult.put("rows", rowsArrayResult);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (cur != null) {
|
||||
cur.close();
|
||||
}
|
||||
|
||||
return rowsResult;
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
private void bindPostHoneycomb(JSONObject row, String key, Cursor cur, int i) throws JSONException {
|
||||
int curType = cur.getType(i);
|
||||
|
||||
switch (curType) {
|
||||
case Cursor.FIELD_TYPE_NULL:
|
||||
row.put(key, JSONObject.NULL);
|
||||
break;
|
||||
case Cursor.FIELD_TYPE_INTEGER:
|
||||
row.put(key, cur.getLong(i));
|
||||
break;
|
||||
case Cursor.FIELD_TYPE_FLOAT:
|
||||
row.put(key, cur.getDouble(i));
|
||||
break;
|
||||
case Cursor.FIELD_TYPE_BLOB:
|
||||
row.put(key, new String(Base64.encode(cur.getBlob(i), Base64.DEFAULT)));
|
||||
break;
|
||||
case Cursor.FIELD_TYPE_STRING:
|
||||
default: /* (not expected) */
|
||||
row.put(key, cur.getString(i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void bindPreHoneycomb(JSONObject row, String key, Cursor cursor, int i) throws JSONException {
|
||||
// Since cursor.getType() is not available pre-honeycomb, this is
|
||||
// a workaround so we don't have to bind everything as a string
|
||||
// Details here: http://stackoverflow.com/q/11658239
|
||||
SQLiteCursor sqLiteCursor = (SQLiteCursor) cursor;
|
||||
CursorWindow cursorWindow = sqLiteCursor.getWindow();
|
||||
int pos = cursor.getPosition();
|
||||
if (cursorWindow.isNull(pos, i)) {
|
||||
row.put(key, JSONObject.NULL);
|
||||
} else if (cursorWindow.isLong(pos, i)) {
|
||||
row.put(key, cursor.getLong(i));
|
||||
} else if (cursorWindow.isFloat(pos, i)) {
|
||||
row.put(key, cursor.getDouble(i));
|
||||
} else if (cursorWindow.isBlob(pos, i)) {
|
||||
row.put(key, new String(Base64.encode(cursor.getBlob(i), Base64.DEFAULT)));
|
||||
} else { // string
|
||||
row.put(key, cursor.getString(i));
|
||||
}
|
||||
}
|
||||
|
||||
static QueryType getQueryType(String query) {
|
||||
Matcher matcher = FIRST_WORD.matcher(query);
|
||||
if (matcher.find()) {
|
||||
try {
|
||||
return QueryType.valueOf(matcher.group(1).toLowerCase());
|
||||
} catch (IllegalArgumentException ignore) {
|
||||
// unknown verb
|
||||
}
|
||||
}
|
||||
return QueryType.other;
|
||||
}
|
||||
|
||||
static enum QueryType {
|
||||
update,
|
||||
insert,
|
||||
delete,
|
||||
select,
|
||||
begin,
|
||||
commit,
|
||||
rollback,
|
||||
other
|
||||
}
|
||||
} /* vim: set expandtab : */
|
||||
@@ -0,0 +1,660 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2015: Christopher J. Brody (aka Chris Brody)
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
|
||||
package io.liteglue;
|
||||
|
||||
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.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
|
||||
import org.json.JSONArray;
|
||||
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.
|
||||
*/
|
||||
static ConcurrentHashMap<String, DBRunner> dbrmap = new ConcurrentHashMap<String, DBRunner>();
|
||||
|
||||
/**
|
||||
* SQLiteGlueConnector (instance of SQLiteConnector) for NDK version:
|
||||
*/
|
||||
static SQLiteConnector connector = new SQLiteConnector();
|
||||
|
||||
/**
|
||||
* NOTE: Using default constructor, no explicit constructor.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Executes the request and returns PluginResult.
|
||||
*
|
||||
* @param actionAsString The action to execute.
|
||||
* @param args JSONArry of arguments for the plugin.
|
||||
* @param cbc Callback context from Cordova API
|
||||
* @return Whether the action was valid.
|
||||
*/
|
||||
@Override
|
||||
public boolean execute(String actionAsString, JSONArray args, CallbackContext cbc) {
|
||||
|
||||
Action action;
|
||||
try {
|
||||
action = Action.valueOf(actionAsString);
|
||||
} catch (IllegalArgumentException e) {
|
||||
// shouldn't ever happen
|
||||
Log.e(SQLitePlugin.class.getSimpleName(), "unexpected error", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
return executeAndPossiblyThrow(action, args, cbc);
|
||||
} catch (JSONException e) {
|
||||
// TODO: signal JSON problem to JS
|
||||
Log.e(SQLitePlugin.class.getSimpleName(), "unexpected error", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean executeAndPossiblyThrow(Action action, JSONArray args, CallbackContext cbc)
|
||||
throws JSONException {
|
||||
|
||||
boolean status = true;
|
||||
JSONObject o;
|
||||
String dbname;
|
||||
|
||||
switch (action) {
|
||||
case open:
|
||||
o = args.getJSONObject(0);
|
||||
dbname = o.getString("name");
|
||||
// open database and start reading its queue
|
||||
this.startDatabase(dbname, o, cbc);
|
||||
break;
|
||||
|
||||
case close:
|
||||
o = args.getJSONObject(0);
|
||||
dbname = o.getString("path");
|
||||
// put request in the q to close the db
|
||||
this.closeDatabase(dbname, cbc);
|
||||
break;
|
||||
|
||||
case delete:
|
||||
o = args.getJSONObject(0);
|
||||
dbname = o.getString("path");
|
||||
|
||||
deleteDatabase(dbname, cbc);
|
||||
|
||||
break;
|
||||
|
||||
case executeSqlBatch:
|
||||
case backgroundExecuteSqlBatch:
|
||||
String[] queries = null;
|
||||
String[] queryIDs = null;
|
||||
|
||||
JSONArray jsonArr = null;
|
||||
int paramLen = 0;
|
||||
JSONArray[] jsonparams = null;
|
||||
|
||||
JSONObject allargs = args.getJSONObject(0);
|
||||
JSONObject dbargs = allargs.getJSONObject("dbargs");
|
||||
dbname = dbargs.getString("dbname");
|
||||
JSONArray txargs = allargs.getJSONArray("executes");
|
||||
|
||||
if (txargs.isNull(0)) {
|
||||
queries = new String[0];
|
||||
} else {
|
||||
int len = txargs.length();
|
||||
queries = new String[len];
|
||||
queryIDs = new String[len];
|
||||
jsonparams = new JSONArray[len];
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
JSONObject a = txargs.getJSONObject(i);
|
||||
queries[i] = a.getString("sql");
|
||||
queryIDs[i] = a.getString("qid");
|
||||
jsonArr = a.getJSONArray("params");
|
||||
paramLen = jsonArr.length();
|
||||
jsonparams[i] = jsonArr;
|
||||
}
|
||||
}
|
||||
|
||||
// put db query in the queue to be executed in the db thread:
|
||||
DBQuery q = new DBQuery(queries, queryIDs, jsonparams, cbc);
|
||||
DBRunner r = dbrmap.get(dbname);
|
||||
if (r != null) {
|
||||
try {
|
||||
r.q.put(q);
|
||||
} catch(Exception e) {
|
||||
Log.e(SQLitePlugin.class.getSimpleName(), "couldn't add to queue", e);
|
||||
cbc.error("couldn't add to queue");
|
||||
}
|
||||
} else {
|
||||
cbc.error("database not open");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up and close all open databases.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
while (!dbrmap.isEmpty()) {
|
||||
String dbname = dbrmap.keySet().iterator().next();
|
||||
|
||||
this.closeDatabaseNow(dbname);
|
||||
|
||||
DBRunner r = dbrmap.get(dbname);
|
||||
try {
|
||||
// stop the db runner thread:
|
||||
r.q.put(new DBQuery());
|
||||
} catch(Exception e) {
|
||||
Log.e(SQLitePlugin.class.getSimpleName(), "couldn't stop db thread", e);
|
||||
}
|
||||
dbrmap.remove(dbname);
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// LOCAL METHODS
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
private void startDatabase(String dbname, JSONObject options, CallbackContext cbc) {
|
||||
// TODO: is it an issue that we can orphan an existing thread? What should we do here?
|
||||
// If we re-use the existing DBRunner it might be in the process of closing...
|
||||
DBRunner r = dbrmap.get(dbname);
|
||||
|
||||
// Brody TODO: It may be better to terminate the existing db thread here & start a new one, instead.
|
||||
if (r != null) {
|
||||
// don't orphan the existing thread; just re-open the existing database.
|
||||
// In the worst case it might be in the process of closing, but even that's less serious
|
||||
// than orphaning the old DBRunner.
|
||||
cbc.success();
|
||||
} else {
|
||||
r = new DBRunner(dbname, options, cbc);
|
||||
dbrmap.put(dbname, r);
|
||||
this.cordova.getThreadPool().execute(r);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Open a database.
|
||||
*
|
||||
* @param dbName The name of the database file
|
||||
*/
|
||||
private SQLiteAndroidDatabase openDatabase(String dbname, CallbackContext cbc, boolean old_impl) throws Exception {
|
||||
try {
|
||||
// ASSUMPTION: no db (connection/handle) is already stored in the map
|
||||
// [should be true according to the code in DBRunner.run()]
|
||||
|
||||
File dbfile = this.cordova.getActivity().getDatabasePath(dbname);
|
||||
|
||||
if (!dbfile.exists()) {
|
||||
dbfile.getParentFile().mkdirs();
|
||||
}
|
||||
|
||||
Log.v("info", "Open sqlite db: " + dbfile.getAbsolutePath());
|
||||
|
||||
SQLiteAndroidDatabase mydb = old_impl ? new SQLiteAndroidDatabase() : new SQLiteDatabaseNDK();
|
||||
mydb.open(dbfile);
|
||||
|
||||
if (cbc != null) // XXX Android locking/closing BUG workaround
|
||||
cbc.success();
|
||||
|
||||
return mydb;
|
||||
} catch (Exception e) {
|
||||
if (cbc != null) // XXX Android locking/closing BUG workaround
|
||||
cbc.error("can't open database " + e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close a database (in another thread).
|
||||
*
|
||||
* @param dbName The name of the database file
|
||||
*/
|
||||
private void closeDatabase(String dbname, CallbackContext cbc) {
|
||||
DBRunner r = dbrmap.get(dbname);
|
||||
if (r != null) {
|
||||
try {
|
||||
r.q.put(new DBQuery(false, cbc));
|
||||
} catch(Exception e) {
|
||||
if (cbc != null) {
|
||||
cbc.error("couldn't close database" + e);
|
||||
}
|
||||
Log.e(SQLitePlugin.class.getSimpleName(), "couldn't close database", e);
|
||||
}
|
||||
} else {
|
||||
if (cbc != null) {
|
||||
cbc.success();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close a database (in the current thread).
|
||||
*
|
||||
* @param dbname The name of the database file
|
||||
*/
|
||||
private void closeDatabaseNow(String dbname) {
|
||||
DBRunner r = dbrmap.get(dbname);
|
||||
|
||||
if (r != null) {
|
||||
SQLiteAndroidDatabase mydb = r.mydb;
|
||||
|
||||
if (mydb != null)
|
||||
mydb.closeDatabaseNow();
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteDatabase(String dbname, CallbackContext cbc) {
|
||||
DBRunner r = dbrmap.get(dbname);
|
||||
if (r != null) {
|
||||
try {
|
||||
r.q.put(new DBQuery(true, cbc));
|
||||
} catch(Exception e) {
|
||||
if (cbc != null) {
|
||||
cbc.error("couldn't close database" + e);
|
||||
}
|
||||
Log.e(SQLitePlugin.class.getSimpleName(), "couldn't close database", e);
|
||||
}
|
||||
} else {
|
||||
boolean deleteResult = this.deleteDatabaseNow(dbname);
|
||||
if (deleteResult) {
|
||||
cbc.success();
|
||||
} else {
|
||||
cbc.error("couldn't delete database");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a database.
|
||||
*
|
||||
* @param dbName The name of the database file
|
||||
*
|
||||
* @return true if successful or false if an exception was encountered
|
||||
*/
|
||||
private boolean deleteDatabaseNow(String dbname) {
|
||||
File dbfile = this.cordova.getActivity().getDatabasePath(dbname);
|
||||
|
||||
try {
|
||||
return cordova.getActivity().deleteDatabase(dbfile.getAbsolutePath());
|
||||
} catch (Exception e) {
|
||||
Log.e(SQLitePlugin.class.getSimpleName(), "couldn't delete database", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: class hierarchy is ugly, done to reduce number of modules for manual installation.
|
||||
// FUTURE TBD SQLiteDatabaseNDK class belongs in its own module.
|
||||
class SQLiteDatabaseNDK extends SQLiteAndroidDatabase {
|
||||
SQLiteConnection mydb;
|
||||
|
||||
/**
|
||||
* Open a database.
|
||||
*
|
||||
* @param dbFile The database File specification
|
||||
*/
|
||||
@Override
|
||||
void open(File dbFile) throws Exception {
|
||||
mydb = connector.newSQLiteConnection(dbFile.getAbsolutePath(),
|
||||
SQLiteOpenFlags.READWRITE | SQLiteOpenFlags.CREATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close a database (in the current thread).
|
||||
*/
|
||||
@Override
|
||||
void closeDatabaseNow() {
|
||||
try {
|
||||
if (mydb != null)
|
||||
mydb.dispose();
|
||||
} catch (Exception e) {
|
||||
Log.e(SQLitePlugin.class.getSimpleName(), "couldn't close database, ignoring", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ignore Android bug workaround for NDK version
|
||||
*/
|
||||
@Override
|
||||
void bugWorkaround() { }
|
||||
|
||||
/**
|
||||
* Executes a batch request and sends the results via cbc.
|
||||
*
|
||||
* @param dbname The name of the database.
|
||||
* @param queryarr Array of query strings
|
||||
* @param jsonparams Array of JSON query parameters
|
||||
* @param queryIDs Array of query ids
|
||||
* @param cbc Callback context from Cordova API
|
||||
*/
|
||||
@Override
|
||||
void executeSqlBatch( String[] queryarr, JSONArray[] jsonparams,
|
||||
String[] queryIDs, CallbackContext cbc) {
|
||||
|
||||
if (mydb == null) {
|
||||
// not allowed - can only happen if someone has closed (and possibly deleted) a database and then re-used the database
|
||||
cbc.error("database has been closed");
|
||||
return;
|
||||
}
|
||||
|
||||
int len = queryarr.length;
|
||||
JSONArray batchResults = new JSONArray();
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
int rowsAffectedCompat = 0;
|
||||
boolean needRowsAffectedCompat = false;
|
||||
String query_id = queryIDs[i];
|
||||
|
||||
JSONObject queryResult = null;
|
||||
String errorMessage = "unknown";
|
||||
|
||||
try {
|
||||
String query = queryarr[i];
|
||||
|
||||
long lastTotal = mydb.getTotalChanges();
|
||||
queryResult = this.executeSqlStatementNDK(query, jsonparams[i], cbc);
|
||||
long newTotal = mydb.getTotalChanges();
|
||||
long rowsAffected = newTotal - lastTotal;
|
||||
|
||||
queryResult.put("rowsAffected", rowsAffected);
|
||||
if (rowsAffected > 0) {
|
||||
long insertId = mydb.getLastInsertRowid();
|
||||
if (insertId > 0) {
|
||||
queryResult.put("insertId", insertId);
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
errorMessage = ex.getMessage();
|
||||
Log.v("executeSqlBatch", "SQLitePlugin.executeSql[Batch](): Error=" + errorMessage);
|
||||
}
|
||||
|
||||
try {
|
||||
if (queryResult != null) {
|
||||
JSONObject r = new JSONObject();
|
||||
r.put("qid", query_id);
|
||||
|
||||
r.put("type", "success");
|
||||
r.put("result", queryResult);
|
||||
|
||||
batchResults.put(r);
|
||||
} else {
|
||||
JSONObject r = new JSONObject();
|
||||
r.put("qid", query_id);
|
||||
r.put("type", "error");
|
||||
|
||||
JSONObject er = new JSONObject();
|
||||
er.put("message", errorMessage);
|
||||
r.put("result", er);
|
||||
|
||||
batchResults.put(r);
|
||||
}
|
||||
} catch (JSONException ex) {
|
||||
ex.printStackTrace();
|
||||
Log.v("executeSqlBatch", "SQLitePlugin.executeSql[Batch](): Error=" + ex.getMessage());
|
||||
// TODO what to do?
|
||||
}
|
||||
}
|
||||
|
||||
cbc.success(batchResults);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get rows results from query cursor.
|
||||
*
|
||||
* @param cur Cursor into query results
|
||||
* @return results in string form
|
||||
*/
|
||||
private JSONObject executeSqlStatementNDK(String query, JSONArray paramsAsJson,
|
||||
CallbackContext cbc) throws Exception {
|
||||
JSONObject rowsResult = new JSONObject();
|
||||
|
||||
boolean hasRows = false;
|
||||
|
||||
SQLiteStatement myStatement = mydb.prepareStatement(query);
|
||||
|
||||
try {
|
||||
String[] params = null;
|
||||
|
||||
params = new String[paramsAsJson.length()];
|
||||
|
||||
for (int i = 0; i < paramsAsJson.length(); ++i) {
|
||||
if (paramsAsJson.isNull(i)) {
|
||||
myStatement.bindNull(i + 1);
|
||||
} else {
|
||||
Object p = paramsAsJson.get(i);
|
||||
if (p instanceof Float || p instanceof Double)
|
||||
myStatement.bindDouble(i + 1, paramsAsJson.getDouble(i));
|
||||
else if (p instanceof Number)
|
||||
myStatement.bindLong(i + 1, paramsAsJson.getLong(i));
|
||||
else
|
||||
myStatement.bindTextNativeString(i + 1, paramsAsJson.getString(i));
|
||||
}
|
||||
}
|
||||
|
||||
hasRows = myStatement.step();
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
String errorMessage = ex.getMessage();
|
||||
Log.v("executeSqlBatch", "SQLitePlugin.executeSql[Batch](): Error=" + errorMessage);
|
||||
|
||||
// cleanup statement and throw the exception:
|
||||
myStatement.dispose();
|
||||
throw ex;
|
||||
}
|
||||
|
||||
// If query result has rows
|
||||
if (hasRows) {
|
||||
JSONArray rowsArrayResult = new JSONArray();
|
||||
String key = "";
|
||||
int colCount = myStatement.getColumnCount();
|
||||
|
||||
// Build up JSON result object for each row
|
||||
do {
|
||||
JSONObject row = new JSONObject();
|
||||
try {
|
||||
for (int i = 0; i < colCount; ++i) {
|
||||
key = myStatement.getColumnName(i);
|
||||
|
||||
switch (myStatement.getColumnType(i)) {
|
||||
case SQLColumnType.NULL:
|
||||
row.put(key, JSONObject.NULL);
|
||||
break;
|
||||
|
||||
case SQLColumnType.REAL:
|
||||
row.put(key, myStatement.getColumnDouble(i));
|
||||
break;
|
||||
|
||||
case SQLColumnType.INTEGER:
|
||||
row.put(key, myStatement.getColumnLong(i));
|
||||
break;
|
||||
|
||||
case SQLColumnType.BLOB:
|
||||
case SQLColumnType.TEXT:
|
||||
default: // (just in case)
|
||||
row.put(key, myStatement.getColumnTextNativeString(i));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
rowsArrayResult.put(row);
|
||||
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} while (myStatement.step());
|
||||
|
||||
try {
|
||||
rowsResult.put("rows", rowsArrayResult);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
myStatement.dispose();
|
||||
|
||||
return rowsResult;
|
||||
}
|
||||
}
|
||||
|
||||
private class DBRunner implements Runnable {
|
||||
final String dbname;
|
||||
private boolean oldImpl;
|
||||
private boolean bugWorkaround;
|
||||
|
||||
final BlockingQueue<DBQuery> q;
|
||||
final CallbackContext openCbc;
|
||||
|
||||
SQLiteAndroidDatabase mydb;
|
||||
|
||||
DBRunner(final String dbname, JSONObject options, CallbackContext cbc) {
|
||||
this.dbname = dbname;
|
||||
this.oldImpl = options.has("androidOldDatabaseImplementation");
|
||||
Log.v(SQLitePlugin.class.getSimpleName(), "Android db implementation: " + (oldImpl ? "OLD" : "default-NDK"));
|
||||
this.bugWorkaround = this.oldImpl && options.has("androidBugWorkaround");
|
||||
if (this.bugWorkaround)
|
||||
Log.v(SQLitePlugin.class.getSimpleName(), "Android db closing/locking workaround applied");
|
||||
|
||||
this.q = new LinkedBlockingQueue<DBQuery>();
|
||||
this.openCbc = cbc;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
this.mydb = openDatabase(dbname, this.openCbc, this.oldImpl);
|
||||
} catch (Exception e) {
|
||||
Log.e(SQLitePlugin.class.getSimpleName(), "unexpected error, stopping db thread", e);
|
||||
dbrmap.remove(dbname);
|
||||
return;
|
||||
}
|
||||
|
||||
DBQuery dbq = null;
|
||||
|
||||
try {
|
||||
dbq = q.take();
|
||||
|
||||
while (!dbq.stop) {
|
||||
mydb.executeSqlBatch(dbq.queries, dbq.jsonparams, dbq.queryIDs, dbq.cbc);
|
||||
|
||||
// NOTE: androidLock[Bug]Workaround is not necessary and IGNORED for default-NDK version.
|
||||
if (this.bugWorkaround && dbq.queries.length == 1 && dbq.queries[0] == "COMMIT")
|
||||
mydb.bugWorkaround();
|
||||
|
||||
dbq = q.take();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(SQLitePlugin.class.getSimpleName(), "unexpected error", e);
|
||||
}
|
||||
|
||||
if (dbq != null && dbq.close) {
|
||||
try {
|
||||
closeDatabaseNow(dbname);
|
||||
|
||||
dbrmap.remove(dbname); // (should) remove ourself
|
||||
|
||||
if (!dbq.delete) {
|
||||
dbq.cbc.success();
|
||||
} else {
|
||||
try {
|
||||
boolean deleteResult = deleteDatabaseNow(dbname);
|
||||
if (deleteResult) {
|
||||
dbq.cbc.success();
|
||||
} else {
|
||||
dbq.cbc.error("couldn't delete database");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(SQLitePlugin.class.getSimpleName(), "couldn't delete database", e);
|
||||
dbq.cbc.error("couldn't delete database: " + e);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(SQLitePlugin.class.getSimpleName(), "couldn't close database", e);
|
||||
if (dbq.cbc != null) {
|
||||
dbq.cbc.error("couldn't close database: " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class DBQuery {
|
||||
// XXX TODO replace with DBRunner action enum:
|
||||
final boolean stop;
|
||||
final boolean close;
|
||||
final boolean delete;
|
||||
final String[] queries;
|
||||
final String[] queryIDs;
|
||||
final JSONArray[] jsonparams;
|
||||
final CallbackContext cbc;
|
||||
|
||||
DBQuery(String[] myqueries, String[] qids, JSONArray[] params, CallbackContext c) {
|
||||
this.stop = false;
|
||||
this.close = false;
|
||||
this.delete = false;
|
||||
this.queries = myqueries;
|
||||
this.queryIDs = qids;
|
||||
this.jsonparams = params;
|
||||
this.cbc = c;
|
||||
}
|
||||
|
||||
DBQuery(boolean delete, CallbackContext cbc) {
|
||||
this.stop = true;
|
||||
this.close = true;
|
||||
this.delete = delete;
|
||||
this.queries = null;
|
||||
this.queryIDs = null;
|
||||
this.jsonparams = null;
|
||||
this.cbc = cbc;
|
||||
}
|
||||
|
||||
// signal the DBRunner thread to stop:
|
||||
DBQuery() {
|
||||
this.stop = true;
|
||||
this.close = false;
|
||||
this.delete = false;
|
||||
this.queries = null;
|
||||
this.queryIDs = null;
|
||||
this.jsonparams = null;
|
||||
this.cbc = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static enum Action {
|
||||
open,
|
||||
close,
|
||||
delete,
|
||||
executeSqlBatch,
|
||||
backgroundExecuteSqlBatch,
|
||||
}
|
||||
}
|
||||
|
||||
/* vim: set expandtab : */
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
155888
plugins/cordova-sqlite-storage/src/common/sqlite3.c
Normal file
155888
plugins/cordova-sqlite-storage/src/common/sqlite3.c
Normal file
File diff suppressed because it is too large
Load Diff
7831
plugins/cordova-sqlite-storage/src/common/sqlite3.h
Normal file
7831
plugins/cordova-sqlite-storage/src/common/sqlite3.h
Normal file
File diff suppressed because it is too large
Load Diff
4
plugins/cordova-sqlite-storage/src/external/.gitignore
vendored
Normal file
4
plugins/cordova-sqlite-storage/src/external/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
# Ignore everything in this directory
|
||||
*
|
||||
# Except this file
|
||||
!.gitignore
|
||||
49
plugins/cordova-sqlite-storage/src/ios/SQLitePlugin.h
Normal file
49
plugins/cordova-sqlite-storage/src/ios/SQLitePlugin.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2015: Christopher J. Brody (aka Chris Brody)
|
||||
* Copyright (C) 2011 Davide Bertola
|
||||
*
|
||||
* This library is available under the terms of the MIT License (2008).
|
||||
* See http://opensource.org/licenses/alphabetical for full text.
|
||||
*/
|
||||
|
||||
#import <Cordova/CDVPlugin.h>
|
||||
|
||||
// Used to remove dependency on sqlite3.h in this header:
|
||||
struct sqlite3;
|
||||
|
||||
enum WebSQLError {
|
||||
UNKNOWN_ERR = 0,
|
||||
DATABASE_ERR = 1,
|
||||
VERSION_ERR = 2,
|
||||
TOO_LARGE_ERR = 3,
|
||||
QUOTA_ERR = 4,
|
||||
SYNTAX_ERR = 5,
|
||||
CONSTRAINT_ERR = 6,
|
||||
TIMEOUT_ERR = 7
|
||||
};
|
||||
typedef int WebSQLError;
|
||||
|
||||
@interface SQLitePlugin : CDVPlugin {
|
||||
NSMutableDictionary *openDBs;
|
||||
}
|
||||
|
||||
@property (nonatomic, copy) NSMutableDictionary *openDBs;
|
||||
@property (nonatomic, copy) NSMutableDictionary *appDBPaths;
|
||||
|
||||
-(void) executeInBackground: (CDVInvokedUrlCommand*)command;
|
||||
|
||||
// Open / Close / Delete
|
||||
-(void) open: (CDVInvokedUrlCommand*)command;
|
||||
-(void) close: (CDVInvokedUrlCommand*)command;
|
||||
-(void) delete: (CDVInvokedUrlCommand*)command;
|
||||
|
||||
-(void) openNow: (CDVInvokedUrlCommand*)command;
|
||||
-(void) closeNow: (CDVInvokedUrlCommand*)command;
|
||||
-(void) deleteNow: (CDVInvokedUrlCommand*)command;
|
||||
|
||||
// Batch processing interface
|
||||
-(void) backgroundExecuteSqlBatch: (CDVInvokedUrlCommand*)command;
|
||||
|
||||
-(void) executeSqlBatchNow: (CDVInvokedUrlCommand*)command;
|
||||
|
||||
@end /* vim: set expandtab : */
|
||||
578
plugins/cordova-sqlite-storage/src/ios/SQLitePlugin.m
Normal file
578
plugins/cordova-sqlite-storage/src/ios/SQLitePlugin.m
Normal file
@@ -0,0 +1,578 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2015: Christopher J. Brody (aka Chris Brody)
|
||||
* Copyright (C) 2011 Davide Bertola
|
||||
*
|
||||
* This library is available under the terms of the MIT License (2008).
|
||||
* See http://opensource.org/licenses/alphabetical for full text.
|
||||
*/
|
||||
|
||||
#import "SQLitePlugin.h"
|
||||
|
||||
#import "sqlite3.h"
|
||||
|
||||
// NOTE: This is now broken by cordova-ios 4.0, see:
|
||||
// https://issues.apache.org/jira/browse/CB-9638
|
||||
// Solution is to use NSJSONSerialization instead.
|
||||
#ifdef READ_BLOB_AS_BASE64
|
||||
#import <Cordova/NSData+Base64.h>
|
||||
#endif
|
||||
|
||||
@implementation SQLitePlugin
|
||||
|
||||
@synthesize openDBs;
|
||||
@synthesize appDBPaths;
|
||||
|
||||
-(void)pluginInitialize
|
||||
{
|
||||
NSLog(@"Initializing SQLitePlugin");
|
||||
|
||||
{
|
||||
openDBs = [NSMutableDictionary dictionaryWithCapacity:0];
|
||||
appDBPaths = [NSMutableDictionary dictionaryWithCapacity:0];
|
||||
#if !__has_feature(objc_arc)
|
||||
[openDBs retain];
|
||||
[appDBPaths retain];
|
||||
#endif
|
||||
|
||||
NSString *docs = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex: 0];
|
||||
NSLog(@"Detected docs path: %@", docs);
|
||||
[appDBPaths setObject: docs forKey:@"docs"];
|
||||
|
||||
NSString *libs = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex: 0];
|
||||
NSLog(@"Detected Library path: %@", libs);
|
||||
[appDBPaths setObject: libs forKey:@"libs"];
|
||||
|
||||
NSString *nosync = [libs stringByAppendingPathComponent:@"LocalDatabase"];
|
||||
NSError *err;
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath: nosync])
|
||||
{
|
||||
NSLog(@"no cloud sync at path: %@", nosync);
|
||||
[appDBPaths setObject: nosync forKey:@"nosync"];
|
||||
}
|
||||
else
|
||||
{
|
||||
if ([[NSFileManager defaultManager] createDirectoryAtPath: nosync withIntermediateDirectories:NO attributes: nil error:&err])
|
||||
{
|
||||
NSURL *nosyncURL = [ NSURL fileURLWithPath: nosync];
|
||||
if (![nosyncURL setResourceValue: [NSNumber numberWithBool: YES] forKey: NSURLIsExcludedFromBackupKey error: &err])
|
||||
{
|
||||
NSLog(@"IGNORED: error setting nobackup flag in LocalDatabase directory: %@", err);
|
||||
}
|
||||
NSLog(@"no cloud sync at path: %@", nosync);
|
||||
[appDBPaths setObject: nosync forKey:@"nosync"];
|
||||
}
|
||||
else
|
||||
{
|
||||
// fallback:
|
||||
NSLog(@"WARNING: error adding LocalDatabase directory: %@", err);
|
||||
[appDBPaths setObject: libs forKey:@"nosync"];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-(id) getDBPath:(NSString *)dbFile at:(NSString *)atkey {
|
||||
if (dbFile == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NSString *dbdir = [appDBPaths objectForKey:atkey];
|
||||
NSString *dbPath = [dbdir stringByAppendingPathComponent: dbFile];
|
||||
return dbPath;
|
||||
}
|
||||
|
||||
// XXX NOTE: This implementation gets _all_ operations working in the background
|
||||
// and _should_ resolve intermittent problems reported with cordova-ios@4.0.1).
|
||||
// This implementation _does_ fail certain rapidly repeated
|
||||
// open-and close and open-and-delete test scenarios.
|
||||
-(void)executeInBackground: (CDVInvokedUrlCommand*)command
|
||||
{
|
||||
[self.commandDelegate runInBackground:^{
|
||||
@synchronized(self) {
|
||||
if ([command.methodName isEqualToString: @"open"])
|
||||
[self openNow: command];
|
||||
else if ([command.methodName isEqualToString: @"close"])
|
||||
[self closeNow: command];
|
||||
else if ([command.methodName isEqualToString: @"delete"])
|
||||
[self deleteNow: command];
|
||||
else if ([command.methodName isEqualToString: @"backgroundExecuteSqlBatch"])
|
||||
[self executeSqlBatchNow: command];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
-(void)open: (CDVInvokedUrlCommand*)command
|
||||
{
|
||||
[self.commandDelegate runInBackground:^{
|
||||
[self executeInBackground: command];
|
||||
}];
|
||||
}
|
||||
|
||||
-(void)openNow: (CDVInvokedUrlCommand*)command
|
||||
{
|
||||
CDVPluginResult* pluginResult = nil;
|
||||
NSMutableDictionary *options = [command.arguments objectAtIndex:0];
|
||||
|
||||
NSString *dbfilename = [options objectForKey:@"name"];
|
||||
|
||||
NSString *dblocation = [options objectForKey:@"dblocation"];
|
||||
if (dblocation == NULL) dblocation = @"docs";
|
||||
//NSLog(@"using db location: %@", dblocation);
|
||||
|
||||
NSString *dbname = [self getDBPath:dbfilename at:dblocation];
|
||||
|
||||
if (dbname == NULL) {
|
||||
NSLog(@"No db name specified for open");
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:@"You must specify database name"];
|
||||
}
|
||||
else {
|
||||
NSValue *dbPointer = [openDBs objectForKey:dbfilename];
|
||||
|
||||
if (dbPointer != NULL) {
|
||||
NSLog(@"Reusing existing database connection for db name %@", dbfilename);
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:@"Database opened"];
|
||||
} else {
|
||||
const char *name = [dbname UTF8String];
|
||||
sqlite3 *db;
|
||||
|
||||
NSLog(@"open full db path: %@", dbname);
|
||||
|
||||
if (sqlite3_open(name, &db) != SQLITE_OK) {
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Unable to open DB"];
|
||||
return;
|
||||
} else {
|
||||
// for SQLCipher version:
|
||||
// NSString *dbkey = [options objectForKey:@"key"];
|
||||
// const char *key = NULL;
|
||||
// if (dbkey != NULL) key = [dbkey UTF8String];
|
||||
// if (key != NULL) sqlite3_key(db, key, strlen(key));
|
||||
|
||||
// Attempt to read the SQLite master table [to support SQLCipher version]:
|
||||
if(sqlite3_exec(db, (const char*)"SELECT count(*) FROM sqlite_master;", NULL, NULL, NULL) == SQLITE_OK) {
|
||||
dbPointer = [NSValue valueWithPointer:db];
|
||||
[openDBs setObject: dbPointer forKey: dbfilename];
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:@"Database opened"];
|
||||
} else {
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Unable to open DB with key"];
|
||||
// XXX TODO: close the db handle & [perhaps] remove from openDBs!!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sqlite3_threadsafe()) {
|
||||
NSLog(@"Good news: SQLite is thread safe!");
|
||||
}
|
||||
else {
|
||||
NSLog(@"Warning: SQLite is not thread safe.");
|
||||
}
|
||||
|
||||
[self.commandDelegate sendPluginResult:pluginResult callbackId: command.callbackId];
|
||||
|
||||
// NSLog(@"open cb finished ok");
|
||||
}
|
||||
|
||||
-(void) close: (CDVInvokedUrlCommand*)command
|
||||
{
|
||||
[self.commandDelegate runInBackground:^{
|
||||
[self executeInBackground: command];
|
||||
}];
|
||||
}
|
||||
|
||||
-(void)closeNow: (CDVInvokedUrlCommand*)command
|
||||
{
|
||||
CDVPluginResult* pluginResult = nil;
|
||||
NSMutableDictionary *options = [command.arguments objectAtIndex:0];
|
||||
|
||||
NSString *dbFileName = [options objectForKey:@"path"];
|
||||
|
||||
if (dbFileName == NULL) {
|
||||
// Should not happen:
|
||||
NSLog(@"No db name specified for close");
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"You must specify database path"];
|
||||
} else {
|
||||
NSValue *val = [openDBs objectForKey:dbFileName];
|
||||
sqlite3 *db = [val pointerValue];
|
||||
|
||||
if (db == NULL) {
|
||||
// Should not happen:
|
||||
NSLog(@"close: db name was not open: %@", dbFileName);
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Specified db was not open"];
|
||||
}
|
||||
else {
|
||||
NSLog(@"close db name: %@", dbFileName);
|
||||
sqlite3_close (db);
|
||||
[openDBs removeObjectForKey:dbFileName];
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:@"DB closed"];
|
||||
}
|
||||
}
|
||||
|
||||
[self.commandDelegate sendPluginResult:pluginResult callbackId: command.callbackId];
|
||||
}
|
||||
|
||||
-(void) delete: (CDVInvokedUrlCommand*)command
|
||||
{
|
||||
[self.commandDelegate runInBackground:^{
|
||||
[self executeInBackground: command];
|
||||
}];
|
||||
}
|
||||
|
||||
-(void)deleteNow: (CDVInvokedUrlCommand*)command
|
||||
{
|
||||
CDVPluginResult* pluginResult = nil;
|
||||
NSMutableDictionary *options = [command.arguments objectAtIndex:0];
|
||||
|
||||
NSString *dbFileName = [options objectForKey:@"path"];
|
||||
|
||||
NSString *dblocation = [options objectForKey:@"dblocation"];
|
||||
if (dblocation == NULL) dblocation = @"docs";
|
||||
|
||||
if (dbFileName==NULL) {
|
||||
// Should not happen:
|
||||
NSLog(@"No db name specified for delete");
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"You must specify database path"];
|
||||
} else {
|
||||
NSString *dbPath = [self getDBPath:dbFileName at:dblocation];
|
||||
|
||||
if ([[NSFileManager defaultManager]fileExistsAtPath:dbPath]) {
|
||||
NSLog(@"delete full db path: %@", dbPath);
|
||||
[[NSFileManager defaultManager]removeItemAtPath:dbPath error:nil];
|
||||
[openDBs removeObjectForKey:dbFileName];
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:@"DB deleted"];
|
||||
} else {
|
||||
NSLog(@"delete: db was not found: %@", dbPath);
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"The database does not exist on that path"];
|
||||
}
|
||||
}
|
||||
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
}
|
||||
|
||||
|
||||
-(void) backgroundExecuteSqlBatch: (CDVInvokedUrlCommand*)command
|
||||
{
|
||||
[self.commandDelegate runInBackground:^{
|
||||
[self executeSqlBatchNow: command];
|
||||
}];
|
||||
}
|
||||
|
||||
-(void) executeSqlBatchNow: (CDVInvokedUrlCommand*)command
|
||||
{
|
||||
NSMutableDictionary *options = [command.arguments objectAtIndex:0];
|
||||
NSMutableArray *results = [NSMutableArray arrayWithCapacity:0];
|
||||
NSMutableDictionary *dbargs = [options objectForKey:@"dbargs"];
|
||||
NSMutableArray *executes = [options objectForKey:@"executes"];
|
||||
|
||||
CDVPluginResult* pluginResult;
|
||||
|
||||
@synchronized(self) {
|
||||
for (NSMutableDictionary *dict in executes) {
|
||||
CDVPluginResult *result = [self executeSqlWithDict:dict andArgs:dbargs];
|
||||
if ([result.status intValue] == CDVCommandStatus_ERROR) {
|
||||
/* add error with result.message: */
|
||||
NSMutableDictionary *r = [NSMutableDictionary dictionaryWithCapacity:0];
|
||||
[r setObject:[dict objectForKey:@"qid"] forKey:@"qid"];
|
||||
[r setObject:@"error" forKey:@"type"];
|
||||
[r setObject:result.message forKey:@"error"];
|
||||
[r setObject:result.message forKey:@"result"];
|
||||
[results addObject: r];
|
||||
} else {
|
||||
/* add result with result.message: */
|
||||
NSMutableDictionary *r = [NSMutableDictionary dictionaryWithCapacity:0];
|
||||
[r setObject:[dict objectForKey:@"qid"] forKey:@"qid"];
|
||||
[r setObject:@"success" forKey:@"type"];
|
||||
[r setObject:result.message forKey:@"result"];
|
||||
[results addObject: r];
|
||||
}
|
||||
}
|
||||
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:results];
|
||||
}
|
||||
|
||||
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
}
|
||||
|
||||
-(void) backgroundExecuteSql: (CDVInvokedUrlCommand*)command
|
||||
{
|
||||
[self.commandDelegate runInBackground:^{
|
||||
[self executeSql:command];
|
||||
}];
|
||||
}
|
||||
|
||||
-(void) executeSql: (CDVInvokedUrlCommand*)command
|
||||
{
|
||||
NSMutableDictionary *options = [command.arguments objectAtIndex:0];
|
||||
NSMutableDictionary *dbargs = [options objectForKey:@"dbargs"];
|
||||
NSMutableDictionary *ex = [options objectForKey:@"ex"];
|
||||
|
||||
CDVPluginResult* pluginResult;
|
||||
@synchronized (self) {
|
||||
pluginResult = [self executeSqlWithDict: ex andArgs: dbargs];
|
||||
}
|
||||
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
}
|
||||
|
||||
-(CDVPluginResult*) executeSqlWithDict: (NSMutableDictionary*)options andArgs: (NSMutableDictionary*)dbargs
|
||||
{
|
||||
NSString *dbFileName = [dbargs objectForKey:@"dbname"];
|
||||
if (dbFileName == NULL) {
|
||||
return [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"You must specify database path"];
|
||||
}
|
||||
|
||||
NSMutableArray *params = [options objectForKey:@"params"]; // optional
|
||||
|
||||
NSValue *dbPointer = [openDBs objectForKey:dbFileName];
|
||||
if (dbPointer == NULL) {
|
||||
return [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"No such database, you must open it first"];
|
||||
}
|
||||
sqlite3 *db = [dbPointer pointerValue];
|
||||
|
||||
NSString *sql = [options objectForKey:@"sql"];
|
||||
if (sql == NULL) {
|
||||
return [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"You must specify a sql query to execute"];
|
||||
}
|
||||
|
||||
const char *sql_stmt = [sql UTF8String];
|
||||
NSDictionary *error = nil;
|
||||
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;
|
||||
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) {
|
||||
error = [SQLitePlugin captureSQLiteErrorFromDb:db];
|
||||
keepGoing = NO;
|
||||
} else if (params != NULL) {
|
||||
for (int b = 0; b < params.count; b++) {
|
||||
[self bindStatement:statement withArg:[params objectAtIndex:b] atIndex:(b+1)];
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
columnValue = nil;
|
||||
columnName = [NSString stringWithFormat:@"%s", sqlite3_column_name(statement, i)];
|
||||
|
||||
column_type = sqlite3_column_type(statement, i);
|
||||
switch (column_type) {
|
||||
case SQLITE_INTEGER:
|
||||
columnValue = [NSNumber numberWithLongLong: sqlite3_column_int64(statement, i)];
|
||||
break;
|
||||
case SQLITE_FLOAT:
|
||||
columnValue = [NSNumber numberWithDouble: sqlite3_column_double(statement, i)];
|
||||
break;
|
||||
case SQLITE_BLOB:
|
||||
#ifdef READ_BLOB_AS_BASE64
|
||||
columnValue = [SQLitePlugin getBlobAsBase64String: sqlite3_column_blob(statement, i)
|
||||
withLength: sqlite3_column_bytes(statement, i)];
|
||||
#ifdef INCLUDE_SQL_BLOB_BINDING // TBD subjet to change:
|
||||
columnValue = [@"sqlblob:;base64," stringByAppendingString:columnValue];
|
||||
#endif
|
||||
break;
|
||||
#endif // else
|
||||
case SQLITE_TEXT:
|
||||
columnValue = [[NSString alloc] initWithBytes:(char *)sqlite3_column_text(statement, i)
|
||||
length:sqlite3_column_bytes(statement, i)
|
||||
encoding:NSUTF8StringEncoding];
|
||||
#if !__has_feature(objc_arc)
|
||||
[columnValue autorelease];
|
||||
#endif
|
||||
break;
|
||||
case SQLITE_NULL:
|
||||
// just in case (should not happen):
|
||||
default:
|
||||
columnValue = [NSNull null];
|
||||
break;
|
||||
}
|
||||
|
||||
if (columnValue) {
|
||||
[entry setObject:columnValue forKey:columnName];
|
||||
}
|
||||
|
||||
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 (nowRowsAffected > 0 && nowInsertId != 0) {
|
||||
hasInsertId = YES;
|
||||
insertId = [NSNumber numberWithLongLong:sqlite3_last_insert_rowid(db)];
|
||||
}
|
||||
keepGoing = NO;
|
||||
break;
|
||||
|
||||
default:
|
||||
error = [SQLitePlugin captureSQLiteErrorFromDb:db];
|
||||
keepGoing = NO;
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_finalize (statement);
|
||||
|
||||
if (error) {
|
||||
return [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:error];
|
||||
}
|
||||
|
||||
[resultSet setObject:resultRows forKey:@"rows"];
|
||||
[resultSet setObject:rowsAffected forKey:@"rowsAffected"];
|
||||
if (hasInsertId) {
|
||||
[resultSet setObject:insertId forKey:@"insertId"];
|
||||
}
|
||||
return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:resultSet];
|
||||
}
|
||||
|
||||
-(void)bindStatement:(sqlite3_stmt *)statement withArg:(NSObject *)arg atIndex:(int)argIndex
|
||||
{
|
||||
if ([arg isEqual:[NSNull null]]) {
|
||||
sqlite3_bind_null(statement, argIndex);
|
||||
} else if ([arg isKindOfClass:[NSNumber class]]) {
|
||||
NSNumber *numberArg = (NSNumber *)arg;
|
||||
const char *numberType = [numberArg objCType];
|
||||
if (strcmp(numberType, @encode(int)) == 0 ||
|
||||
strcmp(numberType, @encode(long long int)) == 0) {
|
||||
sqlite3_bind_int64(statement, argIndex, [numberArg longLongValue]);
|
||||
} else if (strcmp(numberType, @encode(double)) == 0) {
|
||||
sqlite3_bind_double(statement, argIndex, [numberArg doubleValue]);
|
||||
} else {
|
||||
sqlite3_bind_text(statement, argIndex, [[arg description] UTF8String], -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
} else { // NSString
|
||||
NSString *stringArg;
|
||||
|
||||
if ([arg isKindOfClass:[NSString class]]) {
|
||||
stringArg = (NSString *)arg;
|
||||
} else {
|
||||
stringArg = [arg description]; // convert to text
|
||||
}
|
||||
|
||||
#ifdef INCLUDE_SQL_BLOB_BINDING // TBD subjet to change:
|
||||
// If the string is a sqlblob URI then decode it and store the binary directly.
|
||||
//
|
||||
// A sqlblob URI is formatted similar to a data URI which makes it easy to convert:
|
||||
// sqlblob:[<mime type>][;charset=<charset>][;base64],<encoded data>
|
||||
//
|
||||
// The reason the `sqlblob` prefix is used instead of `data` is because
|
||||
// applications may want to use data URI strings directly, so the
|
||||
// `sqlblob` prefix disambiguates the desired behavior.
|
||||
if ([stringArg hasPrefix:@"sqlblob:"]) {
|
||||
// convert to data URI, decode, store as blob
|
||||
stringArg = [stringArg stringByReplacingCharactersInRange:NSMakeRange(0,7) withString:@"data"];
|
||||
NSData *data = [NSData dataWithContentsOfURL: [NSURL URLWithString:stringArg]];
|
||||
sqlite3_bind_blob(statement, argIndex, data.bytes, data.length, SQLITE_TRANSIENT);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
NSData *data = [stringArg dataUsingEncoding:NSUTF8StringEncoding];
|
||||
sqlite3_bind_text(statement, argIndex, data.bytes, (int)data.length, SQLITE_TRANSIENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-(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);
|
||||
}
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
[openDBs release];
|
||||
[appDBPaths release];
|
||||
[super dealloc];
|
||||
#endif
|
||||
}
|
||||
|
||||
+(NSDictionary *)captureSQLiteErrorFromDb:(struct sqlite3 *)db
|
||||
{
|
||||
int code = sqlite3_errcode(db);
|
||||
int webSQLCode = [SQLitePlugin mapSQLiteErrorCode:code];
|
||||
#if INCLUDE_SQLITE_ERROR_INFO
|
||||
int extendedCode = sqlite3_extended_errcode(db);
|
||||
#endif
|
||||
const char *message = sqlite3_errmsg(db);
|
||||
|
||||
NSMutableDictionary *error = [NSMutableDictionary dictionaryWithCapacity:4];
|
||||
|
||||
[error setObject:[NSNumber numberWithInt:webSQLCode] forKey:@"code"];
|
||||
[error setObject:[NSString stringWithUTF8String:message] forKey:@"message"];
|
||||
|
||||
#if INCLUDE_SQLITE_ERROR_INFO
|
||||
[error setObject:[NSNumber numberWithInt:code] forKey:@"sqliteCode"];
|
||||
[error setObject:[NSNumber numberWithInt:extendedCode] forKey:@"sqliteExtendedCode"];
|
||||
[error setObject:[NSString stringWithUTF8String:message] forKey:@"sqliteMessage"];
|
||||
#endif
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
+(int)mapSQLiteErrorCode:(int)code
|
||||
{
|
||||
// map the sqlite error code to
|
||||
// the websql error code
|
||||
switch(code) {
|
||||
case SQLITE_ERROR:
|
||||
return SYNTAX_ERR;
|
||||
case SQLITE_FULL:
|
||||
return QUOTA_ERR;
|
||||
case SQLITE_CONSTRAINT:
|
||||
return CONSTRAINT_ERR;
|
||||
default:
|
||||
return UNKNOWN_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef READ_BLOB_AS_BASE64
|
||||
+(NSString*)getBlobAsBase64String:(const char*)blob_chars
|
||||
withLength:(int)blob_length
|
||||
{
|
||||
size_t outputLength = 0;
|
||||
char* outputBuffer = CDVNewBase64Encode(blob_chars, blob_length, true, &outputLength);
|
||||
|
||||
NSString* result = [[NSString alloc] initWithBytesNoCopy:outputBuffer
|
||||
length:outputLength
|
||||
encoding:NSASCIIStringEncoding
|
||||
freeWhenDone:YES];
|
||||
#if !__has_feature(objc_arc)
|
||||
[result autorelease];
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
@end /* vim: set expandtab : */
|
||||
@@ -0,0 +1 @@
|
||||
#include "Constants.h"
|
||||
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include "sqlite3.h"
|
||||
|
||||
namespace SQLite3
|
||||
{
|
||||
public ref class Datatype sealed
|
||||
{
|
||||
public:
|
||||
static property int Integer { int get() { return SQLITE_INTEGER; } }
|
||||
static property int Float { int get() { return SQLITE_FLOAT; } }
|
||||
static property int Text { int get() { return SQLITE_TEXT; } }
|
||||
static property int Blob { int get() { return SQLITE_BLOB; } }
|
||||
static property int Null { int get() { return SQLITE_NULL; } }
|
||||
};
|
||||
|
||||
public ref class ResultCode sealed
|
||||
{
|
||||
public:
|
||||
static property int Ok { int get() { return SQLITE_OK; } }
|
||||
static property int Error { int get() { return SQLITE_ERROR; } }
|
||||
static property int Internal { int get() { return SQLITE_INTERNAL; } }
|
||||
static property int Perm { int get() { return SQLITE_PERM; } }
|
||||
static property int Abort { int get() { return SQLITE_ABORT; } }
|
||||
static property int Busy { int get() { return SQLITE_BUSY; } }
|
||||
static property int Locked { int get() { return SQLITE_LOCKED; } }
|
||||
static property int NoMem { int get() { return SQLITE_NOMEM; } }
|
||||
static property int ReadOnly { int get() { return SQLITE_READONLY; } }
|
||||
static property int Interrupt { int get() { return SQLITE_INTERRUPT; } }
|
||||
static property int IoErr { int get() { return SQLITE_IOERR; } }
|
||||
static property int Corrupt { int get() { return SQLITE_CORRUPT; } }
|
||||
static property int NotFound { int get() { return SQLITE_NOTFOUND; } }
|
||||
static property int Full { int get() { return SQLITE_FULL; } }
|
||||
static property int CantOpen { int get() { return SQLITE_CANTOPEN; } }
|
||||
static property int Protocol { int get() { return SQLITE_PROTOCOL; } }
|
||||
static property int Empty { int get() { return SQLITE_EMPTY; } }
|
||||
static property int Schema { int get() { return SQLITE_SCHEMA; } }
|
||||
static property int TooBig { int get() { return SQLITE_TOOBIG; } }
|
||||
static property int Constraint { int get() { return SQLITE_CONSTRAINT; } }
|
||||
static property int Mismatch { int get() { return SQLITE_MISMATCH; } }
|
||||
static property int Misuse { int get() { return SQLITE_MISUSE; } }
|
||||
static property int NoLfs { int get() { return SQLITE_NOLFS; } }
|
||||
static property int Auth { int get() { return SQLITE_AUTH; } }
|
||||
static property int Format { int get() { return SQLITE_FORMAT; } }
|
||||
static property int Range { int get() { return SQLITE_RANGE; } }
|
||||
static property int NotADb { int get() { return SQLITE_NOTADB; } }
|
||||
static property int Row { int get() { return SQLITE_ROW; } }
|
||||
static property int Done { int get() { return SQLITE_DONE; } }
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
#include "Winerror.h"
|
||||
|
||||
#include "Database.h"
|
||||
#include "Statement.h"
|
||||
|
||||
namespace SQLite3
|
||||
{
|
||||
Database::Database(Platform::String^ dbPath)
|
||||
: sqlite(nullptr)
|
||||
{
|
||||
int ret = sqlite3_open16(dbPath->Data(), &sqlite);
|
||||
|
||||
if (ret != SQLITE_OK)
|
||||
{
|
||||
sqlite3_close(sqlite);
|
||||
|
||||
HRESULT hresult = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, ret);
|
||||
throw ref new Platform::COMException(hresult);
|
||||
}
|
||||
}
|
||||
|
||||
Database::~Database()
|
||||
{
|
||||
if (sqlite != nullptr) sqlite3_close(sqlite);
|
||||
}
|
||||
|
||||
Statement^ Database::Prepare(Platform::String^ sql)
|
||||
{
|
||||
return ref new Statement(this, sql);
|
||||
}
|
||||
|
||||
int Database::closedb()
|
||||
{
|
||||
int rc = sqlite3_close(sqlite);
|
||||
if (rc == SQLITE_OK) sqlite = nullptr;
|
||||
return rc;
|
||||
}
|
||||
|
||||
int Database::close_v2()
|
||||
{
|
||||
int rc = sqlite3_close_v2(sqlite);
|
||||
if (rc == SQLITE_OK) sqlite = nullptr;
|
||||
return rc;
|
||||
}
|
||||
|
||||
int Database::LastInsertRowid()
|
||||
{
|
||||
return sqlite3_last_insert_rowid(sqlite);
|
||||
}
|
||||
|
||||
int Database::TotalChanges()
|
||||
{
|
||||
return sqlite3_total_changes(sqlite);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include "sqlite3.h"
|
||||
|
||||
namespace SQLite3
|
||||
{
|
||||
ref class Statement;
|
||||
|
||||
public ref class Database sealed
|
||||
{
|
||||
public:
|
||||
Database(Platform::String^ dbPath);
|
||||
virtual ~Database();
|
||||
|
||||
int closedb();
|
||||
int close_v2();
|
||||
|
||||
Statement^ Prepare(Platform::String^ sql);
|
||||
|
||||
int LastInsertRowid();
|
||||
int TotalChanges();
|
||||
|
||||
private:
|
||||
friend Statement;
|
||||
|
||||
sqlite3* sqlite;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup Label="Globals">
|
||||
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
|
||||
<HasSharedItems>true</HasSharedItems>
|
||||
<ItemsProjectGuid>{db84ae51-4b93-44f5-be22-1eae1833ecec}</ItemsProjectGuid>
|
||||
<ItemsRootNamespace>SQLite3</ItemsRootNamespace>
|
||||
<ItemsProjectName>SQLite3.Shared</ItemsProjectName>
|
||||
<CodeSharingProject>248F659F-DAC5-46E8-AC09-60EC9FC95053</CodeSharingProject>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)Constants.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)Database.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)Statement.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)pch.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\..\common\sqlite3.c">
|
||||
<CompileAsWinRT>false</CompileAsWinRT>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)Constants.cpp" />
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)Database.cpp" />
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)Statement.cpp" />
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectCapability Include="SourceItemsFromImports" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,248 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|ARM">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>ARM</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|ARM">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>ARM</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{d1848fee-6a8e-4ef1-8bfb-8652e5a9cd4a}</ProjectGuid>
|
||||
<ProjectName>SQLite3</ProjectName>
|
||||
<RootNamespace>SQLite3</RootNamespace>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<MinimumVisualStudioVersion>12.0</MinimumVisualStudioVersion>
|
||||
<AppContainerApplication>true</AppContainerApplication>
|
||||
<ApplicationType>Windows Store</ApplicationType>
|
||||
<ApplicationTypeRevision>8.1</ApplicationTypeRevision>
|
||||
<ConvergedProjectType>CodeSharingWindowsRuntimeComponent</ConvergedProjectType>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v120</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v120</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v120</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<PlatformToolset>v120</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<PlatformToolset>v120</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<PlatformToolset>v120</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
|
||||
<Import Project="..\SQLite3.Shared.vcxitems" Label="Shared" />
|
||||
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(MSBuildThisFileDirectory)..\..\..\..\common;$(MSBuildThisFileDirectory)..\..\..\..\external</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalDependencies>runtimeobject.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(MSBuildThisFileDirectory)..\..\..\..\common;$(MSBuildThisFileDirectory)..\..\..\..\external</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalDependencies>runtimeobject.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(MSBuildThisFileDirectory)..\..\..\..\common;$(MSBuildThisFileDirectory)..\..\..\..\external</AdditionalIncludeDirectories>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalDependencies>runtimeobject.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(MSBuildThisFileDirectory)..\..\..\..\common;$(MSBuildThisFileDirectory)..\..\..\..\external</AdditionalIncludeDirectories>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalDependencies>runtimeobject.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(MSBuildThisFileDirectory)..\..\..\..\common;$(MSBuildThisFileDirectory)..\..\..\..\external</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalDependencies>runtimeobject.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(MSBuildThisFileDirectory)..\..\..\..\common;$(MSBuildThisFileDirectory)..\..\..\..\external</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalDependencies>runtimeobject.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,162 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|ARM">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>ARM</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|ARM">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>ARM</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{6435c2e4-4fd2-405a-9ee2-5312ae852782}</ProjectGuid>
|
||||
<ProjectName>SQLite3</ProjectName>
|
||||
<RootNamespace>SQLite3</RootNamespace>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<MinimumVisualStudioVersion>12.0</MinimumVisualStudioVersion>
|
||||
<AppContainerApplication>true</AppContainerApplication>
|
||||
<ApplicationType>Windows Phone</ApplicationType>
|
||||
<ApplicationTypeRevision>8.1</ApplicationTypeRevision>
|
||||
<ConvergedProjectType>CodeSharingWindowsRuntimeComponent</ConvergedProjectType>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v120_wp81</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v120_wp81</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<PlatformToolset>v120_wp81</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<PlatformToolset>v120_wp81</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
|
||||
<Import Project="..\SQLite3.Shared.vcxitems" Label="Shared" />
|
||||
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(MSBuildThisFileDirectory)..\..\..\..\common;$(MSBuildThisFileDirectory)..\..\..\..\external</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(MSBuildThisFileDirectory)..\..\..\..\common;$(MSBuildThisFileDirectory)..\..\..\..\external</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(MSBuildThisFileDirectory)..\..\..\..\common;$(MSBuildThisFileDirectory)..\..\..\..\external</AdditionalIncludeDirectories>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(MSBuildThisFileDirectory)..\..\..\..\common;$(MSBuildThisFileDirectory)..\..\..\..\external</AdditionalIncludeDirectories>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,60 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Express 2013 for Windows
|
||||
VisualStudioVersion = 12.0.30501.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SQLite3", "SQLite3", "{F0B5DA26-91CD-44D3-97C8-19B43134837E}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SQLite3.Shared", "SQLite3\SQLite3.Shared\SQLite3.Shared.vcxitems", "{DB84AE51-4B93-44F5-BE22-1EAE1833ECEC}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SQLite3.Windows", "SQLite3\SQLite3.Windows\SQLite3.Windows.vcxproj", "{D1848FEE-6A8E-4EF1-8BFB-8652E5A9CD4A}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SQLite3.WindowsPhone", "SQLite3\SQLite3.WindowsPhone\SQLite3.WindowsPhone.vcxproj", "{6435C2E4-4FD2-405A-9EE2-5312AE852782}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SharedMSBuildProjectFiles) = preSolution
|
||||
SQLite3\SQLite3.Shared\SQLite3.Shared.vcxitems*{db84ae51-4b93-44f5-be22-1eae1833ecec}*SharedItemsImports = 9
|
||||
SQLite3\SQLite3.Shared\SQLite3.Shared.vcxitems*{d1848fee-6a8e-4ef1-8bfb-8652e5a9cd4a}*SharedItemsImports = 4
|
||||
SQLite3\SQLite3.Shared\SQLite3.Shared.vcxitems*{6435c2e4-4fd2-405a-9ee2-5312ae852782}*SharedItemsImports = 4
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|ARM = Debug|ARM
|
||||
Debug|Win32 = Debug|Win32
|
||||
Debug|x64 = Debug|x64
|
||||
Release|ARM = Release|ARM
|
||||
Release|Win32 = Release|Win32
|
||||
Release|x64 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{D1848FEE-6A8E-4EF1-8BFB-8652E5A9CD4A}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{D1848FEE-6A8E-4EF1-8BFB-8652E5A9CD4A}.Debug|ARM.Build.0 = Debug|ARM
|
||||
{D1848FEE-6A8E-4EF1-8BFB-8652E5A9CD4A}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{D1848FEE-6A8E-4EF1-8BFB-8652E5A9CD4A}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{D1848FEE-6A8E-4EF1-8BFB-8652E5A9CD4A}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{D1848FEE-6A8E-4EF1-8BFB-8652E5A9CD4A}.Debug|x64.Build.0 = Debug|x64
|
||||
{D1848FEE-6A8E-4EF1-8BFB-8652E5A9CD4A}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{D1848FEE-6A8E-4EF1-8BFB-8652E5A9CD4A}.Release|ARM.Build.0 = Release|ARM
|
||||
{D1848FEE-6A8E-4EF1-8BFB-8652E5A9CD4A}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{D1848FEE-6A8E-4EF1-8BFB-8652E5A9CD4A}.Release|Win32.Build.0 = Release|Win32
|
||||
{D1848FEE-6A8E-4EF1-8BFB-8652E5A9CD4A}.Release|x64.ActiveCfg = Release|x64
|
||||
{D1848FEE-6A8E-4EF1-8BFB-8652E5A9CD4A}.Release|x64.Build.0 = Release|x64
|
||||
{6435C2E4-4FD2-405A-9EE2-5312AE852782}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{6435C2E4-4FD2-405A-9EE2-5312AE852782}.Debug|ARM.Build.0 = Debug|ARM
|
||||
{6435C2E4-4FD2-405A-9EE2-5312AE852782}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{6435C2E4-4FD2-405A-9EE2-5312AE852782}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{6435C2E4-4FD2-405A-9EE2-5312AE852782}.Debug|x64.ActiveCfg = Debug|Win32
|
||||
{6435C2E4-4FD2-405A-9EE2-5312AE852782}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{6435C2E4-4FD2-405A-9EE2-5312AE852782}.Release|ARM.Build.0 = Release|ARM
|
||||
{6435C2E4-4FD2-405A-9EE2-5312AE852782}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{6435C2E4-4FD2-405A-9EE2-5312AE852782}.Release|Win32.Build.0 = Release|Win32
|
||||
{6435C2E4-4FD2-405A-9EE2-5312AE852782}.Release|x64.ActiveCfg = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{DB84AE51-4B93-44F5-BE22-1EAE1833ECEC} = {F0B5DA26-91CD-44D3-97C8-19B43134837E}
|
||||
{D1848FEE-6A8E-4EF1-8BFB-8652E5A9CD4A} = {F0B5DA26-91CD-44D3-97C8-19B43134837E}
|
||||
{6435C2E4-4FD2-405A-9EE2-5312AE852782} = {F0B5DA26-91CD-44D3-97C8-19B43134837E}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,90 @@
|
||||
#include "Winerror.h"
|
||||
|
||||
#include "Statement.h"
|
||||
#include "Database.h"
|
||||
|
||||
namespace SQLite3
|
||||
{
|
||||
Statement::Statement(Database^ database, Platform::String^ sql)
|
||||
{
|
||||
int ret = sqlite3_prepare16(database->sqlite, sql->Data(), -1, &statement, 0);
|
||||
|
||||
if (ret != SQLITE_OK)
|
||||
{
|
||||
sqlite3_finalize(statement);
|
||||
|
||||
HRESULT hresult = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, ret);
|
||||
throw ref new Platform::COMException(hresult);
|
||||
}
|
||||
}
|
||||
|
||||
Statement::~Statement()
|
||||
{
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
|
||||
int Statement::Step()
|
||||
{
|
||||
return sqlite3_step(statement);
|
||||
}
|
||||
|
||||
int Statement::ColumnCount()
|
||||
{
|
||||
return sqlite3_column_count(statement);
|
||||
}
|
||||
|
||||
int Statement::ColumnType(int index)
|
||||
{
|
||||
return sqlite3_column_type(statement, index);
|
||||
}
|
||||
|
||||
Platform::String^ Statement::ColumnName(int index)
|
||||
{
|
||||
return ref new Platform::String(static_cast<const wchar_t*>(sqlite3_column_name16(statement, index)));
|
||||
}
|
||||
|
||||
Platform::String^ Statement::ColumnText(int index)
|
||||
{
|
||||
return ref new Platform::String(static_cast<const wchar_t*>(sqlite3_column_text16(statement, index)));
|
||||
}
|
||||
|
||||
int Statement::ColumnInt(int index)
|
||||
{
|
||||
return sqlite3_column_int(statement, index);
|
||||
}
|
||||
|
||||
long long Statement::ColumnInt64(int index)
|
||||
{
|
||||
return sqlite3_column_int64(statement, index);
|
||||
}
|
||||
|
||||
double Statement::ColumnDouble(int index)
|
||||
{
|
||||
return sqlite3_column_double(statement, index);
|
||||
}
|
||||
|
||||
int Statement::BindText(int index, Platform::String^ val)
|
||||
{
|
||||
return sqlite3_bind_text16(statement, index, val->Data(), -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
int Statement::BindInt(int index, int val)
|
||||
{
|
||||
return sqlite3_bind_int(statement, index, val);
|
||||
}
|
||||
|
||||
int Statement::BindInt64(int index, long long val)
|
||||
{
|
||||
return sqlite3_bind_int64(statement, index, val);
|
||||
}
|
||||
|
||||
int Statement::BindDouble(int index, double val)
|
||||
{
|
||||
return sqlite3_bind_double(statement, index, val);
|
||||
}
|
||||
|
||||
int Statement::BindNull(int index)
|
||||
{
|
||||
return sqlite3_bind_null(statement, index);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include "sqlite3.h"
|
||||
|
||||
namespace SQLite3
|
||||
{
|
||||
ref class Database;
|
||||
|
||||
public ref class Statement sealed
|
||||
{
|
||||
public:
|
||||
Statement(Database^ database, Platform::String^ sql);
|
||||
virtual ~Statement();
|
||||
|
||||
int Step();
|
||||
|
||||
int ColumnCount();
|
||||
int ColumnType(int index);
|
||||
Platform::String^ ColumnName(int index);
|
||||
|
||||
Platform::String^ ColumnText(int index);
|
||||
int ColumnInt(int index);
|
||||
long long ColumnInt64(int index);
|
||||
double ColumnDouble(int index);
|
||||
|
||||
int BindText(int index, Platform::String^ val);
|
||||
int BindInt(int index, int val);
|
||||
int BindInt64(int index, long long val);
|
||||
int BindDouble(int index, double val);
|
||||
int BindNull(int index);
|
||||
|
||||
private:
|
||||
sqlite3_stmt* statement;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
#include "pch.h"
|
||||
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
#include <collection.h>
|
||||
#include <ppltasks.h>
|
||||
287
plugins/cordova-sqlite-storage/src/windows/SQLite3-WinRT/SQLite3JS/js/SQLite3.js
vendored
Normal file
287
plugins/cordova-sqlite-storage/src/windows/SQLite3-WinRT/SQLite3JS/js/SQLite3.js
vendored
Normal file
@@ -0,0 +1,287 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var Statement, Database, ItemDataSource, GroupDataSource;
|
||||
|
||||
// Alternative typeof implementation yielding more meaningful results,
|
||||
// see http://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/
|
||||
function type(obj) {
|
||||
var typeString;
|
||||
|
||||
typeString = Object.prototype.toString.call(obj);
|
||||
return typeString.substring(8, typeString.length - 1).toLowerCase();
|
||||
}
|
||||
|
||||
function throwSQLiteError(message, comException) {
|
||||
var error = new Error(message);
|
||||
error.resultCode = comException.number & 0xffff;
|
||||
throw error;
|
||||
}
|
||||
|
||||
Statement = WinJS.Class.define(function (db, sql, args) {
|
||||
try {
|
||||
this.statement = db.connection.prepare(sql);
|
||||
} catch (comException) {
|
||||
throwSQLiteError('Error preparing an SQLite statement.', comException);
|
||||
}
|
||||
|
||||
if (args) {
|
||||
this.bind(args);
|
||||
}
|
||||
}, {
|
||||
bind: function (args) {
|
||||
var index, resultCode;
|
||||
|
||||
args.forEach(function (arg, i) {
|
||||
index = i + 1;
|
||||
switch (type(arg)) {
|
||||
case 'number':
|
||||
if (arg % 1 === 0) {
|
||||
resultCode = this.statement.bindInt64(index, arg);
|
||||
} else {
|
||||
resultCode = this.statement.bindDouble(index, arg);
|
||||
}
|
||||
break;
|
||||
case 'string':
|
||||
resultCode = this.statement.bindText(index, arg);
|
||||
break;
|
||||
case 'null':
|
||||
resultCode = this.statement.bindNull(index);
|
||||
break;
|
||||
default:
|
||||
throw new Error("Unsupported argument type: " + type(arg));
|
||||
}
|
||||
if (resultCode !== SQLite3.ResultCode.ok) {
|
||||
throw new Error("Error " + resultCode + " when binding argument to SQL query.");
|
||||
}
|
||||
}, this);
|
||||
},
|
||||
run: function () {
|
||||
this.statement.step();
|
||||
},
|
||||
one: function () {
|
||||
this.statement.step();
|
||||
return this._getRow();
|
||||
},
|
||||
all: function () {
|
||||
var result = [];
|
||||
|
||||
this.each(function (row) {
|
||||
result.push(row);
|
||||
});
|
||||
return result;
|
||||
},
|
||||
each: function (callback) {
|
||||
while (this.statement.step() === SQLite3.ResultCode.row) {
|
||||
callback(this._getRow());
|
||||
}
|
||||
},
|
||||
map: function (callback) {
|
||||
var result = [];
|
||||
|
||||
this.each(function (row) {
|
||||
result.push(callback(row));
|
||||
});
|
||||
return result;
|
||||
},
|
||||
close: function () {
|
||||
this.statement.close();
|
||||
},
|
||||
_getRow: function () {
|
||||
var i, len, name, row = {};
|
||||
|
||||
for (i = 0, len = this.statement.columnCount() ; i < len; i += 1) {
|
||||
name = this.statement.columnName(i);
|
||||
row[name] = this._getColumn(i);
|
||||
}
|
||||
|
||||
return row;
|
||||
},
|
||||
_getColumn: function (index) {
|
||||
switch (this.statement.columnType(index)) {
|
||||
case SQLite3.Datatype.integer:
|
||||
return this.statement.columnInt64(index);
|
||||
case SQLite3.Datatype.float:
|
||||
return this.statement.columnDouble(index);
|
||||
case SQLite3.Datatype.text:
|
||||
return this.statement.columnText(index);
|
||||
case SQLite3.Datatype["null"]:
|
||||
return null;
|
||||
default:
|
||||
throw new Error('Unsupported column type in column ' + index);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Database = WinJS.Class.define(function (dbPath) {
|
||||
try {
|
||||
this.connection = SQLite3.Database(dbPath);
|
||||
} catch (comException) {
|
||||
throwSQLiteError('Error creating an SQLite database connection.', comException);
|
||||
}
|
||||
}, {
|
||||
run: function (sql, args) {
|
||||
var statement = this.prepare(sql, args);
|
||||
|
||||
statement.run();
|
||||
statement.close();
|
||||
},
|
||||
one: function (sql, args) {
|
||||
var row, statement = this.prepare(sql, args);
|
||||
|
||||
row = statement.one();
|
||||
statement.close();
|
||||
return row;
|
||||
},
|
||||
all: function (sql, args) {
|
||||
var rows, statement = this.prepare(sql, args);
|
||||
|
||||
rows = statement.all();
|
||||
statement.close();
|
||||
return rows;
|
||||
},
|
||||
each: function (sql, args, callback) {
|
||||
if (!callback && type(args) === 'function') {
|
||||
callback = args;
|
||||
args = null;
|
||||
}
|
||||
|
||||
var statement = this.prepare(sql, args);
|
||||
|
||||
statement.each(callback);
|
||||
statement.close();
|
||||
},
|
||||
map: function (sql, args, callback) {
|
||||
if (!callback && type(args) === 'function') {
|
||||
callback = args;
|
||||
args = null;
|
||||
}
|
||||
|
||||
var rows, statement = this.prepare(sql, args);
|
||||
|
||||
rows = statement.map(callback);
|
||||
statement.close();
|
||||
return rows;
|
||||
},
|
||||
prepare: function (sql, args) {
|
||||
return new Statement(this, sql, args);
|
||||
},
|
||||
itemDataSource: function (sql, args, keyColumnName, groupKeyColumnName) {
|
||||
if (type(args) === 'string') {
|
||||
groupKeyColumnName = keyColumnName;
|
||||
keyColumnName = args;
|
||||
args = undefined;
|
||||
}
|
||||
|
||||
return new ItemDataSource(this, sql, args, keyColumnName, groupKeyColumnName);
|
||||
},
|
||||
groupDataSource: function (sql, args, keyColumnName, sizeColumnName) {
|
||||
if (type(args) === 'string') {
|
||||
sizeColumnName = keyColumnName;
|
||||
keyColumnName = args;
|
||||
args = undefined;
|
||||
}
|
||||
|
||||
return new GroupDataSource(this, sql, args, keyColumnName, sizeColumnName);
|
||||
},
|
||||
close: function () {
|
||||
return this.connection.closedb();
|
||||
},
|
||||
close_v2: function () {
|
||||
return this.connection.close_v2();
|
||||
},
|
||||
lastInsertRowid: function () {
|
||||
return this.connection.lastInsertRowid();
|
||||
},
|
||||
totalChanges: function () {
|
||||
return this.connection.totalChanges();
|
||||
}
|
||||
});
|
||||
|
||||
ItemDataSource = WinJS.Class.derive(WinJS.UI.VirtualizedDataSource,
|
||||
function (db, sql, args, keyColumnName, groupKeyColumnName) {
|
||||
var dataAdapter = {
|
||||
getCount: function () {
|
||||
var row = db.one('SELECT COUNT(*) AS cnt FROM (' + sql + ')', args);
|
||||
return WinJS.Promise.wrap(row.cnt);
|
||||
},
|
||||
itemsFromIndex: function (requestIndex, countBefore, countAfter) {
|
||||
var items,
|
||||
limit = countBefore + 1 + countAfter,
|
||||
offset = requestIndex - countBefore;
|
||||
|
||||
items = db.map(
|
||||
'SELECT * FROM (' + sql + ') LIMIT ' + limit + ' OFFSET ' + offset,
|
||||
function (row) {
|
||||
var item = {
|
||||
key: row[keyColumnName].toString(),
|
||||
data: row
|
||||
};
|
||||
if (groupKeyColumnName) {
|
||||
item.groupKey = row[groupKeyColumnName].toString();
|
||||
}
|
||||
return item;
|
||||
});
|
||||
|
||||
return WinJS.Promise.wrap({
|
||||
items: items,
|
||||
offset: countBefore,
|
||||
atEnd: items.length < limit
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
this._baseDataSourceConstructor(dataAdapter);
|
||||
}
|
||||
);
|
||||
|
||||
GroupDataSource = WinJS.Class.derive(WinJS.UI.VirtualizedDataSource,
|
||||
function (db, sql, args, keyColumnName, sizeColumnName) {
|
||||
var groups,
|
||||
dataAdapter,
|
||||
keyIndexMap = {},
|
||||
groupIndex = 0,
|
||||
firstItemIndex = 0;
|
||||
|
||||
groups = db.map(sql, args, function (row) {
|
||||
var item = {
|
||||
key: row[keyColumnName].toString(),
|
||||
groupSize: row[sizeColumnName],
|
||||
firstItemIndexHint: firstItemIndex,
|
||||
data: row
|
||||
};
|
||||
|
||||
keyIndexMap[item.key] = groupIndex;
|
||||
groupIndex += 1;
|
||||
firstItemIndex += item.groupSize;
|
||||
|
||||
return item;
|
||||
});
|
||||
|
||||
dataAdapter = {
|
||||
getCount: function () {
|
||||
return WinJS.Promise.wrap(groups.length);
|
||||
},
|
||||
itemsFromIndex: function (requestIndex, countBefore, countAfter) {
|
||||
return WinJS.Promise.wrap({
|
||||
items: groups.slice(),
|
||||
offset: requestIndex,
|
||||
absoluteIndex: requestIndex,
|
||||
atStart: true,
|
||||
atEnd: true
|
||||
});
|
||||
},
|
||||
itemsFromKey: function (key, countBefore, countAfter) {
|
||||
return this.itemsFromIndex(keyIndexMap[key], countBefore, countAfter);
|
||||
}
|
||||
};
|
||||
|
||||
this._baseDataSourceConstructor(dataAdapter);
|
||||
}
|
||||
);
|
||||
|
||||
WinJS.Namespace.define('SQLite3JS', {
|
||||
Database: Database
|
||||
});
|
||||
|
||||
}());
|
||||
162
plugins/cordova-sqlite-storage/src/windows/SQLiteProxy.js
vendored
Normal file
162
plugins/cordova-sqlite-storage/src/windows/SQLiteProxy.js
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
var dbmap = {};
|
||||
|
||||
var nextTick = window.setImmediate || function(fun) {
|
||||
window.setTimeout(fun, 0);
|
||||
};
|
||||
|
||||
function handle(p, win, fail) {
|
||||
if (p)
|
||||
p.done(
|
||||
function (res) {
|
||||
if (res[1])
|
||||
fail(res[1]);
|
||||
else
|
||||
win(res[0]?JSON.parse(res[0]):[]);
|
||||
},
|
||||
function (err) {
|
||||
fail(err);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
open: function(win, fail, args) {
|
||||
var options = args[0];
|
||||
var res;
|
||||
|
||||
function openImmediate(dbname) {
|
||||
//var dbname = options.name;
|
||||
// from @EionRobb / phonegap-win8-sqlite:
|
||||
var opendbname = Windows.Storage.ApplicationData.current.localFolder.path + "\\" + dbname;
|
||||
console.log("open db name: " + dbname + " at full path: " + opendbname);
|
||||
|
||||
var db = new SQLite3JS.Database(opendbname);
|
||||
dbmap[dbname] = db;
|
||||
nextTick(function() {
|
||||
win();
|
||||
});
|
||||
//res = SQLitePluginRT.SQLitePlugin.openAsync(options.name);
|
||||
}
|
||||
|
||||
try {
|
||||
//res = SQLitePluginRT.SQLitePlugin.openAsync(options.name);
|
||||
var dbname = options.name;
|
||||
|
||||
openImmediate(dbname);
|
||||
} catch(ex) {
|
||||
//fail(ex);
|
||||
nextTick(function() {
|
||||
fail(ex);
|
||||
});
|
||||
}
|
||||
//handle(res, win, fail);
|
||||
},
|
||||
close: function(win, fail, args) {
|
||||
var options = args[0];
|
||||
var res;
|
||||
try {
|
||||
//res = SQLitePluginRT.SQLitePlugin.closeAsync(JSON.stringify(options));
|
||||
var dbname = options.path;
|
||||
nextTick(function() {
|
||||
if (!!dbmap[dbname] && dbmap[dbname].close() == 0) {
|
||||
delete dbmap[dbname];
|
||||
win();
|
||||
} else {
|
||||
fail(); // XXX TODO REPORT ERROR
|
||||
}
|
||||
});
|
||||
} catch (ex) {
|
||||
fail(ex);
|
||||
}
|
||||
//handle(res, win, fail);
|
||||
},
|
||||
backgroundExecuteSqlBatch: function(win, fail, args) {
|
||||
var options = args[0];
|
||||
var dbname = options.dbargs.dbname;
|
||||
var executes = options.executes;
|
||||
//var executes = options.executes.map(function (e) { return [String(e.qid), e.sql, e.params]; });
|
||||
var db = dbmap[dbname];
|
||||
var results = [];
|
||||
var i, count=executes.length;
|
||||
//console.log("executes: " + JSON.stringify(executes));
|
||||
//console.log("execute sql count: " + count);
|
||||
for (i=0; i<count; ++i) {
|
||||
var e = executes[i];
|
||||
//console.log("execute sql: " + e.sql + " params: " + JSON.stringify(e.params));
|
||||
try {
|
||||
var oldTotalChanges = db.totalChanges();
|
||||
var rows = db.all(e.sql, e.params);
|
||||
//console.log("got rows: " + JSON.stringify(rows));
|
||||
var rowsAffected = db.totalChanges() - oldTotalChanges;
|
||||
var result = { rows: rows, rowsAffected: rowsAffected };
|
||||
if (rowsAffected > 0) {
|
||||
var lastInsertRowid = db.lastInsertRowid();
|
||||
if (lastInsertRowid !== 0) result.insertId = lastInsertRowid;
|
||||
}
|
||||
results.push({
|
||||
type: "success",
|
||||
qid: e.qid,
|
||||
result: result
|
||||
});
|
||||
} catch(ex) {
|
||||
console.log("sql exception error: " + ex.message);
|
||||
results.push({
|
||||
type: "error",
|
||||
qid: e.qid,
|
||||
result: { code: -1, message: ex.message }
|
||||
});
|
||||
}
|
||||
}
|
||||
//console.log("return results: " + JSON.stringify(results));
|
||||
nextTick(function() {
|
||||
//console.log("return results: " + JSON.stringify(results));
|
||||
win(results);
|
||||
});
|
||||
},
|
||||
"delete": function(win, fail, args) {
|
||||
var options = args[0];
|
||||
var res;
|
||||
try {
|
||||
//res = SQLitePluginRT.SQLitePlugin.deleteAsync(JSON.stringify(options));
|
||||
var dbname = options.path;
|
||||
|
||||
WinJS.Application.local.exists(dbname).then(function(isExisting) {
|
||||
if (!isExisting) {
|
||||
// XXX FUTURE TBD consistent for all platforms:
|
||||
fail("file does not exist");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!!dbmap[dbname]) {
|
||||
dbmap[dbname].close_v2();
|
||||
|
||||
delete dbmap[dbname];
|
||||
}
|
||||
|
||||
//console.log('test db name: ' + dbname);
|
||||
Windows.Storage.ApplicationData.current.localFolder.getFileAsync(dbname)
|
||||
.then(function (dbfile) {
|
||||
//console.log('get db file to delete ok');
|
||||
return dbfile.deleteAsync(Windows.Storage.StorageDeleteOption.permanentDelete);
|
||||
}, function (e) {
|
||||
console.log('get file failure: ' + JSON.stringify(e));
|
||||
// XXX FUTURE TBD consistent for all platforms:
|
||||
fail(e);
|
||||
}).then(function () {
|
||||
//console.log('delete ok');
|
||||
win();
|
||||
}, function (e) {
|
||||
console.log('delete failure: ' + JSON.stringify(e));
|
||||
// XXX FUTURE TBD consistent for all platforms:
|
||||
fail(e);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
} catch(ex) {
|
||||
fail(ex);
|
||||
}
|
||||
//handle(res, win, fail);
|
||||
}
|
||||
};
|
||||
require("cordova/exec/proxy").add("SQLitePlugin", module.exports);
|
||||
3162
plugins/cordova-sqlite-storage/src/wp/SQLite.cs
Normal file
3162
plugins/cordova-sqlite-storage/src/wp/SQLite.cs
Normal file
File diff suppressed because it is too large
Load Diff
722
plugins/cordova-sqlite-storage/src/wp/SQLitePlugin.cs
Normal file
722
plugins/cordova-sqlite-storage/src/wp/SQLitePlugin.cs
Normal file
@@ -0,0 +1,722 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Threading;
|
||||
|
||||
using SQLite;
|
||||
|
||||
using WPCordovaClassLib.Cordova;
|
||||
using WPCordovaClassLib.Cordova.Commands;
|
||||
using WPCordovaClassLib.Cordova.JSON;
|
||||
|
||||
namespace Cordova.Extension.Commands
|
||||
{
|
||||
|
||||
public class SQLitePlugin : BaseCommand
|
||||
{
|
||||
#region SQLitePlugin options
|
||||
|
||||
[DataContract]
|
||||
public class SQLitePluginOpenOptions
|
||||
{
|
||||
[DataMember(IsRequired = true, Name = "name")]
|
||||
public string name { get; set; }
|
||||
|
||||
[DataMember(IsRequired = false, Name = "bgType", EmitDefaultValue = false)]
|
||||
public int bgType = 0;
|
||||
}
|
||||
|
||||
[DataContract]
|
||||
public class SQLitePluginCloseDeleteOptions
|
||||
{
|
||||
[DataMember(IsRequired = true, Name = "path")]
|
||||
public string name { get; set; }
|
||||
}
|
||||
|
||||
[DataContract]
|
||||
public class SQLitePluginExecuteSqlBatchArgs
|
||||
{
|
||||
[DataMember(IsRequired = true, Name = "dbname")]
|
||||
public string name { get; set; }
|
||||
}
|
||||
|
||||
[DataContract]
|
||||
public class SQLitePluginExecuteSqlBatchOptions
|
||||
{
|
||||
[DataMember(IsRequired = true, Name = "dbargs")]
|
||||
public SQLitePluginExecuteSqlBatchArgs dbargs { get; set; }
|
||||
|
||||
[DataMember(IsRequired = true, Name = "executes")]
|
||||
public TransactionsCollection executes { get; set; }
|
||||
}
|
||||
|
||||
[CollectionDataContract]
|
||||
public class TransactionsCollection : Collection<SQLitePluginTransaction>
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[DataContract]
|
||||
public class SQLitePluginTransaction
|
||||
{
|
||||
/// <summary>
|
||||
/// Identifier for transaction
|
||||
/// </summary>
|
||||
[DataMember(IsRequired = false, Name = "trans_id")]
|
||||
public string transId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Callback identifer
|
||||
/// </summary>
|
||||
[DataMember(IsRequired = true, Name = "qid")]
|
||||
public string queryId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Query string
|
||||
/// </summary>
|
||||
[DataMember(IsRequired = true, Name = "sql")]
|
||||
public string query { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Query parameters
|
||||
/// </summary>
|
||||
/// <remarks>TODO: broken in WP8 & 8.1; interprets ["1.5"] as [1.5]</remarks>
|
||||
[DataMember(IsRequired = true, Name = "params")]
|
||||
public object[] query_params { get; set; }
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private readonly DatabaseManager databaseManager;
|
||||
|
||||
public SQLitePlugin()
|
||||
{
|
||||
this.databaseManager = new DatabaseManager(this);
|
||||
}
|
||||
|
||||
public void open(string options)
|
||||
{
|
||||
string mycbid = this.CurrentCommandCallbackId;
|
||||
//System.Diagnostics.Debug.WriteLine("SQLitePlugin.open() with cbid " + mycbid + " options:" + options);
|
||||
|
||||
try
|
||||
{
|
||||
String [] jsonOptions = JsonHelper.Deserialize<string[]>(options);
|
||||
mycbid = jsonOptions[1];
|
||||
|
||||
var dbOptions = JsonHelper.Deserialize<SQLitePluginOpenOptions>(jsonOptions[0]);
|
||||
this.databaseManager.Open(dbOptions.name, mycbid);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION), mycbid);
|
||||
}
|
||||
}
|
||||
|
||||
public void close(string options)
|
||||
{
|
||||
string mycbid = this.CurrentCommandCallbackId;
|
||||
|
||||
try
|
||||
{
|
||||
String[] jsonOptions = JsonHelper.Deserialize<string[]>(options);
|
||||
mycbid = jsonOptions[1];
|
||||
|
||||
var dbOptions = JsonHelper.Deserialize<SQLitePluginCloseDeleteOptions>(jsonOptions[0]);
|
||||
this.databaseManager.Close(dbOptions.name, mycbid);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION), mycbid);
|
||||
}
|
||||
}
|
||||
|
||||
public void delete(string options)
|
||||
{
|
||||
string mycbid = this.CurrentCommandCallbackId;
|
||||
|
||||
try
|
||||
{
|
||||
String[] jsonOptions = JsonHelper.Deserialize<string[]>(options);
|
||||
mycbid = jsonOptions[1];
|
||||
|
||||
var dbOptions = JsonHelper.Deserialize<SQLitePluginCloseDeleteOptions>(jsonOptions[0]);
|
||||
this.databaseManager.Delete(dbOptions.name, mycbid);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION), mycbid);
|
||||
}
|
||||
}
|
||||
|
||||
public void backgroundExecuteSqlBatch(string options)
|
||||
{
|
||||
string mycbid = this.CurrentCommandCallbackId;
|
||||
|
||||
try
|
||||
{
|
||||
String[] jsonOptions = JsonHelper.Deserialize<string[]>(options);
|
||||
mycbid = jsonOptions[1];
|
||||
|
||||
SQLitePluginExecuteSqlBatchOptions batch = JsonHelper.Deserialize<SQLitePluginExecuteSqlBatchOptions>(jsonOptions[0]);
|
||||
this.databaseManager.Query(batch.dbargs.name, batch.executes, mycbid);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION), mycbid);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manage the collection of currently open databases and queue requests for them
|
||||
/// </summary>
|
||||
public class DatabaseManager
|
||||
{
|
||||
private readonly BaseCommand plugin;
|
||||
private readonly IDictionary<string, DBRunner> runners = new Dictionary<string, DBRunner>();
|
||||
private readonly object runnersLock = new object();
|
||||
|
||||
public DatabaseManager(BaseCommand plugin)
|
||||
{
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public void Open(string dbname, string cbc)
|
||||
{
|
||||
DBRunner runner;
|
||||
lock (runnersLock)
|
||||
{
|
||||
if (!runners.TryGetValue(dbname, out runner))
|
||||
{
|
||||
runner = new DBRunner(this, dbname, cbc);
|
||||
runner.Start();
|
||||
runners[dbname] = runner;
|
||||
}
|
||||
else
|
||||
{
|
||||
// acknowledge open if it is scheduled after first query.
|
||||
// Also works for re-opening a database, but means that one cannot close a single instance of a database.
|
||||
this.Ok(cbc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Query(string dbname, TransactionsCollection queries, string callbackId)
|
||||
{
|
||||
lock (runnersLock)
|
||||
{
|
||||
DBRunner runner;
|
||||
if (!runners.TryGetValue(dbname, out runner))
|
||||
{
|
||||
// query may start before open is scheduled
|
||||
runner = new DBRunner(this, dbname, null);
|
||||
runner.Start();
|
||||
runners[dbname] = runner;
|
||||
}
|
||||
|
||||
var query = new DBQuery(queries, callbackId);
|
||||
runner.Enqueue(query);
|
||||
}
|
||||
}
|
||||
|
||||
public void Close(string dbname, string callbackId)
|
||||
{
|
||||
lock (runnersLock)
|
||||
{
|
||||
DBRunner runner;
|
||||
if (runners.TryGetValue(dbname, out runner))
|
||||
{
|
||||
var query = new DBQuery(false, callbackId);
|
||||
runner.Enqueue(query);
|
||||
|
||||
// As we cannot determine the order in which query/open requests arrive,
|
||||
// any such requests should start a new thread rather than being lost on the dying queue.
|
||||
// Hence synchronoous wait for thread-end within request and within lock.
|
||||
runner.WaitForStopped();
|
||||
runners.Remove(dbname);
|
||||
}
|
||||
else
|
||||
{
|
||||
// closing a closed database is trivial
|
||||
Ok(callbackId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Delete(string dbname, string callbackId)
|
||||
{
|
||||
var query = new DBQuery(false, callbackId);
|
||||
lock (runnersLock)
|
||||
{
|
||||
DBRunner runner;
|
||||
if (runners.TryGetValue(dbname, out runner))
|
||||
{
|
||||
runner.Enqueue(query);
|
||||
|
||||
// As we cannot determine the order in which query/open requests arrive,
|
||||
// any such requests should start a new thread rather than being lost on the dying queue.
|
||||
// Hence synchronoous wait for thread-end within request and within lock.
|
||||
runner.WaitForStopped();
|
||||
runners.Remove(dbname);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Deleting a closed database, so no runner to queue the request on
|
||||
// Must be inside lock so databases are not opened while being deleted
|
||||
DBRunner.DeleteDatabaseNow(this, dbname, callbackId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Finished(DBRunner runner)
|
||||
{
|
||||
lock (runnersLock)
|
||||
{
|
||||
runners.Remove(runner.DatabaseName);
|
||||
}
|
||||
}
|
||||
|
||||
public void Ok(string callbackId, string result)
|
||||
{
|
||||
this.plugin.DispatchCommandResult(new PluginResult(PluginResult.Status.OK, result), callbackId);
|
||||
}
|
||||
|
||||
public void Ok(string callbackId)
|
||||
{
|
||||
this.plugin.DispatchCommandResult(new PluginResult(PluginResult.Status.OK), callbackId);
|
||||
}
|
||||
|
||||
public void Error(string callbackId, string message)
|
||||
{
|
||||
this.plugin.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, message), callbackId);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maintain a single database with a thread/queue pair to feed requests to it
|
||||
/// </summary>
|
||||
public class DBRunner
|
||||
{
|
||||
private DatabaseManager databases;
|
||||
private SQLiteConnection db;
|
||||
public string DatabaseName { get; private set; }
|
||||
private readonly string openCalllbackId;
|
||||
private readonly Thread thread;
|
||||
private readonly BlockingQueue<DBQuery> queue;
|
||||
|
||||
public DBRunner(DatabaseManager databases, string dbname, string cbc)
|
||||
{
|
||||
this.databases = databases;
|
||||
this.DatabaseName = dbname;
|
||||
this.queue = new BlockingQueue<DBQuery>();
|
||||
this.openCalllbackId = cbc;
|
||||
this.thread = new Thread(this.Run) { Name = dbname };
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
this.thread.Start();
|
||||
}
|
||||
|
||||
public void WaitForStopped()
|
||||
{
|
||||
this.thread.Join();
|
||||
}
|
||||
|
||||
public void Enqueue(DBQuery dbq)
|
||||
{
|
||||
this.queue.Enqueue(dbq);
|
||||
}
|
||||
|
||||
private void Run()
|
||||
{
|
||||
try
|
||||
{
|
||||
// As a query can be requested for the callback for "open" has been completed,
|
||||
// the thread may not be started by the open request, in which case no open callback id
|
||||
this.db = new SQLiteConnection(this.DatabaseName);
|
||||
|
||||
if (openCalllbackId != null)
|
||||
{
|
||||
this.databases.Ok(openCalllbackId);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogError("Failed to open database " + DatabaseName, ex);
|
||||
if (openCalllbackId != null)
|
||||
{
|
||||
this.databases.Error(openCalllbackId, "Failed to open database " + ex.Message);
|
||||
}
|
||||
databases.Finished(this);
|
||||
return;
|
||||
}
|
||||
|
||||
DBQuery dbq = queue.Take();
|
||||
while (!dbq.Stop)
|
||||
{
|
||||
executeSqlBatch(dbq);
|
||||
dbq = queue.Take();
|
||||
}
|
||||
|
||||
if (!dbq.Delete)
|
||||
{
|
||||
// close and callback
|
||||
CloseDatabaseNow(dbq.CallbackId);
|
||||
}
|
||||
else
|
||||
{
|
||||
// close, then delete and callback
|
||||
CloseDatabaseNow(null);
|
||||
DeleteDatabaseNow(this.databases, DatabaseName, dbq.CallbackId);
|
||||
}
|
||||
}
|
||||
|
||||
public void CloseDatabaseNow(string callBackId)
|
||||
{
|
||||
bool success = true;
|
||||
string error = null;
|
||||
|
||||
try
|
||||
{
|
||||
this.db.Close();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LogError("couldn't close " + this.DatabaseName, e);
|
||||
success = false;
|
||||
error = e.Message;
|
||||
}
|
||||
|
||||
if (callBackId != null)
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
this.databases.Ok(callBackId);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.databases.Error(callBackId, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Needs to be static to support deleting closed databases
|
||||
public static void DeleteDatabaseNow(DatabaseManager databases, string dbname, string callBackId)
|
||||
{
|
||||
bool success = true;
|
||||
string error = null;
|
||||
try
|
||||
{
|
||||
var folderPath = Path.GetDirectoryName(dbname);
|
||||
if (folderPath == "")
|
||||
{
|
||||
folderPath = Windows.Storage.ApplicationData.Current.LocalFolder.Path;
|
||||
}
|
||||
var fileName = Path.GetFileName(dbname);
|
||||
|
||||
if (!System.IO.File.Exists(Path.Combine(folderPath, fileName)))
|
||||
{
|
||||
databases.Error(callBackId, "Database does not exist: " + dbname);
|
||||
}
|
||||
|
||||
var fileExtension = new[] { "", "-journal", "-wal", "-shm" };
|
||||
foreach (var extension in fileExtension)
|
||||
{
|
||||
var fullPath = Path.Combine(folderPath, fileName + extension);
|
||||
System.IO.File.Delete(fullPath);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = ex.Message;
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (success)
|
||||
{
|
||||
databases.Ok(callBackId);
|
||||
}
|
||||
else
|
||||
{
|
||||
databases.Error(callBackId, error);
|
||||
}
|
||||
}
|
||||
|
||||
public void executeSqlBatch(DBQuery dbq)
|
||||
{
|
||||
string batchResultsStr = "";
|
||||
|
||||
// loop through the sql in the transaction
|
||||
foreach (SQLitePluginTransaction transaction in dbq.Queries)
|
||||
{
|
||||
string resultString = "";
|
||||
string errorMessage = "unknown";
|
||||
bool needQuery = true;
|
||||
|
||||
// begin
|
||||
if (transaction.query.StartsWith("BEGIN", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
needQuery = false;
|
||||
|
||||
try
|
||||
{
|
||||
this.db.BeginTransaction();
|
||||
|
||||
resultString = "\"rowsAffected\":0";
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
errorMessage = e.Message;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// commit
|
||||
if (transaction.query.StartsWith("COMMIT", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
needQuery = false;
|
||||
|
||||
try
|
||||
{
|
||||
this.db.Commit();
|
||||
|
||||
resultString = "\"rowsAffected\":0";
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
errorMessage = e.Message;
|
||||
}
|
||||
}
|
||||
|
||||
// rollback
|
||||
if (transaction.query.StartsWith("ROLLBACK", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
needQuery = false;
|
||||
|
||||
try
|
||||
{
|
||||
this.db.Rollback();
|
||||
|
||||
resultString = "\"rowsAffected\":0";
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
errorMessage = e.Message;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// create/drop table
|
||||
if (transaction.query.IndexOf("DROP TABLE", StringComparison.OrdinalIgnoreCase) > -1 || transaction.query.IndexOf("CREATE TABLE", StringComparison.OrdinalIgnoreCase) > -1)
|
||||
{
|
||||
needQuery = false;
|
||||
|
||||
try
|
||||
{
|
||||
var results = db.Execute(transaction.query, transaction.query_params);
|
||||
|
||||
resultString = "\"rowsAffected\":0";
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
errorMessage = e.Message;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// insert/update/delete
|
||||
if (transaction.query.StartsWith("INSERT", StringComparison.OrdinalIgnoreCase) ||
|
||||
transaction.query.StartsWith("UPDATE", StringComparison.OrdinalIgnoreCase) ||
|
||||
transaction.query.StartsWith("DELETE", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
needQuery = false;
|
||||
|
||||
try
|
||||
{
|
||||
// execute our query
|
||||
var res = db.Execute(transaction.query, transaction.query_params);
|
||||
|
||||
// get the primary key of the last inserted row
|
||||
var insertId = SQLite3.LastInsertRowid(db.Handle);
|
||||
|
||||
resultString = String.Format("\"rowsAffected\":{0}", res);
|
||||
|
||||
if (transaction.query.StartsWith("INSERT", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
resultString += String.Format(",\"insertId\":{0}", insertId);
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
errorMessage = e.Message;
|
||||
}
|
||||
}
|
||||
|
||||
if (needQuery)
|
||||
{
|
||||
try
|
||||
{
|
||||
var results = this.db.Query2(transaction.query, transaction.query_params);
|
||||
|
||||
string rowsString = "";
|
||||
|
||||
foreach (SQLiteQueryRow res in results)
|
||||
{
|
||||
string rowString = "";
|
||||
|
||||
if (rowsString.Length != 0) rowsString += ",";
|
||||
|
||||
foreach (SQLiteQueryColumn column in res.column)
|
||||
{
|
||||
if (rowString.Length != 0) rowString += ",";
|
||||
|
||||
if (column.Value != null)
|
||||
{
|
||||
if (column.Value.GetType().Equals(typeof(Int32)))
|
||||
{
|
||||
rowString += String.Format("\"{0}\":{1}",
|
||||
column.Key, Convert.ToInt32(column.Value));
|
||||
}
|
||||
else if (column.Value.GetType().Equals(typeof(Double)))
|
||||
{
|
||||
rowString += String.Format(CultureInfo.InvariantCulture, "\"{0}\":{1}",
|
||||
column.Key, Convert.ToDouble(column.Value, CultureInfo.InvariantCulture));
|
||||
}
|
||||
else
|
||||
{
|
||||
rowString += String.Format("\"{0}\":\"{1}\"",
|
||||
column.Key,
|
||||
column.Value.ToString()
|
||||
.Replace("\\","\\\\")
|
||||
.Replace("\"","\\\"")
|
||||
.Replace("\t","\\t")
|
||||
.Replace("\r","\\r")
|
||||
.Replace("\n","\\n")
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rowString += String.Format("\"{0}\":null", column.Key);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
rowsString += "{" + rowString + "}";
|
||||
}
|
||||
|
||||
resultString = "\"rows\":[" + rowsString + "]";
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
errorMessage = e.Message;
|
||||
}
|
||||
}
|
||||
|
||||
if (batchResultsStr.Length != 0) batchResultsStr += ",";
|
||||
|
||||
if (resultString.Length != 0)
|
||||
{
|
||||
batchResultsStr += "{\"qid\":\"" + transaction.queryId + "\",\"type\":\"success\",\"result\":{" + resultString + "}}";
|
||||
//System.Diagnostics.Debug.WriteLine("batchResultsStr: " + batchResultsStr);
|
||||
}
|
||||
else
|
||||
{
|
||||
batchResultsStr += "{\"qid\":\"" + transaction.queryId + "\",\"type\":\"error\",\"result\":{\"message\":\"" + errorMessage.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\"}}";
|
||||
}
|
||||
}
|
||||
|
||||
this.databases.Ok(dbq.CallbackId, "[" + batchResultsStr + "]");
|
||||
}
|
||||
|
||||
|
||||
private static void LogError(string message, Exception e)
|
||||
{
|
||||
// TODO - good place for a breakpoint
|
||||
}
|
||||
|
||||
private static void LogWarning(string message)
|
||||
{
|
||||
// TODO - good place for a breakpoint
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A single, queueable, request for a database
|
||||
/// </summary>
|
||||
public class DBQuery
|
||||
{
|
||||
public bool Stop { get; private set; }
|
||||
public bool Delete { get; private set; }
|
||||
public TransactionsCollection Queries { get; private set; }
|
||||
public string CallbackId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a request to run a query
|
||||
/// </summary>
|
||||
public DBQuery(TransactionsCollection queries, string callbackId)
|
||||
{
|
||||
this.Stop = false;
|
||||
this.Delete = false;
|
||||
|
||||
this.Queries = queries;
|
||||
|
||||
this.CallbackId = callbackId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a request to close, and optionally delete, a database
|
||||
/// </summary>
|
||||
public DBQuery(bool delete, string callbackId)
|
||||
{
|
||||
this.Stop = true;
|
||||
this.Delete = delete;
|
||||
|
||||
this.Queries = null;
|
||||
|
||||
this.CallbackId = callbackId;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Minimal blocking queue
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Expects one reader and n writers - no support for a reader closing down the queue and releasing other readers.
|
||||
/// </remarks>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
class BlockingQueue<T>
|
||||
{
|
||||
private readonly Queue<T> items = new Queue<T>();
|
||||
private readonly object locker = new object();
|
||||
|
||||
public T Take()
|
||||
{
|
||||
lock (locker)
|
||||
{
|
||||
while (items.Count == 0)
|
||||
{
|
||||
Monitor.Wait(locker);
|
||||
}
|
||||
return items.Dequeue();
|
||||
}
|
||||
}
|
||||
|
||||
public void Enqueue(T value)
|
||||
{
|
||||
lock (locker)
|
||||
{
|
||||
items.Enqueue(value);
|
||||
Monitor.PulseAll(locker);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,823 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
using i16 = System.Int16;
|
||||
using i64 = System.Int64;
|
||||
using u8 = System.Byte;
|
||||
using u16 = System.UInt16;
|
||||
using u32 = System.UInt32;
|
||||
using u64 = System.UInt64;
|
||||
|
||||
using sqlite3_int64 = System.Int64;
|
||||
using Pgno = System.UInt32;
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
using DbPage = Sqlite3.PgHdr;
|
||||
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/*
|
||||
** 2004 April 6
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** This file implements a external (disk-based) database using BTrees.
|
||||
** For a detailed discussion of BTrees, refer to
|
||||
**
|
||||
** Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3:
|
||||
** "Sorting And Searching", pages 473-480. Addison-Wesley
|
||||
** Publishing Company, Reading, Massachusetts.
|
||||
**
|
||||
** The basic idea is that each page of the file contains N database
|
||||
** entries and N+1 pointers to subpages.
|
||||
**
|
||||
** ----------------------------------------------------------------
|
||||
** | Ptr(0) | Key(0) | Ptr(1) | Key(1) | ... | Key(N-1) | Ptr(N) |
|
||||
** ----------------------------------------------------------------
|
||||
**
|
||||
** All of the keys on the page that Ptr(0) points to have values less
|
||||
** than Key(0). All of the keys on page Ptr(1) and its subpages have
|
||||
** values greater than Key(0) and less than Key(1). All of the keys
|
||||
** on Ptr(N) and its subpages have values greater than Key(N-1). And
|
||||
** so forth.
|
||||
**
|
||||
** Finding a particular key requires reading O(log(M)) pages from the
|
||||
** disk where M is the number of entries in the tree.
|
||||
**
|
||||
** In this implementation, a single file can hold one or more separate
|
||||
** BTrees. Each BTree is identified by the index of its root page. The
|
||||
** key and data for any entry are combined to form the "payload". A
|
||||
** fixed amount of payload can be carried directly on the database
|
||||
** page. If the payload is larger than the preset amount then surplus
|
||||
** bytes are stored on overflow pages. The payload for an entry
|
||||
** and the preceding pointer are combined to form a "Cell". Each
|
||||
** page has a small header which contains the Ptr(N) pointer and other
|
||||
** information such as the size of key and data.
|
||||
**
|
||||
** FORMAT DETAILS
|
||||
**
|
||||
** The file is divided into pages. The first page is called page 1,
|
||||
** the second is page 2, and so forth. A page number of zero indicates
|
||||
** "no such page". The page size can be any power of 2 between 512 and 65536.
|
||||
** Each page can be either a btree page, a freelist page, an overflow
|
||||
** page, or a pointer-map page.
|
||||
**
|
||||
** The first page is always a btree page. The first 100 bytes of the first
|
||||
** page contain a special header (the "file header") that describes the file.
|
||||
** The format of the file header is as follows:
|
||||
**
|
||||
** OFFSET SIZE DESCRIPTION
|
||||
** 0 16 Header string: "SQLite format 3\000"
|
||||
** 16 2 Page size in bytes.
|
||||
** 18 1 File format write version
|
||||
** 19 1 File format read version
|
||||
** 20 1 Bytes of unused space at the end of each page
|
||||
** 21 1 Max embedded payload fraction
|
||||
** 22 1 Min embedded payload fraction
|
||||
** 23 1 Min leaf payload fraction
|
||||
** 24 4 File change counter
|
||||
** 28 4 Reserved for future use
|
||||
** 32 4 First freelist page
|
||||
** 36 4 Number of freelist pages in the file
|
||||
** 40 60 15 4-byte meta values passed to higher layers
|
||||
**
|
||||
** 40 4 Schema cookie
|
||||
** 44 4 File format of schema layer
|
||||
** 48 4 Size of page cache
|
||||
** 52 4 Largest root-page (auto/incr_vacuum)
|
||||
** 56 4 1=UTF-8 2=UTF16le 3=UTF16be
|
||||
** 60 4 User version
|
||||
** 64 4 Incremental vacuum mode
|
||||
** 68 4 unused
|
||||
** 72 4 unused
|
||||
** 76 4 unused
|
||||
**
|
||||
** All of the integer values are big-endian (most significant byte first).
|
||||
**
|
||||
** The file change counter is incremented when the database is changed
|
||||
** This counter allows other processes to know when the file has changed
|
||||
** and thus when they need to flush their cache.
|
||||
**
|
||||
** The max embedded payload fraction is the amount of the total usable
|
||||
** space in a page that can be consumed by a single cell for standard
|
||||
** B-tree (non-LEAFDATA) tables. A value of 255 means 100%. The default
|
||||
** is to limit the maximum cell size so that at least 4 cells will fit
|
||||
** on one page. Thus the default max embedded payload fraction is 64.
|
||||
**
|
||||
** If the payload for a cell is larger than the max payload, then extra
|
||||
** payload is spilled to overflow pages. Once an overflow page is allocated,
|
||||
** as many bytes as possible are moved into the overflow pages without letting
|
||||
** the cell size drop below the min embedded payload fraction.
|
||||
**
|
||||
** The min leaf payload fraction is like the min embedded payload fraction
|
||||
** except that it applies to leaf nodes in a LEAFDATA tree. The maximum
|
||||
** payload fraction for a LEAFDATA tree is always 100% (or 255) and it
|
||||
** not specified in the header.
|
||||
**
|
||||
** Each btree pages is divided into three sections: The header, the
|
||||
** cell pointer array, and the cell content area. Page 1 also has a 100-byte
|
||||
** file header that occurs before the page header.
|
||||
**
|
||||
** |----------------|
|
||||
** | file header | 100 bytes. Page 1 only.
|
||||
** |----------------|
|
||||
** | page header | 8 bytes for leaves. 12 bytes for interior nodes
|
||||
** |----------------|
|
||||
** | cell pointer | | 2 bytes per cell. Sorted order.
|
||||
** | array | | Grows downward
|
||||
** | | v
|
||||
** |----------------|
|
||||
** | unallocated |
|
||||
** | space |
|
||||
** |----------------| ^ Grows upwards
|
||||
** | cell content | | Arbitrary order interspersed with freeblocks.
|
||||
** | area | | and free space fragments.
|
||||
** |----------------|
|
||||
**
|
||||
** The page headers looks like this:
|
||||
**
|
||||
** OFFSET SIZE DESCRIPTION
|
||||
** 0 1 Flags. 1: intkey, 2: zerodata, 4: leafdata, 8: leaf
|
||||
** 1 2 byte offset to the first freeblock
|
||||
** 3 2 number of cells on this page
|
||||
** 5 2 first byte of the cell content area
|
||||
** 7 1 number of fragmented free bytes
|
||||
** 8 4 Right child (the Ptr(N) value). Omitted on leaves.
|
||||
**
|
||||
** The flags define the format of this btree page. The leaf flag means that
|
||||
** this page has no children. The zerodata flag means that this page carries
|
||||
** only keys and no data. The intkey flag means that the key is a integer
|
||||
** which is stored in the key size entry of the cell header rather than in
|
||||
** the payload area.
|
||||
**
|
||||
** The cell pointer array begins on the first byte after the page header.
|
||||
** The cell pointer array contains zero or more 2-byte numbers which are
|
||||
** offsets from the beginning of the page to the cell content in the cell
|
||||
** content area. The cell pointers occur in sorted order. The system strives
|
||||
** to keep free space after the last cell pointer so that new cells can
|
||||
** be easily added without having to defragment the page.
|
||||
**
|
||||
** Cell content is stored at the very end of the page and grows toward the
|
||||
** beginning of the page.
|
||||
**
|
||||
** Unused space within the cell content area is collected into a linked list of
|
||||
** freeblocks. Each freeblock is at least 4 bytes in size. The byte offset
|
||||
** to the first freeblock is given in the header. Freeblocks occur in
|
||||
** increasing order. Because a freeblock must be at least 4 bytes in size,
|
||||
** any group of 3 or fewer unused bytes in the cell content area cannot
|
||||
** exist on the freeblock chain. A group of 3 or fewer free bytes is called
|
||||
** a fragment. The total number of bytes in all fragments is recorded.
|
||||
** in the page header at offset 7.
|
||||
**
|
||||
** SIZE DESCRIPTION
|
||||
** 2 Byte offset of the next freeblock
|
||||
** 2 Bytes in this freeblock
|
||||
**
|
||||
** Cells are of variable length. Cells are stored in the cell content area at
|
||||
** the end of the page. Pointers to the cells are in the cell pointer array
|
||||
** that immediately follows the page header. Cells is not necessarily
|
||||
** contiguous or in order, but cell pointers are contiguous and in order.
|
||||
**
|
||||
** Cell content makes use of variable length integers. A variable
|
||||
** length integer is 1 to 9 bytes where the lower 7 bits of each
|
||||
** byte are used. The integer consists of all bytes that have bit 8 set and
|
||||
** the first byte with bit 8 clear. The most significant byte of the integer
|
||||
** appears first. A variable-length integer may not be more than 9 bytes long.
|
||||
** As a special case, all 8 bytes of the 9th byte are used as data. This
|
||||
** allows a 64-bit integer to be encoded in 9 bytes.
|
||||
**
|
||||
** 0x00 becomes 0x00000000
|
||||
** 0x7f becomes 0x0000007f
|
||||
** 0x81 0x00 becomes 0x00000080
|
||||
** 0x82 0x00 becomes 0x00000100
|
||||
** 0x80 0x7f becomes 0x0000007f
|
||||
** 0x8a 0x91 0xd1 0xac 0x78 becomes 0x12345678
|
||||
** 0x81 0x81 0x81 0x81 0x01 becomes 0x10204081
|
||||
**
|
||||
** Variable length integers are used for rowids and to hold the number of
|
||||
** bytes of key and data in a btree cell.
|
||||
**
|
||||
** The content of a cell looks like this:
|
||||
**
|
||||
** SIZE DESCRIPTION
|
||||
** 4 Page number of the left child. Omitted if leaf flag is set.
|
||||
** var Number of bytes of data. Omitted if the zerodata flag is set.
|
||||
** var Number of bytes of key. Or the key itself if intkey flag is set.
|
||||
** * Payload
|
||||
** 4 First page of the overflow chain. Omitted if no overflow
|
||||
**
|
||||
** Overflow pages form a linked list. Each page except the last is completely
|
||||
** filled with data (pagesize - 4 bytes). The last page can have as little
|
||||
** as 1 byte of data.
|
||||
**
|
||||
** SIZE DESCRIPTION
|
||||
** 4 Page number of next overflow page
|
||||
** * Data
|
||||
**
|
||||
** Freelist pages come in two subtypes: trunk pages and leaf pages. The
|
||||
** file header points to the first in a linked list of trunk page. Each trunk
|
||||
** page points to multiple leaf pages. The content of a leaf page is
|
||||
** unspecified. A trunk page looks like this:
|
||||
**
|
||||
** SIZE DESCRIPTION
|
||||
** 4 Page number of next trunk page
|
||||
** 4 Number of leaf pointers on this page
|
||||
** * zero or more pages numbers of leaves
|
||||
*************************************************************************
|
||||
** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart
|
||||
** C#-SQLite is an independent reimplementation of the SQLite software library
|
||||
**
|
||||
** SQLITE_SOURCE_ID: 2011-05-19 13:26:54 ed1da510a239ea767a01dc332b667119fa3c908e
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
//#include "sqliteInt.h"
|
||||
|
||||
/* The following value is the maximum cell size assuming a maximum page
|
||||
** size give above.
|
||||
*/
|
||||
//#define MX_CELL_SIZE(pBt) ((int)(pBt->pageSize-8))
|
||||
static int MX_CELL_SIZE( BtShared pBt )
|
||||
{
|
||||
return (int)( pBt.pageSize - 8 );
|
||||
}
|
||||
|
||||
/* The maximum number of cells on a single page of the database. This
|
||||
** assumes a minimum cell size of 6 bytes (4 bytes for the cell itself
|
||||
** plus 2 bytes for the index to the cell in the page header). Such
|
||||
** small cells will be rare, but they are possible.
|
||||
*/
|
||||
//#define MX_CELL(pBt) ((pBt.pageSize-8)/6)
|
||||
static int MX_CELL( BtShared pBt )
|
||||
{
|
||||
return ( (int)( pBt.pageSize - 8 ) / 6 );
|
||||
}
|
||||
|
||||
/* Forward declarations */
|
||||
//typedef struct MemPage MemPage;
|
||||
//typedef struct BtLock BtLock;
|
||||
|
||||
/*
|
||||
** This is a magic string that appears at the beginning of every
|
||||
** SQLite database in order to identify the file as a real database.
|
||||
**
|
||||
** You can change this value at compile-time by specifying a
|
||||
** -DSQLITE_FILE_HEADER="..." on the compiler command-line. The
|
||||
** header must be exactly 16 bytes including the zero-terminator so
|
||||
** the string itself should be 15 characters long. If you change
|
||||
** the header, then your custom library will not be able to read
|
||||
** databases generated by the standard tools and the standard tools
|
||||
** will not be able to read databases created by your custom library.
|
||||
*/
|
||||
#if !SQLITE_FILE_HEADER //* 123456789 123456 */
|
||||
const string SQLITE_FILE_HEADER = "SQLite format 3\0";
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Page type flags. An ORed combination of these flags appear as the
|
||||
** first byte of on-disk image of every BTree page.
|
||||
*/
|
||||
const byte PTF_INTKEY = 0x01;
|
||||
const byte PTF_ZERODATA = 0x02;
|
||||
const byte PTF_LEAFDATA = 0x04;
|
||||
const byte PTF_LEAF = 0x08;
|
||||
|
||||
/*
|
||||
** As each page of the file is loaded into memory, an instance of the following
|
||||
** structure is appended and initialized to zero. This structure stores
|
||||
** information about the page that is decoded from the raw file page.
|
||||
**
|
||||
** The pParent field points back to the parent page. This allows us to
|
||||
** walk up the BTree from any leaf to the root. Care must be taken to
|
||||
** unref() the parent page pointer when this page is no longer referenced.
|
||||
** The pageDestructor() routine handles that chore.
|
||||
**
|
||||
** Access to all fields of this structure is controlled by the mutex
|
||||
** stored in MemPage.pBt.mutex.
|
||||
*/
|
||||
public struct _OvflCell
|
||||
{ /* Cells that will not fit on aData[] */
|
||||
public u8[] pCell; /* Pointers to the body of the overflow cell */
|
||||
public u16 idx; /* Insert this cell before idx-th non-overflow cell */
|
||||
public _OvflCell Copy()
|
||||
{
|
||||
_OvflCell cp = new _OvflCell();
|
||||
if ( pCell != null )
|
||||
{
|
||||
cp.pCell = sqlite3Malloc( pCell.Length );
|
||||
Buffer.BlockCopy( pCell, 0, cp.pCell, 0, pCell.Length );
|
||||
}
|
||||
cp.idx = idx;
|
||||
return cp;
|
||||
}
|
||||
};
|
||||
public class MemPage
|
||||
{
|
||||
public u8 isInit; /* True if previously initialized. MUST BE FIRST! */
|
||||
public u8 nOverflow; /* Number of overflow cell bodies in aCell[] */
|
||||
public u8 intKey; /* True if u8key flag is set */
|
||||
public u8 leaf; /* 1 if leaf flag is set */
|
||||
public u8 hasData; /* True if this page stores data */
|
||||
public u8 hdrOffset; /* 100 for page 1. 0 otherwise */
|
||||
public u8 childPtrSize; /* 0 if leaf==1. 4 if leaf==0 */
|
||||
public u16 maxLocal; /* Copy of BtShared.maxLocal or BtShared.maxLeaf */
|
||||
public u16 minLocal; /* Copy of BtShared.minLocal or BtShared.minLeaf */
|
||||
public u16 cellOffset; /* Index in aData of first cell pou16er */
|
||||
public u16 nFree; /* Number of free bytes on the page */
|
||||
public u16 nCell; /* Number of cells on this page, local and ovfl */
|
||||
public u16 maskPage; /* Mask for page offset */
|
||||
public _OvflCell[] aOvfl = new _OvflCell[5];
|
||||
public BtShared pBt; /* Pointer to BtShared that this page is part of */
|
||||
public byte[] aData; /* Pointer to disk image of the page data */
|
||||
public DbPage pDbPage; /* Pager page handle */
|
||||
public Pgno pgno; /* Page number for this page */
|
||||
|
||||
//public byte[] aData
|
||||
//{
|
||||
// get
|
||||
// {
|
||||
// Debug.Assert( pgno != 1 || pDbPage.pData == _aData );
|
||||
// return _aData;
|
||||
// }
|
||||
// set
|
||||
// {
|
||||
// _aData = value;
|
||||
// Debug.Assert( pgno != 1 || pDbPage.pData == _aData );
|
||||
// }
|
||||
//}
|
||||
|
||||
public MemPage Copy()
|
||||
{
|
||||
MemPage cp = (MemPage)MemberwiseClone();
|
||||
if ( aOvfl != null )
|
||||
{
|
||||
cp.aOvfl = new _OvflCell[aOvfl.Length];
|
||||
for ( int i = 0; i < aOvfl.Length; i++ )
|
||||
cp.aOvfl[i] = aOvfl[i].Copy();
|
||||
}
|
||||
if ( aData != null )
|
||||
{
|
||||
cp.aData = sqlite3Malloc( aData.Length );
|
||||
Buffer.BlockCopy( aData, 0, cp.aData, 0, aData.Length );
|
||||
}
|
||||
return cp;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
** The in-memory image of a disk page has the auxiliary information appended
|
||||
** to the end. EXTRA_SIZE is the number of bytes of space needed to hold
|
||||
** that extra information.
|
||||
*/
|
||||
const int EXTRA_SIZE = 0;// No used in C#, since we use create a class; was MemPage.Length;
|
||||
|
||||
/*
|
||||
** A linked list of the following structures is stored at BtShared.pLock.
|
||||
** Locks are added (or upgraded from READ_LOCK to WRITE_LOCK) when a cursor
|
||||
** is opened on the table with root page BtShared.iTable. Locks are removed
|
||||
** from this list when a transaction is committed or rolled back, or when
|
||||
** a btree handle is closed.
|
||||
*/
|
||||
public class BtLock
|
||||
{
|
||||
Btree pBtree; /* Btree handle holding this lock */
|
||||
Pgno iTable; /* Root page of table */
|
||||
u8 eLock; /* READ_LOCK or WRITE_LOCK */
|
||||
BtLock pNext; /* Next in BtShared.pLock list */
|
||||
};
|
||||
|
||||
/* Candidate values for BtLock.eLock */
|
||||
//#define READ_LOCK 1
|
||||
//#define WRITE_LOCK 2
|
||||
const int READ_LOCK = 1;
|
||||
const int WRITE_LOCK = 2;
|
||||
|
||||
/* A Btree handle
|
||||
**
|
||||
** A database connection contains a pointer to an instance of
|
||||
** this object for every database file that it has open. This structure
|
||||
** is opaque to the database connection. The database connection cannot
|
||||
** see the internals of this structure and only deals with pointers to
|
||||
** this structure.
|
||||
**
|
||||
** For some database files, the same underlying database cache might be
|
||||
** shared between multiple connections. In that case, each connection
|
||||
** has it own instance of this object. But each instance of this object
|
||||
** points to the same BtShared object. The database cache and the
|
||||
** schema associated with the database file are all contained within
|
||||
** the BtShared object.
|
||||
**
|
||||
** All fields in this structure are accessed under sqlite3.mutex.
|
||||
** The pBt pointer itself may not be changed while there exists cursors
|
||||
** in the referenced BtShared that point back to this Btree since those
|
||||
** cursors have to go through this Btree to find their BtShared and
|
||||
** they often do so without holding sqlite3.mutex.
|
||||
*/
|
||||
public class Btree
|
||||
{
|
||||
public sqlite3 db; /* The database connection holding this Btree */
|
||||
public BtShared pBt; /* Sharable content of this Btree */
|
||||
public u8 inTrans; /* TRANS_NONE, TRANS_READ or TRANS_WRITE */
|
||||
public bool sharable; /* True if we can share pBt with another db */
|
||||
public bool locked; /* True if db currently has pBt locked */
|
||||
public int wantToLock; /* Number of nested calls to sqlite3BtreeEnter() */
|
||||
public int nBackup; /* Number of backup operations reading this btree */
|
||||
public Btree pNext; /* List of other sharable Btrees from the same db */
|
||||
public Btree pPrev; /* Back pointer of the same list */
|
||||
#if NO_SQLITE_OMIT_SHARED_CACHE //#if !SQLITE_OMIT_SHARED_CACHE
|
||||
BtLock lock; /* Object used to lock page 1 */
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
** Btree.inTrans may take one of the following values.
|
||||
**
|
||||
** If the shared-data extension is enabled, there may be multiple users
|
||||
** of the Btree structure. At most one of these may open a write transaction,
|
||||
** but any number may have active read transactions.
|
||||
*/
|
||||
const byte TRANS_NONE = 0;
|
||||
const byte TRANS_READ = 1;
|
||||
const byte TRANS_WRITE = 2;
|
||||
|
||||
/*
|
||||
** An instance of this object represents a single database file.
|
||||
**
|
||||
** A single database file can be in use as the same time by two
|
||||
** or more database connections. When two or more connections are
|
||||
** sharing the same database file, each connection has it own
|
||||
** private Btree object for the file and each of those Btrees points
|
||||
** to this one BtShared object. BtShared.nRef is the number of
|
||||
** connections currently sharing this database file.
|
||||
**
|
||||
** Fields in this structure are accessed under the BtShared.mutex
|
||||
** mutex, except for nRef and pNext which are accessed under the
|
||||
** global SQLITE_MUTEX_STATIC_MASTER mutex. The pPager field
|
||||
** may not be modified once it is initially set as long as nRef>0.
|
||||
** The pSchema field may be set once under BtShared.mutex and
|
||||
** thereafter is unchanged as long as nRef>0.
|
||||
**
|
||||
** isPending:
|
||||
**
|
||||
** If a BtShared client fails to obtain a write-lock on a database
|
||||
** table (because there exists one or more read-locks on the table),
|
||||
** the shared-cache enters 'pending-lock' state and isPending is
|
||||
** set to true.
|
||||
**
|
||||
** The shared-cache leaves the 'pending lock' state when either of
|
||||
** the following occur:
|
||||
**
|
||||
** 1) The current writer (BtShared.pWriter) concludes its transaction, OR
|
||||
** 2) The number of locks held by other connections drops to zero.
|
||||
**
|
||||
** while in the 'pending-lock' state, no connection may start a new
|
||||
** transaction.
|
||||
**
|
||||
** This feature is included to help prevent writer-starvation.
|
||||
*/
|
||||
public class BtShared
|
||||
{
|
||||
public Pager pPager; /* The page cache */
|
||||
public sqlite3 db; /* Database connection currently using this Btree */
|
||||
public BtCursor pCursor; /* A list of all open cursors */
|
||||
public MemPage pPage1; /* First page of the database */
|
||||
public bool readOnly; /* True if the underlying file is readonly */
|
||||
public bool pageSizeFixed; /* True if the page size can no longer be changed */
|
||||
public bool secureDelete; /* True if secure_delete is enabled */
|
||||
public bool initiallyEmpty; /* Database is empty at start of transaction */
|
||||
public u8 openFlags; /* Flags to sqlite3BtreeOpen() */
|
||||
#if !SQLITE_OMIT_AUTOVACUUM
|
||||
public bool autoVacuum; /* True if auto-vacuum is enabled */
|
||||
public bool incrVacuum; /* True if incr-vacuum is enabled */
|
||||
#endif
|
||||
public u8 inTransaction; /* Transaction state */
|
||||
public bool doNotUseWAL; /* If true, do not open write-ahead-log file */
|
||||
public u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */
|
||||
public u16 minLocal; /* Minimum local payload in non-LEAFDATA tables */
|
||||
public u16 maxLeaf; /* Maximum local payload in a LEAFDATA table */
|
||||
public u16 minLeaf; /* Minimum local payload in a LEAFDATA table */
|
||||
public u32 pageSize; /* Total number of bytes on a page */
|
||||
public u32 usableSize; /* Number of usable bytes on each page */
|
||||
public int nTransaction; /* Number of open transactions (read + write) */
|
||||
public Pgno nPage; /* Number of pages in the database */
|
||||
public Schema pSchema; /* Pointer to space allocated by sqlite3BtreeSchema() */
|
||||
public dxFreeSchema xFreeSchema;/* Destructor for BtShared.pSchema */
|
||||
public sqlite3_mutex mutex; /* Non-recursive mutex required to access this object */
|
||||
public Bitvec pHasContent; /* Set of pages moved to free-list this transaction */
|
||||
#if NO_SQLITE_OMIT_SHARED_CACHE //#if !SQLITE_OMIT_SHARED_CACHE
|
||||
public int nRef; /* Number of references to this structure */
|
||||
public BtShared pNext; /* Next on a list of sharable BtShared structs */
|
||||
public BtLock pLock; /* List of locks held on this shared-btree struct */
|
||||
public Btree pWriter; /* Btree with currently open write transaction */
|
||||
public u8 isExclusive; /* True if pWriter has an EXCLUSIVE lock on the db */
|
||||
public u8 isPending; /* If waiting for read-locks to clear */
|
||||
#endif
|
||||
public byte[] pTmpSpace; /* BtShared.pageSize bytes of space for tmp use */
|
||||
};
|
||||
|
||||
/*
|
||||
** An instance of the following structure is used to hold information
|
||||
** about a cell. The parseCellPtr() function fills in this structure
|
||||
** based on information extract from the raw disk page.
|
||||
*/
|
||||
//typedef struct CellInfo CellInfo;
|
||||
public struct CellInfo
|
||||
{
|
||||
public int iCell; /* Offset to start of cell content -- Needed for C# */
|
||||
public byte[] pCell; /* Pointer to the start of cell content */
|
||||
public i64 nKey; /* The key for INTKEY tables, or number of bytes in key */
|
||||
public u32 nData; /* Number of bytes of data */
|
||||
public u32 nPayload; /* Total amount of payload */
|
||||
public u16 nHeader; /* Size of the cell content header in bytes */
|
||||
public u16 nLocal; /* Amount of payload held locally */
|
||||
public u16 iOverflow; /* Offset to overflow page number. Zero if no overflow */
|
||||
public u16 nSize; /* Size of the cell content on the main b-tree page */
|
||||
public bool Equals( CellInfo ci )
|
||||
{
|
||||
if ( ci.iCell >= ci.pCell.Length || iCell >= this.pCell.Length )
|
||||
return false;
|
||||
if ( ci.pCell[ci.iCell] != this.pCell[iCell] )
|
||||
return false;
|
||||
if ( ci.nKey != this.nKey || ci.nData != this.nData || ci.nPayload != this.nPayload )
|
||||
return false;
|
||||
if ( ci.nHeader != this.nHeader || ci.nLocal != this.nLocal )
|
||||
return false;
|
||||
if ( ci.iOverflow != this.iOverflow || ci.nSize != this.nSize )
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
** Maximum depth of an SQLite B-Tree structure. Any B-Tree deeper than
|
||||
** this will be declared corrupt. This value is calculated based on a
|
||||
** maximum database size of 2^31 pages a minimum fanout of 2 for a
|
||||
** root-node and 3 for all other internal nodes.
|
||||
**
|
||||
** If a tree that appears to be taller than this is encountered, it is
|
||||
** assumed that the database is corrupt.
|
||||
*/
|
||||
//#define BTCURSOR_MAX_DEPTH 20
|
||||
const int BTCURSOR_MAX_DEPTH = 20;
|
||||
|
||||
/*
|
||||
** A cursor is a pointer to a particular entry within a particular
|
||||
** b-tree within a database file.
|
||||
**
|
||||
** The entry is identified by its MemPage and the index in
|
||||
** MemPage.aCell[] of the entry.
|
||||
**
|
||||
** A single database file can shared by two more database connections,
|
||||
** but cursors cannot be shared. Each cursor is associated with a
|
||||
** particular database connection identified BtCursor.pBtree.db.
|
||||
**
|
||||
** Fields in this structure are accessed under the BtShared.mutex
|
||||
** found at self.pBt.mutex.
|
||||
*/
|
||||
public class BtCursor
|
||||
{
|
||||
public Btree pBtree; /* The Btree to which this cursor belongs */
|
||||
public BtShared pBt; /* The BtShared this cursor points to */
|
||||
public BtCursor pNext;
|
||||
public BtCursor pPrev; /* Forms a linked list of all cursors */
|
||||
public KeyInfo pKeyInfo; /* Argument passed to comparison function */
|
||||
public Pgno pgnoRoot; /* The root page of this tree */
|
||||
public sqlite3_int64 cachedRowid; /* Next rowid cache. 0 means not valid */
|
||||
public CellInfo info = new CellInfo(); /* A parse of the cell we are pointing at */
|
||||
public byte[] pKey; /* Saved key that was cursor's last known position */
|
||||
public i64 nKey; /* Size of pKey, or last integer key */
|
||||
public int skipNext; /* Prev() is noop if negative. Next() is noop if positive */
|
||||
public u8 wrFlag; /* True if writable */
|
||||
public u8 atLast; /* VdbeCursor pointing to the last entry */
|
||||
public bool validNKey; /* True if info.nKey is valid */
|
||||
public int eState; /* One of the CURSOR_XXX constants (see below) */
|
||||
#if NOT_SQLITE_OMIT_INCRBLOB //#if !SQLITE_OMIT_INCRBLOB
|
||||
public Pgno[] aOverflow; /* Cache of overflow page locations */
|
||||
public bool isIncrblobHandle; /* True if this cursor is an incr. io handle */
|
||||
#endif
|
||||
public i16 iPage; /* Index of current page in apPage */
|
||||
public u16[] aiIdx = new u16[BTCURSOR_MAX_DEPTH]; /* Current index in apPage[i] */
|
||||
public MemPage[] apPage = new MemPage[BTCURSOR_MAX_DEPTH]; /* Pages from root to current page */
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
pNext = null;
|
||||
pPrev = null;
|
||||
pKeyInfo = null;
|
||||
pgnoRoot = 0;
|
||||
cachedRowid = 0;
|
||||
info = new CellInfo();
|
||||
wrFlag = 0;
|
||||
atLast = 0;
|
||||
validNKey = false;
|
||||
eState = 0;
|
||||
pKey = null;
|
||||
nKey = 0;
|
||||
skipNext = 0;
|
||||
#if NOT_SQLITE_OMIT_INCRBLOB //#if !SQLITE_OMIT_INCRBLOB
|
||||
isIncrblobHandle=false;
|
||||
aOverflow= null;
|
||||
#endif
|
||||
iPage = 0;
|
||||
}
|
||||
public BtCursor Copy()
|
||||
{
|
||||
BtCursor cp = (BtCursor)MemberwiseClone();
|
||||
return cp;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
** Potential values for BtCursor.eState.
|
||||
**
|
||||
** CURSOR_VALID:
|
||||
** VdbeCursor points to a valid entry. getPayload() etc. may be called.
|
||||
**
|
||||
** CURSOR_INVALID:
|
||||
** VdbeCursor does not point to a valid entry. This can happen (for example)
|
||||
** because the table is empty or because BtreeCursorFirst() has not been
|
||||
** called.
|
||||
**
|
||||
** CURSOR_REQUIRESEEK:
|
||||
** The table that this cursor was opened on still exists, but has been
|
||||
** modified since the cursor was last used. The cursor position is saved
|
||||
** in variables BtCursor.pKey and BtCursor.nKey. When a cursor is in
|
||||
** this state, restoreCursorPosition() can be called to attempt to
|
||||
** seek the cursor to the saved position.
|
||||
**
|
||||
** CURSOR_FAULT:
|
||||
** A unrecoverable error (an I/O error or a malloc failure) has occurred
|
||||
** on a different connection that shares the BtShared cache with this
|
||||
** cursor. The error has left the cache in an inconsistent state.
|
||||
** Do nothing else with this cursor. Any attempt to use the cursor
|
||||
** should return the error code stored in BtCursor.skip
|
||||
*/
|
||||
const int CURSOR_INVALID = 0;
|
||||
const int CURSOR_VALID = 1;
|
||||
const int CURSOR_REQUIRESEEK = 2;
|
||||
const int CURSOR_FAULT = 3;
|
||||
|
||||
/*
|
||||
** The database page the PENDING_BYTE occupies. This page is never used.
|
||||
*/
|
||||
//# define PENDING_BYTE_PAGE(pBt) PAGER_MJ_PGNO(pBt)
|
||||
// TODO -- Convert PENDING_BYTE_PAGE to inline
|
||||
static u32 PENDING_BYTE_PAGE( BtShared pBt )
|
||||
{
|
||||
return (u32)PAGER_MJ_PGNO( pBt.pPager );
|
||||
}
|
||||
|
||||
/*
|
||||
** These macros define the location of the pointer-map entry for a
|
||||
** database page. The first argument to each is the number of usable
|
||||
** bytes on each page of the database (often 1024). The second is the
|
||||
** page number to look up in the pointer map.
|
||||
**
|
||||
** PTRMAP_PAGENO returns the database page number of the pointer-map
|
||||
** page that stores the required pointer. PTRMAP_PTROFFSET returns
|
||||
** the offset of the requested map entry.
|
||||
**
|
||||
** If the pgno argument passed to PTRMAP_PAGENO is a pointer-map page,
|
||||
** then pgno is returned. So (pgno==PTRMAP_PAGENO(pgsz, pgno)) can be
|
||||
** used to test if pgno is a pointer-map page. PTRMAP_ISPAGE implements
|
||||
** this test.
|
||||
*/
|
||||
//#define PTRMAP_PAGENO(pBt, pgno) ptrmapPageno(pBt, pgno)
|
||||
static Pgno PTRMAP_PAGENO( BtShared pBt, Pgno pgno )
|
||||
{
|
||||
return ptrmapPageno( pBt, pgno );
|
||||
}
|
||||
//#define PTRMAP_PTROFFSET(pgptrmap, pgno) (5*(pgno-pgptrmap-1))
|
||||
static u32 PTRMAP_PTROFFSET( u32 pgptrmap, u32 pgno )
|
||||
{
|
||||
return ( 5 * ( pgno - pgptrmap - 1 ) );
|
||||
}
|
||||
//#define PTRMAP_ISPAGE(pBt, pgno) (PTRMAP_PAGENO((pBt),(pgno))==(pgno))
|
||||
static bool PTRMAP_ISPAGE( BtShared pBt, u32 pgno )
|
||||
{
|
||||
return ( PTRMAP_PAGENO( ( pBt ), ( pgno ) ) == ( pgno ) );
|
||||
}
|
||||
/*
|
||||
** The pointer map is a lookup table that identifies the parent page for
|
||||
** each child page in the database file. The parent page is the page that
|
||||
** contains a pointer to the child. Every page in the database contains
|
||||
** 0 or 1 parent pages. (In this context 'database page' refers
|
||||
** to any page that is not part of the pointer map itself.) Each pointer map
|
||||
** entry consists of a single byte 'type' and a 4 byte parent page number.
|
||||
** The PTRMAP_XXX identifiers below are the valid types.
|
||||
**
|
||||
** The purpose of the pointer map is to facility moving pages from one
|
||||
** position in the file to another as part of autovacuum. When a page
|
||||
** is moved, the pointer in its parent must be updated to point to the
|
||||
** new location. The pointer map is used to locate the parent page quickly.
|
||||
**
|
||||
** PTRMAP_ROOTPAGE: The database page is a root-page. The page-number is not
|
||||
** used in this case.
|
||||
**
|
||||
** PTRMAP_FREEPAGE: The database page is an unused (free) page. The page-number
|
||||
** is not used in this case.
|
||||
**
|
||||
** PTRMAP_OVERFLOW1: The database page is the first page in a list of
|
||||
** overflow pages. The page number identifies the page that
|
||||
** contains the cell with a pointer to this overflow page.
|
||||
**
|
||||
** PTRMAP_OVERFLOW2: The database page is the second or later page in a list of
|
||||
** overflow pages. The page-number identifies the previous
|
||||
** page in the overflow page list.
|
||||
**
|
||||
** PTRMAP_BTREE: The database page is a non-root btree page. The page number
|
||||
** identifies the parent page in the btree.
|
||||
*/
|
||||
//#define PTRMAP_ROOTPAGE 1
|
||||
//#define PTRMAP_FREEPAGE 2
|
||||
//#define PTRMAP_OVERFLOW1 3
|
||||
//#define PTRMAP_OVERFLOW2 4
|
||||
//#define PTRMAP_BTREE 5
|
||||
const int PTRMAP_ROOTPAGE = 1;
|
||||
const int PTRMAP_FREEPAGE = 2;
|
||||
const int PTRMAP_OVERFLOW1 = 3;
|
||||
const int PTRMAP_OVERFLOW2 = 4;
|
||||
const int PTRMAP_BTREE = 5;
|
||||
|
||||
/* A bunch of Debug.Assert() statements to check the transaction state variables
|
||||
** of handle p (type Btree*) are internally consistent.
|
||||
*/
|
||||
#if DEBUG
|
||||
//#define btreeIntegrity(p) \
|
||||
// Debug.Assert( p.pBt.inTransaction!=TRANS_NONE || p.pBt.nTransaction==0 ); \
|
||||
// Debug.Assert( p.pBt.inTransaction>=p.inTrans );
|
||||
static void btreeIntegrity( Btree p )
|
||||
{
|
||||
Debug.Assert( p.pBt.inTransaction != TRANS_NONE || p.pBt.nTransaction == 0 );
|
||||
Debug.Assert( p.pBt.inTransaction >= p.inTrans );
|
||||
}
|
||||
#else
|
||||
static void btreeIntegrity(Btree p) { }
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The ISAUTOVACUUM macro is used within balance_nonroot() to determine
|
||||
** if the database supports auto-vacuum or not. Because it is used
|
||||
** within an expression that is an argument to another macro
|
||||
** (sqliteMallocRaw), it is not possible to use conditional compilation.
|
||||
** So, this macro is defined instead.
|
||||
*/
|
||||
#if !SQLITE_OMIT_AUTOVACUUM
|
||||
//#define ISAUTOVACUUM (pBt.autoVacuum)
|
||||
#else
|
||||
//#define ISAUTOVACUUM 0
|
||||
public static bool ISAUTOVACUUM =false;
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** This structure is passed around through all the sanity checking routines
|
||||
** in order to keep track of some global state information.
|
||||
*/
|
||||
//typedef struct IntegrityCk IntegrityCk;
|
||||
public class IntegrityCk
|
||||
{
|
||||
public BtShared pBt; /* The tree being checked out */
|
||||
public Pager pPager; /* The associated pager. Also accessible by pBt.pPager */
|
||||
public Pgno nPage; /* Number of pages in the database */
|
||||
public int[] anRef; /* Number of times each page is referenced */
|
||||
public int mxErr; /* Stop accumulating errors when this reaches zero */
|
||||
public int nErr; /* Number of messages written to zErrMsg so far */
|
||||
//public int mallocFailed; /* A memory allocation error has occurred */
|
||||
public StrAccum errMsg = new StrAccum( 100 ); /* Accumulate the error message text here */
|
||||
};
|
||||
|
||||
/*
|
||||
** Read or write a two- and four-byte big-endian integer values.
|
||||
*/
|
||||
//#define get2byte(x) ((x)[0]<<8 | (x)[1])
|
||||
static int get2byte( byte[] p, int offset )
|
||||
{
|
||||
return p[offset + 0] << 8 | p[offset + 1];
|
||||
}
|
||||
|
||||
//#define put2byte(p,v) ((p)[0] = (u8)((v)>>8), (p)[1] = (u8)(v))
|
||||
static void put2byte( byte[] pData, int Offset, u32 v )
|
||||
{
|
||||
pData[Offset + 0] = (byte)( v >> 8 );
|
||||
pData[Offset + 1] = (byte)v;
|
||||
}
|
||||
static void put2byte( byte[] pData, int Offset, int v )
|
||||
{
|
||||
pData[Offset + 0] = (byte)( v >> 8 );
|
||||
pData[Offset + 1] = (byte)v;
|
||||
}
|
||||
|
||||
//#define get4byte sqlite3Get4byte
|
||||
//#define put4byte sqlite3Put4byte
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,306 @@
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/*
|
||||
** 2001 September 15
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This header file defines the interface that the sqlite B-Tree file
|
||||
** subsystem. See comments in the source code for a detailed description
|
||||
** of what each interface routine does.
|
||||
*************************************************************************
|
||||
** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart
|
||||
** C#-SQLite is an independent reimplementation of the SQLite software library
|
||||
**
|
||||
** SQLITE_SOURCE_ID: 2011-06-23 19:49:22 4374b7e83ea0a3fbc3691f9c0c936272862f32f2
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
//#if !_BTREE_H_
|
||||
//#define _BTREE_H_
|
||||
|
||||
/* TODO: This definition is just included so other modules compile. It
|
||||
** needs to be revisited.
|
||||
*/
|
||||
const int SQLITE_N_BTREE_META = 10;
|
||||
|
||||
/*
|
||||
** If defined as non-zero, auto-vacuum is enabled by default. Otherwise
|
||||
** it must be turned on for each database using "PRAGMA auto_vacuum = 1".
|
||||
*/
|
||||
#if !SQLITE_DEFAULT_AUTOVACUUM
|
||||
const int SQLITE_DEFAULT_AUTOVACUUM = 0;
|
||||
#endif
|
||||
|
||||
const int BTREE_AUTOVACUUM_NONE = 0; /* Do not do auto-vacuum */
|
||||
const int BTREE_AUTOVACUUM_FULL = 1; /* Do full auto-vacuum */
|
||||
const int BTREE_AUTOVACUUM_INCR = 2; /* Incremental vacuum */
|
||||
|
||||
/*
|
||||
** Forward declarations of structure
|
||||
*/
|
||||
//typedef struct Btree Btree;
|
||||
//typedef struct BtCursor BtCursor;
|
||||
//typedef struct BtShared BtShared;
|
||||
|
||||
//int sqlite3BtreeOpen(
|
||||
// sqlite3_vfs *pVfs, /* VFS to use with this b-tree */
|
||||
// string zFilename, /* Name of database file to open */
|
||||
// sqlite3 db, /* Associated database connection */
|
||||
// Btree **ppBtree, /* Return open Btree* here */
|
||||
// int flags, /* Flags */
|
||||
// int vfsFlags /* Flags passed through to VFS open */
|
||||
//);
|
||||
|
||||
/* The flags parameter to sqlite3BtreeOpen can be the bitwise or of the
|
||||
** following values.
|
||||
**
|
||||
** NOTE: These values must match the corresponding PAGER_ values in
|
||||
** pager.h.
|
||||
*/
|
||||
//#define BTREE_OMIT_JOURNAL 1 /* Do not create or use a rollback journal */
|
||||
//#define BTREE_NO_READLOCK 2 /* Omit readlocks on readonly files */
|
||||
//#define BTREE_MEMORY 4 /* This is an in-memory DB */
|
||||
//#define BTREE_SINGLE 8 /* The file contains at most 1 b-tree */
|
||||
//#define BTREE_UNORDERED 16 /* Use of a hash implementation is OK */
|
||||
const int BTREE_OMIT_JOURNAL = 1; /* Do not create or use a rollback journal */
|
||||
const int BTREE_NO_READLOCK = 2; /* Omit readlocks on readonly files */
|
||||
const int BTREE_MEMORY = 4; /* This is an in-memory DB */
|
||||
const int BTREE_SINGLE = 8; /* The file contains at most 1 b-tree */
|
||||
const int BTREE_UNORDERED = 16; /* Use of a hash implementation is OK */
|
||||
|
||||
//int sqlite3BtreeClose(Btree);
|
||||
//int sqlite3BtreeSetCacheSize(Btree*,int);
|
||||
//int sqlite3BtreeSetSafetyLevel(Btree*,int,int,int);
|
||||
//int sqlite3BtreeSyncDisabled(Btree);
|
||||
//int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix);
|
||||
//int sqlite3BtreeGetPageSize(Btree);
|
||||
//int sqlite3BtreeMaxPageCount(Btree*,int);
|
||||
//u32 sqlite3BtreeLastPage(Btree);
|
||||
//int sqlite3BtreeSecureDelete(Btree*,int);
|
||||
//int sqlite3BtreeGetReserve(Btree);
|
||||
//int sqlite3BtreeSetAutoVacuum(Btree , int);
|
||||
//int sqlite3BtreeGetAutoVacuum(Btree );
|
||||
//int sqlite3BtreeBeginTrans(Btree*,int);
|
||||
//int sqlite3BtreeCommitPhaseOne(Btree*, string zMaster);
|
||||
//int sqlite3BtreeCommitPhaseTwo(Btree*, int);
|
||||
//int sqlite3BtreeCommit(Btree);
|
||||
//int sqlite3BtreeRollback(Btree);
|
||||
//int sqlite3BtreeBeginStmt(Btree);
|
||||
//int sqlite3BtreeCreateTable(Btree*, int*, int flags);
|
||||
//int sqlite3BtreeIsInTrans(Btree);
|
||||
//int sqlite3BtreeIsInReadTrans(Btree);
|
||||
//int sqlite3BtreeIsInBackup(Btree);
|
||||
//void *sqlite3BtreeSchema(Btree , int, void()(void ));
|
||||
//int sqlite3BtreeSchemaLocked( Btree* pBtree );
|
||||
//int sqlite3BtreeLockTable( Btree* pBtree, int iTab, u8 isWriteLock );
|
||||
//int sqlite3BtreeSavepoint(Btree *, int, int);
|
||||
|
||||
//string sqlite3BtreeGetFilename(Btree );
|
||||
//string sqlite3BtreeGetJournalname(Btree );
|
||||
//int sqlite3BtreeCopyFile(Btree *, Btree );
|
||||
|
||||
//int sqlite3BtreeIncrVacuum(Btree );
|
||||
|
||||
/* The flags parameter to sqlite3BtreeCreateTable can be the bitwise OR
|
||||
** of the flags shown below.
|
||||
**
|
||||
** Every SQLite table must have either BTREE_INTKEY or BTREE_BLOBKEY set.
|
||||
** With BTREE_INTKEY, the table key is a 64-bit integer and arbitrary data
|
||||
** is stored in the leaves. (BTREE_INTKEY is used for SQL tables.) With
|
||||
** BTREE_BLOBKEY, the key is an arbitrary BLOB and no content is stored
|
||||
** anywhere - the key is the content. (BTREE_BLOBKEY is used for SQL
|
||||
** indices.)
|
||||
*/
|
||||
//#define BTREE_INTKEY 1 /* Table has only 64-bit signed integer keys */
|
||||
//#define BTREE_BLOBKEY 2 /* Table has keys only - no data */
|
||||
const int BTREE_INTKEY = 1;
|
||||
const int BTREE_BLOBKEY = 2;
|
||||
|
||||
//int sqlite3BtreeDropTable(Btree*, int, int);
|
||||
//int sqlite3BtreeClearTable(Btree*, int, int);
|
||||
//void sqlite3BtreeTripAllCursors(Btree*, int);
|
||||
|
||||
//void sqlite3BtreeGetMeta(Btree *pBtree, int idx, u32 *pValue);
|
||||
//int sqlite3BtreeUpdateMeta(Btree*, int idx, u32 value);
|
||||
|
||||
|
||||
/*
|
||||
** The second parameter to sqlite3BtreeGetMeta or sqlite3BtreeUpdateMeta
|
||||
** should be one of the following values. The integer values are assigned
|
||||
** to constants so that the offset of the corresponding field in an
|
||||
** SQLite database header may be found using the following formula:
|
||||
**
|
||||
** offset = 36 + (idx * 4)
|
||||
**
|
||||
** For example, the free-page-count field is located at byte offset 36 of
|
||||
** the database file header. The incr-vacuum-flag field is located at
|
||||
** byte offset 64 (== 36+4*7).
|
||||
*/
|
||||
//#define BTREE_FREE_PAGE_COUNT 0
|
||||
//#define BTREE_SCHEMA_VERSION 1
|
||||
//#define BTREE_FILE_FORMAT 2
|
||||
//#define BTREE_DEFAULT_CACHE_SIZE 3
|
||||
//#define BTREE_LARGEST_ROOT_PAGE 4
|
||||
//#define BTREE_TEXT_ENCODING 5
|
||||
//#define BTREE_USER_VERSION 6
|
||||
//#define BTREE_INCR_VACUUM 7
|
||||
const int BTREE_FREE_PAGE_COUNT = 0;
|
||||
const int BTREE_SCHEMA_VERSION = 1;
|
||||
const int BTREE_FILE_FORMAT = 2;
|
||||
const int BTREE_DEFAULT_CACHE_SIZE = 3;
|
||||
const int BTREE_LARGEST_ROOT_PAGE = 4;
|
||||
const int BTREE_TEXT_ENCODING = 5;
|
||||
const int BTREE_USER_VERSION = 6;
|
||||
const int BTREE_INCR_VACUUM = 7;
|
||||
|
||||
//int sqlite3BtreeCursor(
|
||||
// Btree*, /* BTree containing table to open */
|
||||
// int iTable, /* Index of root page */
|
||||
// int wrFlag, /* 1 for writing. 0 for read-only */
|
||||
// struct KeyInfo*, /* First argument to compare function */
|
||||
// BtCursor pCursor /* Space to write cursor structure */
|
||||
//);
|
||||
//int sqlite3BtreeCursorSize(void);
|
||||
//void sqlite3BtreeCursorZero(BtCursor);
|
||||
|
||||
//int sqlite3BtreeCloseCursor(BtCursor);
|
||||
//int sqlite3BtreeMovetoUnpacked(
|
||||
// BtCursor*,
|
||||
// UnpackedRecord pUnKey,
|
||||
// i64 intKey,
|
||||
// int bias,
|
||||
// int pRes
|
||||
//);
|
||||
//int sqlite3BtreeCursorHasMoved(BtCursor*, int);
|
||||
//int sqlite3BtreeDelete(BtCursor);
|
||||
//int sqlite3BtreeInsert(BtCursor*, const void pKey, i64 nKey,
|
||||
// const void pData, int nData,
|
||||
// int nZero, int bias, int seekResult);
|
||||
//int sqlite3BtreeFirst(BtCursor*, int pRes);
|
||||
//int sqlite3BtreeLast(BtCursor*, int pRes);
|
||||
//int sqlite3BtreeNext(BtCursor*, int pRes);
|
||||
//int sqlite3BtreeEof(BtCursor);
|
||||
//int sqlite3BtreePrevious(BtCursor*, int pRes);
|
||||
//int sqlite3BtreeKeySize(BtCursor*, i64 pSize);
|
||||
//int sqlite3BtreeKey(BtCursor*, u32 offset, u32 amt, void);
|
||||
//const void *sqlite3BtreeKeyFetch(BtCursor*, int pAmt);
|
||||
//const void *sqlite3BtreeDataFetch(BtCursor*, int pAmt);
|
||||
//int sqlite3BtreeDataSize(BtCursor*, u32 pSize);
|
||||
//int sqlite3BtreeData(BtCursor*, u32 offset, u32 amt, void);
|
||||
//void sqlite3BtreeSetCachedRowid(BtCursor*, sqlite3_int64);
|
||||
//sqlite3_int64 sqlite3BtreeGetCachedRowid(BtCursor);
|
||||
|
||||
//char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int);
|
||||
//struct Pager *sqlite3BtreePager(Btree);
|
||||
|
||||
//int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void);
|
||||
//void sqlite3BtreeCacheOverflow(BtCursor );
|
||||
//void sqlite3BtreeClearCursor(BtCursor );
|
||||
|
||||
//int sqlite3BtreeSetVersion(Btree *pBt, int iVersion);
|
||||
|
||||
//#if !NDEBUG
|
||||
//int sqlite3BtreeCursorIsValid(BtCursor);
|
||||
//#endif
|
||||
|
||||
//#if !SQLITE_OMIT_BTREECOUNT
|
||||
//int sqlite3BtreeCount(BtCursor *, i64 );
|
||||
//#endif
|
||||
|
||||
//#if SQLITE_TEST
|
||||
//int sqlite3BtreeCursorInfo(BtCursor*, int*, int);
|
||||
//void sqlite3BtreeCursorList(Btree);
|
||||
//#endif
|
||||
|
||||
//#if !SQLITE_OMIT_WAL
|
||||
//int sqlite3BtreeCheckpoint(Btree*, int, int *, int );
|
||||
//#endif
|
||||
|
||||
/*
|
||||
** If we are not using shared cache, then there is no need to
|
||||
** use mutexes to access the BtShared structures. So make the
|
||||
** Enter and Leave procedures no-ops.
|
||||
*/
|
||||
#if NO_SQLITE_OMIT_SHARED_CACHE //#if !SQLITE_OMIT_SHARED_CACHE
|
||||
//void sqlite3BtreeEnter(Btree);
|
||||
//void sqlite3BtreeEnterAll(sqlite3);
|
||||
#else
|
||||
//# define sqlite3BtreeEnter(X)
|
||||
static void sqlite3BtreeEnter( Btree bt )
|
||||
{
|
||||
}
|
||||
//# define sqlite3BtreeEnterAll(X)
|
||||
static void sqlite3BtreeEnterAll( sqlite3 p )
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE
|
||||
//int sqlite3BtreeSharable(Btree);
|
||||
//void sqlite3BtreeLeave(Btree);
|
||||
//void sqlite3BtreeEnterCursor(BtCursor);
|
||||
//void sqlite3BtreeLeaveCursor(BtCursor);
|
||||
//void sqlite3BtreeLeaveAll(sqlite3);
|
||||
#if !NDEBUG
|
||||
/* These routines are used inside Debug.Assert() statements only. */
|
||||
int sqlite3BtreeHoldsMutex(Btree);
|
||||
int sqlite3BtreeHoldsAllMutexes(sqlite3);
|
||||
int sqlite3SchemaMutexHeld(sqlite3*,int,Schema);
|
||||
#endif
|
||||
#else
|
||||
//# define sqlite3BtreeSharable(X) 0
|
||||
static bool sqlite3BtreeSharable( Btree X )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//# define sqlite3BtreeLeave(X)
|
||||
static void sqlite3BtreeLeave( Btree X )
|
||||
{
|
||||
}
|
||||
|
||||
//# define sqlite3BtreeEnterCursor(X)
|
||||
static void sqlite3BtreeEnterCursor( BtCursor X )
|
||||
{
|
||||
}
|
||||
|
||||
//# define sqlite3BtreeLeaveCursor(X)
|
||||
static void sqlite3BtreeLeaveCursor( BtCursor X )
|
||||
{
|
||||
}
|
||||
|
||||
//# define sqlite3BtreeLeaveAll(X)
|
||||
static void sqlite3BtreeLeaveAll( sqlite3 X )
|
||||
{
|
||||
}
|
||||
|
||||
//# define sqlite3BtreeHoldsMutex(X) 1
|
||||
static bool sqlite3BtreeHoldsMutex( Btree X )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//# define sqlite3BtreeHoldsAllMutexes(X) 1
|
||||
static bool sqlite3BtreeHoldsAllMutexes( sqlite3 X )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
//# define sqlite3SchemaMutexHeld(X,Y,Z) 1
|
||||
static bool sqlite3SchemaMutexHeld( sqlite3 X, int y, Schema z )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
//#endif // * _BTREE_H_ */
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,375 @@
|
||||
/*
|
||||
*************************************************************************
|
||||
** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart
|
||||
** C#-SQLite is an independent reimplementation of the SQLite software library
|
||||
*************************************************************************
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
using HANDLE = System.IntPtr;
|
||||
using i16 = System.Int16;
|
||||
using sqlite3_int64 = System.Int64;
|
||||
using u32 = System.UInt32;
|
||||
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
using DbPage = Sqlite3.PgHdr;
|
||||
using sqlite3_pcache = Sqlite3.PCache1;
|
||||
using sqlite3_stmt = Sqlite3.Vdbe;
|
||||
using sqlite3_value = Sqlite3.Mem;
|
||||
|
||||
public partial class Sqlite3
|
||||
{
|
||||
public delegate void dxAuth( object pAuthArg, int b, string c, string d, string e, string f );
|
||||
public delegate int dxBusy( object pBtShared, int iValue );
|
||||
public delegate void dxFreeAux( object pAuxArg );
|
||||
public delegate int dxCallback( object pCallbackArg, sqlite3_int64 argc, object p2, object p3 );
|
||||
public delegate void dxalarmCallback( object pNotUsed, sqlite3_int64 iNotUsed, int size );
|
||||
public delegate void dxCollNeeded( object pCollNeededArg, sqlite3 db, int eTextRep, string collationName );
|
||||
public delegate int dxCommitCallback( object pCommitArg );
|
||||
public delegate int dxCompare( object pCompareArg, int size1, string Key1, int size2, string Key2 );
|
||||
public delegate bool dxCompare4( string Key1, int size1, string Key2, int size2 );
|
||||
public delegate void dxDel( ref string pDelArg ); // needs ref
|
||||
public delegate void dxDelCollSeq( ref object pDelArg ); // needs ref
|
||||
public delegate void dxLog( object pLogArg, int i, string msg );
|
||||
public delegate void dxLogcallback( object pCallbackArg, int argc, string p2 );
|
||||
public delegate void dxProfile( object pProfileArg, string msg, sqlite3_int64 time );
|
||||
public delegate int dxProgress( object pProgressArg );
|
||||
public delegate void dxRollbackCallback( object pRollbackArg );
|
||||
public delegate void dxTrace( object pTraceArg, string msg );
|
||||
public delegate void dxUpdateCallback( object pUpdateArg, int b, string c, string d, sqlite3_int64 e );
|
||||
public delegate int dxWalCallback( object pWalArg, sqlite3 db, string zDb, int nEntry );
|
||||
|
||||
/*
|
||||
* FUNCTIONS
|
||||
*
|
||||
*/
|
||||
public delegate void dxFunc( sqlite3_context ctx, int intValue, sqlite3_value[] value );
|
||||
public delegate void dxStep( sqlite3_context ctx, int intValue, sqlite3_value[] value );
|
||||
public delegate void dxFinal( sqlite3_context ctx );
|
||||
public delegate void dxFDestroy( object pArg );
|
||||
//
|
||||
public delegate string dxColname( sqlite3_value pVal );
|
||||
public delegate int dxFuncBtree( Btree p );
|
||||
public delegate int dxExprTreeFunction( ref int pArg, Expr pExpr );
|
||||
public delegate int dxExprTreeFunction_NC( NameContext pArg, ref Expr pExpr );
|
||||
public delegate int dxExprTreeFunction_OBJ( object pArg, Expr pExpr );
|
||||
/*
|
||||
VFS Delegates
|
||||
*/
|
||||
public delegate int dxClose( sqlite3_file File_ID );
|
||||
public delegate int dxCheckReservedLock( sqlite3_file File_ID, ref int pRes );
|
||||
public delegate int dxDeviceCharacteristics( sqlite3_file File_ID );
|
||||
public delegate int dxFileControl( sqlite3_file File_ID, int op, ref sqlite3_int64 pArgs );
|
||||
public delegate int dxFileSize( sqlite3_file File_ID, ref long size );
|
||||
public delegate int dxLock( sqlite3_file File_ID, int locktype );
|
||||
public delegate int dxRead( sqlite3_file File_ID, byte[] buffer, int amount, sqlite3_int64 offset );
|
||||
public delegate int dxSectorSize( sqlite3_file File_ID );
|
||||
public delegate int dxSync( sqlite3_file File_ID, int flags );
|
||||
public delegate int dxTruncate( sqlite3_file File_ID, sqlite3_int64 size );
|
||||
public delegate int dxUnlock( sqlite3_file File_ID, int locktype );
|
||||
public delegate int dxWrite( sqlite3_file File_ID, byte[] buffer, int amount, sqlite3_int64 offset );
|
||||
public delegate int dxShmMap( sqlite3_file File_ID, int iPg, int pgsz, int pInt, out object pvolatile );
|
||||
public delegate int dxShmLock( sqlite3_file File_ID, int offset, int n, int flags );
|
||||
public delegate void dxShmBarrier( sqlite3_file File_ID );
|
||||
public delegate int dxShmUnmap( sqlite3_file File_ID, int deleteFlag );
|
||||
/*
|
||||
sqlite_vfs Delegates
|
||||
*/
|
||||
public delegate int dxOpen( sqlite3_vfs vfs, string zName, sqlite3_file db, int flags, out int pOutFlags );
|
||||
public delegate int dxDelete( sqlite3_vfs vfs, string zName, int syncDir );
|
||||
public delegate int dxAccess( sqlite3_vfs vfs, string zName, int flags, out int pResOut );
|
||||
public delegate int dxFullPathname( sqlite3_vfs vfs, string zName, int nOut, StringBuilder zOut );
|
||||
public delegate HANDLE dxDlOpen( sqlite3_vfs vfs, string zFilename );
|
||||
public delegate int dxDlError( sqlite3_vfs vfs, int nByte, string zErrMsg );
|
||||
public delegate HANDLE dxDlSym( sqlite3_vfs vfs, HANDLE data, string zSymbol );
|
||||
public delegate int dxDlClose( sqlite3_vfs vfs, HANDLE data );
|
||||
public delegate int dxRandomness( sqlite3_vfs vfs, int nByte, byte[] buffer );
|
||||
public delegate int dxSleep( sqlite3_vfs vfs, int microseconds );
|
||||
public delegate int dxCurrentTime( sqlite3_vfs vfs, ref double currenttime );
|
||||
public delegate int dxGetLastError( sqlite3_vfs pVfs, int nBuf, ref string zBuf );
|
||||
public delegate int dxCurrentTimeInt64( sqlite3_vfs pVfs, ref sqlite3_int64 pTime );
|
||||
public delegate int dxSetSystemCall( sqlite3_vfs pVfs, string zName, sqlite3_int64 sqlite3_syscall_ptr );
|
||||
public delegate int dxGetSystemCall( sqlite3_vfs pVfs, string zName, sqlite3_int64 sqlite3_syscall_ptr );
|
||||
public delegate int dxNextSystemCall( sqlite3_vfs pVfs, string zName, sqlite3_int64 sqlite3_syscall_ptr );
|
||||
|
||||
/*
|
||||
* Pager Delegates
|
||||
*/
|
||||
|
||||
public delegate void dxDestructor( DbPage dbPage ); /* Call this routine when freeing pages */
|
||||
public delegate int dxBusyHandler( object pBusyHandlerArg );
|
||||
public delegate void dxReiniter( DbPage dbPage ); /* Call this routine when reloading pages */
|
||||
|
||||
public delegate void dxFreeSchema( Schema schema );
|
||||
|
||||
#if !NOT_SQLITE_HAS_CODEC
|
||||
public delegate byte[] dxCodec( codec_ctx pCodec, byte[] D, uint pageNumber, int X ); //void *(*xCodec)(void*,void*,Pgno,int); /* Routine for en/decoding data */
|
||||
public delegate void dxCodecSizeChng( codec_ctx pCodec, int pageSize, i16 nReserve ); //void (*xCodecSizeChng)(void*,int,int); /* Notify of page size changes */
|
||||
public delegate void dxCodecFree( ref codec_ctx pCodec ); //void (*xCodecFree)(void); /* Destructor for the codec */
|
||||
#endif
|
||||
|
||||
//Module
|
||||
public delegate void dxDestroy( ref PgHdr pDestroyArg );
|
||||
public delegate int dxStress( object obj, PgHdr pPhHdr );
|
||||
|
||||
//sqlite3_module
|
||||
public delegate int smdxCreateConnect( sqlite3 db, object pAux, int argc, string[] constargv, out sqlite3_vtab ppVTab, out string pError );
|
||||
public delegate int smdxBestIndex( sqlite3_vtab pVTab, ref sqlite3_index_info pIndex );
|
||||
public delegate int smdxDisconnect( ref object pVTab );
|
||||
public delegate int smdxDestroy(ref object pVTab );
|
||||
public delegate int smdxOpen( sqlite3_vtab pVTab, out sqlite3_vtab_cursor ppCursor );
|
||||
public delegate int smdxClose( ref sqlite3_vtab_cursor pCursor );
|
||||
public delegate int smdxFilter( sqlite3_vtab_cursor pCursor, int idxNum, string idxStr, int argc, sqlite3_value[] argv );
|
||||
public delegate int smdxNext( sqlite3_vtab_cursor pCursor );
|
||||
public delegate int smdxEof( sqlite3_vtab_cursor pCursor );
|
||||
public delegate int smdxColumn( sqlite3_vtab_cursor pCursor, sqlite3_context p2, int p3 );
|
||||
public delegate int smdxRowid( sqlite3_vtab_cursor pCursor, out sqlite3_int64 pRowid );
|
||||
public delegate int smdxUpdate( sqlite3_vtab pVTab, int p1, sqlite3_value[] p2, out sqlite3_int64 p3 );
|
||||
public delegate int smdxFunction ( sqlite3_vtab pVTab );
|
||||
public delegate int smdxFindFunction( sqlite3_vtab pVtab, int nArg, string zName, ref dxFunc pxFunc, ref object ppArg );
|
||||
public delegate int smdxRename( sqlite3_vtab pVtab, string zNew );
|
||||
public delegate int smdxFunctionArg (sqlite3_vtab pVTab, int nArg );
|
||||
|
||||
//AutoExtention
|
||||
public delegate int dxInit( sqlite3 db, ref string zMessage, sqlite3_api_routines sar );
|
||||
#if !SQLITE_OMIT_VIRTUALTABLE
|
||||
public delegate int dmxCreate(sqlite3 db, object pAux, int argc, string p4, object argv, sqlite3_vtab ppVTab, char p7);
|
||||
public delegate int dmxConnect(sqlite3 db, object pAux, int argc, string p4, object argv, sqlite3_vtab ppVTab, char p7);
|
||||
public delegate int dmxBestIndex(sqlite3_vtab pVTab, ref sqlite3_index_info pIndexInfo);
|
||||
public delegate int dmxDisconnect(sqlite3_vtab pVTab);
|
||||
public delegate int dmxDestroy(sqlite3_vtab pVTab);
|
||||
public delegate int dmxOpen(sqlite3_vtab pVTab, sqlite3_vtab_cursor ppCursor);
|
||||
public delegate int dmxClose(sqlite3_vtab_cursor pCursor);
|
||||
public delegate int dmxFilter(sqlite3_vtab_cursor pCursor, int idmxNum, string idmxStr, int argc, sqlite3_value argv);
|
||||
public delegate int dmxNext(sqlite3_vtab_cursor pCursor);
|
||||
public delegate int dmxEof(sqlite3_vtab_cursor pCursor);
|
||||
public delegate int dmxColumn(sqlite3_vtab_cursor pCursor, sqlite3_context ctx, int i3);
|
||||
public delegate int dmxRowid(sqlite3_vtab_cursor pCursor, sqlite3_int64 pRowid);
|
||||
public delegate int dmxUpdate(sqlite3_vtab pVTab, int i2, sqlite3_value sv3, sqlite3_int64 v4);
|
||||
public delegate int dmxBegin(sqlite3_vtab pVTab);
|
||||
public delegate int dmxSync(sqlite3_vtab pVTab);
|
||||
public delegate int dmxCommit(sqlite3_vtab pVTab);
|
||||
public delegate int dmxRollback(sqlite3_vtab pVTab);
|
||||
public delegate int dmxFindFunction(sqlite3_vtab pVtab, int nArg, string zName);
|
||||
public delegate int dmxRename(sqlite3_vtab pVtab, string zNew);
|
||||
#endif
|
||||
//Faults
|
||||
public delegate void void_function();
|
||||
|
||||
//Mem Methods
|
||||
public delegate int dxMemInit( object o );
|
||||
public delegate void dxMemShutdown( object o );
|
||||
public delegate byte[] dxMalloc( int nSize );
|
||||
public delegate int[] dxMallocInt( int nSize );
|
||||
public delegate Mem dxMallocMem( Mem pMem );
|
||||
public delegate void dxFree( ref byte[] pOld );
|
||||
public delegate void dxFreeInt( ref int[] pOld );
|
||||
public delegate void dxFreeMem( ref Mem pOld );
|
||||
public delegate byte[] dxRealloc( byte[] pOld, int nSize );
|
||||
public delegate int dxSize( byte[] pArray );
|
||||
public delegate int dxRoundup( int nSize );
|
||||
|
||||
//Mutex Methods
|
||||
public delegate int dxMutexInit();
|
||||
public delegate int dxMutexEnd();
|
||||
public delegate sqlite3_mutex dxMutexAlloc( int iNumber );
|
||||
public delegate void dxMutexFree( sqlite3_mutex sm );
|
||||
public delegate void dxMutexEnter( sqlite3_mutex sm );
|
||||
public delegate int dxMutexTry( sqlite3_mutex sm );
|
||||
public delegate void dxMutexLeave( sqlite3_mutex sm );
|
||||
public delegate bool dxMutexHeld( sqlite3_mutex sm );
|
||||
public delegate bool dxMutexNotheld( sqlite3_mutex sm );
|
||||
|
||||
public delegate object dxColumn( sqlite3_stmt pStmt, int i );
|
||||
public delegate int dxColumn_I( sqlite3_stmt pStmt, int i );
|
||||
|
||||
// Walker Methods
|
||||
public delegate int dxExprCallback( Walker W, ref Expr E ); /* Callback for expressions */
|
||||
public delegate int dxSelectCallback( Walker W, Select S ); /* Callback for SELECTs */
|
||||
|
||||
|
||||
// pcache Methods
|
||||
public delegate int dxPC_Init( object NotUsed );
|
||||
public delegate void dxPC_Shutdown( object NotUsed );
|
||||
public delegate sqlite3_pcache dxPC_Create( int szPage, bool bPurgeable );
|
||||
public delegate void dxPC_Cachesize( sqlite3_pcache pCache, int nCachesize );
|
||||
public delegate int dxPC_Pagecount( sqlite3_pcache pCache );
|
||||
public delegate PgHdr dxPC_Fetch( sqlite3_pcache pCache, u32 key, int createFlag );
|
||||
public delegate void dxPC_Unpin( sqlite3_pcache pCache, PgHdr p2, bool discard );
|
||||
public delegate void dxPC_Rekey( sqlite3_pcache pCache, PgHdr p2, u32 oldKey, u32 newKey );
|
||||
public delegate void dxPC_Truncate( sqlite3_pcache pCache, u32 iLimit );
|
||||
public delegate void dxPC_Destroy( ref sqlite3_pcache pCache );
|
||||
|
||||
public delegate void dxIter( PgHdr p );
|
||||
|
||||
|
||||
#if NET_35 || NET_40
|
||||
//API Simplifications -- Actions
|
||||
public static Action<sqlite3_context, String, Int32, dxDel> ResultBlob = sqlite3_result_blob;
|
||||
public static Action<sqlite3_context, Double> ResultDouble = sqlite3_result_double;
|
||||
public static Action<sqlite3_context, String, Int32> ResultError = sqlite3_result_error;
|
||||
public static Action<sqlite3_context, Int32> ResultErrorCode = sqlite3_result_error_code;
|
||||
public static Action<sqlite3_context> ResultErrorNoMem = sqlite3_result_error_nomem;
|
||||
public static Action<sqlite3_context> ResultErrorTooBig = sqlite3_result_error_toobig;
|
||||
public static Action<sqlite3_context, Int32> ResultInt = sqlite3_result_int;
|
||||
public static Action<sqlite3_context, Int64> ResultInt64 = sqlite3_result_int64;
|
||||
public static Action<sqlite3_context> ResultNull = sqlite3_result_null;
|
||||
public static Action<sqlite3_context, String, Int32, dxDel> ResultText = sqlite3_result_text;
|
||||
public static Action<sqlite3_context, String, Int32, Int32, dxDel> ResultText_Offset = sqlite3_result_text;
|
||||
public static Action<sqlite3_context, sqlite3_value> ResultValue = sqlite3_result_value;
|
||||
public static Action<sqlite3_context, Int32> ResultZeroblob = sqlite3_result_zeroblob;
|
||||
public static Action<sqlite3_context, Int32, String> SetAuxdata = sqlite3_set_auxdata;
|
||||
|
||||
//API Simplifications -- Functions
|
||||
public delegate Int32 FinalizeDelegate( sqlite3_stmt pStmt );
|
||||
public static FinalizeDelegate Finalize = sqlite3_finalize;
|
||||
|
||||
public static Func<sqlite3_stmt, Int32> ClearBindings = sqlite3_clear_bindings;
|
||||
public static Func<sqlite3_stmt, Int32, Byte[]> ColumnBlob = sqlite3_column_blob;
|
||||
public static Func<sqlite3_stmt, Int32, Int32> ColumnBytes = sqlite3_column_bytes;
|
||||
public static Func<sqlite3_stmt, Int32, Int32> ColumnBytes16 = sqlite3_column_bytes16;
|
||||
public static Func<sqlite3_stmt, Int32> ColumnCount = sqlite3_column_count;
|
||||
public static Func<sqlite3_stmt, Int32, String> ColumnDecltype = sqlite3_column_decltype;
|
||||
public static Func<sqlite3_stmt, Int32, Double> ColumnDouble = sqlite3_column_double;
|
||||
public static Func<sqlite3_stmt, Int32, Int32> ColumnInt = sqlite3_column_int;
|
||||
public static Func<sqlite3_stmt, Int32, Int64> ColumnInt64 = sqlite3_column_int64;
|
||||
public static Func<sqlite3_stmt, Int32, String> ColumnName = sqlite3_column_name;
|
||||
public static Func<sqlite3_stmt, Int32, String> ColumnText = sqlite3_column_text;
|
||||
public static Func<sqlite3_stmt, Int32, Int32> ColumnType = sqlite3_column_type;
|
||||
public static Func<sqlite3_stmt, Int32, sqlite3_value> ColumnValue = sqlite3_column_value;
|
||||
public static Func<sqlite3_stmt, Int32> DataCount = sqlite3_data_count;
|
||||
public static Func<sqlite3_stmt, Int32> Reset = sqlite3_reset;
|
||||
public static Func<sqlite3_stmt, Int32> Step = sqlite3_step;
|
||||
|
||||
public static Func<sqlite3_stmt, Int32, Byte[], Int32, dxDel, Int32> BindBlob = sqlite3_bind_blob;
|
||||
public static Func<sqlite3_stmt, Int32, Double, Int32> BindDouble = sqlite3_bind_double;
|
||||
public static Func<sqlite3_stmt, Int32, Int32, Int32> BindInt = sqlite3_bind_int;
|
||||
public static Func<sqlite3_stmt, Int32, Int64, Int32> BindInt64 = sqlite3_bind_int64;
|
||||
public static Func<sqlite3_stmt, Int32, Int32> BindNull = sqlite3_bind_null;
|
||||
public static Func<sqlite3_stmt, Int32> BindParameterCount = sqlite3_bind_parameter_count;
|
||||
public static Func<sqlite3_stmt, String, Int32> BindParameterIndex = sqlite3_bind_parameter_index;
|
||||
public static Func<sqlite3_stmt, Int32, String> BindParameterName = sqlite3_bind_parameter_name;
|
||||
public static Func<sqlite3_stmt, Int32, String, Int32, dxDel, Int32> BindText = sqlite3_bind_text;
|
||||
public static Func<sqlite3_stmt, Int32, sqlite3_value, Int32> BindValue = sqlite3_bind_value;
|
||||
public static Func<sqlite3_stmt, Int32, Int32, Int32> BindZeroblob = sqlite3_bind_zeroblob;
|
||||
|
||||
public delegate Int32 OpenDelegate( string zFilename, out sqlite3 ppDb );
|
||||
public static Func<sqlite3, Int32> Close = sqlite3_close;
|
||||
public static Func<sqlite3_stmt, sqlite3> DbHandle = sqlite3_db_handle;
|
||||
public static Func<sqlite3, String> Errmsg = sqlite3_errmsg;
|
||||
public static OpenDelegate Open = sqlite3_open;
|
||||
public static Func<sqlite3, sqlite3_stmt, sqlite3_stmt> NextStmt = sqlite3_next_stmt;
|
||||
public static Func<Int32> Shutdown = sqlite3_shutdown;
|
||||
public static Func<sqlite3_stmt, Int32, Int32, Int32> StmtStatus = sqlite3_stmt_status;
|
||||
|
||||
public delegate Int32 PrepareDelegate( sqlite3 db, String zSql, Int32 nBytes, ref sqlite3_stmt ppStmt, ref string pzTail );
|
||||
public delegate Int32 PrepareDelegateNoTail( sqlite3 db, String zSql, Int32 nBytes, ref sqlite3_stmt ppStmt, Int32 iDummy );
|
||||
public static PrepareDelegate Prepare = sqlite3_prepare;
|
||||
public static PrepareDelegate PrepareV2 = sqlite3_prepare_v2;
|
||||
public static PrepareDelegateNoTail PrepareV2NoTail = sqlite3_prepare_v2;
|
||||
|
||||
public static Func<sqlite3_context, Int32, Mem> AggregateContext = sqlite3_aggregate_context;
|
||||
public static Func<sqlite3_context, Int32, Object> GetAuxdata = sqlite3_get_auxdata;
|
||||
public static Func<sqlite3_context, sqlite3> ContextDbHandle = sqlite3_context_db_handle;
|
||||
public static Func<sqlite3_context, Object> UserData = sqlite3_user_data;
|
||||
|
||||
public static Func<sqlite3_value, Byte[]> ValueBlob = sqlite3_value_blob;
|
||||
public static Func<sqlite3_value, Int32> ValueBytes = sqlite3_value_bytes;
|
||||
public static Func<sqlite3_value, Int32> ValueBytes16 = sqlite3_value_bytes16;
|
||||
public static Func<sqlite3_value, Double> ValueDouble = sqlite3_value_double;
|
||||
public static Func<sqlite3_value, Int32> ValueInt = sqlite3_value_int;
|
||||
public static Func<sqlite3_value, Int64> ValueInt64 = sqlite3_value_int64;
|
||||
public static Func<sqlite3_value, String> ValueText = sqlite3_value_text;
|
||||
public static Func<sqlite3_value, Int32> ValueType = sqlite3_value_type;
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//#if( NET_35 && !NET_40) || WINDOWS_PHONE
|
||||
namespace System
|
||||
{
|
||||
// Summary:
|
||||
// Encapsulates a method that has four parameters and does not return a value.
|
||||
//
|
||||
// Parameters:
|
||||
// arg1:
|
||||
// The first parameter of the method that this delegate encapsulates.
|
||||
//
|
||||
// arg2:
|
||||
// The second parameter of the method that this delegate encapsulates.
|
||||
//
|
||||
// arg3:
|
||||
// The third parameter of the method that this delegate encapsulates.
|
||||
//
|
||||
// arg4:
|
||||
// The fourth parameter of the method that this delegate encapsulates.
|
||||
//
|
||||
// arg5:
|
||||
// The fifth parameter of the method that this delegate encapsulates.
|
||||
//
|
||||
// Type parameters:
|
||||
// T1:
|
||||
// The type of the first parameter of the method that this delegate encapsulates.
|
||||
//
|
||||
// T2:
|
||||
// The type of the second parameter of the method that this delegate encapsulates.
|
||||
//
|
||||
// T3:
|
||||
// The type of the third parameter of the method that this delegate encapsulates.
|
||||
//
|
||||
// T4:
|
||||
// The type of the fourth parameter of the method that this delegate encapsulates.
|
||||
//
|
||||
// T5:
|
||||
// The type of the fifth parameter of the method that this delegate encapsulates.
|
||||
public delegate void Action<T1, T2, T3, T4, T5>( T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5 );
|
||||
|
||||
// Summary:
|
||||
// Encapsulates a method that has three parameters and returns a value of the
|
||||
// type specified by the TResult parameter.
|
||||
//
|
||||
// Parameters:
|
||||
// arg1:
|
||||
// The first parameter of the method that this delegate encapsulates.
|
||||
//
|
||||
// arg2:
|
||||
// The second parameter of the method that this delegate encapsulates.
|
||||
//
|
||||
// arg3:
|
||||
// The third parameter of the method that this delegate encapsulates.
|
||||
//
|
||||
// arg4:
|
||||
// The fourth parameter of the method that this delegate encapsulates.
|
||||
//
|
||||
// arg5:
|
||||
// The fifth parameter of the method that this delegate encapsulates.
|
||||
//
|
||||
// Type parameters:
|
||||
// T1:
|
||||
// The type of the first parameter of the method that this delegate encapsulates.
|
||||
//
|
||||
// T2:
|
||||
// The type of the second parameter of the method that this delegate encapsulates.
|
||||
//
|
||||
// T3:
|
||||
// The type of the third parameter of the method that this delegate encapsulates.
|
||||
//
|
||||
// T4:
|
||||
// The type of the fourth parameter of the method that this delegate encapsulates.
|
||||
//
|
||||
// T5:
|
||||
// The type of the fifth parameter of the method that this delegate encapsulates.
|
||||
//
|
||||
// TResult:
|
||||
// The type of the return value of the method that this delegate encapsulates.
|
||||
//
|
||||
// Returns:
|
||||
// The return value of the method that this delegate encapsulates.
|
||||
public delegate TResult Func<T1, T2, T3, T4, TResult>( T1 arg1, T2 arg2, T3 arg3, T4 arg4 );
|
||||
public delegate TResult Func<T1, T2, T3, T4, T5, TResult>( T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5 );
|
||||
}
|
||||
//#endif
|
||||
@@ -0,0 +1,141 @@
|
||||
using u8 = System.Byte;
|
||||
using u32 = System.UInt32;
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/*
|
||||
** 2001 September 22
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This is the header file for the generic hash-table implemenation
|
||||
** used in SQLite.
|
||||
*************************************************************************
|
||||
** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart
|
||||
** C#-SQLite is an independent reimplementation of the SQLite software library
|
||||
**
|
||||
** SQLITE_SOURCE_ID: 2010-08-23 18:52:01 42537b60566f288167f1b5864a5435986838e3a3
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
//#if !_SQLITE_HASH_H_
|
||||
//#define _SQLITE_HASH_H_
|
||||
|
||||
/* Forward declarations of structures. */
|
||||
//typedef struct Hash Hash;
|
||||
//typedef struct HashElem HashElem;
|
||||
|
||||
/* A complete hash table is an instance of the following structure.
|
||||
** The internals of this structure are intended to be opaque -- client
|
||||
** code should not attempt to access or modify the fields of this structure
|
||||
** directly. Change this structure only by using the routines below.
|
||||
** However, some of the "procedures" and "functions" for modifying and
|
||||
** accessing this structure are really macros, so we can't really make
|
||||
** this structure opaque.
|
||||
**
|
||||
** All elements of the hash table are on a single doubly-linked list.
|
||||
** Hash.first points to the head of this list.
|
||||
**
|
||||
** There are Hash.htsize buckets. Each bucket points to a spot in
|
||||
** the global doubly-linked list. The contents of the bucket are the
|
||||
** element pointed to plus the next _ht.count-1 elements in the list.
|
||||
**
|
||||
** Hash.htsize and Hash.ht may be zero. In that case lookup is done
|
||||
** by a linear search of the global list. For small tables, the
|
||||
** Hash.ht table is never allocated because if there are few elements
|
||||
** in the table, it is faster to do a linear search than to manage
|
||||
** the hash table.
|
||||
*/
|
||||
public class _ht
|
||||
{ /* the hash table */
|
||||
public int count; /* Number of entries with this hash */
|
||||
public HashElem chain; /* Pointer to first entry with this hash */
|
||||
};
|
||||
|
||||
public class Hash
|
||||
{
|
||||
public u32 htsize = 31; /* Number of buckets in the hash table */
|
||||
public u32 count; /* Number of entries in this table */
|
||||
public HashElem first; /* The first element of the array */
|
||||
public _ht[] ht;
|
||||
public Hash Copy()
|
||||
{
|
||||
if ( this == null )
|
||||
return null;
|
||||
else
|
||||
{
|
||||
Hash cp = (Hash)MemberwiseClone();
|
||||
return cp;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/* Each element in the hash table is an instance of the following
|
||||
** structure. All elements are stored on a single doubly-linked list.
|
||||
**
|
||||
** Again, this structure is intended to be opaque, but it can't really
|
||||
** be opaque because it is used by macros.
|
||||
*/
|
||||
public class HashElem
|
||||
{
|
||||
public HashElem next;
|
||||
public HashElem prev; /* Next and previous elements in the table */
|
||||
public object data; /* Data associated with this element */
|
||||
public string pKey;
|
||||
public int nKey; /* Key associated with this element */
|
||||
};
|
||||
|
||||
/*
|
||||
** Access routines. To delete, insert a NULL pointer.
|
||||
*/
|
||||
//void sqlite3HashInit(Hash);
|
||||
//void *sqlite3HashInsert(Hash*, string pKey, int nKey, object *pData);
|
||||
//void *sqlite3HashFind(const Hash*, string pKey, int nKey);
|
||||
//void sqlite3HashClear(Hash);
|
||||
|
||||
/*
|
||||
** Macros for looping over all elements of a hash table. The idiom is
|
||||
** like this:
|
||||
**
|
||||
** Hash h;
|
||||
** HashElem p;
|
||||
** ...
|
||||
** for(p=sqliteHashFirst(&h); p; p=sqliteHashNext(p)){
|
||||
** SomeStructure pData = sqliteHashData(p);
|
||||
** // do something with pData
|
||||
** }
|
||||
*/
|
||||
//#define sqliteHashFirst(H) ((H).first)
|
||||
static HashElem sqliteHashFirst( Hash H )
|
||||
{
|
||||
return H.first;
|
||||
}
|
||||
//#define sqliteHashNext(E) ((E).next)
|
||||
static HashElem sqliteHashNext( HashElem E )
|
||||
{
|
||||
return E.next;
|
||||
}
|
||||
//#define sqliteHashData(E) ((E).data)
|
||||
static object sqliteHashData( HashElem E )
|
||||
{
|
||||
return E.data;
|
||||
}
|
||||
/* #define sqliteHashKey(E) ((E)->pKey) // NOT USED */
|
||||
/* #define sqliteHashKeysize(E) ((E)->nKey) // NOT USED */
|
||||
|
||||
/*
|
||||
** Number of entries in a hash table
|
||||
*/
|
||||
/* #define sqliteHashCount(H) ((H)->count) // NOT USED */
|
||||
|
||||
//#endif // * _SQLITE_HASH_H_ */
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,680 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using FILE = System.IO.TextWriter;
|
||||
|
||||
using i64 = System.Int64;
|
||||
using u8 = System.Byte;
|
||||
using u16 = System.UInt16;
|
||||
using u32 = System.UInt32;
|
||||
using u64 = System.UInt64;
|
||||
using unsigned = System.UIntPtr;
|
||||
|
||||
using Pgno = System.UInt32;
|
||||
|
||||
#if !SQLITE_MAX_VARIABLE_NUMBER
|
||||
using ynVar = System.Int16;
|
||||
#else
|
||||
using ynVar = System.Int32;
|
||||
#endif
|
||||
/*
|
||||
** The yDbMask datatype for the bitmask of all attached databases.
|
||||
*/
|
||||
#if SQLITE_MAX_ATTACHED//>30
|
||||
// typedef sqlite3_uint64 yDbMask;
|
||||
using yDbMask = System.Int64;
|
||||
#else
|
||||
// typedef unsigned int yDbMask;
|
||||
using yDbMask = System.Int32;
|
||||
#endif
|
||||
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
using Op = Sqlite3.VdbeOp;
|
||||
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/*
|
||||
** 2003 September 6
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This is the header file for information that is private to the
|
||||
** VDBE. This information used to all be at the top of the single
|
||||
** source code file "vdbe.c". When that file became too big (over
|
||||
** 6000 lines long) it was split up into several smaller files and
|
||||
** this header information was factored out.
|
||||
*************************************************************************
|
||||
** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart
|
||||
** C#-SQLite is an independent reimplementation of the SQLite software library
|
||||
**
|
||||
** SQLITE_SOURCE_ID: 2011-06-23 19:49:22 4374b7e83ea0a3fbc3691f9c0c936272862f32f2
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
//#if !_VDBEINT_H_
|
||||
//#define _VDBEINT_H_
|
||||
|
||||
/*
|
||||
** SQL is translated into a sequence of instructions to be
|
||||
** executed by a virtual machine. Each instruction is an instance
|
||||
** of the following structure.
|
||||
*/
|
||||
//typedef struct VdbeOp Op;
|
||||
|
||||
/*
|
||||
** Boolean values
|
||||
*/
|
||||
//typedef unsigned char Bool;
|
||||
|
||||
/*
|
||||
** A cursor is a pointer into a single BTree within a database file.
|
||||
** The cursor can seek to a BTree entry with a particular key, or
|
||||
** loop over all entries of the Btree. You can also insert new BTree
|
||||
** entries or retrieve the key or data from the entry that the cursor
|
||||
** is currently pointing to.
|
||||
**
|
||||
** Every cursor that the virtual machine has open is represented by an
|
||||
** instance of the following structure.
|
||||
*/
|
||||
public class VdbeCursor
|
||||
{
|
||||
public BtCursor pCursor; /* The cursor structure of the backend */
|
||||
public Btree pBt; /* Separate file holding temporary table */
|
||||
public KeyInfo pKeyInfo; /* Info about index keys needed by index cursors */
|
||||
public int iDb; /* Index of cursor database in db->aDb[] (or -1) */
|
||||
public int pseudoTableReg; /* Register holding pseudotable content. */
|
||||
public int nField; /* Number of fields in the header */
|
||||
public bool zeroed; /* True if zeroed out and ready for reuse */
|
||||
public bool rowidIsValid; /* True if lastRowid is valid */
|
||||
public bool atFirst; /* True if pointing to first entry */
|
||||
public bool useRandomRowid; /* Generate new record numbers semi-randomly */
|
||||
public bool nullRow; /* True if pointing to a row with no data */
|
||||
public bool deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */
|
||||
public bool isTable; /* True if a table requiring integer keys */
|
||||
public bool isIndex; /* True if an index containing keys only - no data */
|
||||
public bool isOrdered; /* True if the underlying table is BTREE_UNORDERED */
|
||||
#if !SQLITE_OMIT_VIRTUALTABLE
|
||||
public sqlite3_vtab_cursor pVtabCursor; /* The cursor for a virtual table */
|
||||
public sqlite3_module pModule; /* Module for cursor pVtabCursor */
|
||||
#endif
|
||||
public i64 seqCount; /* Sequence counter */
|
||||
public i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */
|
||||
public i64 lastRowid; /* Last rowid from a Next or NextIdx operation */
|
||||
|
||||
/* Result of last sqlite3BtreeMoveto() done by an OP_NotExists or
|
||||
** OP_IsUnique opcode on this cursor. */
|
||||
public int seekResult;
|
||||
|
||||
/* Cached information about the header for the data record that the
|
||||
** cursor is currently pointing to. Only valid if cacheStatus matches
|
||||
** Vdbe.cacheCtr. Vdbe.cacheCtr will never take on the value of
|
||||
** CACHE_STALE and so setting cacheStatus=CACHE_STALE guarantees that
|
||||
** the cache is out of date.
|
||||
**
|
||||
** aRow might point to (ephemeral) data for the current row, or it might
|
||||
** be NULL.
|
||||
*/
|
||||
public u32 cacheStatus; /* Cache is valid if this matches Vdbe.cacheCtr */
|
||||
public Pgno payloadSize; /* Total number of bytes in the record */
|
||||
public u32[] aType; /* Type values for all entries in the record */
|
||||
public u32[] aOffset; /* Cached offsets to the start of each columns data */
|
||||
public int aRow; /* Pointer to Data for the current row, if all on one page */
|
||||
|
||||
public VdbeCursor Copy()
|
||||
{
|
||||
return (VdbeCursor)MemberwiseClone();
|
||||
}
|
||||
};
|
||||
//typedef struct VdbeCursor VdbeCursor;
|
||||
|
||||
|
||||
/*
|
||||
** When a sub-program is executed (OP_Program), a structure of this type
|
||||
** is allocated to store the current value of the program counter, as
|
||||
** well as the current memory cell array and various other frame specific
|
||||
** values stored in the Vdbe struct. When the sub-program is finished,
|
||||
** these values are copied back to the Vdbe from the VdbeFrame structure,
|
||||
** restoring the state of the VM to as it was before the sub-program
|
||||
** began executing.
|
||||
**
|
||||
** The memory for a VdbeFrame object is allocated and managed by a memory
|
||||
** cell in the parent (calling) frame. When the memory cell is deleted or
|
||||
** overwritten, the VdbeFrame object is not freed immediately. Instead, it
|
||||
** is linked into the Vdbe.pDelFrame list. The contents of the Vdbe.pDelFrame
|
||||
** list is deleted when the VM is reset in VdbeHalt(). The reason for doing
|
||||
** this instead of deleting the VdbeFrame immediately is to avoid recursive
|
||||
** calls to sqlite3VdbeMemRelease() when the memory cells belonging to the
|
||||
** child frame are released.
|
||||
**
|
||||
** The currently executing frame is stored in Vdbe.pFrame. Vdbe.pFrame is
|
||||
** set to NULL if the currently executing frame is the main program.
|
||||
*/
|
||||
//typedef struct VdbeFrame VdbeFrame;
|
||||
public class VdbeFrame
|
||||
{
|
||||
public Vdbe v; /* VM this frame belongs to */
|
||||
public int pc; /* Program Counter in parent (calling) frame */
|
||||
public Op[] aOp; /* Program instructions for parent frame */
|
||||
public int nOp; /* Size of aOp array */
|
||||
public Mem[] aMem; /* Array of memory cells for parent frame */
|
||||
public int nMem; /* Number of entries in aMem */
|
||||
public VdbeCursor[] apCsr; /* Array of Vdbe cursors for parent frame */
|
||||
public u16 nCursor; /* Number of entries in apCsr */
|
||||
public int token; /* Copy of SubProgram.token */
|
||||
public int nChildMem; /* Number of memory cells for child frame */
|
||||
public int nChildCsr; /* Number of cursors for child frame */
|
||||
public i64 lastRowid; /* Last insert rowid (sqlite3.lastRowid) */
|
||||
public int nChange; /* Statement changes (Vdbe.nChanges) */
|
||||
public VdbeFrame pParent; /* Parent of this frame, or NULL if parent is main */
|
||||
//
|
||||
// Needed for C# Implementation
|
||||
//
|
||||
public Mem[] aChildMem; /* Array of memory cells for child frame */
|
||||
public VdbeCursor[] aChildCsr; /* Array of cursors for child frame */
|
||||
};
|
||||
|
||||
//#define VdbeFrameMem(p) ((Mem )&((u8 )p)[ROUND8(sizeof(VdbeFrame))])
|
||||
/*
|
||||
** A value for VdbeCursor.cacheValid that means the cache is always invalid.
|
||||
*/
|
||||
const int CACHE_STALE = 0;
|
||||
|
||||
/*
|
||||
** Internally, the vdbe manipulates nearly all SQL values as Mem
|
||||
** structures. Each Mem struct may cache multiple representations (string,
|
||||
** integer etc.) of the same value.
|
||||
*/
|
||||
public class Mem
|
||||
{
|
||||
public sqlite3 db; /* The associated database connection */
|
||||
public string z; /* String value */
|
||||
public double r; /* Real value */
|
||||
public struct union_ip
|
||||
{
|
||||
#if DEBUG_CLASS_MEM || DEBUG_CLASS_ALL
|
||||
public i64 _i; /* First operand */
|
||||
public i64 i
|
||||
{
|
||||
get { return _i; }
|
||||
set { _i = value; }
|
||||
}
|
||||
#else
|
||||
public i64 i; /* Integer value used when MEM_Int is set in flags */
|
||||
#endif
|
||||
public int nZero; /* Used when bit MEM_Zero is set in flags */
|
||||
public FuncDef pDef; /* Used only when flags==MEM_Agg */
|
||||
public RowSet pRowSet; /* Used only when flags==MEM_RowSet */
|
||||
public VdbeFrame pFrame; /* Used when flags==MEM_Frame */
|
||||
};
|
||||
public union_ip u;
|
||||
public byte[] zBLOB; /* BLOB value */
|
||||
public int n; /* Number of characters in string value, excluding '\0' */
|
||||
#if DEBUG_CLASS_MEM || DEBUG_CLASS_ALL
|
||||
public u16 _flags; /* First operand */
|
||||
public u16 flags
|
||||
{
|
||||
get { return _flags; }
|
||||
set { _flags = value; }
|
||||
}
|
||||
#else
|
||||
public u16 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */
|
||||
#endif
|
||||
public u8 type; /* One of SQLITE_NULL, SQLITE_TEXT, SQLITE_INTEGER, etc */
|
||||
public u8 enc; /* SQLITE_UTF8, SQLITE_UTF16BE, SQLITE_UTF16LE */
|
||||
#if SQLITE_DEBUG
|
||||
public Mem pScopyFrom; /* This Mem is a shallow copy of pScopyFrom */
|
||||
public object pFiller; /* So that sizeof(Mem) is a multiple of 8 */
|
||||
#endif
|
||||
public dxDel xDel; /* If not null, call this function to delete Mem.z */
|
||||
// Not used under c#
|
||||
//public string zMalloc; /* Dynamic buffer allocated by sqlite3Malloc() */
|
||||
public Mem _Mem; /* Used when C# overload Z as MEM space */
|
||||
public SumCtx _SumCtx; /* Used when C# overload Z as Sum context */
|
||||
public SubProgram[] _SubProgram;/* Used when C# overload Z as SubProgram*/
|
||||
public StrAccum _StrAccum; /* Used when C# overload Z as STR context */
|
||||
public object _MD5Context; /* Used when C# overload Z as MD5 context */
|
||||
|
||||
public Mem()
|
||||
{
|
||||
}
|
||||
|
||||
public Mem( sqlite3 db, string z, double r, int i, int n, u16 flags, u8 type, u8 enc
|
||||
#if SQLITE_DEBUG
|
||||
, Mem pScopyFrom, object pFiller /* pScopyFrom, pFiller */
|
||||
#endif
|
||||
)
|
||||
{
|
||||
this.db = db;
|
||||
this.z = z;
|
||||
this.r = r;
|
||||
this.u.i = i;
|
||||
this.n = n;
|
||||
this.flags = flags;
|
||||
#if SQLITE_DEBUG
|
||||
this.pScopyFrom = pScopyFrom;
|
||||
this.pFiller = pFiller;
|
||||
#endif
|
||||
this.type = type;
|
||||
this.enc = enc;
|
||||
}
|
||||
|
||||
public void CopyTo( ref Mem ct )
|
||||
{
|
||||
if ( ct == null )
|
||||
ct = new Mem();
|
||||
ct.u = u;
|
||||
ct.r = r;
|
||||
ct.db = db;
|
||||
ct.z = z;
|
||||
if ( zBLOB == null )
|
||||
ct.zBLOB = null;
|
||||
else
|
||||
{
|
||||
ct.zBLOB = sqlite3Malloc( zBLOB.Length );
|
||||
Buffer.BlockCopy( zBLOB, 0, ct.zBLOB, 0, zBLOB.Length );
|
||||
}
|
||||
ct.n = n;
|
||||
ct.flags = flags;
|
||||
ct.type = type;
|
||||
ct.enc = enc;
|
||||
ct.xDel = xDel;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/* One or more of the following flags are set to indicate the validOK
|
||||
** representations of the value stored in the Mem struct.
|
||||
**
|
||||
** If the MEM_Null flag is set, then the value is an SQL NULL value.
|
||||
** No other flags may be set in this case.
|
||||
**
|
||||
** If the MEM_Str flag is set then Mem.z points at a string representation.
|
||||
** Usually this is encoded in the same unicode encoding as the main
|
||||
** database (see below for exceptions). If the MEM_Term flag is also
|
||||
** set, then the string is nul terminated. The MEM_Int and MEM_Real
|
||||
** flags may coexist with the MEM_Str flag.
|
||||
*/
|
||||
//#define MEM_Null 0x0001 /* Value is NULL */
|
||||
//#define MEM_Str 0x0002 /* Value is a string */
|
||||
//#define MEM_Int 0x0004 /* Value is an integer */
|
||||
//#define MEM_Real 0x0008 /* Value is a real number */
|
||||
//#define MEM_Blob 0x0010 /* Value is a BLOB */
|
||||
//#define MEM_RowSet 0x0020 /* Value is a RowSet object */
|
||||
//#define MEM_Frame 0x0040 /* Value is a VdbeFrame object */
|
||||
//#define MEM_Invalid 0x0080 /* Value is undefined */
|
||||
//#define MEM_TypeMask 0x00ff /* Mask of type bits */
|
||||
const int MEM_Null = 0x0001;
|
||||
const int MEM_Str = 0x0002;
|
||||
const int MEM_Int = 0x0004;
|
||||
const int MEM_Real = 0x0008;
|
||||
const int MEM_Blob = 0x0010;
|
||||
const int MEM_RowSet = 0x0020;
|
||||
const int MEM_Frame = 0x0040;
|
||||
const int MEM_Invalid = 0x0080;
|
||||
const int MEM_TypeMask = 0x00ff;
|
||||
|
||||
/* Whenever Mem contains a valid string or blob representation, one of
|
||||
** the following flags must be set to determine the memory management
|
||||
** policy for Mem.z. The MEM_Term flag tells us whether or not the
|
||||
** string is \000 or \u0000 terminated
|
||||
// */
|
||||
//#define MEM_Term 0x0200 /* String rep is nul terminated */
|
||||
//#define MEM_Dyn 0x0400 /* Need to call sqliteFree() on Mem.z */
|
||||
//#define MEM_Static 0x0800 /* Mem.z points to a static string */
|
||||
//#define MEM_Ephem 0x1000 /* Mem.z points to an ephemeral string */
|
||||
//#define MEM_Agg 0x2000 /* Mem.z points to an agg function context */
|
||||
//#define MEM_Zero 0x4000 /* Mem.i contains count of 0s appended to blob */
|
||||
//#if SQLITE_OMIT_INCRBLOB
|
||||
// #undef MEM_Zero
|
||||
// #define MEM_Zero 0x0000
|
||||
//#endif
|
||||
const int MEM_Term = 0x0200;
|
||||
const int MEM_Dyn = 0x0400;
|
||||
const int MEM_Static = 0x0800;
|
||||
const int MEM_Ephem = 0x1000;
|
||||
const int MEM_Agg = 0x2000;
|
||||
#if NOT_SQLITE_OMIT_INCRBLOB //#if !SQLITE_OMIT_INCRBLOB
|
||||
const int MEM_Zero = 0x4000;
|
||||
#else
|
||||
const int MEM_Zero = 0x0000;
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Clear any existing type flags from a Mem and replace them with f
|
||||
*/
|
||||
//#define MemSetTypeFlag(p, f) \
|
||||
// ((p)->flags = ((p)->flags&~(MEM_TypeMask|MEM_Zero))|f)
|
||||
static void MemSetTypeFlag( Mem p, int f )
|
||||
{
|
||||
p.flags = (u16)( p.flags & ~( MEM_TypeMask | MEM_Zero ) | f );
|
||||
}// TODO -- Convert back to inline for speed
|
||||
|
||||
/*
|
||||
** Return true if a memory cell is not marked as invalid. This macro
|
||||
** is for use inside Debug.Assert() statements only.
|
||||
*/
|
||||
#if SQLITE_DEBUG
|
||||
//#define memIsValid(M) ((M)->flags & MEM_Invalid)==0
|
||||
static bool memIsValid( Mem M )
|
||||
{
|
||||
return ( ( M ).flags & MEM_Invalid ) == 0;
|
||||
}
|
||||
#else
|
||||
static bool memIsValid( Mem M ) { return true; }
|
||||
#endif
|
||||
|
||||
/* A VdbeFunc is just a FuncDef (defined in sqliteInt.h) that contains
|
||||
** additional information about auxiliary information bound to arguments
|
||||
** of the function. This is used to implement the sqlite3_get_auxdata()
|
||||
** and sqlite3_set_auxdata() APIs. The "auxdata" is some auxiliary data
|
||||
** that can be associated with a constant argument to a function. This
|
||||
** allows functions such as "regexp" to compile their constant regular
|
||||
** expression argument once and reused the compiled code for multiple
|
||||
** invocations.
|
||||
*/
|
||||
public class AuxData
|
||||
{
|
||||
public object pAux; /* Aux data for the i-th argument */
|
||||
//(void ); /* Destructor for the aux data */
|
||||
};
|
||||
|
||||
public class VdbeFunc : FuncDef
|
||||
{
|
||||
public FuncDef pFunc; /* The definition of the function */
|
||||
public int nAux; /* Number of entries allocated for apAux[] */
|
||||
public AuxData[] apAux = new AuxData[2]; /* One slot for each function argument */
|
||||
};
|
||||
|
||||
/*
|
||||
** The "context" argument for a installable function. A pointer to an
|
||||
** instance of this structure is the first argument to the routines used
|
||||
** implement the SQL functions.
|
||||
**
|
||||
** There is a typedef for this structure in sqlite.h. So all routines,
|
||||
** even the public interface to SQLite, can use a pointer to this structure.
|
||||
** But this file is the only place where the internal details of this
|
||||
** structure are known.
|
||||
**
|
||||
** This structure is defined inside of vdbeInt.h because it uses substructures
|
||||
** (Mem) which are only defined there.
|
||||
*/
|
||||
public class sqlite3_context
|
||||
{
|
||||
public FuncDef pFunc; /* Pointer to function information. MUST BE FIRST */
|
||||
public VdbeFunc pVdbeFunc; /* Auxilary data, if created. */
|
||||
public Mem s = new Mem(); /* The return value is stored here */
|
||||
public Mem pMem; /* Memory cell used to store aggregate context */
|
||||
public int isError; /* Error code returned by the function. */
|
||||
public CollSeq pColl; /* Collating sequence */
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
** An instance of the virtual machine. This structure contains the complete
|
||||
** state of the virtual machine.
|
||||
**
|
||||
** The "sqlite3_stmt" structure pointer that is returned by sqlite3_prepare()
|
||||
** is really a pointer to an instance of this structure.
|
||||
**
|
||||
** The Vdbe.inVtabMethod variable is set to non-zero for the duration of
|
||||
** any virtual table method invocations made by the vdbe program. It is
|
||||
** set to 2 for xDestroy method calls and 1 for all other methods. This
|
||||
** variable is used for two purposes: to allow xDestroy methods to execute
|
||||
** "DROP TABLE" statements and to prevent some nasty side effects of
|
||||
** malloc failure when SQLite is invoked recursively by a virtual table
|
||||
** method function.
|
||||
*/
|
||||
public class Vdbe
|
||||
{
|
||||
public sqlite3 db; /* The database connection that owns this statement */
|
||||
public Op[] aOp; /* Space to hold the virtual machine's program */
|
||||
public Mem[] aMem; /* The memory locations */
|
||||
public Mem[] apArg; /* Arguments to currently executing user function */
|
||||
public Mem[] aColName; /* Column names to return */
|
||||
public Mem[] pResultSet; /* Pointer to an array of results */
|
||||
public int nMem; /* Number of memory locations currently allocated */
|
||||
public int nOp; /* Number of instructions in the program */
|
||||
public int nOpAlloc; /* Number of slots allocated for aOp[] */
|
||||
public int nLabel; /* Number of labels used */
|
||||
public int nLabelAlloc; /* Number of slots allocated in aLabel[] */
|
||||
public int[] aLabel; /* Space to hold the labels */
|
||||
public u16 nResColumn; /* Number of columns in one row of the result set */
|
||||
public u16 nCursor; /* Number of slots in apCsr[] */
|
||||
public u32 magic; /* Magic number for sanity checking */
|
||||
public string zErrMsg; /* Error message written here */
|
||||
public Vdbe pPrev; /* Linked list of VDBEs with the same Vdbe.db */
|
||||
public Vdbe pNext; /* Linked list of VDBEs with the same Vdbe.db */
|
||||
public VdbeCursor[] apCsr; /* One element of this array for each open cursor */
|
||||
public Mem[] aVar; /* Values for the OP_Variable opcode. */
|
||||
public string[] azVar; /* Name of variables */
|
||||
public ynVar nVar; /* Number of entries in aVar[] */
|
||||
public ynVar nzVar; /* Number of entries in azVar[] */
|
||||
public u32 cacheCtr; /* VdbeCursor row cache generation counter */
|
||||
public int pc; /* The program counter */
|
||||
public int rc; /* Value to return */
|
||||
public u8 errorAction; /* Recovery action to do in case of an error */
|
||||
public int explain; /* True if EXPLAIN present on SQL command */
|
||||
public bool changeCntOn; /* True to update the change-counter */
|
||||
public bool expired; /* True if the VM needs to be recompiled */
|
||||
public u8 runOnlyOnce; /* Automatically expire on reset */
|
||||
public int minWriteFileFormat; /* Minimum file format for writable database files */
|
||||
public int inVtabMethod; /* See comments above */
|
||||
public bool usesStmtJournal; /* True if uses a statement journal */
|
||||
public bool readOnly; /* True for read-only statements */
|
||||
public int nChange; /* Number of db changes made since last reset */
|
||||
public bool isPrepareV2; /* True if prepared with prepare_v2() */
|
||||
public yDbMask btreeMask; /* Bitmask of db.aDb[] entries referenced */
|
||||
public yDbMask lockMask; /* Subset of btreeMask that requires a lock */
|
||||
|
||||
public int iStatement; /* Statement number (or 0 if has not opened stmt) */
|
||||
public int[] aCounter = new int[3]; /* Counters used by sqlite3_stmt_status() */
|
||||
#if !SQLITE_OMIT_TRACE
|
||||
public i64 startTime; /* Time when query started - used for profiling */
|
||||
#endif
|
||||
public i64 nFkConstraint; /* Number of imm. FK constraints this VM */
|
||||
public i64 nStmtDefCons; /* Number of def. constraints when stmt started */
|
||||
public string zSql = ""; /* Text of the SQL statement that generated this */
|
||||
public object pFree; /* Free this when deleting the vdbe */
|
||||
#if SQLITE_DEBUG
|
||||
public FILE trace; /* Write an execution trace here, if not NULL */
|
||||
#endif
|
||||
public VdbeFrame pFrame; /* Parent frame */
|
||||
public VdbeFrame pDelFrame; /* List of frame objects to free on VM reset */
|
||||
public int nFrame; /* Number of frames in pFrame list */
|
||||
public u32 expmask; /* Binding to these vars invalidates VM */
|
||||
public SubProgram pProgram; /* Linked list of all sub-programs used by VM */
|
||||
|
||||
public Vdbe Copy()
|
||||
{
|
||||
Vdbe cp = (Vdbe)MemberwiseClone();
|
||||
return cp;
|
||||
}
|
||||
public void CopyTo( Vdbe ct )
|
||||
{
|
||||
ct.db = db;
|
||||
ct.pPrev = pPrev;
|
||||
ct.pNext = pNext;
|
||||
ct.nOp = nOp;
|
||||
ct.nOpAlloc = nOpAlloc;
|
||||
ct.aOp = aOp;
|
||||
ct.nLabel = nLabel;
|
||||
ct.nLabelAlloc = nLabelAlloc;
|
||||
ct.aLabel = aLabel;
|
||||
ct.apArg = apArg;
|
||||
ct.aColName = aColName;
|
||||
ct.nCursor = nCursor;
|
||||
ct.apCsr = apCsr;
|
||||
ct.aVar = aVar;
|
||||
ct.azVar = azVar;
|
||||
ct.nVar = nVar;
|
||||
ct.nzVar = nzVar;
|
||||
ct.magic = magic;
|
||||
ct.nMem = nMem;
|
||||
ct.aMem = aMem;
|
||||
ct.cacheCtr = cacheCtr;
|
||||
ct.pc = pc;
|
||||
ct.rc = rc;
|
||||
ct.errorAction = errorAction;
|
||||
ct.nResColumn = nResColumn;
|
||||
ct.zErrMsg = zErrMsg;
|
||||
ct.pResultSet = pResultSet;
|
||||
ct.explain = explain;
|
||||
ct.changeCntOn = changeCntOn;
|
||||
ct.expired = expired;
|
||||
ct.minWriteFileFormat = minWriteFileFormat;
|
||||
ct.inVtabMethod = inVtabMethod;
|
||||
ct.usesStmtJournal = usesStmtJournal;
|
||||
ct.readOnly = readOnly;
|
||||
ct.nChange = nChange;
|
||||
ct.isPrepareV2 = isPrepareV2;
|
||||
#if !SQLITE_OMIT_TRACE
|
||||
ct.startTime = startTime;
|
||||
#endif
|
||||
ct.btreeMask = btreeMask;
|
||||
ct.lockMask = lockMask;
|
||||
aCounter.CopyTo( ct.aCounter, 0 );
|
||||
ct.zSql = zSql;
|
||||
ct.pFree = pFree;
|
||||
#if SQLITE_DEBUG
|
||||
ct.trace = trace;
|
||||
#endif
|
||||
ct.nFkConstraint = nFkConstraint;
|
||||
ct.nStmtDefCons = nStmtDefCons;
|
||||
ct.iStatement = iStatement;
|
||||
ct.pFrame = pFrame;
|
||||
ct.nFrame = nFrame;
|
||||
ct.expmask = expmask;
|
||||
ct.pProgram = pProgram;
|
||||
#if SQLITE_SSE
|
||||
ct.fetchId=fetchId;
|
||||
ct.lru=lru;
|
||||
#endif
|
||||
#if SQLITE_ENABLE_MEMORY_MANAGEMENT
|
||||
ct.pLruPrev=pLruPrev;
|
||||
ct.pLruNext=pLruNext;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
** The following are allowed values for Vdbe.magic
|
||||
*/
|
||||
//#define VDBE_MAGIC_INIT 0x26bceaa5 /* Building a VDBE program */
|
||||
//#define VDBE_MAGIC_RUN 0xbdf20da3 /* VDBE is ready to execute */
|
||||
//#define VDBE_MAGIC_HALT 0x519c2973 /* VDBE has completed execution */
|
||||
//#define VDBE_MAGIC_DEAD 0xb606c3c8 /* The VDBE has been deallocated */
|
||||
const u32 VDBE_MAGIC_INIT = 0x26bceaa5; /* Building a VDBE program */
|
||||
const u32 VDBE_MAGIC_RUN = 0xbdf20da3; /* VDBE is ready to execute */
|
||||
const u32 VDBE_MAGIC_HALT = 0x519c2973; /* VDBE has completed execution */
|
||||
const u32 VDBE_MAGIC_DEAD = 0xb606c3c8; /* The VDBE has been deallocated */
|
||||
/*
|
||||
** Function prototypes
|
||||
*/
|
||||
//void sqlite3VdbeFreeCursor(Vdbe *, VdbeCursor);
|
||||
//void sqliteVdbePopStack(Vdbe*,int);
|
||||
//int sqlite3VdbeCursorMoveto(VdbeCursor);
|
||||
//#if (SQLITE_DEBUG) || defined(VDBE_PROFILE)
|
||||
//void sqlite3VdbePrintOp(FILE*, int, Op);
|
||||
//#endif
|
||||
//u32 sqlite3VdbeSerialTypeLen(u32);
|
||||
//u32 sqlite3VdbeSerialType(Mem*, int);
|
||||
//u32sqlite3VdbeSerialPut(unsigned char*, int, Mem*, int);
|
||||
//u32 sqlite3VdbeSerialGet(const unsigned char*, u32, Mem);
|
||||
//void sqlite3VdbeDeleteAuxData(VdbeFunc*, int);
|
||||
|
||||
//int sqlite2BtreeKeyCompare(BtCursor *, const void *, int, int, int );
|
||||
//int sqlite3VdbeIdxKeyCompare(VdbeCursor*,UnpackedRecord*,int);
|
||||
//int sqlite3VdbeIdxRowid(sqlite3 *, i64 );
|
||||
//int sqlite3MemCompare(const Mem*, const Mem*, const CollSeq);
|
||||
//int sqlite3VdbeExec(Vdbe);
|
||||
//int sqlite3VdbeList(Vdbe);
|
||||
//int sqlite3VdbeHalt(Vdbe);
|
||||
//int sqlite3VdbeChangeEncoding(Mem *, int);
|
||||
//int sqlite3VdbeMemTooBig(Mem);
|
||||
//int sqlite3VdbeMemCopy(Mem*, const Mem);
|
||||
//void sqlite3VdbeMemShallowCopy(Mem*, const Mem*, int);
|
||||
//void sqlite3VdbeMemMove(Mem*, Mem);
|
||||
//int sqlite3VdbeMemNulTerminate(Mem);
|
||||
//int sqlite3VdbeMemSetStr(Mem*, const char*, int, u8, void()(void));
|
||||
//void sqlite3VdbeMemSetInt64(Mem*, i64);
|
||||
#if SQLITE_OMIT_FLOATING_POINT
|
||||
//# define sqlite3VdbeMemSetDouble sqlite3VdbeMemSetInt64
|
||||
#else
|
||||
//void sqlite3VdbeMemSetDouble(Mem*, double);
|
||||
#endif
|
||||
//void sqlite3VdbeMemSetNull(Mem);
|
||||
//void sqlite3VdbeMemSetZeroBlob(Mem*,int);
|
||||
//void sqlite3VdbeMemSetRowSet(Mem);
|
||||
//int sqlite3VdbeMemMakeWriteable(Mem);
|
||||
//int sqlite3VdbeMemStringify(Mem*, int);
|
||||
//i64 sqlite3VdbeIntValue(Mem);
|
||||
//int sqlite3VdbeMemIntegerify(Mem);
|
||||
//double sqlite3VdbeRealValue(Mem);
|
||||
//void sqlite3VdbeIntegerAffinity(Mem);
|
||||
//int sqlite3VdbeMemRealify(Mem);
|
||||
//int sqlite3VdbeMemNumerify(Mem);
|
||||
//int sqlite3VdbeMemFromBtree(BtCursor*,int,int,int,Mem);
|
||||
//void sqlite3VdbeMemRelease(Mem p);
|
||||
//void sqlite3VdbeMemReleaseExternal(Mem p);
|
||||
//int sqlite3VdbeMemFinalize(Mem*, FuncDef);
|
||||
//string sqlite3OpcodeName(int);
|
||||
//int sqlite3VdbeMemGrow(Mem pMem, int n, int preserve);
|
||||
//int sqlite3VdbeCloseStatement(Vdbe *, int);
|
||||
//void sqlite3VdbeFrameDelete(VdbeFrame);
|
||||
//int sqlite3VdbeFrameRestore(VdbeFrame );
|
||||
//void sqlite3VdbeMemStoreType(Mem *pMem);
|
||||
|
||||
#if !(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE//>0
|
||||
//void sqlite3VdbeEnter(Vdbe);
|
||||
//void sqlite3VdbeLeave(Vdbe);
|
||||
#else
|
||||
//# define sqlite3VdbeEnter(X)
|
||||
static void sqlite3VdbeEnter( Vdbe p )
|
||||
{
|
||||
}
|
||||
//# define sqlite3VdbeLeave(X)
|
||||
static void sqlite3VdbeLeave( Vdbe p )
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SQLITE_DEBUG
|
||||
//void sqlite3VdbeMemPrepareToChange(Vdbe*,Mem);
|
||||
#endif
|
||||
|
||||
#if !SQLITE_OMIT_FOREIGN_KEY
|
||||
//int sqlite3VdbeCheckFk(Vdbe *, int);
|
||||
#else
|
||||
//# define sqlite3VdbeCheckFk(p,i) 0
|
||||
static int sqlite3VdbeCheckFk( Vdbe p, int i ) { return 0; }
|
||||
#endif
|
||||
|
||||
//int sqlite3VdbeMemTranslate(Mem*, u8);
|
||||
//#if SQLITE_DEBUG
|
||||
// void sqlite3VdbePrintSql(Vdbe);
|
||||
// void sqlite3VdbeMemPrettyPrint(Mem pMem, string zBuf);
|
||||
//#endif
|
||||
//int sqlite3VdbeMemHandleBom(Mem pMem);
|
||||
|
||||
#if NOT_SQLITE_OMIT_INCRBLOB //#if !SQLITE_OMIT_INCRBLOB
|
||||
// int sqlite3VdbeMemExpandBlob(Mem );
|
||||
#else
|
||||
// #define sqlite3VdbeMemExpandBlob(x) SQLITE_OK
|
||||
static int sqlite3VdbeMemExpandBlob( Mem x )
|
||||
{
|
||||
return SQLITE_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
//#endif //* !_VDBEINT_H_) */
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,312 @@
|
||||
using i64 = System.Int64;
|
||||
using u8 = System.Byte;
|
||||
using u64 = System.UInt64;
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/*
|
||||
** 2001 September 15
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** Header file for the Virtual DataBase Engine (VDBE)
|
||||
**
|
||||
** This header defines the interface to the virtual database engine
|
||||
** or VDBE. The VDBE implements an abstract machine that runs a
|
||||
** simple program to access and modify the underlying database.
|
||||
*************************************************************************
|
||||
** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart
|
||||
** C#-SQLite is an independent reimplementation of the SQLite software library
|
||||
**
|
||||
** SQLITE_SOURCE_ID: 2011-06-23 19:49:22 4374b7e83ea0a3fbc3691f9c0c936272862f32f2
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
//#if !_SQLITE_VDBE_H_
|
||||
//#define _SQLITE_VDBE_H_
|
||||
//#include <stdio.h>
|
||||
|
||||
/*
|
||||
** A single VDBE is an opaque structure named "Vdbe". Only routines
|
||||
** in the source file sqliteVdbe.c are allowed to see the insides
|
||||
** of this structure.
|
||||
*/
|
||||
//typedef struct Vdbe Vdbe;
|
||||
|
||||
/*
|
||||
** The names of the following types declared in vdbeInt.h are required
|
||||
** for the VdbeOp definition.
|
||||
*/
|
||||
//typedef struct VdbeFunc VdbeFunc;
|
||||
//typedef struct Mem Mem;
|
||||
//typedef struct SubProgram SubProgram;
|
||||
|
||||
/*
|
||||
** A single instruction of the virtual machine has an opcode
|
||||
** and as many as three operands. The instruction is recorded
|
||||
** as an instance of the following structure:
|
||||
*/
|
||||
public class union_p4
|
||||
{ /* fourth parameter */
|
||||
public int i; /* Integer value if p4type==P4_INT32 */
|
||||
public object p; /* Generic pointer */
|
||||
//public string z; /* Pointer to data for string (char array) types */
|
||||
public string z; // In C# string is unicode, so use byte[] instead
|
||||
public i64 pI64; /* Used when p4type is P4_INT64 */
|
||||
public double pReal; /* Used when p4type is P4_REAL */
|
||||
public FuncDef pFunc; /* Used when p4type is P4_FUNCDEF */
|
||||
public VdbeFunc pVdbeFunc; /* Used when p4type is P4_VDBEFUNC */
|
||||
public CollSeq pColl; /* Used when p4type is P4_COLLSEQ */
|
||||
public Mem pMem; /* Used when p4type is P4_MEM */
|
||||
public VTable pVtab; /* Used when p4type is P4_VTAB */
|
||||
public KeyInfo pKeyInfo; /* Used when p4type is P4_KEYINFO */
|
||||
public int[] ai; /* Used when p4type is P4_INTARRAY */
|
||||
public SubProgram pProgram; /* Used when p4type is P4_SUBPROGRAM */
|
||||
public dxDel pFuncDel; /* Used when p4type is P4_FUNCDEL */
|
||||
} ;
|
||||
public class VdbeOp
|
||||
{
|
||||
public u8 opcode; /* What operation to perform */
|
||||
public int p4type; /* One of the P4_xxx constants for p4 */
|
||||
public u8 opflags; /* Mask of the OPFLG_* flags in opcodes.h */
|
||||
public u8 p5; /* Fifth parameter is an unsigned character */
|
||||
#if DEBUG_CLASS_VDBEOP || DEBUG_CLASS_ALL
|
||||
public int _p1; /* First operand */
|
||||
public int p1
|
||||
{
|
||||
get { return _p1; }
|
||||
set { _p1 = value; }
|
||||
}
|
||||
|
||||
public int _p2; /* Second parameter (often the jump destination) */
|
||||
public int p2
|
||||
{
|
||||
get { return _p2; }
|
||||
set { _p2 = value; }
|
||||
}
|
||||
|
||||
public int _p3; /* The third parameter */
|
||||
public int p3
|
||||
{
|
||||
get { return _p3; }
|
||||
set { _p3 = value; }
|
||||
}
|
||||
#else
|
||||
public int p1; /* First operand */
|
||||
public int p2; /* Second parameter (often the jump destination) */
|
||||
public int p3; /* The third parameter */
|
||||
#endif
|
||||
public union_p4 p4 = new union_p4();
|
||||
#if SQLITE_DEBUG || DEBUG
|
||||
public string zComment; /* Comment to improve readability */
|
||||
#endif
|
||||
#if VDBE_PROFILE
|
||||
public int cnt; /* Number of times this instruction was executed */
|
||||
public u64 cycles; /* Total time spend executing this instruction */
|
||||
#endif
|
||||
};
|
||||
//typedef struct VdbeOp VdbeOp;
|
||||
|
||||
/*
|
||||
** A sub-routine used to implement a trigger program.
|
||||
*/
|
||||
public class SubProgram
|
||||
{
|
||||
public VdbeOp[] aOp; /* Array of opcodes for sub-program */
|
||||
public int nOp; /* Elements in aOp[] */
|
||||
public int nMem; /* Number of memory cells required */
|
||||
public int nCsr; /* Number of cursors required */
|
||||
public int token; /* id that may be used to recursive triggers */
|
||||
public SubProgram pNext; /* Next sub-program already visited */
|
||||
};
|
||||
|
||||
/*
|
||||
** A smaller version of VdbeOp used for the VdbeAddOpList() function because
|
||||
** it takes up less space.
|
||||
*/
|
||||
public struct VdbeOpList
|
||||
{
|
||||
public u8 opcode; /* What operation to perform */
|
||||
public int p1; /* First operand */
|
||||
public int p2; /* Second parameter (often the jump destination) */
|
||||
public int p3; /* Third parameter */
|
||||
public VdbeOpList( u8 opcode, int p1, int p2, int p3 )
|
||||
{
|
||||
this.opcode = opcode;
|
||||
this.p1 = p1;
|
||||
this.p2 = p2;
|
||||
this.p3 = p3;
|
||||
}
|
||||
|
||||
};
|
||||
//typedef struct VdbeOpList VdbeOpList;
|
||||
|
||||
/*
|
||||
** Allowed values of VdbeOp.p4type
|
||||
*/
|
||||
const int P4_NOTUSED = 0; /* The P4 parameter is not used */
|
||||
const int P4_DYNAMIC = ( -1 ); /* Pointer to a string obtained from sqliteMalloc=(); */
|
||||
const int P4_STATIC = ( -2 ); /* Pointer to a static string */
|
||||
const int P4_COLLSEQ = ( -4 ); /* P4 is a pointer to a CollSeq structure */
|
||||
const int P4_FUNCDEF = ( -5 ); /* P4 is a pointer to a FuncDef structure */
|
||||
const int P4_KEYINFO = ( -6 ); /* P4 is a pointer to a KeyInfo structure */
|
||||
const int P4_VDBEFUNC = ( -7 ); /* P4 is a pointer to a VdbeFunc structure */
|
||||
const int P4_MEM = ( -8 ); /* P4 is a pointer to a Mem* structure */
|
||||
const int P4_TRANSIENT = 0; /* P4 is a pointer to a transient string */
|
||||
const int P4_VTAB = ( -10 ); /* P4 is a pointer to an sqlite3_vtab structure */
|
||||
const int P4_MPRINTF = ( -11 ); /* P4 is a string obtained from sqlite3_mprintf=(); */
|
||||
const int P4_REAL = ( -12 ); /* P4 is a 64-bit floating point value */
|
||||
const int P4_INT64 = ( -13 ); /* P4 is a 64-bit signed integer */
|
||||
const int P4_INT32 = ( -14 ); /* P4 is a 32-bit signed integer */
|
||||
const int P4_INTARRAY = ( -15 ); /* #define P4_INTARRAY (-15) /* P4 is a vector of 32-bit integers */
|
||||
const int P4_SUBPROGRAM = ( -18 );/* #define P4_SUBPROGRAM (-18) /* P4 is a pointer to a SubProgram structure */
|
||||
|
||||
/* When adding a P4 argument using P4_KEYINFO, a copy of the KeyInfo structure
|
||||
** is made. That copy is freed when the Vdbe is finalized. But if the
|
||||
** argument is P4_KEYINFO_HANDOFF, the passed in pointer is used. It still
|
||||
** gets freed when the Vdbe is finalized so it still should be obtained
|
||||
** from a single sqliteMalloc(). But no copy is made and the calling
|
||||
** function should *not* try to free the KeyInfo.
|
||||
*/
|
||||
const int P4_KEYINFO_HANDOFF = ( -16 ); // #define P4_KEYINFO_HANDOFF (-16)
|
||||
const int P4_KEYINFO_STATIC = ( -17 ); // #define P4_KEYINFO_STATIC (-17)
|
||||
|
||||
/*
|
||||
** The Vdbe.aColName array contains 5n Mem structures, where n is the
|
||||
** number of columns of data returned by the statement.
|
||||
*/
|
||||
//#define COLNAME_NAME 0
|
||||
//#define COLNAME_DECLTYPE 1
|
||||
//#define COLNAME_DATABASE 2
|
||||
//#define COLNAME_TABLE 3
|
||||
//#define COLNAME_COLUMN 4
|
||||
//#if SQLITE_ENABLE_COLUMN_METADATA
|
||||
//# define COLNAME_N 5 /* Number of COLNAME_xxx symbols */
|
||||
//#else
|
||||
//# ifdef SQLITE_OMIT_DECLTYPE
|
||||
//# define COLNAME_N 1 /* Store only the name */
|
||||
//# else
|
||||
//# define COLNAME_N 2 /* Store the name and decltype */
|
||||
//# endif
|
||||
//#endif
|
||||
const int COLNAME_NAME = 0;
|
||||
const int COLNAME_DECLTYPE = 1;
|
||||
const int COLNAME_DATABASE = 2;
|
||||
const int COLNAME_TABLE = 3;
|
||||
const int COLNAME_COLUMN = 4;
|
||||
#if SQLITE_ENABLE_COLUMN_METADATA
|
||||
const int COLNAME_N = 5; /* Number of COLNAME_xxx symbols */
|
||||
#else
|
||||
# if SQLITE_OMIT_DECLTYPE
|
||||
const int COLNAME_N = 1; /* Number of COLNAME_xxx symbols */
|
||||
# else
|
||||
const int COLNAME_N = 2;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The following macro converts a relative address in the p2 field
|
||||
** of a VdbeOp structure into a negative number so that
|
||||
** sqlite3VdbeAddOpList() knows that the address is relative. Calling
|
||||
** the macro again restores the address.
|
||||
*/
|
||||
//#define ADDR(X) (-1-(X))
|
||||
static int ADDR( int x )
|
||||
{
|
||||
return -1 - x;
|
||||
}
|
||||
/*
|
||||
** The makefile scans the vdbe.c source file and creates the "opcodes.h"
|
||||
** header file that defines a number for each opcode used by the VDBE.
|
||||
*/
|
||||
//#include "opcodes.h"
|
||||
|
||||
/*
|
||||
** Prototypes for the VDBE interface. See comments on the implementation
|
||||
** for a description of what each of these routines does.
|
||||
*/
|
||||
/*
|
||||
** Prototypes for the VDBE interface. See comments on the implementation
|
||||
** for a description of what each of these routines does.
|
||||
*/
|
||||
//Vdbe *sqlite3VdbeCreate(sqlite3);
|
||||
//int sqlite3VdbeAddOp0(Vdbe*,int);
|
||||
//int sqlite3VdbeAddOp1(Vdbe*,int,int);
|
||||
//int sqlite3VdbeAddOp2(Vdbe*,int,int,int);
|
||||
//int sqlite3VdbeAddOp3(Vdbe*,int,int,int,int);
|
||||
//int sqlite3VdbeAddOp4(Vdbe*,int,int,int,int,string zP4,int);
|
||||
//int sqlite3VdbeAddOp4Int(Vdbe*,int,int,int,int,int);
|
||||
//int sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp);
|
||||
//void sqlite3VdbeAddParseSchemaOp(Vdbe*,int,char);
|
||||
//void sqlite3VdbeChangeP1(Vdbe*, int addr, int P1);
|
||||
//void sqlite3VdbeChangeP2(Vdbe*, int addr, int P2);
|
||||
//void sqlite3VdbeChangeP3(Vdbe*, int addr, int P3);
|
||||
//void sqlite3VdbeChangeP5(Vdbe*, u8 P5);
|
||||
//void sqlite3VdbeJumpHere(Vdbe*, int addr);
|
||||
//void sqlite3VdbeChangeToNoop(Vdbe*, int addr, int N);
|
||||
//void sqlite3VdbeChangeP4(Vdbe*, int addr, string zP4, int N);
|
||||
//void sqlite3VdbeUsesBtree(Vdbe*, int);
|
||||
//VdbeOp *sqlite3VdbeGetOp(Vdbe*, int);
|
||||
//int sqlite3VdbeMakeLabel(Vdbe);
|
||||
//void sqlite3VdbeRunOnlyOnce(Vdbe);
|
||||
//void sqlite3VdbeDelete(Vdbe);
|
||||
//void sqlite3VdbeDeleteObject(sqlite3*,Vdbe);
|
||||
//void sqlite3VdbeMakeReady(Vdbe*,Parse);
|
||||
//int sqlite3VdbeFinalize(Vdbe);
|
||||
//void sqlite3VdbeResolveLabel(Vdbe*, int);
|
||||
//int sqlite3VdbeCurrentAddr(Vdbe);
|
||||
//#if SQLITE_DEBUG
|
||||
// int sqlite3VdbeAssertMayAbort(Vdbe *, int);
|
||||
// void sqlite3VdbeTrace(Vdbe*,FILE);
|
||||
//#endif
|
||||
//void sqlite3VdbeResetStepResult(Vdbe);
|
||||
//void sqlite3VdbeRewind(Vdbe);
|
||||
//int sqlite3VdbeReset(Vdbe);
|
||||
//void sqlite3VdbeSetNumCols(Vdbe*,int);
|
||||
//int sqlite3VdbeSetColName(Vdbe*, int, int, string , void()(void));
|
||||
//void sqlite3VdbeCountChanges(Vdbe);
|
||||
//sqlite3 *sqlite3VdbeDb(Vdbe);
|
||||
//void sqlite3VdbeSetSql(Vdbe*, string z, int n, int);
|
||||
//void sqlite3VdbeSwap(Vdbe*,Vdbe);
|
||||
//VdbeOp *sqlite3VdbeTakeOpArray(Vdbe*, int*, int);
|
||||
//sqlite3_value *sqlite3VdbeGetValue(Vdbe*, int, u8);
|
||||
//void sqlite3VdbeSetVarmask(Vdbe*, int);
|
||||
//#if !SQLITE_OMIT_TRACE
|
||||
// char *sqlite3VdbeExpandSql(Vdbe*, const char);
|
||||
//#endif
|
||||
|
||||
//UnpackedRecord *sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,char*,int);
|
||||
//void sqlite3VdbeDeleteUnpackedRecord(UnpackedRecord);
|
||||
//int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord);
|
||||
|
||||
//#if !SQLITE_OMIT_TRIGGER
|
||||
//void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram );
|
||||
//#endif
|
||||
//#if !NDEBUG
|
||||
#if XXNDEBUG
|
||||
//void sqlite3VdbeComment(Vdbe*, const char*, ...);
|
||||
static void VdbeComment( Vdbe v, string zFormat, params object[] ap )
|
||||
{
|
||||
sqlite3VdbeComment( v, zFormat, ap );
|
||||
}//# define VdbeComment(X) sqlite3VdbeComment X
|
||||
//void sqlite3VdbeNoopComment(Vdbe*, const char*, ...);
|
||||
static void VdbeNoopComment( Vdbe v, string zFormat, params object[] ap )
|
||||
{
|
||||
sqlite3VdbeNoopComment( v, zFormat, ap );
|
||||
}//# define VdbeNoopComment(X) sqlite3VdbeNoopComment X
|
||||
#else
|
||||
//# define VdbeComment(X)
|
||||
static void VdbeComment( Vdbe v, string zFormat, params object[] ap ) { }
|
||||
//# define VdbeNoopComment(X)
|
||||
static void VdbeNoopComment( Vdbe v, string zFormat, params object[] ap ) { }
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,659 @@
|
||||
/*
|
||||
*************************************************************************
|
||||
** Custom classes used by C#
|
||||
*************************************************************************
|
||||
*/
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
//#if !(SQLITE_SILVERLIGHT || WINDOWS_MOBILE)
|
||||
//using System.Management;
|
||||
//#endif
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
|
||||
using i64 = System.Int64;
|
||||
|
||||
using u32 = System.UInt32;
|
||||
using time_t = System.Int64;
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
using sqlite3_value = Sqlite3.Mem;
|
||||
|
||||
public partial class Sqlite3
|
||||
{
|
||||
|
||||
static int atoi( byte[] inStr )
|
||||
{
|
||||
return atoi( Encoding.UTF8.GetString( inStr, 0, inStr.Length ) );
|
||||
}
|
||||
|
||||
static int atoi( string inStr )
|
||||
{
|
||||
int i;
|
||||
for ( i = 0; i < inStr.Length; i++ )
|
||||
{
|
||||
//if ( !sqlite3Isdigit( inStr[i] ) && inStr[i] != '-' )
|
||||
if ( inStr[i] < '0'&& inStr[i]> '9' && inStr[i] != '-' )
|
||||
break;
|
||||
}
|
||||
int result = 0;
|
||||
#if WINDOWS_MOBILE
|
||||
try { result = Int32.Parse(inStr.Substring(0, i)); }
|
||||
catch { }
|
||||
return result;
|
||||
#else
|
||||
return ( Int32.TryParse( inStr.Substring( 0, i ), out result ) ? result : 0 );
|
||||
#endif
|
||||
}
|
||||
|
||||
static void fprintf( TextWriter tw, string zFormat, params object[] ap )
|
||||
{
|
||||
tw.Write( sqlite3_mprintf( zFormat, ap ) );
|
||||
}
|
||||
static void printf( string zFormat, params object[] ap )
|
||||
{
|
||||
Console.Out.Write( sqlite3_mprintf( zFormat, ap ) );
|
||||
}
|
||||
|
||||
|
||||
//Byte Buffer Testing
|
||||
static int memcmp( byte[] bA, byte[] bB, int Limit )
|
||||
{
|
||||
if ( bA.Length < Limit )
|
||||
return ( bA.Length < bB.Length ) ? -1 : +1;
|
||||
if ( bB.Length < Limit )
|
||||
return +1;
|
||||
for ( int i = 0; i < Limit; i++ )
|
||||
{
|
||||
if ( bA[i] != bB[i] )
|
||||
return ( bA[i] < bB[i] ) ? -1 : 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//Byte Buffer & String Testing
|
||||
static int memcmp( string A, byte[] bB, int Limit )
|
||||
{
|
||||
if ( A.Length < Limit )
|
||||
return ( A.Length < bB.Length ) ? -1 : +1;
|
||||
if ( bB.Length < Limit )
|
||||
return +1;
|
||||
char[] cA = A.ToCharArray();
|
||||
for ( int i = 0; i < Limit; i++ )
|
||||
{
|
||||
if ( cA[i] != bB[i] )
|
||||
return ( cA[i] < bB[i] ) ? -1 : 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//byte with Offset & String Testing
|
||||
static int memcmp( byte[] a, int Offset, byte[] b, int Limit )
|
||||
{
|
||||
if ( a.Length < Offset + Limit )
|
||||
return ( a.Length - Offset < b.Length ) ? -1 : +1;
|
||||
if ( b.Length < Limit )
|
||||
return +1;
|
||||
for ( int i = 0; i < Limit; i++ )
|
||||
{
|
||||
if ( a[i + Offset] != b[i] )
|
||||
return ( a[i + Offset] < b[i] ) ? -1 : 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//byte with Offset & String Testing
|
||||
static int memcmp( byte[] a, int Aoffset, byte[] b, int Boffset, int Limit )
|
||||
{
|
||||
if ( a.Length < Aoffset + Limit )
|
||||
return ( a.Length - Aoffset < b.Length - Boffset ) ? -1 : +1;
|
||||
if ( b.Length < Boffset + Limit )
|
||||
return +1;
|
||||
for ( int i = 0; i < Limit; i++ )
|
||||
{
|
||||
if ( a[i + Aoffset] != b[i + Boffset] )
|
||||
return ( a[i + Aoffset] < b[i + Boffset] ) ? -1 : 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int memcmp( byte[] a, int Offset, string b, int Limit )
|
||||
{
|
||||
if ( a.Length < Offset + Limit )
|
||||
return ( a.Length - Offset < b.Length ) ? -1 : +1;
|
||||
if ( b.Length < Limit )
|
||||
return +1;
|
||||
for ( int i = 0; i < Limit; i++ )
|
||||
{
|
||||
if ( a[i + Offset] != b[i] )
|
||||
return ( a[i + Offset] < b[i] ) ? -1 : 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
//String Testing
|
||||
static int memcmp( string A, string B, int Limit )
|
||||
{
|
||||
if ( A.Length < Limit )
|
||||
return ( A.Length < B.Length ) ? -1 : +1;
|
||||
if ( B.Length < Limit )
|
||||
return +1;
|
||||
int rc;
|
||||
if ( ( rc = String.Compare( A, 0, B, 0, Limit, StringComparison.Ordinal ) ) == 0 )
|
||||
return 0;
|
||||
return rc < 0 ? -1 : +1;
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------
|
||||
// ** Builtin Functions
|
||||
// ----------------------------
|
||||
|
||||
|
||||
static Regex oRegex = null;
|
||||
/*
|
||||
** The regexp() function. two arguments are both strings
|
||||
** Collating sequences are not used.
|
||||
*/
|
||||
static void regexpFunc(
|
||||
sqlite3_context context,
|
||||
int argc,
|
||||
sqlite3_value[] argv
|
||||
)
|
||||
{
|
||||
string zTest; /* The input string A */
|
||||
string zRegex; /* The regex string B */
|
||||
|
||||
Debug.Assert( argc == 2 );
|
||||
UNUSED_PARAMETER( argc );
|
||||
zRegex = sqlite3_value_text( argv[0] );
|
||||
zTest = sqlite3_value_text( argv[1] );
|
||||
|
||||
if ( zTest == null || String.IsNullOrEmpty( zRegex ) )
|
||||
{
|
||||
sqlite3_result_int( context, 0 );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( oRegex == null || oRegex.ToString() == zRegex )
|
||||
{
|
||||
oRegex = new Regex( zRegex, RegexOptions.IgnoreCase );
|
||||
}
|
||||
sqlite3_result_int( context, oRegex.IsMatch( zTest ) ? 1 : 0 );
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------
|
||||
// ** Convertion routines
|
||||
// ----------------------------
|
||||
static Object lock_va_list = new Object();
|
||||
|
||||
static string vaFORMAT;
|
||||
static int vaNEXT;
|
||||
|
||||
static void va_start( object[] ap, string zFormat )
|
||||
{
|
||||
vaFORMAT = zFormat;
|
||||
vaNEXT = 0;
|
||||
}
|
||||
|
||||
static Boolean va_arg( object[] ap, Boolean sysType )
|
||||
{
|
||||
return Convert.ToBoolean( ap[vaNEXT++] );
|
||||
}
|
||||
|
||||
static Byte[] va_arg( object[] ap, Byte[] sysType )
|
||||
{
|
||||
return (Byte[])ap[vaNEXT++];
|
||||
}
|
||||
|
||||
static Byte[][] va_arg( object[] ap, Byte[][] sysType )
|
||||
{
|
||||
if ( ap[vaNEXT] == null )
|
||||
{
|
||||
{
|
||||
vaNEXT++;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return (Byte[][])ap[vaNEXT++];
|
||||
}
|
||||
}
|
||||
|
||||
static Char va_arg( object[] ap, Char sysType )
|
||||
{
|
||||
if ( ap[vaNEXT] is Int32 && (int)ap[vaNEXT] == 0 )
|
||||
{
|
||||
vaNEXT++;
|
||||
return (char)'0';
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( ap[vaNEXT] is Int64 )
|
||||
if ( (i64)ap[vaNEXT] == 0 )
|
||||
{
|
||||
vaNEXT++;
|
||||
return (char)'0';
|
||||
}
|
||||
else
|
||||
return (char)( (i64)ap[vaNEXT++] );
|
||||
else
|
||||
return (char)ap[vaNEXT++];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static Double va_arg( object[] ap, Double sysType )
|
||||
{
|
||||
return Convert.ToDouble( ap[vaNEXT++] );
|
||||
}
|
||||
|
||||
static dxLog va_arg( object[] ap, dxLog sysType )
|
||||
{
|
||||
return (dxLog)ap[vaNEXT++];
|
||||
}
|
||||
|
||||
static Int64 va_arg( object[] ap, Int64 sysType )
|
||||
{
|
||||
if ( ap[vaNEXT] is System.Int64)
|
||||
return Convert.ToInt64( ap[vaNEXT++] );
|
||||
else
|
||||
return (Int64)( ap[vaNEXT++].GetHashCode() );
|
||||
}
|
||||
|
||||
static Int32 va_arg( object[] ap, Int32 sysType )
|
||||
{
|
||||
if ( Convert.ToInt64( ap[vaNEXT] ) > 0 && ( Convert.ToUInt32( ap[vaNEXT] ) > Int32.MaxValue ) )
|
||||
return (Int32)( Convert.ToUInt32( ap[vaNEXT++] ) - System.UInt32.MaxValue - 1 );
|
||||
else
|
||||
return (Int32)Convert.ToInt32( ap[vaNEXT++] );
|
||||
}
|
||||
|
||||
static Int32[] va_arg( object[] ap, Int32[] sysType )
|
||||
{
|
||||
if ( ap[vaNEXT] == null )
|
||||
{
|
||||
{
|
||||
vaNEXT++;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return (Int32[])ap[vaNEXT++];
|
||||
}
|
||||
}
|
||||
|
||||
static MemPage va_arg( object[] ap, MemPage sysType )
|
||||
{
|
||||
return (MemPage)ap[vaNEXT++];
|
||||
}
|
||||
|
||||
static Object va_arg( object[] ap, Object sysType )
|
||||
{
|
||||
return (Object)ap[vaNEXT++];
|
||||
}
|
||||
|
||||
static sqlite3 va_arg( object[] ap, sqlite3 sysType )
|
||||
{
|
||||
return (sqlite3)ap[vaNEXT++];
|
||||
}
|
||||
|
||||
static sqlite3_mem_methods va_arg( object[] ap, sqlite3_mem_methods sysType )
|
||||
{
|
||||
return (sqlite3_mem_methods)ap[vaNEXT++];
|
||||
}
|
||||
|
||||
static sqlite3_mutex_methods va_arg( object[] ap, sqlite3_mutex_methods sysType )
|
||||
{
|
||||
return (sqlite3_mutex_methods)ap[vaNEXT++];
|
||||
}
|
||||
|
||||
static SrcList va_arg( object[] ap, SrcList sysType )
|
||||
{
|
||||
return (SrcList)ap[vaNEXT++];
|
||||
}
|
||||
|
||||
static String va_arg( object[] ap, String sysType )
|
||||
{
|
||||
if ( ap.Length < vaNEXT - 1 || ap[vaNEXT] == null )
|
||||
{
|
||||
vaNEXT++;
|
||||
return "NULL";
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( ap[vaNEXT] is Byte[] )
|
||||
if ( Encoding.UTF8.GetString( (byte[])ap[vaNEXT], 0, ( (byte[])ap[vaNEXT] ).Length ) == "\0" )
|
||||
{
|
||||
vaNEXT++;
|
||||
return "";
|
||||
}
|
||||
else
|
||||
return Encoding.UTF8.GetString( (byte[])ap[vaNEXT], 0, ( (byte[])ap[vaNEXT++] ).Length );
|
||||
else if ( ap[vaNEXT] is Int32 )
|
||||
{
|
||||
vaNEXT++;
|
||||
return null;
|
||||
}
|
||||
else if ( ap[vaNEXT] is StringBuilder )
|
||||
return (String)ap[vaNEXT++].ToString();
|
||||
else if ( ap[vaNEXT] is Char )
|
||||
return ( (Char)ap[vaNEXT++] ).ToString();
|
||||
else
|
||||
return (String)ap[vaNEXT++];
|
||||
}
|
||||
}
|
||||
|
||||
static Token va_arg( object[] ap, Token sysType )
|
||||
{
|
||||
return (Token)ap[vaNEXT++];
|
||||
}
|
||||
|
||||
static UInt32 va_arg( object[] ap, UInt32 sysType )
|
||||
{
|
||||
if ( ap[vaNEXT].GetType().IsClass )
|
||||
{
|
||||
return (UInt32)ap[vaNEXT++].GetHashCode();
|
||||
}
|
||||
else
|
||||
{
|
||||
return (UInt32)Convert.ToUInt32( ap[vaNEXT++] );
|
||||
}
|
||||
}
|
||||
|
||||
static UInt64 va_arg( object[] ap, UInt64 sysType )
|
||||
{
|
||||
if ( ap[vaNEXT].GetType().IsClass )
|
||||
{
|
||||
return (UInt64)ap[vaNEXT++].GetHashCode();
|
||||
}
|
||||
else
|
||||
{
|
||||
return (UInt64)Convert.ToUInt64( ap[vaNEXT++] );
|
||||
}
|
||||
}
|
||||
|
||||
static void_function va_arg( object[] ap, void_function sysType )
|
||||
{
|
||||
return (void_function)ap[vaNEXT++];
|
||||
}
|
||||
|
||||
|
||||
static void va_end( ref string[] ap )
|
||||
{
|
||||
ap = null;
|
||||
vaNEXT = -1;
|
||||
vaFORMAT = "";
|
||||
}
|
||||
static void va_end( ref object[] ap )
|
||||
{
|
||||
ap = null;
|
||||
vaNEXT = -1;
|
||||
vaFORMAT = "";
|
||||
}
|
||||
|
||||
|
||||
public static tm localtime( time_t baseTime )
|
||||
{
|
||||
System.DateTime RefTime = new System.DateTime( 1970, 1, 1, 0, 0, 0, 0 );
|
||||
RefTime = RefTime.AddSeconds( Convert.ToDouble( baseTime ) ).ToLocalTime();
|
||||
tm tm = new tm();
|
||||
tm.tm_sec = RefTime.Second;
|
||||
tm.tm_min = RefTime.Minute;
|
||||
tm.tm_hour = RefTime.Hour;
|
||||
tm.tm_mday = RefTime.Day;
|
||||
tm.tm_mon = RefTime.Month;
|
||||
tm.tm_year = RefTime.Year;
|
||||
tm.tm_wday = (int)RefTime.DayOfWeek;
|
||||
tm.tm_yday = RefTime.DayOfYear;
|
||||
tm.tm_isdst = RefTime.IsDaylightSavingTime() ? 1 : 0;
|
||||
return tm;
|
||||
}
|
||||
|
||||
public static long ToUnixtime( System.DateTime date )
|
||||
{
|
||||
System.DateTime unixStartTime = new System.DateTime( 1970, 1, 1, 0, 0, 0, 0 );
|
||||
System.TimeSpan timeSpan = date - unixStartTime;
|
||||
return Convert.ToInt64( timeSpan.TotalSeconds );
|
||||
}
|
||||
|
||||
public static System.DateTime ToCSharpTime( long unixTime )
|
||||
{
|
||||
System.DateTime unixStartTime = new System.DateTime( 1970, 1, 1, 0, 0, 0, 0 );
|
||||
return unixStartTime.AddSeconds( Convert.ToDouble( unixTime ) );
|
||||
}
|
||||
|
||||
public class tm
|
||||
{
|
||||
public int tm_sec; /* seconds after the minute - [0,59] */
|
||||
public int tm_min; /* minutes after the hour - [0,59] */
|
||||
public int tm_hour; /* hours since midnight - [0,23] */
|
||||
public int tm_mday; /* day of the month - [1,31] */
|
||||
public int tm_mon; /* months since January - [0,11] */
|
||||
public int tm_year; /* years since 1900 */
|
||||
public int tm_wday; /* days since Sunday - [0,6] */
|
||||
public int tm_yday; /* days since January 1 - [0,365] */
|
||||
public int tm_isdst; /* daylight savings time flag */
|
||||
};
|
||||
|
||||
public struct FILETIME
|
||||
{
|
||||
public u32 dwLowDateTime;
|
||||
public u32 dwHighDateTime;
|
||||
}
|
||||
|
||||
// Example (C#)
|
||||
public static int GetbytesPerSector( StringBuilder diskPath )
|
||||
{
|
||||
#if NO_SQLITE_SILVERLIGHT // #if !(SQLITE_SILVERLIGHT || WINDOWS_MOBILE)
|
||||
ManagementObjectSearcher mosLogicalDisks = new ManagementObjectSearcher( "select * from Win32_LogicalDisk where DeviceID = '" + diskPath.ToString().Remove( diskPath.Length - 1, 1 ) + "'" );
|
||||
try
|
||||
{
|
||||
foreach ( ManagementObject moLogDisk in mosLogicalDisks.Get() )
|
||||
{
|
||||
ManagementObjectSearcher mosDiskDrives = new ManagementObjectSearcher( "select * from Win32_DiskDrive where SystemName = '" + moLogDisk["SystemName"] + "'" );
|
||||
foreach ( ManagementObject moPDisk in mosDiskDrives.Get() )
|
||||
{
|
||||
return int.Parse( moPDisk["BytesPerSector"].ToString() );
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
return 4096;
|
||||
#else
|
||||
return 4096;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void SWAP<T>( ref T A, ref T B )
|
||||
{
|
||||
T t = A;
|
||||
A = B;
|
||||
B = t;
|
||||
}
|
||||
|
||||
static void x_CountStep(
|
||||
sqlite3_context context,
|
||||
int argc,
|
||||
sqlite3_value[] argv
|
||||
)
|
||||
{
|
||||
SumCtx p;
|
||||
|
||||
int type;
|
||||
Debug.Assert( argc <= 1 );
|
||||
Mem pMem = sqlite3_aggregate_context( context, 1 );//sizeof(*p));
|
||||
if ( pMem._SumCtx == null )
|
||||
pMem._SumCtx = new SumCtx();
|
||||
p = pMem._SumCtx;
|
||||
if ( p.Context == null )
|
||||
p.Context = pMem;
|
||||
if ( argc == 0 || SQLITE_NULL == sqlite3_value_type( argv[0] ) )
|
||||
{
|
||||
p.cnt++;
|
||||
p.iSum += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
type = sqlite3_value_numeric_type( argv[0] );
|
||||
if ( p != null && type != SQLITE_NULL )
|
||||
{
|
||||
p.cnt++;
|
||||
if ( type == SQLITE_INTEGER )
|
||||
{
|
||||
i64 v = sqlite3_value_int64( argv[0] );
|
||||
if ( v == 40 || v == 41 )
|
||||
{
|
||||
sqlite3_result_error( context, "value of " + v + " handed to x_count", -1 );
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
p.iSum += v;
|
||||
if ( !( p.approx | p.overflow != 0 ) )
|
||||
{
|
||||
i64 iNewSum = p.iSum + v;
|
||||
int s1 = (int)( p.iSum >> ( sizeof( i64 ) * 8 - 1 ) );
|
||||
int s2 = (int)( v >> ( sizeof( i64 ) * 8 - 1 ) );
|
||||
int s3 = (int)( iNewSum >> ( sizeof( i64 ) * 8 - 1 ) );
|
||||
p.overflow = ( ( s1 & s2 & ~s3 ) | ( ~s1 & ~s2 & s3 ) ) != 0 ? 1 : 0;
|
||||
p.iSum = iNewSum;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
p.rSum += sqlite3_value_double( argv[0] );
|
||||
p.approx = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
static void x_CountFinalize( sqlite3_context context )
|
||||
{
|
||||
SumCtx p;
|
||||
Mem pMem = sqlite3_aggregate_context( context, 0 );
|
||||
p = pMem._SumCtx;
|
||||
if ( p != null && p.cnt > 0 )
|
||||
{
|
||||
if ( p.overflow != 0 )
|
||||
{
|
||||
sqlite3_result_error( context, "integer overflow", -1 );
|
||||
}
|
||||
else if ( p.approx )
|
||||
{
|
||||
sqlite3_result_double( context, p.rSum );
|
||||
}
|
||||
else if ( p.iSum == 42 )
|
||||
{
|
||||
sqlite3_result_error( context, "x_count totals to 42", -1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
sqlite3_result_int64( context, p.iSum );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if SQLITE_MUTEX_W32
|
||||
//---------------------WIN32 Definitions
|
||||
static int GetCurrentThreadId()
|
||||
{
|
||||
return Thread.CurrentThread.ManagedThreadId;
|
||||
}
|
||||
static long InterlockedIncrement( long location )
|
||||
{
|
||||
Interlocked.Increment( ref location );
|
||||
return location;
|
||||
}
|
||||
static void EnterCriticalSection( Object mtx )
|
||||
{
|
||||
//long mid = mtx.GetHashCode();
|
||||
//int tid = Thread.CurrentThread.ManagedThreadId;
|
||||
//long ticks = cnt++;
|
||||
//Debug.WriteLine(String.Format( "{2}: +EnterCriticalSection; Mutex {0} Thread {1}", mtx.GetHashCode(), Thread.CurrentThread.ManagedThreadId, ticks) );
|
||||
Monitor.Enter( mtx );
|
||||
}
|
||||
static void InitializeCriticalSection( Object mtx )
|
||||
{
|
||||
//Debug.WriteLine(String.Format( "{2}: +InitializeCriticalSection; Mutex {0} Thread {1}", mtx.GetHashCode(), Thread.CurrentThread.ManagedThreadId, System.DateTime.Now.Ticks ));
|
||||
Monitor.Enter( mtx );
|
||||
}
|
||||
static void DeleteCriticalSection( Object mtx )
|
||||
{
|
||||
//Debug.WriteLine(String.Format( "{2}: +DeleteCriticalSection; Mutex {0} Thread {1}", mtx.GetHashCode(), Thread.CurrentThread.ManagedThreadId, System.DateTime.Now.Ticks) );
|
||||
Monitor.Exit( mtx );
|
||||
}
|
||||
static void LeaveCriticalSection( Object mtx )
|
||||
{
|
||||
//Debug.WriteLine(String.Format("{2}: +LeaveCriticalSection; Mutex {0} Thread {1}", mtx.GetHashCode(), Thread.CurrentThread.ManagedThreadId, System.DateTime.Now.Ticks ));
|
||||
Monitor.Exit( mtx );
|
||||
}
|
||||
#endif
|
||||
|
||||
// Miscellaneous Windows Constants
|
||||
//#define ERROR_FILE_NOT_FOUND 2L
|
||||
//#define ERROR_HANDLE_DISK_FULL 39L
|
||||
//#define ERROR_NOT_SUPPORTED 50L
|
||||
//#define ERROR_DISK_FULL 112L
|
||||
const long ERROR_FILE_NOT_FOUND = 2L;
|
||||
const long ERROR_HANDLE_DISK_FULL = 39L;
|
||||
const long ERROR_NOT_SUPPORTED = 50L;
|
||||
const long ERROR_DISK_FULL = 112L;
|
||||
|
||||
private class SQLite3UpperToLower
|
||||
{
|
||||
static int[] sqlite3UpperToLower = new int[] {
|
||||
#if !NO_SQLITE_ASCII
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
|
||||
18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
|
||||
36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
|
||||
54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99,100,101,102,103,
|
||||
104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,
|
||||
122, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103,104,105,106,107,
|
||||
108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,
|
||||
126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
|
||||
144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,
|
||||
162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,
|
||||
180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,
|
||||
198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,
|
||||
216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,
|
||||
234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,
|
||||
252,253,254,255
|
||||
#endif
|
||||
};
|
||||
public int this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
if ( index < sqlite3UpperToLower.Length )
|
||||
return sqlite3UpperToLower[index];
|
||||
else
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
public int this[u32 index]
|
||||
{
|
||||
get
|
||||
{
|
||||
if ( index < sqlite3UpperToLower.Length )
|
||||
return sqlite3UpperToLower[index];
|
||||
else
|
||||
return (int)index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static SQLite3UpperToLower sqlite3UpperToLower = new SQLite3UpperToLower();
|
||||
static SQLite3UpperToLower UpperToLower = sqlite3UpperToLower;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,935 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
using sqlite3_value = Sqlite3.Mem;
|
||||
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/*
|
||||
** 2005 February 15
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file contains C code routines that used to generate VDBE code
|
||||
** that implements the ALTER TABLE command.
|
||||
*************************************************************************
|
||||
** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart
|
||||
** C#-SQLite is an independent reimplementation of the SQLite software library
|
||||
**
|
||||
** SQLITE_SOURCE_ID: 2011-06-23 19:49:22 4374b7e83ea0a3fbc3691f9c0c936272862f32f2
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
//#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** The code in this file only exists if we are not omitting the
|
||||
** ALTER TABLE logic from the build.
|
||||
*/
|
||||
#if !SQLITE_OMIT_ALTERTABLE
|
||||
|
||||
|
||||
/*
|
||||
** This function is used by SQL generated to implement the
|
||||
** ALTER TABLE command. The first argument is the text of a CREATE TABLE or
|
||||
** CREATE INDEX command. The second is a table name. The table name in
|
||||
** the CREATE TABLE or CREATE INDEX statement is replaced with the third
|
||||
** argument and the result returned. Examples:
|
||||
**
|
||||
** sqlite_rename_table('CREATE TABLE abc(a, b, c)', 'def')
|
||||
** . 'CREATE TABLE def(a, b, c)'
|
||||
**
|
||||
** sqlite_rename_table('CREATE INDEX i ON abc(a)', 'def')
|
||||
** . 'CREATE INDEX i ON def(a, b, c)'
|
||||
*/
|
||||
static void renameTableFunc(
|
||||
sqlite3_context context,
|
||||
int NotUsed,
|
||||
sqlite3_value[] argv
|
||||
)
|
||||
{
|
||||
string bResult = sqlite3_value_text( argv[0] );
|
||||
string zSql = bResult == null ? "" : bResult;
|
||||
string zTableName = sqlite3_value_text( argv[1] );
|
||||
|
||||
int token = 0;
|
||||
Token tname = new Token();
|
||||
int zCsr = 0;
|
||||
int zLoc = 0;
|
||||
int len = 0;
|
||||
string zRet;
|
||||
|
||||
sqlite3 db = sqlite3_context_db_handle( context );
|
||||
|
||||
UNUSED_PARAMETER( NotUsed );
|
||||
|
||||
/* The principle used to locate the table name in the CREATE TABLE
|
||||
** statement is that the table name is the first non-space token that
|
||||
** is immediately followed by a TK_LP or TK_USING token.
|
||||
*/
|
||||
if ( zSql != "" )
|
||||
{
|
||||
do
|
||||
{
|
||||
if ( zCsr == zSql.Length )
|
||||
{
|
||||
/* Ran out of input before finding an opening bracket. Return NULL. */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Store the token that zCsr points to in tname. */
|
||||
zLoc = zCsr;
|
||||
tname.z = zSql.Substring( zCsr );//(char*)zCsr;
|
||||
tname.n = len;
|
||||
|
||||
/* Advance zCsr to the next token. Store that token type in 'token',
|
||||
** and its length in 'len' (to be used next iteration of this loop).
|
||||
*/
|
||||
do
|
||||
{
|
||||
zCsr += len;
|
||||
len = ( zCsr == zSql.Length ) ? 1 : sqlite3GetToken( zSql, zCsr, ref token );
|
||||
} while ( token == TK_SPACE );
|
||||
Debug.Assert( len > 0 );
|
||||
} while ( token != TK_LP && token != TK_USING );
|
||||
|
||||
zRet = sqlite3MPrintf( db, "%.*s\"%w\"%s", zLoc, zSql.Substring( 0, zLoc ),
|
||||
zTableName, zSql.Substring( zLoc + tname.n ) );
|
||||
|
||||
sqlite3_result_text( context, zRet, -1, SQLITE_DYNAMIC );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** This C function implements an SQL user function that is used by SQL code
|
||||
** generated by the ALTER TABLE ... RENAME command to modify the definition
|
||||
** of any foreign key constraints that use the table being renamed as the
|
||||
** parent table. It is passed three arguments:
|
||||
**
|
||||
** 1) The complete text of the CREATE TABLE statement being modified,
|
||||
** 2) The old name of the table being renamed, and
|
||||
** 3) The new name of the table being renamed.
|
||||
**
|
||||
** It returns the new CREATE TABLE statement. For example:
|
||||
**
|
||||
** sqlite_rename_parent('CREATE TABLE t1(a REFERENCES t2)', 't2', 't3')
|
||||
** -> 'CREATE TABLE t1(a REFERENCES t3)'
|
||||
*/
|
||||
#if !SQLITE_OMIT_FOREIGN_KEY
|
||||
static void renameParentFunc(
|
||||
sqlite3_context context,
|
||||
int NotUsed,
|
||||
sqlite3_value[] argv
|
||||
)
|
||||
{
|
||||
sqlite3 db = sqlite3_context_db_handle( context );
|
||||
string zOutput = "";
|
||||
string zResult;
|
||||
string zInput = sqlite3_value_text( argv[0] );
|
||||
string zOld = sqlite3_value_text( argv[1] );
|
||||
string zNew = sqlite3_value_text( argv[2] );
|
||||
|
||||
int zIdx; /* Pointer to token */
|
||||
int zLeft = 0; /* Pointer to remainder of String */
|
||||
int n = 0; /* Length of token z */
|
||||
int token = 0; /* Type of token */
|
||||
|
||||
UNUSED_PARAMETER( NotUsed );
|
||||
for ( zIdx = 0; zIdx < zInput.Length; zIdx += n )//z=zInput; *z; z=z+n)
|
||||
{
|
||||
n = sqlite3GetToken( zInput, zIdx, ref token );
|
||||
if ( token == TK_REFERENCES )
|
||||
{
|
||||
string zParent;
|
||||
do
|
||||
{
|
||||
zIdx += n;
|
||||
n = sqlite3GetToken( zInput, zIdx, ref token );
|
||||
} while ( token == TK_SPACE );
|
||||
|
||||
zParent = zIdx + n < zInput.Length ? zInput.Substring( zIdx, n ) : "";//sqlite3DbStrNDup(db, zIdx, n);
|
||||
if ( String.IsNullOrEmpty( zParent ) )
|
||||
break;
|
||||
sqlite3Dequote( ref zParent );
|
||||
if ( zOld.Equals( zParent, StringComparison.InvariantCultureIgnoreCase ) )
|
||||
{
|
||||
string zOut = sqlite3MPrintf( db, "%s%.*s\"%w\"",
|
||||
zOutput, zIdx - zLeft, zInput.Substring( zLeft ), zNew
|
||||
);
|
||||
sqlite3DbFree( db, ref zOutput );
|
||||
zOutput = zOut;
|
||||
zIdx += n;// zInput = &z[n];
|
||||
zLeft = zIdx;
|
||||
}
|
||||
sqlite3DbFree( db, ref zParent );
|
||||
}
|
||||
}
|
||||
|
||||
zResult = sqlite3MPrintf( db, "%s%s", zOutput, zInput.Substring( zLeft ) );
|
||||
sqlite3_result_text( context, zResult, -1, SQLITE_DYNAMIC );
|
||||
sqlite3DbFree( db, ref zOutput );
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !SQLITE_OMIT_TRIGGER
|
||||
/* This function is used by SQL generated to implement the
|
||||
** ALTER TABLE command. The first argument is the text of a CREATE TRIGGER
|
||||
** statement. The second is a table name. The table name in the CREATE
|
||||
** TRIGGER statement is replaced with the third argument and the result
|
||||
** returned. This is analagous to renameTableFunc() above, except for CREATE
|
||||
** TRIGGER, not CREATE INDEX and CREATE TABLE.
|
||||
*/
|
||||
static void renameTriggerFunc(
|
||||
sqlite3_context context,
|
||||
int NotUsed,
|
||||
sqlite3_value[] argv
|
||||
)
|
||||
{
|
||||
string zSql = sqlite3_value_text( argv[0] );
|
||||
string zTableName = sqlite3_value_text( argv[1] );
|
||||
|
||||
int token = 0;
|
||||
Token tname = new Token();
|
||||
int dist = 3;
|
||||
int zCsr = 0;
|
||||
int zLoc = 0;
|
||||
int len = 1;
|
||||
string zRet;
|
||||
|
||||
sqlite3 db = sqlite3_context_db_handle( context );
|
||||
|
||||
UNUSED_PARAMETER( NotUsed );
|
||||
|
||||
/* The principle used to locate the table name in the CREATE TRIGGER
|
||||
** statement is that the table name is the first token that is immediatedly
|
||||
** preceded by either TK_ON or TK_DOT and immediatedly followed by one
|
||||
** of TK_WHEN, TK_BEGIN or TK_FOR.
|
||||
*/
|
||||
if ( zSql != null )
|
||||
{
|
||||
do
|
||||
{
|
||||
|
||||
if ( zCsr == zSql.Length )
|
||||
{
|
||||
/* Ran out of input before finding the table name. Return NULL. */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Store the token that zCsr points to in tname. */
|
||||
zLoc = zCsr;
|
||||
tname.z = zSql.Substring( zCsr, len );//(char*)zCsr;
|
||||
tname.n = len;
|
||||
|
||||
/* Advance zCsr to the next token. Store that token type in 'token',
|
||||
** and its length in 'len' (to be used next iteration of this loop).
|
||||
*/
|
||||
do
|
||||
{
|
||||
zCsr += len;
|
||||
len = ( zCsr == zSql.Length ) ? 1 : sqlite3GetToken( zSql, zCsr, ref token );
|
||||
} while ( token == TK_SPACE );
|
||||
Debug.Assert( len > 0 );
|
||||
|
||||
/* Variable 'dist' stores the number of tokens read since the most
|
||||
** recent TK_DOT or TK_ON. This means that when a WHEN, FOR or BEGIN
|
||||
** token is read and 'dist' equals 2, the condition stated above
|
||||
** to be met.
|
||||
**
|
||||
** Note that ON cannot be a database, table or column name, so
|
||||
** there is no need to worry about syntax like
|
||||
** "CREATE TRIGGER ... ON ON.ON BEGIN ..." etc.
|
||||
*/
|
||||
dist++;
|
||||
if ( token == TK_DOT || token == TK_ON )
|
||||
{
|
||||
dist = 0;
|
||||
}
|
||||
} while ( dist != 2 || ( token != TK_WHEN && token != TK_FOR && token != TK_BEGIN ) );
|
||||
|
||||
/* Variable tname now contains the token that is the old table-name
|
||||
** in the CREATE TRIGGER statement.
|
||||
*/
|
||||
zRet = sqlite3MPrintf( db, "%.*s\"%w\"%s", zLoc, zSql.Substring( 0, zLoc ),
|
||||
zTableName, zSql.Substring( zLoc + tname.n ) );
|
||||
sqlite3_result_text( context, zRet, -1, SQLITE_DYNAMIC );
|
||||
}
|
||||
}
|
||||
#endif // * !SQLITE_OMIT_TRIGGER */
|
||||
|
||||
/*
|
||||
** Register built-in functions used to help implement ALTER TABLE
|
||||
*/
|
||||
static FuncDef[] aAlterTableFuncs;
|
||||
static void sqlite3AlterFunctions()
|
||||
{
|
||||
aAlterTableFuncs = new FuncDef[] {
|
||||
FUNCTION("sqlite_rename_table", 2, 0, 0, renameTableFunc),
|
||||
#if !SQLITE_OMIT_TRIGGER
|
||||
FUNCTION("sqlite_rename_trigger", 2, 0, 0, renameTriggerFunc),
|
||||
#endif
|
||||
#if !SQLITE_OMIT_FOREIGN_KEY
|
||||
FUNCTION("sqlite_rename_parent", 3, 0, 0, renameParentFunc),
|
||||
#endif
|
||||
};
|
||||
int i;
|
||||
#if SQLITE_OMIT_WSD
|
||||
FuncDefHash pHash = GLOBAL(FuncDefHash, sqlite3GlobalFunctions);
|
||||
FuncDef[] aFunc = GLOBAL(FuncDef, aAlterTableFuncs);
|
||||
#else
|
||||
FuncDefHash pHash = sqlite3GlobalFunctions;
|
||||
FuncDef[] aFunc = aAlterTableFuncs;
|
||||
#endif
|
||||
|
||||
for ( i = 0; i < ArraySize( aAlterTableFuncs ); i++ )
|
||||
{
|
||||
sqlite3FuncDefInsert( pHash, aFunc[i] );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is used to create the text of expressions of the form:
|
||||
**
|
||||
** name=<constant1> OR name=<constant2> OR ...
|
||||
**
|
||||
** If argument zWhere is NULL, then a pointer string containing the text
|
||||
** "name=<constant>" is returned, where <constant> is the quoted version
|
||||
** of the string passed as argument zConstant. The returned buffer is
|
||||
** allocated using sqlite3DbMalloc(). It is the responsibility of the
|
||||
** caller to ensure that it is eventually freed.
|
||||
**
|
||||
** If argument zWhere is not NULL, then the string returned is
|
||||
** "<where> OR name=<constant>", where <where> is the contents of zWhere.
|
||||
** In this case zWhere is passed to sqlite3DbFree() before returning.
|
||||
**
|
||||
*/
|
||||
static string whereOrName( sqlite3 db, string zWhere, string zConstant )
|
||||
{
|
||||
string zNew;
|
||||
if ( String.IsNullOrEmpty( zWhere ) )
|
||||
{
|
||||
zNew = sqlite3MPrintf( db, "name=%Q", zConstant );
|
||||
}
|
||||
else
|
||||
{
|
||||
zNew = sqlite3MPrintf( db, "%s OR name=%Q", zWhere, zConstant );
|
||||
sqlite3DbFree( db, ref zWhere );
|
||||
}
|
||||
return zNew;
|
||||
}
|
||||
|
||||
#if !(SQLITE_OMIT_FOREIGN_KEY) && !(SQLITE_OMIT_TRIGGER)
|
||||
/*
|
||||
** Generate the text of a WHERE expression which can be used to select all
|
||||
** tables that have foreign key constraints that refer to table pTab (i.e.
|
||||
** constraints for which pTab is the parent table) from the sqlite_master
|
||||
** table.
|
||||
*/
|
||||
static string whereForeignKeys( Parse pParse, Table pTab )
|
||||
{
|
||||
FKey p;
|
||||
string zWhere = "";
|
||||
for ( p = sqlite3FkReferences( pTab ); p != null; p = p.pNextTo )
|
||||
{
|
||||
zWhere = whereOrName( pParse.db, zWhere, p.pFrom.zName );
|
||||
}
|
||||
return zWhere;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Generate the text of a WHERE expression which can be used to select all
|
||||
** temporary triggers on table pTab from the sqlite_temp_master table. If
|
||||
** table pTab has no temporary triggers, or is itself stored in the
|
||||
** temporary database, NULL is returned.
|
||||
*/
|
||||
static string whereTempTriggers( Parse pParse, Table pTab )
|
||||
{
|
||||
Trigger pTrig;
|
||||
string zWhere = "";
|
||||
Schema pTempSchema = pParse.db.aDb[1].pSchema; /* Temp db schema */
|
||||
|
||||
/* If the table is not located in the temp.db (in which case NULL is
|
||||
** returned, loop through the tables list of triggers. For each trigger
|
||||
** that is not part of the temp.db schema, add a clause to the WHERE
|
||||
** expression being built up in zWhere.
|
||||
*/
|
||||
if ( pTab.pSchema != pTempSchema )
|
||||
{
|
||||
sqlite3 db = pParse.db;
|
||||
for ( pTrig = sqlite3TriggerList( pParse, pTab ); pTrig != null; pTrig = pTrig.pNext )
|
||||
{
|
||||
if ( pTrig.pSchema == pTempSchema )
|
||||
{
|
||||
zWhere = whereOrName( db, zWhere, pTrig.zName );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( !String.IsNullOrEmpty( zWhere ) )
|
||||
{
|
||||
zWhere = sqlite3MPrintf( pParse.db, "type='trigger' AND (%s)", zWhere );
|
||||
//sqlite3DbFree( pParse.db, ref zWhere );
|
||||
//zWhere = zNew;
|
||||
}
|
||||
return zWhere;
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code to drop and reload the internal representation of table
|
||||
** pTab from the database, including triggers and temporary triggers.
|
||||
** Argument zName is the name of the table in the database schema at
|
||||
** the time the generated code is executed. This can be different from
|
||||
** pTab.zName if this function is being called to code part of an
|
||||
** "ALTER TABLE RENAME TO" statement.
|
||||
*/
|
||||
static void reloadTableSchema( Parse pParse, Table pTab, string zName )
|
||||
{
|
||||
Vdbe v;
|
||||
string zWhere;
|
||||
int iDb; /* Index of database containing pTab */
|
||||
#if !SQLITE_OMIT_TRIGGER
|
||||
Trigger pTrig;
|
||||
#endif
|
||||
|
||||
v = sqlite3GetVdbe( pParse );
|
||||
if ( NEVER( v == null ) )
|
||||
return;
|
||||
Debug.Assert( sqlite3BtreeHoldsAllMutexes( pParse.db ) );
|
||||
iDb = sqlite3SchemaToIndex( pParse.db, pTab.pSchema );
|
||||
Debug.Assert( iDb >= 0 );
|
||||
|
||||
#if !SQLITE_OMIT_TRIGGER
|
||||
/* Drop any table triggers from the internal schema. */
|
||||
for ( pTrig = sqlite3TriggerList( pParse, pTab ); pTrig != null; pTrig = pTrig.pNext )
|
||||
{
|
||||
int iTrigDb = sqlite3SchemaToIndex( pParse.db, pTrig.pSchema );
|
||||
Debug.Assert( iTrigDb == iDb || iTrigDb == 1 );
|
||||
sqlite3VdbeAddOp4( v, OP_DropTrigger, iTrigDb, 0, 0, pTrig.zName, 0 );
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Drop the table and index from the internal schema. */
|
||||
sqlite3VdbeAddOp4( v, OP_DropTable, iDb, 0, 0, pTab.zName, 0 );
|
||||
|
||||
/* Reload the table, index and permanent trigger schemas. */
|
||||
zWhere = sqlite3MPrintf( pParse.db, "tbl_name=%Q", zName );
|
||||
if ( zWhere == null )
|
||||
return;
|
||||
sqlite3VdbeAddParseSchemaOp( v, iDb, zWhere );
|
||||
|
||||
#if !SQLITE_OMIT_TRIGGER
|
||||
/* Now, if the table is not stored in the temp database, reload any temp
|
||||
** triggers. Don't use IN(...) in case SQLITE_OMIT_SUBQUERY is defined.
|
||||
*/
|
||||
if ( ( zWhere = whereTempTriggers( pParse, pTab ) ) != "" )
|
||||
{
|
||||
sqlite3VdbeAddParseSchemaOp( v, 1, zWhere );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** Parameter zName is the name of a table that is about to be altered
|
||||
** (either with ALTER TABLE ... RENAME TO or ALTER TABLE ... ADD COLUMN).
|
||||
** If the table is a system table, this function leaves an error message
|
||||
** in pParse->zErr (system tables may not be altered) and returns non-zero.
|
||||
**
|
||||
** Or, if zName is not a system table, zero is returned.
|
||||
*/
|
||||
static int isSystemTable( Parse pParse, string zName )
|
||||
{
|
||||
if ( zName.StartsWith( "sqlite_", System.StringComparison.InvariantCultureIgnoreCase ) )
|
||||
{
|
||||
sqlite3ErrorMsg( pParse, "table %s may not be altered", zName );
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code to implement the "ALTER TABLE xxx RENAME TO yyy"
|
||||
** command.
|
||||
*/
|
||||
static void sqlite3AlterRenameTable(
|
||||
Parse pParse, /* Parser context. */
|
||||
SrcList pSrc, /* The table to rename. */
|
||||
Token pName /* The new table name. */
|
||||
)
|
||||
{
|
||||
int iDb; /* Database that contains the table */
|
||||
string zDb; /* Name of database iDb */
|
||||
Table pTab; /* Table being renamed */
|
||||
string zName = null; /* NULL-terminated version of pName */
|
||||
sqlite3 db = pParse.db; /* Database connection */
|
||||
int nTabName; /* Number of UTF-8 characters in zTabName */
|
||||
string zTabName; /* Original name of the table */
|
||||
Vdbe v;
|
||||
#if !SQLITE_OMIT_TRIGGER
|
||||
string zWhere = ""; /* Where clause to locate temp triggers */
|
||||
#endif
|
||||
VTable pVTab = null; /* Non-zero if this is a v-tab with an xRename() */
|
||||
int savedDbFlags; /* Saved value of db->flags */
|
||||
|
||||
savedDbFlags = db.flags;
|
||||
|
||||
//if ( NEVER( db.mallocFailed != 0 ) ) goto exit_rename_table;
|
||||
Debug.Assert( pSrc.nSrc == 1 );
|
||||
Debug.Assert( sqlite3BtreeHoldsAllMutexes( pParse.db ) );
|
||||
pTab = sqlite3LocateTable( pParse, 0, pSrc.a[0].zName, pSrc.a[0].zDatabase );
|
||||
if ( pTab == null )
|
||||
goto exit_rename_table;
|
||||
iDb = sqlite3SchemaToIndex( pParse.db, pTab.pSchema );
|
||||
zDb = db.aDb[iDb].zName;
|
||||
db.flags |= SQLITE_PreferBuiltin;
|
||||
|
||||
/* Get a NULL terminated version of the new table name. */
|
||||
zName = sqlite3NameFromToken( db, pName );
|
||||
if ( zName == null )
|
||||
goto exit_rename_table;
|
||||
|
||||
/* Check that a table or index named 'zName' does not already exist
|
||||
** in database iDb. If so, this is an error.
|
||||
*/
|
||||
if ( sqlite3FindTable( db, zName, zDb ) != null || sqlite3FindIndex( db, zName, zDb ) != null )
|
||||
{
|
||||
sqlite3ErrorMsg( pParse,
|
||||
"there is already another table or index with this name: %s", zName );
|
||||
goto exit_rename_table;
|
||||
}
|
||||
|
||||
/* Make sure it is not a system table being altered, or a reserved name
|
||||
** that the table is being renamed to.
|
||||
*/
|
||||
if ( SQLITE_OK!=isSystemTable(pParse, pTab.zName) )
|
||||
{
|
||||
goto exit_rename_table;
|
||||
}
|
||||
if ( SQLITE_OK != sqlite3CheckObjectName( pParse, zName ) )
|
||||
{
|
||||
goto exit_rename_table;
|
||||
}
|
||||
|
||||
#if !SQLITE_OMIT_VIEW
|
||||
if ( pTab.pSelect != null )
|
||||
{
|
||||
sqlite3ErrorMsg( pParse, "view %s may not be altered", pTab.zName );
|
||||
goto exit_rename_table;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if NO_SQLITE_OMIT_AUTHORIZATION //#if !SQLITE_OMIT_AUTHORIZATION
|
||||
/* Invoke the authorization callback. */
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab.zName, 0) ){
|
||||
goto exit_rename_table;
|
||||
}
|
||||
#endif
|
||||
|
||||
if ( sqlite3ViewGetColumnNames( pParse, pTab ) != 0 )
|
||||
{
|
||||
goto exit_rename_table;
|
||||
}
|
||||
#if !SQLITE_OMIT_VIRTUALTABLE
|
||||
if ( IsVirtual( pTab ) )
|
||||
{
|
||||
pVTab = sqlite3GetVTable( db, pTab );
|
||||
if ( pVTab.pVtab.pModule.xRename == null )
|
||||
{
|
||||
pVTab = null;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/* Begin a transaction and code the VerifyCookie for database iDb.
|
||||
** Then modify the schema cookie (since the ALTER TABLE modifies the
|
||||
** schema). Open a statement transaction if the table is a virtual
|
||||
** table.
|
||||
*/
|
||||
v = sqlite3GetVdbe( pParse );
|
||||
if ( v == null )
|
||||
{
|
||||
goto exit_rename_table;
|
||||
}
|
||||
sqlite3BeginWriteOperation( pParse, pVTab != null ? 1 : 0, iDb );
|
||||
sqlite3ChangeCookie( pParse, iDb );
|
||||
|
||||
/* If this is a virtual table, invoke the xRename() function if
|
||||
** one is defined. The xRename() callback will modify the names
|
||||
** of any resources used by the v-table implementation (including other
|
||||
** SQLite tables) that are identified by the name of the virtual table.
|
||||
*/
|
||||
#if !SQLITE_OMIT_VIRTUALTABLE
|
||||
if ( pVTab !=null)
|
||||
{
|
||||
int i = ++pParse.nMem;
|
||||
sqlite3VdbeAddOp4( v, OP_String8, 0, i, 0, zName, 0 );
|
||||
sqlite3VdbeAddOp4( v, OP_VRename, i, 0, 0, pVTab, P4_VTAB );
|
||||
sqlite3MayAbort(pParse);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* figure out how many UTF-8 characters are in zName */
|
||||
zTabName = pTab.zName;
|
||||
nTabName = sqlite3Utf8CharLen( zTabName, -1 );
|
||||
|
||||
#if !(SQLITE_OMIT_FOREIGN_KEY) && !(SQLITE_OMIT_TRIGGER)
|
||||
if ( ( db.flags & SQLITE_ForeignKeys ) != 0 )
|
||||
{
|
||||
/* If foreign-key support is enabled, rewrite the CREATE TABLE
|
||||
** statements corresponding to all child tables of foreign key constraints
|
||||
** for which the renamed table is the parent table. */
|
||||
if ( ( zWhere = whereForeignKeys( pParse, pTab ) ) != null )
|
||||
{
|
||||
sqlite3NestedParse( pParse,
|
||||
"UPDATE \"%w\".%s SET " +
|
||||
"sql = sqlite_rename_parent(sql, %Q, %Q) " +
|
||||
"WHERE %s;", zDb, SCHEMA_TABLE( iDb ), zTabName, zName, zWhere );
|
||||
sqlite3DbFree( db, ref zWhere );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Modify the sqlite_master table to use the new table name. */
|
||||
sqlite3NestedParse( pParse,
|
||||
"UPDATE %Q.%s SET " +
|
||||
#if SQLITE_OMIT_TRIGGER
|
||||
"sql = sqlite_rename_table(sql, %Q), " +
|
||||
#else
|
||||
"sql = CASE " +
|
||||
"WHEN type = 'trigger' THEN sqlite_rename_trigger(sql, %Q)" +
|
||||
"ELSE sqlite_rename_table(sql, %Q) END, " +
|
||||
#endif
|
||||
"tbl_name = %Q, " +
|
||||
"name = CASE " +
|
||||
"WHEN type='table' THEN %Q " +
|
||||
"WHEN name LIKE 'sqlite_autoindex%%' AND type='index' THEN " +
|
||||
"'sqlite_autoindex_' || %Q || substr(name,%d+18) " +
|
||||
"ELSE name END " +
|
||||
"WHERE tbl_name=%Q AND " +
|
||||
"(type='table' OR type='index' OR type='trigger');",
|
||||
zDb, SCHEMA_TABLE( iDb ), zName, zName, zName,
|
||||
#if !SQLITE_OMIT_TRIGGER
|
||||
zName,
|
||||
#endif
|
||||
zName, nTabName, zTabName
|
||||
);
|
||||
|
||||
#if !SQLITE_OMIT_AUTOINCREMENT
|
||||
/* If the sqlite_sequence table exists in this database, then update
|
||||
** it with the new table name.
|
||||
*/
|
||||
if ( sqlite3FindTable( db, "sqlite_sequence", zDb ) != null )
|
||||
{
|
||||
sqlite3NestedParse( pParse,
|
||||
"UPDATE \"%w\".sqlite_sequence set name = %Q WHERE name = %Q",
|
||||
zDb, zName, pTab.zName
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !SQLITE_OMIT_TRIGGER
|
||||
/* If there are TEMP triggers on this table, modify the sqlite_temp_master
|
||||
** table. Don't do this if the table being ALTERed is itself located in
|
||||
** the temp database.
|
||||
*/
|
||||
if ( ( zWhere = whereTempTriggers( pParse, pTab ) ) != "" )
|
||||
{
|
||||
sqlite3NestedParse( pParse,
|
||||
"UPDATE sqlite_temp_master SET " +
|
||||
"sql = sqlite_rename_trigger(sql, %Q), " +
|
||||
"tbl_name = %Q " +
|
||||
"WHERE %s;", zName, zName, zWhere );
|
||||
sqlite3DbFree( db, ref zWhere );
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !(SQLITE_OMIT_FOREIGN_KEY) && !(SQLITE_OMIT_TRIGGER)
|
||||
if ( ( db.flags & SQLITE_ForeignKeys ) != 0 )
|
||||
{
|
||||
FKey p;
|
||||
for ( p = sqlite3FkReferences( pTab ); p != null; p = p.pNextTo )
|
||||
{
|
||||
Table pFrom = p.pFrom;
|
||||
if ( pFrom != pTab )
|
||||
{
|
||||
reloadTableSchema( pParse, p.pFrom, pFrom.zName );
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Drop and reload the internal table schema. */
|
||||
reloadTableSchema( pParse, pTab, zName );
|
||||
|
||||
exit_rename_table:
|
||||
sqlite3SrcListDelete( db, ref pSrc );
|
||||
sqlite3DbFree( db, ref zName );
|
||||
db.flags = savedDbFlags;
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code to make sure the file format number is at least minFormat.
|
||||
** The generated code will increase the file format number if necessary.
|
||||
*/
|
||||
static void sqlite3MinimumFileFormat( Parse pParse, int iDb, int minFormat )
|
||||
{
|
||||
Vdbe v;
|
||||
v = sqlite3GetVdbe( pParse );
|
||||
/* The VDBE should have been allocated before this routine is called.
|
||||
** If that allocation failed, we would have quit before reaching this
|
||||
** point */
|
||||
if ( ALWAYS( v ) )
|
||||
{
|
||||
int r1 = sqlite3GetTempReg( pParse );
|
||||
int r2 = sqlite3GetTempReg( pParse );
|
||||
int j1;
|
||||
sqlite3VdbeAddOp3( v, OP_ReadCookie, iDb, r1, BTREE_FILE_FORMAT );
|
||||
sqlite3VdbeUsesBtree( v, iDb );
|
||||
sqlite3VdbeAddOp2( v, OP_Integer, minFormat, r2 );
|
||||
j1 = sqlite3VdbeAddOp3( v, OP_Ge, r2, 0, r1 );
|
||||
sqlite3VdbeAddOp3( v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, r2 );
|
||||
sqlite3VdbeJumpHere( v, j1 );
|
||||
sqlite3ReleaseTempReg( pParse, r1 );
|
||||
sqlite3ReleaseTempReg( pParse, r2 );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is called after an "ALTER TABLE ... ADD" statement
|
||||
** has been parsed. Argument pColDef contains the text of the new
|
||||
** column definition.
|
||||
**
|
||||
** The Table structure pParse.pNewTable was extended to include
|
||||
** the new column during parsing.
|
||||
*/
|
||||
static void sqlite3AlterFinishAddColumn( Parse pParse, Token pColDef )
|
||||
{
|
||||
Table pNew; /* Copy of pParse.pNewTable */
|
||||
Table pTab; /* Table being altered */
|
||||
int iDb; /* Database number */
|
||||
string zDb; /* Database name */
|
||||
string zTab; /* Table name */
|
||||
string zCol; /* Null-terminated column definition */
|
||||
Column pCol; /* The new column */
|
||||
Expr pDflt; /* Default value for the new column */
|
||||
sqlite3 db; /* The database connection; */
|
||||
|
||||
db = pParse.db;
|
||||
if ( pParse.nErr != 0 /*|| db.mallocFailed != 0 */ )
|
||||
return;
|
||||
pNew = pParse.pNewTable;
|
||||
Debug.Assert( pNew != null );
|
||||
Debug.Assert( sqlite3BtreeHoldsAllMutexes( db ) );
|
||||
iDb = sqlite3SchemaToIndex( db, pNew.pSchema );
|
||||
zDb = db.aDb[iDb].zName;
|
||||
zTab = pNew.zName.Substring( 16 );// zTab = &pNew->zName[16]; /* Skip the "sqlite_altertab_" prefix on the name */
|
||||
pCol = pNew.aCol[pNew.nCol - 1];
|
||||
pDflt = pCol.pDflt;
|
||||
pTab = sqlite3FindTable( db, zTab, zDb );
|
||||
Debug.Assert( pTab != null );
|
||||
|
||||
#if NO_SQLITE_OMIT_AUTHORIZATION //#if !SQLITE_OMIT_AUTHORIZATION
|
||||
/* Invoke the authorization callback. */
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab.zName, 0) ){
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If the default value for the new column was specified with a
|
||||
** literal NULL, then set pDflt to 0. This simplifies checking
|
||||
** for an SQL NULL default below.
|
||||
*/
|
||||
if ( pDflt != null && pDflt.op == TK_NULL )
|
||||
{
|
||||
pDflt = null;
|
||||
}
|
||||
|
||||
/* Check that the new column is not specified as PRIMARY KEY or UNIQUE.
|
||||
** If there is a NOT NULL constraint, then the default value for the
|
||||
** column must not be NULL.
|
||||
*/
|
||||
if ( pCol.isPrimKey != 0 )
|
||||
{
|
||||
sqlite3ErrorMsg( pParse, "Cannot add a PRIMARY KEY column" );
|
||||
return;
|
||||
}
|
||||
if ( pNew.pIndex != null )
|
||||
{
|
||||
sqlite3ErrorMsg( pParse, "Cannot add a UNIQUE column" );
|
||||
return;
|
||||
}
|
||||
if ( ( db.flags & SQLITE_ForeignKeys ) != 0 && pNew.pFKey != null && pDflt != null )
|
||||
{
|
||||
sqlite3ErrorMsg( pParse,
|
||||
"Cannot add a REFERENCES column with non-NULL default value" );
|
||||
return;
|
||||
}
|
||||
if ( pCol.notNull != 0 && pDflt == null )
|
||||
{
|
||||
sqlite3ErrorMsg( pParse,
|
||||
"Cannot add a NOT NULL column with default value NULL" );
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ensure the default expression is something that sqlite3ValueFromExpr()
|
||||
** can handle (i.e. not CURRENT_TIME etc.)
|
||||
*/
|
||||
if ( pDflt != null )
|
||||
{
|
||||
sqlite3_value pVal = null;
|
||||
if ( sqlite3ValueFromExpr( db, pDflt, SQLITE_UTF8, SQLITE_AFF_NONE, ref pVal ) != 0 )
|
||||
{
|
||||
// db.mallocFailed = 1;
|
||||
return;
|
||||
}
|
||||
if ( pVal == null )
|
||||
{
|
||||
sqlite3ErrorMsg( pParse, "Cannot add a column with non-constant default" );
|
||||
return;
|
||||
}
|
||||
sqlite3ValueFree( ref pVal );
|
||||
}
|
||||
|
||||
/* Modify the CREATE TABLE statement. */
|
||||
zCol = pColDef.z.Substring( 0, pColDef.n ).Replace( ";", " " ).Trim();//sqlite3DbStrNDup(db, (char*)pColDef.z, pColDef.n);
|
||||
if ( zCol != null )
|
||||
{
|
||||
// char zEnd = zCol[pColDef.n-1];
|
||||
int savedDbFlags = db.flags;
|
||||
// while( zEnd>zCol && (*zEnd==';' || sqlite3Isspace(*zEnd)) ){
|
||||
// zEnd-- = '\0';
|
||||
// }
|
||||
db.flags |= SQLITE_PreferBuiltin;
|
||||
sqlite3NestedParse( pParse,
|
||||
"UPDATE \"%w\".%s SET " +
|
||||
"sql = substr(sql,1,%d) || ', ' || %Q || substr(sql,%d) " +
|
||||
"WHERE type = 'table' AND name = %Q",
|
||||
zDb, SCHEMA_TABLE( iDb ), pNew.addColOffset, zCol, pNew.addColOffset + 1,
|
||||
zTab
|
||||
);
|
||||
sqlite3DbFree( db, ref zCol );
|
||||
db.flags = savedDbFlags;
|
||||
}
|
||||
|
||||
/* If the default value of the new column is NULL, then set the file
|
||||
** format to 2. If the default value of the new column is not NULL,
|
||||
** the file format becomes 3.
|
||||
*/
|
||||
sqlite3MinimumFileFormat( pParse, iDb, pDflt != null ? 3 : 2 );
|
||||
|
||||
/* Reload the schema of the modified table. */
|
||||
reloadTableSchema( pParse, pTab, pTab.zName );
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is called by the parser after the table-name in
|
||||
** an "ALTER TABLE <table-name> ADD" statement is parsed. Argument
|
||||
** pSrc is the full-name of the table being altered.
|
||||
**
|
||||
** This routine makes a (partial) copy of the Table structure
|
||||
** for the table being altered and sets Parse.pNewTable to point
|
||||
** to it. Routines called by the parser as the column definition
|
||||
** is parsed (i.e. sqlite3AddColumn()) add the new Column data to
|
||||
** the copy. The copy of the Table structure is deleted by tokenize.c
|
||||
** after parsing is finished.
|
||||
**
|
||||
** Routine sqlite3AlterFinishAddColumn() will be called to complete
|
||||
** coding the "ALTER TABLE ... ADD" statement.
|
||||
*/
|
||||
static void sqlite3AlterBeginAddColumn( Parse pParse, SrcList pSrc )
|
||||
{
|
||||
Table pNew;
|
||||
Table pTab;
|
||||
Vdbe v;
|
||||
int iDb;
|
||||
int i;
|
||||
int nAlloc;
|
||||
sqlite3 db = pParse.db;
|
||||
|
||||
/* Look up the table being altered. */
|
||||
Debug.Assert( pParse.pNewTable == null );
|
||||
Debug.Assert( sqlite3BtreeHoldsAllMutexes( db ) );
|
||||
// if ( db.mallocFailed != 0 ) goto exit_begin_add_column;
|
||||
pTab = sqlite3LocateTable( pParse, 0, pSrc.a[0].zName, pSrc.a[0].zDatabase );
|
||||
if ( pTab == null )
|
||||
goto exit_begin_add_column;
|
||||
|
||||
if ( IsVirtual( pTab ) )
|
||||
{
|
||||
sqlite3ErrorMsg( pParse, "virtual tables may not be altered" );
|
||||
goto exit_begin_add_column;
|
||||
}
|
||||
|
||||
/* Make sure this is not an attempt to ALTER a view. */
|
||||
if ( pTab.pSelect != null )
|
||||
{
|
||||
sqlite3ErrorMsg( pParse, "Cannot add a column to a view" );
|
||||
goto exit_begin_add_column;
|
||||
}
|
||||
if ( SQLITE_OK != isSystemTable( pParse, pTab.zName ) )
|
||||
{
|
||||
goto exit_begin_add_column;
|
||||
}
|
||||
|
||||
Debug.Assert( pTab.addColOffset > 0 );
|
||||
iDb = sqlite3SchemaToIndex( db, pTab.pSchema );
|
||||
|
||||
/* Put a copy of the Table struct in Parse.pNewTable for the
|
||||
** sqlite3AddColumn() function and friends to modify. But modify
|
||||
** the name by adding an "sqlite_altertab_" prefix. By adding this
|
||||
** prefix, we insure that the name will not collide with an existing
|
||||
** table because user table are not allowed to have the "sqlite_"
|
||||
** prefix on their name.
|
||||
*/
|
||||
pNew = new Table();// (Table*)sqlite3DbMallocZero( db, sizeof(Table))
|
||||
if ( pNew == null )
|
||||
goto exit_begin_add_column;
|
||||
pParse.pNewTable = pNew;
|
||||
pNew.nRef = 1;
|
||||
pNew.nCol = pTab.nCol;
|
||||
Debug.Assert( pNew.nCol > 0 );
|
||||
nAlloc = ( ( ( pNew.nCol - 1 ) / 8 ) * 8 ) + 8;
|
||||
Debug.Assert( nAlloc >= pNew.nCol && nAlloc % 8 == 0 && nAlloc - pNew.nCol < 8 );
|
||||
pNew.aCol = new Column[nAlloc];// (Column*)sqlite3DbMallocZero( db, sizeof(Column) * nAlloc );
|
||||
pNew.zName = sqlite3MPrintf( db, "sqlite_altertab_%s", pTab.zName );
|
||||
if ( pNew.aCol == null || pNew.zName == null )
|
||||
{
|
||||
// db.mallocFailed = 1;
|
||||
goto exit_begin_add_column;
|
||||
}
|
||||
// memcpy( pNew.aCol, pTab.aCol, sizeof(Column) * pNew.nCol );
|
||||
for ( i = 0; i < pNew.nCol; i++ )
|
||||
{
|
||||
Column pCol = pTab.aCol[i].Copy();
|
||||
// sqlite3DbStrDup( db, pCol.zName );
|
||||
pCol.zColl = null;
|
||||
pCol.zType = null;
|
||||
pCol.pDflt = null;
|
||||
pCol.zDflt = null;
|
||||
pNew.aCol[i] = pCol;
|
||||
}
|
||||
pNew.pSchema = db.aDb[iDb].pSchema;
|
||||
pNew.addColOffset = pTab.addColOffset;
|
||||
pNew.nRef = 1;
|
||||
|
||||
/* Begin a transaction and increment the schema cookie. */
|
||||
sqlite3BeginWriteOperation( pParse, 0, iDb );
|
||||
v = sqlite3GetVdbe( pParse );
|
||||
if ( v == null )
|
||||
goto exit_begin_add_column;
|
||||
sqlite3ChangeCookie( pParse, iDb );
|
||||
|
||||
exit_begin_add_column:
|
||||
sqlite3SrcListDelete( db, ref pSrc );
|
||||
return;
|
||||
}
|
||||
#endif // * SQLITE_ALTER_TABLE */
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,866 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
using u8 = System.Byte;
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
using sqlite3_int64 = System.Int64;
|
||||
using sqlite3_stmt = Sqlite3.Vdbe;
|
||||
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/*
|
||||
** 2005 July 8
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file contains code associated with the ANALYZE command.
|
||||
*************************************************************************
|
||||
** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart
|
||||
** C#-SQLite is an independent reimplementation of the SQLite software library
|
||||
**
|
||||
** SQLITE_SOURCE_ID: 2011-05-19 13:26:54 ed1da510a239ea767a01dc332b667119fa3c908e
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
#if !SQLITE_OMIT_ANALYZE
|
||||
//#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** This routine generates code that opens the sqlite_stat1 table for
|
||||
** writing with cursor iStatCur. If the library was built with the
|
||||
** SQLITE_ENABLE_STAT2 macro defined, then the sqlite_stat2 table is
|
||||
** opened for writing using cursor (iStatCur+1)
|
||||
**
|
||||
** If the sqlite_stat1 tables does not previously exist, it is created.
|
||||
** Similarly, if the sqlite_stat2 table does not exist and the library
|
||||
** is compiled with SQLITE_ENABLE_STAT2 defined, it is created.
|
||||
**
|
||||
** Argument zWhere may be a pointer to a buffer containing a table name,
|
||||
** or it may be a NULL pointer. If it is not NULL, then all entries in
|
||||
** the sqlite_stat1 and (if applicable) sqlite_stat2 tables associated
|
||||
** with the named table are deleted. If zWhere==0, then code is generated
|
||||
** to delete all stat table entries.
|
||||
*/
|
||||
public struct _aTable
|
||||
{
|
||||
public string zName;
|
||||
public string zCols;
|
||||
public _aTable( string zName, string zCols )
|
||||
{
|
||||
this.zName = zName;
|
||||
this.zCols = zCols;
|
||||
}
|
||||
};
|
||||
static _aTable[] aTable = new _aTable[]{
|
||||
new _aTable( "sqlite_stat1", "tbl,idx,stat" ),
|
||||
#if SQLITE_ENABLE_STAT2
|
||||
new _aTable( "sqlite_stat2", "tbl,idx,sampleno,sample" ),
|
||||
#endif
|
||||
};
|
||||
|
||||
static void openStatTable(
|
||||
Parse pParse, /* Parsing context */
|
||||
int iDb, /* The database we are looking in */
|
||||
int iStatCur, /* Open the sqlite_stat1 table on this cursor */
|
||||
string zWhere, /* Delete entries for this table or index */
|
||||
string zWhereType /* Either "tbl" or "idx" */
|
||||
)
|
||||
{
|
||||
int[] aRoot = new int[] { 0, 0 };
|
||||
u8[] aCreateTbl = new u8[] { 0, 0 };
|
||||
|
||||
int i;
|
||||
sqlite3 db = pParse.db;
|
||||
Db pDb;
|
||||
Vdbe v = sqlite3GetVdbe( pParse );
|
||||
|
||||
if ( v == null )
|
||||
return;
|
||||
Debug.Assert( sqlite3BtreeHoldsAllMutexes( db ) );
|
||||
Debug.Assert( sqlite3VdbeDb( v ) == db );
|
||||
pDb = db.aDb[iDb];
|
||||
|
||||
for ( i = 0; i < ArraySize( aTable ); i++ )
|
||||
{
|
||||
string zTab = aTable[i].zName;
|
||||
Table pStat;
|
||||
if ( ( pStat = sqlite3FindTable( db, zTab, pDb.zName ) ) == null )
|
||||
{
|
||||
/* The sqlite_stat[12] table does not exist. Create it. Note that a
|
||||
** side-effect of the CREATE TABLE statement is to leave the rootpage
|
||||
** of the new table in register pParse.regRoot. This is important
|
||||
** because the OpenWrite opcode below will be needing it. */
|
||||
sqlite3NestedParse( pParse,
|
||||
"CREATE TABLE %Q.%s(%s)", pDb.zName, zTab, aTable[i].zCols
|
||||
);
|
||||
aRoot[i] = pParse.regRoot;
|
||||
aCreateTbl[i] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The table already exists. If zWhere is not NULL, delete all entries
|
||||
** associated with the table zWhere. If zWhere is NULL, delete the
|
||||
** entire contents of the table. */
|
||||
aRoot[i] = pStat.tnum;
|
||||
sqlite3TableLock( pParse, iDb, aRoot[i], 1, zTab );
|
||||
if ( !String.IsNullOrEmpty( zWhere ) )
|
||||
{
|
||||
sqlite3NestedParse( pParse,
|
||||
"DELETE FROM %Q.%s WHERE %s=%Q", pDb.zName, zTab, zWhereType, zWhere
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The sqlite_stat[12] table already exists. Delete all rows. */
|
||||
sqlite3VdbeAddOp2( v, OP_Clear, aRoot[i], iDb );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Open the sqlite_stat[12] tables for writing. */
|
||||
for ( i = 0; i < ArraySize( aTable ); i++ )
|
||||
{
|
||||
sqlite3VdbeAddOp3( v, OP_OpenWrite, iStatCur + i, aRoot[i], iDb );
|
||||
sqlite3VdbeChangeP4( v, -1, 3, P4_INT32 );
|
||||
sqlite3VdbeChangeP5( v, aCreateTbl[i] );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code to do an analysis of all indices associated with
|
||||
** a single table.
|
||||
*/
|
||||
static void analyzeOneTable(
|
||||
Parse pParse, /* Parser context */
|
||||
Table pTab, /* Table whose indices are to be analyzed */
|
||||
Index pOnlyIdx, /* If not NULL, only analyze this one index */
|
||||
int iStatCur, /* Index of VdbeCursor that writes the sqlite_stat1 table */
|
||||
int iMem /* Available memory locations begin here */
|
||||
)
|
||||
{
|
||||
sqlite3 db = pParse.db; /* Database handle */
|
||||
Index pIdx; /* An index to being analyzed */
|
||||
int iIdxCur; /* Cursor open on index being analyzed */
|
||||
Vdbe v; /* The virtual machine being built up */
|
||||
int i; /* Loop counter */
|
||||
int topOfLoop; /* The top of the loop */
|
||||
int endOfLoop; /* The end of the loop */
|
||||
int jZeroRows = -1; /* Jump from here if number of rows is zero */
|
||||
int iDb; /* Index of database containing pTab */
|
||||
int regTabname = iMem++; /* Register containing table name */
|
||||
int regIdxname = iMem++; /* Register containing index name */
|
||||
int regSampleno = iMem++; /* Register containing next sample number */
|
||||
int regCol = iMem++; /* Content of a column analyzed table */
|
||||
int regRec = iMem++; /* Register holding completed record */
|
||||
int regTemp = iMem++; /* Temporary use register */
|
||||
int regRowid = iMem++; /* Rowid for the inserted record */
|
||||
|
||||
#if SQLITE_ENABLE_STAT2
|
||||
int addr = 0; /* Instruction address */
|
||||
int regTemp2 = iMem++; /* Temporary use register */
|
||||
int regSamplerecno = iMem++; /* Index of next sample to record */
|
||||
int regRecno = iMem++; /* Current sample index */
|
||||
int regLast = iMem++; /* Index of last sample to record */
|
||||
int regFirst = iMem++; /* Index of first sample to record */
|
||||
#endif
|
||||
|
||||
v = sqlite3GetVdbe( pParse );
|
||||
if ( v == null || NEVER( pTab == null ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
if ( pTab.tnum == 0 )
|
||||
{
|
||||
/* Do not gather statistics on views or virtual tables */
|
||||
return;
|
||||
}
|
||||
if ( pTab.zName.StartsWith( "sqlite_", StringComparison.InvariantCultureIgnoreCase ) )
|
||||
{
|
||||
/* Do not gather statistics on system tables */
|
||||
return;
|
||||
}
|
||||
Debug.Assert( sqlite3BtreeHoldsAllMutexes( db ) );
|
||||
iDb = sqlite3SchemaToIndex( db, pTab.pSchema );
|
||||
Debug.Assert( iDb >= 0 );
|
||||
Debug.Assert( sqlite3SchemaMutexHeld(db, iDb, null) );
|
||||
#if NO_SQLITE_OMIT_AUTHORIZATION
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_ANALYZE, pTab.zName, 0,
|
||||
db.aDb[iDb].zName ) ){
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Establish a read-lock on the table at the shared-cache level. */
|
||||
sqlite3TableLock( pParse, iDb, pTab.tnum, 0, pTab.zName );
|
||||
|
||||
iIdxCur = pParse.nTab++;
|
||||
sqlite3VdbeAddOp4( v, OP_String8, 0, regTabname, 0, pTab.zName, 0 );
|
||||
for ( pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext )
|
||||
{
|
||||
int nCol;
|
||||
KeyInfo pKey;
|
||||
if ( pOnlyIdx != null && pOnlyIdx != pIdx )
|
||||
continue;
|
||||
nCol = pIdx.nColumn;
|
||||
pKey = sqlite3IndexKeyinfo( pParse, pIdx );
|
||||
|
||||
if ( iMem + 1 + ( nCol * 2 ) > pParse.nMem )
|
||||
{
|
||||
pParse.nMem = iMem + 1 + ( nCol * 2 );
|
||||
}
|
||||
|
||||
/* Open a cursor to the index to be analyzed. */
|
||||
Debug.Assert( iDb == sqlite3SchemaToIndex( db, pIdx.pSchema ) );
|
||||
sqlite3VdbeAddOp4( v, OP_OpenRead, iIdxCur, pIdx.tnum, iDb,
|
||||
pKey, P4_KEYINFO_HANDOFF );
|
||||
VdbeComment( v, "%s", pIdx.zName );
|
||||
|
||||
/* Populate the registers containing the index names. */
|
||||
sqlite3VdbeAddOp4( v, OP_String8, 0, regIdxname, 0, pIdx.zName, 0 );
|
||||
|
||||
#if SQLITE_ENABLE_STAT2
|
||||
|
||||
/* If this iteration of the loop is generating code to analyze the
|
||||
** first index in the pTab.pIndex list, then register regLast has
|
||||
** not been populated. In this case populate it now. */
|
||||
if ( pTab.pIndex == pIdx )
|
||||
{
|
||||
sqlite3VdbeAddOp2( v, OP_Integer, SQLITE_INDEX_SAMPLES, regSamplerecno );
|
||||
sqlite3VdbeAddOp2( v, OP_Integer, SQLITE_INDEX_SAMPLES * 2 - 1, regTemp );
|
||||
sqlite3VdbeAddOp2( v, OP_Integer, SQLITE_INDEX_SAMPLES * 2, regTemp2 );
|
||||
|
||||
sqlite3VdbeAddOp2( v, OP_Count, iIdxCur, regLast );
|
||||
sqlite3VdbeAddOp2( v, OP_Null, 0, regFirst );
|
||||
addr = sqlite3VdbeAddOp3( v, OP_Lt, regSamplerecno, 0, regLast );
|
||||
sqlite3VdbeAddOp3( v, OP_Divide, regTemp2, regLast, regFirst );
|
||||
sqlite3VdbeAddOp3( v, OP_Multiply, regLast, regTemp, regLast );
|
||||
sqlite3VdbeAddOp2( v, OP_AddImm, regLast, SQLITE_INDEX_SAMPLES * 2 - 2 );
|
||||
sqlite3VdbeAddOp3( v, OP_Divide, regTemp2, regLast, regLast );
|
||||
sqlite3VdbeJumpHere( v, addr );
|
||||
}
|
||||
|
||||
/* Zero the regSampleno and regRecno registers. */
|
||||
sqlite3VdbeAddOp2( v, OP_Integer, 0, regSampleno );
|
||||
sqlite3VdbeAddOp2( v, OP_Integer, 0, regRecno );
|
||||
sqlite3VdbeAddOp2( v, OP_Copy, regFirst, regSamplerecno );
|
||||
#endif
|
||||
|
||||
/* The block of memory cells initialized here is used as follows.
|
||||
**
|
||||
** iMem:
|
||||
** The total number of rows in the table.
|
||||
**
|
||||
** iMem+1 .. iMem+nCol:
|
||||
** Number of distinct entries in index considering the
|
||||
** left-most N columns only, where N is between 1 and nCol,
|
||||
** inclusive.
|
||||
**
|
||||
** iMem+nCol+1 .. Mem+2*nCol:
|
||||
** Previous value of indexed columns, from left to right.
|
||||
**
|
||||
** Cells iMem through iMem+nCol are initialized to 0. The others are
|
||||
** initialized to contain an SQL NULL.
|
||||
*/
|
||||
for ( i = 0; i <= nCol; i++ )
|
||||
{
|
||||
sqlite3VdbeAddOp2( v, OP_Integer, 0, iMem + i );
|
||||
}
|
||||
for ( i = 0; i < nCol; i++ )
|
||||
{
|
||||
sqlite3VdbeAddOp2( v, OP_Null, 0, iMem + nCol + i + 1 );
|
||||
}
|
||||
|
||||
/* Start the analysis loop. This loop runs through all the entries in
|
||||
** the index b-tree. */
|
||||
endOfLoop = sqlite3VdbeMakeLabel( v );
|
||||
sqlite3VdbeAddOp2( v, OP_Rewind, iIdxCur, endOfLoop );
|
||||
topOfLoop = sqlite3VdbeCurrentAddr( v );
|
||||
sqlite3VdbeAddOp2( v, OP_AddImm, iMem, 1 );
|
||||
|
||||
for ( i = 0; i < nCol; i++ )
|
||||
{
|
||||
sqlite3VdbeAddOp3( v, OP_Column, iIdxCur, i, regCol );
|
||||
CollSeq pColl;
|
||||
if ( i == 0 )
|
||||
{
|
||||
#if SQLITE_ENABLE_STAT2
|
||||
/* Check if the record that cursor iIdxCur points to contains a
|
||||
** value that should be stored in the sqlite_stat2 table. If so,
|
||||
** store it. */
|
||||
int ne = sqlite3VdbeAddOp3( v, OP_Ne, regRecno, 0, regSamplerecno );
|
||||
Debug.Assert( regTabname + 1 == regIdxname
|
||||
&& regTabname + 2 == regSampleno
|
||||
&& regTabname + 3 == regCol
|
||||
);
|
||||
sqlite3VdbeChangeP5( v, SQLITE_JUMPIFNULL );
|
||||
sqlite3VdbeAddOp4( v, OP_MakeRecord, regTabname, 4, regRec, "aaab", 0 );
|
||||
sqlite3VdbeAddOp2( v, OP_NewRowid, iStatCur + 1, regRowid );
|
||||
sqlite3VdbeAddOp3( v, OP_Insert, iStatCur + 1, regRec, regRowid );
|
||||
|
||||
/* Calculate new values for regSamplerecno and regSampleno.
|
||||
**
|
||||
** sampleno = sampleno + 1
|
||||
** samplerecno = samplerecno+(remaining records)/(remaining samples)
|
||||
*/
|
||||
sqlite3VdbeAddOp2( v, OP_AddImm, regSampleno, 1 );
|
||||
sqlite3VdbeAddOp3( v, OP_Subtract, regRecno, regLast, regTemp );
|
||||
sqlite3VdbeAddOp2( v, OP_AddImm, regTemp, -1 );
|
||||
sqlite3VdbeAddOp2( v, OP_Integer, SQLITE_INDEX_SAMPLES, regTemp2 );
|
||||
sqlite3VdbeAddOp3( v, OP_Subtract, regSampleno, regTemp2, regTemp2 );
|
||||
sqlite3VdbeAddOp3( v, OP_Divide, regTemp2, regTemp, regTemp );
|
||||
sqlite3VdbeAddOp3( v, OP_Add, regSamplerecno, regTemp, regSamplerecno );
|
||||
|
||||
sqlite3VdbeJumpHere( v, ne );
|
||||
sqlite3VdbeAddOp2( v, OP_AddImm, regRecno, 1 );
|
||||
#endif
|
||||
|
||||
/* Always record the very first row */
|
||||
sqlite3VdbeAddOp1( v, OP_IfNot, iMem + 1 );
|
||||
}
|
||||
Debug.Assert( pIdx.azColl != null );
|
||||
Debug.Assert( pIdx.azColl[i] != null );
|
||||
pColl = sqlite3LocateCollSeq( pParse, pIdx.azColl[i] );
|
||||
sqlite3VdbeAddOp4( v, OP_Ne, regCol, 0, iMem + nCol + i + 1,
|
||||
pColl, P4_COLLSEQ );
|
||||
sqlite3VdbeChangeP5( v, SQLITE_NULLEQ );
|
||||
}
|
||||
//if( db.mallocFailed ){
|
||||
// /* If a malloc failure has occurred, then the result of the expression
|
||||
// ** passed as the second argument to the call to sqlite3VdbeJumpHere()
|
||||
// ** below may be negative. Which causes an Debug.Assert() to fail (or an
|
||||
// ** out-of-bounds write if SQLITE_DEBUG is not defined). */
|
||||
// return;
|
||||
//}
|
||||
sqlite3VdbeAddOp2( v, OP_Goto, 0, endOfLoop );
|
||||
for ( i = 0; i < nCol; i++ )
|
||||
{
|
||||
int addr2 = sqlite3VdbeCurrentAddr( v ) - ( nCol * 2 );
|
||||
if ( i == 0 )
|
||||
{
|
||||
sqlite3VdbeJumpHere( v, addr2 - 1 ); /* Set jump dest for the OP_IfNot */
|
||||
}
|
||||
sqlite3VdbeJumpHere( v, addr2 ); /* Set jump dest for the OP_Ne */
|
||||
sqlite3VdbeAddOp2( v, OP_AddImm, iMem + i + 1, 1 );
|
||||
sqlite3VdbeAddOp3( v, OP_Column, iIdxCur, i, iMem + nCol + i + 1 );
|
||||
}
|
||||
|
||||
/* End of the analysis loop. */
|
||||
sqlite3VdbeResolveLabel( v, endOfLoop );
|
||||
sqlite3VdbeAddOp2( v, OP_Next, iIdxCur, topOfLoop );
|
||||
sqlite3VdbeAddOp1( v, OP_Close, iIdxCur );
|
||||
|
||||
/* Store the results in sqlite_stat1.
|
||||
**
|
||||
** The result is a single row of the sqlite_stat1 table. The first
|
||||
** two columns are the names of the table and index. The third column
|
||||
** is a string composed of a list of integer statistics about the
|
||||
** index. The first integer in the list is the total number of entries
|
||||
** in the index. There is one additional integer in the list for each
|
||||
** column of the table. This additional integer is a guess of how many
|
||||
** rows of the table the index will select. If D is the count of distinct
|
||||
** values and K is the total number of rows, then the integer is computed
|
||||
** as:
|
||||
**
|
||||
** I = (K+D-1)/D
|
||||
**
|
||||
** If K==0 then no entry is made into the sqlite_stat1 table.
|
||||
** If K>0 then it is always the case the D>0 so division by zero
|
||||
** is never possible.
|
||||
*/
|
||||
sqlite3VdbeAddOp2( v, OP_SCopy, iMem, regSampleno );
|
||||
if ( jZeroRows < 0 )
|
||||
{
|
||||
jZeroRows = sqlite3VdbeAddOp1( v, OP_IfNot, iMem );
|
||||
}
|
||||
for ( i = 0; i < nCol; i++ )
|
||||
{
|
||||
sqlite3VdbeAddOp4( v, OP_String8, 0, regTemp, 0, " ", 0 );
|
||||
sqlite3VdbeAddOp3( v, OP_Concat, regTemp, regSampleno, regSampleno );
|
||||
sqlite3VdbeAddOp3( v, OP_Add, iMem, iMem + i + 1, regTemp );
|
||||
sqlite3VdbeAddOp2( v, OP_AddImm, regTemp, -1 );
|
||||
sqlite3VdbeAddOp3( v, OP_Divide, iMem + i + 1, regTemp, regTemp );
|
||||
sqlite3VdbeAddOp1( v, OP_ToInt, regTemp );
|
||||
sqlite3VdbeAddOp3( v, OP_Concat, regTemp, regSampleno, regSampleno );
|
||||
}
|
||||
sqlite3VdbeAddOp4( v, OP_MakeRecord, regTabname, 3, regRec, "aaa", 0 );
|
||||
sqlite3VdbeAddOp2( v, OP_NewRowid, iStatCur, regRowid );
|
||||
sqlite3VdbeAddOp3( v, OP_Insert, iStatCur, regRec, regRowid );
|
||||
sqlite3VdbeChangeP5( v, OPFLAG_APPEND );
|
||||
}
|
||||
|
||||
/* If the table has no indices, create a single sqlite_stat1 entry
|
||||
** containing NULL as the index name and the row count as the content.
|
||||
*/
|
||||
if ( pTab.pIndex == null )
|
||||
{
|
||||
sqlite3VdbeAddOp3( v, OP_OpenRead, iIdxCur, pTab.tnum, iDb );
|
||||
VdbeComment( v, "%s", pTab.zName );
|
||||
sqlite3VdbeAddOp2( v, OP_Count, iIdxCur, regSampleno );
|
||||
sqlite3VdbeAddOp1( v, OP_Close, iIdxCur );
|
||||
jZeroRows = sqlite3VdbeAddOp1( v, OP_IfNot, regSampleno );
|
||||
}
|
||||
else
|
||||
{
|
||||
sqlite3VdbeJumpHere( v, jZeroRows );
|
||||
jZeroRows = sqlite3VdbeAddOp0( v, OP_Goto );
|
||||
}
|
||||
sqlite3VdbeAddOp2( v, OP_Null, 0, regIdxname );
|
||||
sqlite3VdbeAddOp4( v, OP_MakeRecord, regTabname, 3, regRec, "aaa", 0 );
|
||||
sqlite3VdbeAddOp2( v, OP_NewRowid, iStatCur, regRowid );
|
||||
sqlite3VdbeAddOp3( v, OP_Insert, iStatCur, regRec, regRowid );
|
||||
sqlite3VdbeChangeP5( v, OPFLAG_APPEND );
|
||||
if ( pParse.nMem < regRec )
|
||||
pParse.nMem = regRec;
|
||||
sqlite3VdbeJumpHere( v, jZeroRows );
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code that will cause the most recent index analysis to
|
||||
** be loaded into internal hash tables where is can be used.
|
||||
*/
|
||||
static void loadAnalysis( Parse pParse, int iDb )
|
||||
{
|
||||
Vdbe v = sqlite3GetVdbe( pParse );
|
||||
if ( v != null )
|
||||
{
|
||||
sqlite3VdbeAddOp1( v, OP_LoadAnalysis, iDb );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code that will do an analysis of an entire database
|
||||
*/
|
||||
static void analyzeDatabase( Parse pParse, int iDb )
|
||||
{
|
||||
sqlite3 db = pParse.db;
|
||||
Schema pSchema = db.aDb[iDb].pSchema; /* Schema of database iDb */
|
||||
HashElem k;
|
||||
int iStatCur;
|
||||
int iMem;
|
||||
|
||||
sqlite3BeginWriteOperation( pParse, 0, iDb );
|
||||
iStatCur = pParse.nTab;
|
||||
pParse.nTab += 2;
|
||||
openStatTable( pParse, iDb, iStatCur, null, null );
|
||||
iMem = pParse.nMem + 1;
|
||||
Debug.Assert( sqlite3SchemaMutexHeld( db, iDb, null ) );
|
||||
//for(k=sqliteHashFirst(pSchema.tblHash); k; k=sqliteHashNext(k)){
|
||||
for ( k = pSchema.tblHash.first; k != null; k = k.next )
|
||||
{
|
||||
Table pTab = (Table)k.data;// sqliteHashData( k );
|
||||
analyzeOneTable( pParse, pTab, null, iStatCur, iMem );
|
||||
}
|
||||
loadAnalysis( pParse, iDb );
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code that will do an analysis of a single table in
|
||||
** a database. If pOnlyIdx is not NULL then it is a single index
|
||||
** in pTab that should be analyzed.
|
||||
*/
|
||||
static void analyzeTable( Parse pParse, Table pTab, Index pOnlyIdx)
|
||||
{
|
||||
int iDb;
|
||||
int iStatCur;
|
||||
|
||||
Debug.Assert( pTab != null );
|
||||
Debug.Assert( sqlite3BtreeHoldsAllMutexes( pParse.db ) );
|
||||
iDb = sqlite3SchemaToIndex( pParse.db, pTab.pSchema );
|
||||
sqlite3BeginWriteOperation( pParse, 0, iDb );
|
||||
iStatCur = pParse.nTab;
|
||||
pParse.nTab += 2;
|
||||
if ( pOnlyIdx != null )
|
||||
{
|
||||
openStatTable( pParse, iDb, iStatCur, pOnlyIdx.zName, "idx" );
|
||||
}
|
||||
else
|
||||
{
|
||||
openStatTable( pParse, iDb, iStatCur, pTab.zName, "tbl" );
|
||||
}
|
||||
analyzeOneTable( pParse, pTab, pOnlyIdx, iStatCur, pParse.nMem + 1 );
|
||||
loadAnalysis( pParse, iDb );
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code for the ANALYZE command. The parser calls this routine
|
||||
** when it recognizes an ANALYZE command.
|
||||
**
|
||||
** ANALYZE -- 1
|
||||
** ANALYZE <database> -- 2
|
||||
** ANALYZE ?<database>.?<tablename> -- 3
|
||||
**
|
||||
** Form 1 causes all indices in all attached databases to be analyzed.
|
||||
** Form 2 analyzes all indices the single database named.
|
||||
** Form 3 analyzes all indices associated with the named table.
|
||||
*/
|
||||
// OVERLOADS, so I don't need to rewrite parse.c
|
||||
static void sqlite3Analyze( Parse pParse, int null_2, int null_3 )
|
||||
{
|
||||
sqlite3Analyze( pParse, null, null );
|
||||
}
|
||||
static void sqlite3Analyze( Parse pParse, Token pName1, Token pName2 )
|
||||
{
|
||||
sqlite3 db = pParse.db;
|
||||
int iDb;
|
||||
int i;
|
||||
string z, zDb;
|
||||
Table pTab;
|
||||
Index pIdx;
|
||||
Token pTableName = null;
|
||||
|
||||
/* Read the database schema. If an error occurs, leave an error message
|
||||
** and code in pParse and return NULL. */
|
||||
Debug.Assert( sqlite3BtreeHoldsAllMutexes( pParse.db ) );
|
||||
if ( SQLITE_OK != sqlite3ReadSchema( pParse ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Assert( pName2 != null || pName1 == null );
|
||||
if ( pName1 == null )
|
||||
{
|
||||
/* Form 1: Analyze everything */
|
||||
for ( i = 0; i < db.nDb; i++ )
|
||||
{
|
||||
if ( i == 1 )
|
||||
continue; /* Do not analyze the TEMP database */
|
||||
analyzeDatabase( pParse, i );
|
||||
}
|
||||
}
|
||||
else if ( pName2.n == 0 )
|
||||
{
|
||||
/* Form 2: Analyze the database or table named */
|
||||
iDb = sqlite3FindDb( db, pName1 );
|
||||
if ( iDb >= 0 )
|
||||
{
|
||||
analyzeDatabase( pParse, iDb );
|
||||
}
|
||||
else
|
||||
{
|
||||
z = sqlite3NameFromToken( db, pName1 );
|
||||
if ( z != null )
|
||||
{
|
||||
if ( ( pIdx = sqlite3FindIndex( db, z, null ) ) != null )
|
||||
{
|
||||
analyzeTable( pParse, pIdx.pTable, pIdx );
|
||||
}
|
||||
else if ( ( pTab = sqlite3LocateTable( pParse, 0, z, null ) ) != null )
|
||||
{
|
||||
analyzeTable( pParse, pTab, null );
|
||||
}
|
||||
z = null;//sqlite3DbFree( db, z );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Form 3: Analyze the fully qualified table name */
|
||||
iDb = sqlite3TwoPartName( pParse, pName1, pName2, ref pTableName );
|
||||
if ( iDb >= 0 )
|
||||
{
|
||||
zDb = db.aDb[iDb].zName;
|
||||
z = sqlite3NameFromToken( db, pTableName );
|
||||
if ( z != null )
|
||||
{
|
||||
if ( ( pIdx = sqlite3FindIndex( db, z, zDb ) ) != null )
|
||||
{
|
||||
analyzeTable( pParse, pIdx.pTable, pIdx );
|
||||
}
|
||||
else if ( ( pTab = sqlite3LocateTable( pParse, 0, z, zDb ) ) != null )
|
||||
{
|
||||
analyzeTable( pParse, pTab, null );
|
||||
}
|
||||
z = null; //sqlite3DbFree( db, z );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Used to pass information from the analyzer reader through to the
|
||||
** callback routine.
|
||||
*/
|
||||
//typedef struct analysisInfo analysisInfo;
|
||||
public struct analysisInfo
|
||||
{
|
||||
public sqlite3 db;
|
||||
public string zDatabase;
|
||||
};
|
||||
|
||||
/*
|
||||
** This callback is invoked once for each index when reading the
|
||||
** sqlite_stat1 table.
|
||||
**
|
||||
** argv[0] = name of the table
|
||||
** argv[1] = name of the index (might be NULL)
|
||||
** argv[2] = results of analysis - on integer for each column
|
||||
**
|
||||
** Entries for which argv[1]==NULL simply record the number of rows in
|
||||
** the table.
|
||||
*/
|
||||
static int analysisLoader( object pData, sqlite3_int64 argc, object Oargv, object NotUsed )
|
||||
{
|
||||
string[] argv = (string[])Oargv;
|
||||
analysisInfo pInfo = (analysisInfo)pData;
|
||||
Index pIndex;
|
||||
Table pTable;
|
||||
int i, c, n;
|
||||
int v;
|
||||
string z;
|
||||
|
||||
Debug.Assert( argc == 3 );
|
||||
UNUSED_PARAMETER2( NotUsed, argc );
|
||||
if ( argv == null || argv[0] == null || argv[2] == null )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
pTable = sqlite3FindTable( pInfo.db, argv[0], pInfo.zDatabase );
|
||||
if ( pTable == null )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if ( !String.IsNullOrEmpty( argv[1] ) )
|
||||
{
|
||||
pIndex = sqlite3FindIndex( pInfo.db, argv[1], pInfo.zDatabase );
|
||||
}
|
||||
else
|
||||
{
|
||||
pIndex = null;
|
||||
}
|
||||
|
||||
n = pIndex != null ? pIndex.nColumn : 0;
|
||||
z = argv[2];
|
||||
int zIndex = 0;
|
||||
for ( i = 0; z != null && i <= n; i++ )
|
||||
{
|
||||
v = 0;
|
||||
while ( zIndex < z.Length && ( c = z[zIndex] ) >= '0' && c <= '9' )
|
||||
{
|
||||
v = v * 10 + c - '0';
|
||||
zIndex++;
|
||||
}
|
||||
if ( i == 0 )
|
||||
pTable.nRowEst = (uint)v;
|
||||
if ( pIndex == null )
|
||||
break;
|
||||
pIndex.aiRowEst[i] = v;
|
||||
if ( zIndex < z.Length && z[zIndex] == ' ' )
|
||||
zIndex++;
|
||||
if ( z.Substring(zIndex).CompareTo("unordered")==0)//memcmp( z, "unordered", 10 ) == 0 )
|
||||
{
|
||||
pIndex.bUnordered = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** If the Index.aSample variable is not NULL, delete the aSample[] array
|
||||
** and its contents.
|
||||
*/
|
||||
static void sqlite3DeleteIndexSamples( sqlite3 db, Index pIdx )
|
||||
{
|
||||
#if SQLITE_ENABLE_STAT2
|
||||
if ( pIdx.aSample != null )
|
||||
{
|
||||
int j;
|
||||
for ( j = 0; j < SQLITE_INDEX_SAMPLES; j++ )
|
||||
{
|
||||
IndexSample p = pIdx.aSample[j];
|
||||
if ( p.eType == SQLITE_TEXT || p.eType == SQLITE_BLOB )
|
||||
{
|
||||
p.u.z = null;//sqlite3DbFree(db, p.u.z);
|
||||
p.u.zBLOB = null;
|
||||
}
|
||||
}
|
||||
sqlite3DbFree( db, ref pIdx.aSample );
|
||||
}
|
||||
#else
|
||||
UNUSED_PARAMETER(db);
|
||||
UNUSED_PARAMETER( pIdx );
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** Load the content of the sqlite_stat1 and sqlite_stat2 tables. The
|
||||
** contents of sqlite_stat1 are used to populate the Index.aiRowEst[]
|
||||
** arrays. The contents of sqlite_stat2 are used to populate the
|
||||
** Index.aSample[] arrays.
|
||||
**
|
||||
** If the sqlite_stat1 table is not present in the database, SQLITE_ERROR
|
||||
** is returned. In this case, even if SQLITE_ENABLE_STAT2 was defined
|
||||
** during compilation and the sqlite_stat2 table is present, no data is
|
||||
** read from it.
|
||||
**
|
||||
** If SQLITE_ENABLE_STAT2 was defined during compilation and the
|
||||
** sqlite_stat2 table is not present in the database, SQLITE_ERROR is
|
||||
** returned. However, in this case, data is read from the sqlite_stat1
|
||||
** table (if it is present) before returning.
|
||||
**
|
||||
** If an OOM error occurs, this function always sets db.mallocFailed.
|
||||
** This means if the caller does not care about other errors, the return
|
||||
** code may be ignored.
|
||||
*/
|
||||
static int sqlite3AnalysisLoad( sqlite3 db, int iDb )
|
||||
{
|
||||
analysisInfo sInfo;
|
||||
HashElem i;
|
||||
string zSql;
|
||||
int rc;
|
||||
|
||||
Debug.Assert( iDb >= 0 && iDb < db.nDb );
|
||||
Debug.Assert( db.aDb[iDb].pBt != null );
|
||||
/* Clear any prior statistics */
|
||||
Debug.Assert( sqlite3SchemaMutexHeld( db, iDb, null ) );
|
||||
//for(i=sqliteHashFirst(&db.aDb[iDb].pSchema.idxHash);i;i=sqliteHashNext(i)){
|
||||
for ( i = db.aDb[iDb].pSchema.idxHash.first; i != null; i = i.next )
|
||||
{
|
||||
Index pIdx = (Index)i.data;// sqliteHashData( i );
|
||||
sqlite3DefaultRowEst( pIdx );
|
||||
sqlite3DeleteIndexSamples( db, pIdx );
|
||||
pIdx.aSample = null;
|
||||
}
|
||||
|
||||
/* Check to make sure the sqlite_stat1 table exists */
|
||||
sInfo.db = db;
|
||||
sInfo.zDatabase = db.aDb[iDb].zName;
|
||||
if ( sqlite3FindTable( db, "sqlite_stat1", sInfo.zDatabase ) == null )
|
||||
{
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
|
||||
/* Load new statistics out of the sqlite_stat1 table */
|
||||
zSql = sqlite3MPrintf( db,
|
||||
"SELECT tbl, idx, stat FROM %Q.sqlite_stat1", sInfo.zDatabase );
|
||||
//if ( zSql == null )
|
||||
//{
|
||||
// rc = SQLITE_NOMEM;
|
||||
//}
|
||||
//else
|
||||
{
|
||||
rc = sqlite3_exec( db, zSql, (dxCallback)analysisLoader, sInfo, 0 );
|
||||
sqlite3DbFree( db, ref zSql );
|
||||
}
|
||||
|
||||
|
||||
/* Load the statistics from the sqlite_stat2 table. */
|
||||
#if SQLITE_ENABLE_STAT2
|
||||
if ( rc == SQLITE_OK && null == sqlite3FindTable( db, "sqlite_stat2", sInfo.zDatabase ) )
|
||||
{
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
if ( rc == SQLITE_OK )
|
||||
{
|
||||
sqlite3_stmt pStmt = null;
|
||||
|
||||
zSql = sqlite3MPrintf( db,
|
||||
"SELECT idx,sampleno,sample FROM %Q.sqlite_stat2", sInfo.zDatabase );
|
||||
//if( null==zSql ){
|
||||
//rc = SQLITE_NOMEM;
|
||||
//}else{
|
||||
rc = sqlite3_prepare( db, zSql, -1, ref pStmt, 0 );
|
||||
sqlite3DbFree( db, ref zSql );
|
||||
//}
|
||||
|
||||
if ( rc == SQLITE_OK )
|
||||
{
|
||||
while ( sqlite3_step( pStmt ) == SQLITE_ROW )
|
||||
{
|
||||
string zIndex; /* Index name */
|
||||
Index pIdx; /* Pointer to the index object */
|
||||
zIndex = sqlite3_column_text( pStmt, 0 );
|
||||
pIdx = !String.IsNullOrEmpty( zIndex ) ? sqlite3FindIndex( db, zIndex, sInfo.zDatabase ) : null;
|
||||
if ( pIdx != null )
|
||||
{
|
||||
int iSample = sqlite3_column_int( pStmt, 1 );
|
||||
if ( iSample < SQLITE_INDEX_SAMPLES && iSample >= 0 )
|
||||
{
|
||||
int eType = sqlite3_column_type( pStmt, 2 );
|
||||
|
||||
if ( pIdx.aSample == null )
|
||||
{
|
||||
//static const int sz = sizeof(IndexSample)*SQLITE_INDEX_SAMPLES;
|
||||
//pIdx->aSample = (IndexSample )sqlite3DbMallocRaw(0, sz);
|
||||
//if( pIdx.aSample==0 ){
|
||||
//db.mallocFailed = 1;
|
||||
//break;
|
||||
//}
|
||||
pIdx.aSample = new IndexSample[SQLITE_INDEX_SAMPLES];//memset(pIdx->aSample, 0, sz);
|
||||
}
|
||||
|
||||
//Debug.Assert( pIdx.aSample != null );
|
||||
if ( pIdx.aSample[iSample] == null )
|
||||
pIdx.aSample[iSample] = new IndexSample();
|
||||
IndexSample pSample = pIdx.aSample[iSample];
|
||||
{
|
||||
pSample.eType = (u8)eType;
|
||||
if ( eType == SQLITE_INTEGER || eType == SQLITE_FLOAT )
|
||||
{
|
||||
pSample.u.r = sqlite3_column_double( pStmt, 2 );
|
||||
}
|
||||
else if ( eType == SQLITE_TEXT || eType == SQLITE_BLOB )
|
||||
{
|
||||
string z = null;
|
||||
byte[] zBLOB = null;
|
||||
//string z = (string )(
|
||||
//(eType==SQLITE_BLOB) ?
|
||||
//sqlite3_column_blob(pStmt, 2):
|
||||
//sqlite3_column_text(pStmt, 2)
|
||||
//);
|
||||
if ( eType == SQLITE_BLOB )
|
||||
zBLOB = sqlite3_column_blob( pStmt, 2 );
|
||||
else
|
||||
z = sqlite3_column_text( pStmt, 2 );
|
||||
int n = sqlite3_column_bytes( pStmt, 2 );
|
||||
if ( n > 24 )
|
||||
{
|
||||
n = 24;
|
||||
}
|
||||
pSample.nByte = (u8)n;
|
||||
if ( n < 1 )
|
||||
{
|
||||
pSample.u.z = null;
|
||||
pSample.u.zBLOB = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
pSample.u.z = z;
|
||||
pSample.u.zBLOB = zBLOB;
|
||||
//pSample->u.z = sqlite3DbMallocRaw(dbMem, n);
|
||||
//if( pSample->u.z ){
|
||||
// memcpy(pSample->u.z, z, n);
|
||||
//}else{
|
||||
// db->mallocFailed = 1;
|
||||
// break;
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
rc = sqlite3_finalize( pStmt );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//if( rc==SQLITE_NOMEM ){
|
||||
// db.mallocFailed = 1;
|
||||
//}
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif // * SQLITE_OMIT_ANALYZE */
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,666 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
using u8 = System.Byte;
|
||||
using u32 = System.UInt32;
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
using sqlite3_value = Sqlite3.Mem;
|
||||
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/*
|
||||
** 2003 April 6
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file contains code used to implement the ATTACH and DETACH commands.
|
||||
*************************************************************************
|
||||
** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart
|
||||
** C#-SQLite is an independent reimplementation of the SQLite software library
|
||||
**
|
||||
** SQLITE_SOURCE_ID: 2011-06-23 19:49:22 4374b7e83ea0a3fbc3691f9c0c936272862f32f2
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
//#include "sqliteInt.h"
|
||||
|
||||
#if !SQLITE_OMIT_ATTACH
|
||||
/*
|
||||
** Resolve an expression that was part of an ATTACH or DETACH statement. This
|
||||
** is slightly different from resolving a normal SQL expression, because simple
|
||||
** identifiers are treated as strings, not possible column names or aliases.
|
||||
**
|
||||
** i.e. if the parser sees:
|
||||
**
|
||||
** ATTACH DATABASE abc AS def
|
||||
**
|
||||
** it treats the two expressions as literal strings 'abc' and 'def' instead of
|
||||
** looking for columns of the same name.
|
||||
**
|
||||
** This only applies to the root node of pExpr, so the statement:
|
||||
**
|
||||
** ATTACH DATABASE abc||def AS 'db2'
|
||||
**
|
||||
** will fail because neither abc or def can be resolved.
|
||||
*/
|
||||
static int resolveAttachExpr( NameContext pName, Expr pExpr )
|
||||
{
|
||||
int rc = SQLITE_OK;
|
||||
if ( pExpr != null )
|
||||
{
|
||||
if ( pExpr.op != TK_ID )
|
||||
{
|
||||
rc = sqlite3ResolveExprNames( pName, ref pExpr );
|
||||
if ( rc == SQLITE_OK && sqlite3ExprIsConstant( pExpr ) == 0 )
|
||||
{
|
||||
sqlite3ErrorMsg( pName.pParse, "invalid name: \"%s\"", pExpr.u.zToken );
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pExpr.op = TK_STRING;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** An SQL user-function registered to do the work of an ATTACH statement. The
|
||||
** three arguments to the function come directly from an attach statement:
|
||||
**
|
||||
** ATTACH DATABASE x AS y KEY z
|
||||
**
|
||||
** SELECT sqlite_attach(x, y, z)
|
||||
**
|
||||
** If the optional "KEY z" syntax is omitted, an SQL NULL is passed as the
|
||||
** third argument.
|
||||
*/
|
||||
static void attachFunc(
|
||||
sqlite3_context context,
|
||||
int NotUsed,
|
||||
sqlite3_value[] argv
|
||||
)
|
||||
{
|
||||
int i;
|
||||
int rc = 0;
|
||||
sqlite3 db = sqlite3_context_db_handle( context );
|
||||
string zName;
|
||||
string zFile;
|
||||
string zPath = "";
|
||||
string zErr = "";
|
||||
int flags;
|
||||
|
||||
Db aNew = null;
|
||||
string zErrDyn = "";
|
||||
sqlite3_vfs pVfs = null;
|
||||
|
||||
UNUSED_PARAMETER( NotUsed );
|
||||
|
||||
zFile = argv[0].z != null && ( argv[0].z.Length > 0 ) && argv[0].flags != MEM_Null ? sqlite3_value_text( argv[0] ) : "";
|
||||
zName = argv[1].z != null && ( argv[1].z.Length > 0 ) && argv[1].flags != MEM_Null ? sqlite3_value_text( argv[1] ) : "";
|
||||
//if( zFile==null ) zFile = "";
|
||||
//if ( zName == null ) zName = "";
|
||||
|
||||
|
||||
/* Check for the following errors:
|
||||
**
|
||||
** * Too many attached databases,
|
||||
** * Transaction currently open
|
||||
** * Specified database name already being used.
|
||||
*/
|
||||
if ( db.nDb >= db.aLimit[SQLITE_LIMIT_ATTACHED] + 2 )
|
||||
{
|
||||
zErrDyn = sqlite3MPrintf( db, "too many attached databases - max %d",
|
||||
db.aLimit[SQLITE_LIMIT_ATTACHED]
|
||||
);
|
||||
goto attach_error;
|
||||
}
|
||||
if ( 0 == db.autoCommit )
|
||||
{
|
||||
zErrDyn = sqlite3MPrintf( db, "cannot ATTACH database within transaction" );
|
||||
goto attach_error;
|
||||
}
|
||||
for ( i = 0; i < db.nDb; i++ )
|
||||
{
|
||||
string z = db.aDb[i].zName;
|
||||
Debug.Assert( z != null && zName != null );
|
||||
if ( z.Equals( zName, StringComparison.InvariantCultureIgnoreCase ) )
|
||||
{
|
||||
zErrDyn = sqlite3MPrintf( db, "database %s is already in use", zName );
|
||||
goto attach_error;
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate the new entry in the db.aDb[] array and initialise the schema
|
||||
** hash tables.
|
||||
*/
|
||||
/* Allocate the new entry in the db.aDb[] array and initialise the schema
|
||||
** hash tables.
|
||||
*/
|
||||
//if( db.aDb==db.aDbStatic ){
|
||||
// aNew = sqlite3DbMallocRaw(db, sizeof(db.aDb[0])*3 );
|
||||
// if( aNew==0 ) return;
|
||||
// memcpy(aNew, db.aDb, sizeof(db.aDb[0])*2);
|
||||
//}else {
|
||||
if ( db.aDb.Length <= db.nDb )
|
||||
Array.Resize( ref db.aDb, db.nDb + 1 );//aNew = sqlite3DbRealloc(db, db.aDb, sizeof(db.aDb[0])*(db.nDb+1) );
|
||||
if ( db.aDb == null )
|
||||
return; // if( aNew==0 ) return;
|
||||
//}
|
||||
db.aDb[db.nDb] = new Db();//db.aDb = aNew;
|
||||
aNew = db.aDb[db.nDb];//memset(aNew, 0, sizeof(*aNew));
|
||||
// memset(aNew, 0, sizeof(*aNew));
|
||||
|
||||
/* Open the database file. If the btree is successfully opened, use
|
||||
** it to obtain the database schema. At this point the schema may
|
||||
** or may not be initialised.
|
||||
*/
|
||||
flags = (int)db.openFlags;
|
||||
rc = sqlite3ParseUri( db.pVfs.zName, zFile, ref flags, ref pVfs, ref zPath, ref zErr );
|
||||
if ( rc != SQLITE_OK )
|
||||
{
|
||||
//if ( rc == SQLITE_NOMEM )
|
||||
//db.mallocFailed = 1;
|
||||
sqlite3_result_error( context, zErr, -1 );
|
||||
//sqlite3_free( zErr );
|
||||
return;
|
||||
}
|
||||
Debug.Assert( pVfs != null);
|
||||
flags |= SQLITE_OPEN_MAIN_DB;
|
||||
rc = sqlite3BtreeOpen( pVfs, zPath, db, ref aNew.pBt, 0, (int)flags );
|
||||
//sqlite3_free( zPath );
|
||||
|
||||
db.nDb++;
|
||||
if ( rc == SQLITE_CONSTRAINT )
|
||||
{
|
||||
rc = SQLITE_ERROR;
|
||||
zErrDyn = sqlite3MPrintf( db, "database is already attached" );
|
||||
}
|
||||
else if ( rc == SQLITE_OK )
|
||||
{
|
||||
Pager pPager;
|
||||
aNew.pSchema = sqlite3SchemaGet( db, aNew.pBt );
|
||||
//if ( aNew.pSchema == null )
|
||||
//{
|
||||
// rc = SQLITE_NOMEM;
|
||||
//}
|
||||
//else
|
||||
if ( aNew.pSchema.file_format != 0 && aNew.pSchema.enc != ENC( db ) )
|
||||
{
|
||||
zErrDyn = sqlite3MPrintf( db,
|
||||
"attached databases must use the same text encoding as main database" );
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
pPager = sqlite3BtreePager( aNew.pBt );
|
||||
sqlite3PagerLockingMode( pPager, db.dfltLockMode );
|
||||
sqlite3BtreeSecureDelete( aNew.pBt,
|
||||
sqlite3BtreeSecureDelete( db.aDb[0].pBt, -1 ) );
|
||||
}
|
||||
aNew.safety_level = 3;
|
||||
aNew.zName = zName;//sqlite3DbStrDup(db, zName);
|
||||
//if( rc==SQLITE_OK && aNew.zName==0 ){
|
||||
// rc = SQLITE_NOMEM;
|
||||
//}
|
||||
|
||||
#if !NOT_SQLITE_HAS_CODEC //#if SQLITE_HAS_CODEC
|
||||
if ( rc == SQLITE_OK )
|
||||
{
|
||||
//extern int sqlite3CodecAttach(sqlite3*, int, const void*, int);
|
||||
//extern void sqlite3CodecGetKey(sqlite3*, int, void**, int*);
|
||||
int nKey;
|
||||
string zKey;
|
||||
int t = sqlite3_value_type( argv[2] );
|
||||
switch ( t )
|
||||
{
|
||||
case SQLITE_INTEGER:
|
||||
case SQLITE_FLOAT:
|
||||
zErrDyn = "Invalid key value"; //sqlite3DbStrDup( db, "Invalid key value" );
|
||||
rc = SQLITE_ERROR;
|
||||
break;
|
||||
|
||||
case SQLITE_TEXT:
|
||||
case SQLITE_BLOB:
|
||||
nKey = sqlite3_value_bytes( argv[2] );
|
||||
zKey = sqlite3_value_blob( argv[2] ).ToString(); // (char *)sqlite3_value_blob(argv[2]);
|
||||
rc = sqlite3CodecAttach( db, db.nDb - 1, zKey, nKey );
|
||||
break;
|
||||
|
||||
case SQLITE_NULL:
|
||||
/* No key specified. Use the key from the main database */
|
||||
sqlite3CodecGetKey( db, 0, out zKey, out nKey ); //sqlite3CodecGetKey(db, 0, (void**)&zKey, nKey);
|
||||
if ( nKey > 0 || sqlite3BtreeGetReserve( db.aDb[0].pBt ) > 0 )
|
||||
{
|
||||
rc = sqlite3CodecAttach( db, db.nDb - 1, zKey, nKey );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If the file was opened successfully, read the schema for the new database.
|
||||
** If this fails, or if opening the file failed, then close the file and
|
||||
** remove the entry from the db.aDb[] array. i.e. put everything back the way
|
||||
** we found it.
|
||||
*/
|
||||
if ( rc == SQLITE_OK )
|
||||
{
|
||||
sqlite3BtreeEnterAll( db );
|
||||
rc = sqlite3Init( db, ref zErrDyn );
|
||||
sqlite3BtreeLeaveAll( db );
|
||||
}
|
||||
if ( rc != 0 )
|
||||
{
|
||||
int iDb = db.nDb - 1;
|
||||
Debug.Assert( iDb >= 2 );
|
||||
if ( db.aDb[iDb].pBt != null )
|
||||
{
|
||||
sqlite3BtreeClose( ref db.aDb[iDb].pBt );
|
||||
db.aDb[iDb].pBt = null;
|
||||
db.aDb[iDb].pSchema = null;
|
||||
}
|
||||
sqlite3ResetInternalSchema( db, -1 );
|
||||
db.nDb = iDb;
|
||||
if ( rc == SQLITE_NOMEM || rc == SQLITE_IOERR_NOMEM )
|
||||
{
|
||||
//// db.mallocFailed = 1;
|
||||
sqlite3DbFree( db, ref zErrDyn );
|
||||
zErrDyn = sqlite3MPrintf( db, "out of memory" );
|
||||
}
|
||||
else if ( zErrDyn == "" )
|
||||
{
|
||||
zErrDyn = sqlite3MPrintf( db, "unable to open database: %s", zFile );
|
||||
}
|
||||
goto attach_error;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
attach_error:
|
||||
/* Return an error if we get here */
|
||||
if ( zErrDyn != "" )
|
||||
{
|
||||
sqlite3_result_error( context, zErrDyn, -1 );
|
||||
sqlite3DbFree( db, ref zErrDyn );
|
||||
}
|
||||
if ( rc != 0 )
|
||||
sqlite3_result_error_code( context, rc );
|
||||
}
|
||||
|
||||
/*
|
||||
** An SQL user-function registered to do the work of an DETACH statement. The
|
||||
** three arguments to the function come directly from a detach statement:
|
||||
**
|
||||
** DETACH DATABASE x
|
||||
**
|
||||
** SELECT sqlite_detach(x)
|
||||
*/
|
||||
static void detachFunc(
|
||||
sqlite3_context context,
|
||||
int NotUsed,
|
||||
sqlite3_value[] argv
|
||||
)
|
||||
{
|
||||
string zName = zName = argv[0].z != null && ( argv[0].z.Length > 0 ) ? sqlite3_value_text( argv[0] ) : "";//(sqlite3_value_text(argv[0]);
|
||||
sqlite3 db = sqlite3_context_db_handle( context );
|
||||
int i;
|
||||
Db pDb = null;
|
||||
StringBuilder zErr = new StringBuilder( 200 );
|
||||
|
||||
UNUSED_PARAMETER( NotUsed );
|
||||
|
||||
if ( zName == null )
|
||||
zName = "";
|
||||
for ( i = 0; i < db.nDb; i++ )
|
||||
{
|
||||
pDb = db.aDb[i];
|
||||
if ( pDb.pBt == null )
|
||||
continue;
|
||||
if ( pDb.zName.Equals( zName, StringComparison.InvariantCultureIgnoreCase ) )
|
||||
break;
|
||||
}
|
||||
|
||||
if ( i >= db.nDb )
|
||||
{
|
||||
sqlite3_snprintf( 200, zErr, "no such database: %s", zName );
|
||||
goto detach_error;
|
||||
}
|
||||
if ( i < 2 )
|
||||
{
|
||||
sqlite3_snprintf( 200, zErr, "cannot detach database %s", zName );
|
||||
goto detach_error;
|
||||
}
|
||||
if ( 0 == db.autoCommit )
|
||||
{
|
||||
sqlite3_snprintf( 200, zErr,
|
||||
"cannot DETACH database within transaction" );
|
||||
goto detach_error;
|
||||
}
|
||||
if ( sqlite3BtreeIsInReadTrans( pDb.pBt ) || sqlite3BtreeIsInBackup( pDb.pBt ) )
|
||||
{
|
||||
sqlite3_snprintf( 200, zErr, "database %s is locked", zName );
|
||||
goto detach_error;
|
||||
}
|
||||
|
||||
sqlite3BtreeClose( ref pDb.pBt );
|
||||
pDb.pBt = null;
|
||||
pDb.pSchema = null;
|
||||
sqlite3ResetInternalSchema( db, -1 );
|
||||
return;
|
||||
|
||||
detach_error:
|
||||
sqlite3_result_error( context, zErr.ToString(), -1 );
|
||||
}
|
||||
|
||||
/*
|
||||
** This procedure generates VDBE code for a single invocation of either the
|
||||
** sqlite_detach() or sqlite_attach() SQL user functions.
|
||||
*/
|
||||
static void codeAttach(
|
||||
Parse pParse, /* The parser context */
|
||||
int type, /* Either SQLITE_ATTACH or SQLITE_DETACH */
|
||||
FuncDef pFunc, /* FuncDef wrapper for detachFunc() or attachFunc() */
|
||||
Expr pAuthArg, /* Expression to pass to authorization callback */
|
||||
Expr pFilename, /* Name of database file */
|
||||
Expr pDbname, /* Name of the database to use internally */
|
||||
Expr pKey /* Database key for encryption extension */
|
||||
)
|
||||
{
|
||||
int rc;
|
||||
NameContext sName;
|
||||
Vdbe v;
|
||||
sqlite3 db = pParse.db;
|
||||
int regArgs;
|
||||
|
||||
sName = new NameContext();// memset( &sName, 0, sizeof(NameContext));
|
||||
sName.pParse = pParse;
|
||||
|
||||
if (
|
||||
SQLITE_OK != ( rc = resolveAttachExpr( sName, pFilename ) ) ||
|
||||
SQLITE_OK != ( rc = resolveAttachExpr( sName, pDbname ) ) ||
|
||||
SQLITE_OK != ( rc = resolveAttachExpr( sName, pKey ) )
|
||||
)
|
||||
{
|
||||
pParse.nErr++;
|
||||
goto attach_end;
|
||||
}
|
||||
|
||||
#if NO_SQLITE_OMIT_AUTHORIZATION
|
||||
if( pAuthArg ){
|
||||
char *zAuthArg;
|
||||
if( pAuthArg->op==TK_STRING ){
|
||||
zAuthArg = pAuthArg->u.zToken;
|
||||
}else{
|
||||
zAuthArg = 0;
|
||||
}
|
||||
rc = sqlite3AuthCheck(pParse, type, zAuthArg, 0, 0);
|
||||
if(rc!=SQLITE_OK ){
|
||||
goto attach_end;
|
||||
}
|
||||
}
|
||||
#endif //* SQLITE_OMIT_AUTHORIZATION */
|
||||
|
||||
v = sqlite3GetVdbe( pParse );
|
||||
regArgs = sqlite3GetTempRange( pParse, 4 );
|
||||
sqlite3ExprCode( pParse, pFilename, regArgs );
|
||||
sqlite3ExprCode( pParse, pDbname, regArgs + 1 );
|
||||
sqlite3ExprCode( pParse, pKey, regArgs + 2 );
|
||||
|
||||
Debug.Assert( v != null /*|| db.mallocFailed != 0 */ );
|
||||
if ( v != null )
|
||||
{
|
||||
sqlite3VdbeAddOp3( v, OP_Function, 0, regArgs + 3 - pFunc.nArg, regArgs + 3 );
|
||||
Debug.Assert( pFunc.nArg == -1 || ( pFunc.nArg & 0xff ) == pFunc.nArg );
|
||||
sqlite3VdbeChangeP5( v, (u8)( pFunc.nArg ) );
|
||||
sqlite3VdbeChangeP4( v, -1, pFunc, P4_FUNCDEF );
|
||||
|
||||
/* Code an OP_Expire. For an ATTACH statement, set P1 to true (expire this
|
||||
** statement only). For DETACH, set it to false (expire all existing
|
||||
** statements).
|
||||
*/
|
||||
sqlite3VdbeAddOp1( v, OP_Expire, ( type == SQLITE_ATTACH ) ? 1 : 0 );
|
||||
}
|
||||
|
||||
attach_end:
|
||||
sqlite3ExprDelete( db, ref pFilename );
|
||||
sqlite3ExprDelete( db, ref pDbname );
|
||||
sqlite3ExprDelete( db, ref pKey );
|
||||
}
|
||||
|
||||
/*
|
||||
** Called by the parser to compile a DETACH statement.
|
||||
**
|
||||
** DETACH pDbname
|
||||
*/
|
||||
static FuncDef detach_func = new FuncDef(
|
||||
1, /* nArg */
|
||||
SQLITE_UTF8, /* iPrefEnc */
|
||||
0, /* flags */
|
||||
null, /* pUserData */
|
||||
null, /* pNext */
|
||||
detachFunc, /* xFunc */
|
||||
null, /* xStep */
|
||||
null, /* xFinalize */
|
||||
"sqlite_detach", /* zName */
|
||||
null, /* pHash */
|
||||
null /* pDestructor */
|
||||
);
|
||||
static void sqlite3Detach( Parse pParse, Expr pDbname )
|
||||
{
|
||||
codeAttach( pParse, SQLITE_DETACH, detach_func, pDbname, null, null, pDbname );
|
||||
}
|
||||
|
||||
/*
|
||||
** Called by the parser to compile an ATTACH statement.
|
||||
**
|
||||
** ATTACH p AS pDbname KEY pKey
|
||||
*/
|
||||
static FuncDef attach_func = new FuncDef(
|
||||
3, /* nArg */
|
||||
SQLITE_UTF8, /* iPrefEnc */
|
||||
0, /* flags */
|
||||
null, /* pUserData */
|
||||
null, /* pNext */
|
||||
attachFunc, /* xFunc */
|
||||
null, /* xStep */
|
||||
null, /* xFinalize */
|
||||
"sqlite_attach", /* zName */
|
||||
null, /* pHash */
|
||||
null /* pDestructor */
|
||||
);
|
||||
static void sqlite3Attach( Parse pParse, Expr p, Expr pDbname, Expr pKey )
|
||||
{
|
||||
codeAttach( pParse, SQLITE_ATTACH, attach_func, p, p, pDbname, pKey );
|
||||
}
|
||||
#endif // * SQLITE_OMIT_ATTACH */
|
||||
|
||||
/*
|
||||
** Initialize a DbFixer structure. This routine must be called prior
|
||||
** to passing the structure to one of the sqliteFixAAAA() routines below.
|
||||
**
|
||||
** The return value indicates whether or not fixation is required. TRUE
|
||||
** means we do need to fix the database references, FALSE means we do not.
|
||||
*/
|
||||
static int sqlite3FixInit(
|
||||
DbFixer pFix, /* The fixer to be initialized */
|
||||
Parse pParse, /* Error messages will be written here */
|
||||
int iDb, /* This is the database that must be used */
|
||||
string zType, /* "view", "trigger", or "index" */
|
||||
Token pName /* Name of the view, trigger, or index */
|
||||
)
|
||||
{
|
||||
sqlite3 db;
|
||||
|
||||
if ( NEVER( iDb < 0 ) || iDb == 1 )
|
||||
return 0;
|
||||
db = pParse.db;
|
||||
Debug.Assert( db.nDb > iDb );
|
||||
pFix.pParse = pParse;
|
||||
pFix.zDb = db.aDb[iDb].zName;
|
||||
pFix.zType = zType;
|
||||
pFix.pName = pName;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** The following set of routines walk through the parse tree and assign
|
||||
** a specific database to all table references where the database name
|
||||
** was left unspecified in the original SQL statement. The pFix structure
|
||||
** must have been initialized by a prior call to sqlite3FixInit().
|
||||
**
|
||||
** These routines are used to make sure that an index, trigger, or
|
||||
** view in one database does not refer to objects in a different database.
|
||||
** (Exception: indices, triggers, and views in the TEMP database are
|
||||
** allowed to refer to anything.) If a reference is explicitly made
|
||||
** to an object in a different database, an error message is added to
|
||||
** pParse.zErrMsg and these routines return non-zero. If everything
|
||||
** checks out, these routines return 0.
|
||||
*/
|
||||
static int sqlite3FixSrcList(
|
||||
DbFixer pFix, /* Context of the fixation */
|
||||
SrcList pList /* The Source list to check and modify */
|
||||
)
|
||||
{
|
||||
int i;
|
||||
string zDb;
|
||||
SrcList_item pItem;
|
||||
|
||||
if ( NEVER( pList == null ) )
|
||||
return 0;
|
||||
zDb = pFix.zDb;
|
||||
for ( i = 0; i < pList.nSrc; i++ )
|
||||
{//, pItem++){
|
||||
pItem = pList.a[i];
|
||||
if ( pItem.zDatabase == null )
|
||||
{
|
||||
pItem.zDatabase = zDb;// sqlite3DbStrDup( pFix.pParse.db, zDb );
|
||||
}
|
||||
else if ( !pItem.zDatabase.Equals( zDb ,StringComparison.InvariantCultureIgnoreCase ) )
|
||||
{
|
||||
sqlite3ErrorMsg( pFix.pParse,
|
||||
"%s %T cannot reference objects in database %s",
|
||||
pFix.zType, pFix.pName, pItem.zDatabase );
|
||||
return 1;
|
||||
}
|
||||
#if !SQLITE_OMIT_VIEW || !SQLITE_OMIT_TRIGGER
|
||||
if ( sqlite3FixSelect( pFix, pItem.pSelect ) != 0 )
|
||||
return 1;
|
||||
if ( sqlite3FixExpr( pFix, pItem.pOn ) != 0 )
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#if !SQLITE_OMIT_VIEW || !SQLITE_OMIT_TRIGGER
|
||||
static int sqlite3FixSelect(
|
||||
DbFixer pFix, /* Context of the fixation */
|
||||
Select pSelect /* The SELECT statement to be fixed to one database */
|
||||
)
|
||||
{
|
||||
while ( pSelect != null )
|
||||
{
|
||||
if ( sqlite3FixExprList( pFix, pSelect.pEList ) != 0 )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if ( sqlite3FixSrcList( pFix, pSelect.pSrc ) != 0 )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if ( sqlite3FixExpr( pFix, pSelect.pWhere ) != 0 )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if ( sqlite3FixExpr( pFix, pSelect.pHaving ) != 0 )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
pSelect = pSelect.pPrior;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static int sqlite3FixExpr(
|
||||
DbFixer pFix, /* Context of the fixation */
|
||||
Expr pExpr /* The expression to be fixed to one database */
|
||||
)
|
||||
{
|
||||
while ( pExpr != null )
|
||||
{
|
||||
if ( ExprHasAnyProperty( pExpr, EP_TokenOnly ) )
|
||||
break;
|
||||
if ( ExprHasProperty( pExpr, EP_xIsSelect ) )
|
||||
{
|
||||
if ( sqlite3FixSelect( pFix, pExpr.x.pSelect ) != 0 )
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( sqlite3FixExprList( pFix, pExpr.x.pList ) != 0 )
|
||||
return 1;
|
||||
}
|
||||
if ( sqlite3FixExpr( pFix, pExpr.pRight ) != 0 )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
pExpr = pExpr.pLeft;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static int sqlite3FixExprList(
|
||||
DbFixer pFix, /* Context of the fixation */
|
||||
ExprList pList /* The expression to be fixed to one database */
|
||||
)
|
||||
{
|
||||
int i;
|
||||
ExprList_item pItem;
|
||||
if ( pList == null )
|
||||
return 0;
|
||||
for ( i = 0; i < pList.nExpr; i++ )//, pItem++ )
|
||||
{
|
||||
pItem = pList.a[i];
|
||||
if ( sqlite3FixExpr( pFix, pItem.pExpr ) != 0 )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !SQLITE_OMIT_TRIGGER
|
||||
static int sqlite3FixTriggerStep(
|
||||
DbFixer pFix, /* Context of the fixation */
|
||||
TriggerStep pStep /* The trigger step be fixed to one database */
|
||||
)
|
||||
{
|
||||
while ( pStep != null )
|
||||
{
|
||||
if ( sqlite3FixSelect( pFix, pStep.pSelect ) != 0 )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if ( sqlite3FixExpr( pFix, pStep.pWhere ) != 0 )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if ( sqlite3FixExprList( pFix, pStep.pExprList ) != 0 )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
pStep = pStep.pNext;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
using sqlite3_value = Sqlite3.Mem;
|
||||
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/*
|
||||
** 2003 January 11
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file contains code used to implement the sqlite3_set_authorizer()
|
||||
** API. This facility is an optional feature of the library. Embedded
|
||||
** systems that do not need this facility may omit it by recompiling
|
||||
** the library with -DSQLITE_OMIT_AUTHORIZATION=1
|
||||
*************************************************************************
|
||||
** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart
|
||||
** C#-SQLite is an independent reimplementation of the SQLite software library
|
||||
**
|
||||
** SQLITE_SOURCE_ID: 2010-08-23 18:52:01 42537b60566f288167f1b5864a5435986838e3a3
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
//#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** All of the code in this file may be omitted by defining a single
|
||||
** macro.
|
||||
*/
|
||||
#if NO_SQLITE_OMIT_AUTHORIZATION
|
||||
|
||||
/*
|
||||
** Set or clear the access authorization function.
|
||||
**
|
||||
** The access authorization function is be called during the compilation
|
||||
** phase to verify that the user has read and/or write access permission on
|
||||
** various fields of the database. The first argument to the auth function
|
||||
** is a copy of the 3rd argument to this routine. The second argument
|
||||
** to the auth function is one of these constants:
|
||||
**
|
||||
** SQLITE_CREATE_INDEX
|
||||
** SQLITE_CREATE_TABLE
|
||||
** SQLITE_CREATE_TEMP_INDEX
|
||||
** SQLITE_CREATE_TEMP_TABLE
|
||||
** SQLITE_CREATE_TEMP_TRIGGER
|
||||
** SQLITE_CREATE_TEMP_VIEW
|
||||
** SQLITE_CREATE_TRIGGER
|
||||
** SQLITE_CREATE_VIEW
|
||||
** SQLITE_DELETE
|
||||
** SQLITE_DROP_INDEX
|
||||
** SQLITE_DROP_TABLE
|
||||
** SQLITE_DROP_TEMP_INDEX
|
||||
** SQLITE_DROP_TEMP_TABLE
|
||||
** SQLITE_DROP_TEMP_TRIGGER
|
||||
** SQLITE_DROP_TEMP_VIEW
|
||||
** SQLITE_DROP_TRIGGER
|
||||
** SQLITE_DROP_VIEW
|
||||
** SQLITE_INSERT
|
||||
** SQLITE_PRAGMA
|
||||
** SQLITE_READ
|
||||
** SQLITE_SELECT
|
||||
** SQLITE_TRANSACTION
|
||||
** SQLITE_UPDATE
|
||||
**
|
||||
** The third and fourth arguments to the auth function are the name of
|
||||
** the table and the column that are being accessed. The auth function
|
||||
** should return either SQLITE_OK, SQLITE_DENY, or SQLITE_IGNORE. If
|
||||
** SQLITE_OK is returned, it means that access is allowed. SQLITE_DENY
|
||||
** means that the SQL statement will never-run - the sqlite3_exec() call
|
||||
** will return with an error. SQLITE_IGNORE means that the SQL statement
|
||||
** should run but attempts to read the specified column will return NULL
|
||||
** and attempts to write the column will be ignored.
|
||||
**
|
||||
** Setting the auth function to NULL disables this hook. The default
|
||||
** setting of the auth function is NULL.
|
||||
*/
|
||||
int sqlite3_set_authorizer(
|
||||
sqlite3 db,
|
||||
int (*xAuth)(void*,int,const char*,const char*,const char*,const char),
|
||||
void *pArg
|
||||
){
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
db->xAuth = xAuth;
|
||||
db->pAuthArg = pArg;
|
||||
sqlite3ExpirePreparedStatements(db);
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Write an error message into pParse->zErrMsg that explains that the
|
||||
** user-supplied authorization function returned an illegal value.
|
||||
*/
|
||||
static void sqliteAuthBadReturnCode(Parse *pParse){
|
||||
sqlite3ErrorMsg(pParse, "authorizer malfunction");
|
||||
pParse->rc = SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
** Invoke the authorization callback for permission to read column zCol from
|
||||
** table zTab in database zDb. This function assumes that an authorization
|
||||
** callback has been registered (i.e. that sqlite3.xAuth is not NULL).
|
||||
**
|
||||
** If SQLITE_IGNORE is returned and pExpr is not NULL, then pExpr is changed
|
||||
** to an SQL NULL expression. Otherwise, if pExpr is NULL, then SQLITE_IGNORE
|
||||
** is treated as SQLITE_DENY. In this case an error is left in pParse.
|
||||
*/
|
||||
int sqlite3AuthReadCol(
|
||||
Parse *pParse, /* The parser context */
|
||||
string zTab, /* Table name */
|
||||
string zCol, /* Column name */
|
||||
int iDb /* Index of containing database. */
|
||||
){
|
||||
sqlite3 db = pParse->db; /* Database handle */
|
||||
string zDb = db->aDb[iDb].zName; /* Name of attached database */
|
||||
int rc; /* Auth callback return code */
|
||||
|
||||
rc = db->xAuth(db->pAuthArg, SQLITE_READ, zTab,zCol,zDb,pParse->zAuthContext);
|
||||
if( rc==SQLITE_DENY ){
|
||||
if( db->nDb>2 || iDb!=0 ){
|
||||
sqlite3ErrorMsg(pParse, "access to %s.%s.%s is prohibited",zDb,zTab,zCol);
|
||||
}else{
|
||||
sqlite3ErrorMsg(pParse, "access to %s.%s is prohibited", zTab, zCol);
|
||||
}
|
||||
pParse->rc = SQLITE_AUTH;
|
||||
}else if( rc!=SQLITE_IGNORE && rc!=SQLITE_OK ){
|
||||
sqliteAuthBadReturnCode(pParse);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** The pExpr should be a TK_COLUMN expression. The table referred to
|
||||
** is in pTabList or else it is the NEW or OLD table of a trigger.
|
||||
** Check to see if it is OK to read this particular column.
|
||||
**
|
||||
** If the auth function returns SQLITE_IGNORE, change the TK_COLUMN
|
||||
** instruction into a TK_NULL. If the auth function returns SQLITE_DENY,
|
||||
** then generate an error.
|
||||
*/
|
||||
void sqlite3AuthRead(
|
||||
Parse *pParse, /* The parser context */
|
||||
Expr *pExpr, /* The expression to check authorization on */
|
||||
Schema *pSchema, /* The schema of the expression */
|
||||
SrcList *pTabList /* All table that pExpr might refer to */
|
||||
){
|
||||
sqlite3 db = pParse->db;
|
||||
Table *pTab = 0; /* The table being read */
|
||||
string zCol; /* Name of the column of the table */
|
||||
int iSrc; /* Index in pTabList->a[] of table being read */
|
||||
int iDb; /* The index of the database the expression refers to */
|
||||
int iCol; /* Index of column in table */
|
||||
|
||||
if( db->xAuth==0 ) return;
|
||||
iDb = sqlite3SchemaToIndex(pParse->db, pSchema);
|
||||
if( iDb<0 ){
|
||||
/* An attempt to read a column out of a subquery or other
|
||||
** temporary table. */
|
||||
return;
|
||||
}
|
||||
Debug.Assert( pExpr->op==TK_COLUMN || pExpr->op==TK_TRIGGER );
|
||||
if( pExpr->op==TK_TRIGGER ){
|
||||
pTab = pParse->pTriggerTab;
|
||||
}else{
|
||||
Debug.Assert( pTabList );
|
||||
for(iSrc=0; ALWAYS(iSrc<pTabList->nSrc); iSrc++){
|
||||
if( pExpr->iTable==pTabList->a[iSrc].iCursor ){
|
||||
pTab = pTabList->a[iSrc].pTab;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
iCol = pExpr->iColumn;
|
||||
if( NEVER(pTab==0) ) return;
|
||||
|
||||
if( iCol>=0 ){
|
||||
Debug.Assert( iCol<pTab->nCol );
|
||||
zCol = pTab->aCol[iCol].zName;
|
||||
}else if( pTab->iPKey>=0 ){
|
||||
Debug.Assert( pTab->iPKey<pTab->nCol );
|
||||
zCol = pTab->aCol[pTab->iPKey].zName;
|
||||
}else{
|
||||
zCol = "ROWID";
|
||||
}
|
||||
Debug.Assert( iDb>=0 && iDb<db->nDb );
|
||||
if( SQLITE_IGNORE==sqlite3AuthReadCol(pParse, pTab->zName, zCol, iDb) ){
|
||||
pExpr->op = TK_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Do an authorization check using the code and arguments given. Return
|
||||
** either SQLITE_OK (zero) or SQLITE_IGNORE or SQLITE_DENY. If SQLITE_DENY
|
||||
** is returned, then the error count and error message in pParse are
|
||||
** modified appropriately.
|
||||
*/
|
||||
int sqlite3AuthCheck(
|
||||
Parse *pParse,
|
||||
int code,
|
||||
string zArg1,
|
||||
string zArg2,
|
||||
string zArg3
|
||||
){
|
||||
sqlite3 db = pParse->db;
|
||||
int rc;
|
||||
|
||||
/* Don't do any authorization checks if the database is initialising
|
||||
** or if the parser is being invoked from within sqlite3_declare_vtab.
|
||||
*/
|
||||
if( db->init.busy || IN_DECLARE_VTAB ){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
if( db->xAuth==0 ){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
rc = db->xAuth(db->pAuthArg, code, zArg1, zArg2, zArg3, pParse->zAuthContext);
|
||||
if( rc==SQLITE_DENY ){
|
||||
sqlite3ErrorMsg(pParse, "not authorized");
|
||||
pParse->rc = SQLITE_AUTH;
|
||||
}else if( rc!=SQLITE_OK && rc!=SQLITE_IGNORE ){
|
||||
rc = SQLITE_DENY;
|
||||
sqliteAuthBadReturnCode(pParse);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Push an authorization context. After this routine is called, the
|
||||
** zArg3 argument to authorization callbacks will be zContext until
|
||||
** popped. Or if pParse==0, this routine is a no-op.
|
||||
*/
|
||||
void sqlite3AuthContextPush(
|
||||
Parse *pParse,
|
||||
AuthContext *pContext,
|
||||
string zContext
|
||||
){
|
||||
Debug.Assert( pParse );
|
||||
pContext->pParse = pParse;
|
||||
pContext->zAuthContext = pParse->zAuthContext;
|
||||
pParse->zAuthContext = zContext;
|
||||
}
|
||||
|
||||
/*
|
||||
** Pop an authorization context that was previously pushed
|
||||
** by sqlite3AuthContextPush
|
||||
*/
|
||||
void sqlite3AuthContextPop(AuthContext *pContext){
|
||||
if( pContext->pParse ){
|
||||
pContext->pParse->zAuthContext = pContext->zAuthContext;
|
||||
pContext->pParse = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif //* SQLITE_OMIT_AUTHORIZATION */
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,818 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
using i64 = System.Int64;
|
||||
using u8 = System.Byte;
|
||||
using u32 = System.UInt32;
|
||||
|
||||
using Pgno = System.UInt32;
|
||||
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
using sqlite3_int64 = System.Int64;
|
||||
using DbPage = Sqlite3.PgHdr;
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/*
|
||||
** 2009 January 28
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file contains the implementation of the sqlite3_backup_XXX()
|
||||
** API functions and the related features.
|
||||
*************************************************************************
|
||||
** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart
|
||||
** C#-SQLite is an independent reimplementation of the SQLite software library
|
||||
**
|
||||
** SQLITE_SOURCE_ID: 2011-05-19 13:26:54 ed1da510a239ea767a01dc332b667119fa3c908e
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
//#include "sqliteInt.h"
|
||||
//#include "btreeInt.h"
|
||||
|
||||
/* Macro to find the minimum of two numeric values.
|
||||
*/
|
||||
#if !MIN
|
||||
//# define MIN(x,y) ((x)<(y)?(x):(y))
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Structure allocated for each backup operation.
|
||||
*/
|
||||
public class sqlite3_backup
|
||||
{
|
||||
public sqlite3 pDestDb; /* Destination database handle */
|
||||
public Btree pDest; /* Destination b-tree file */
|
||||
public u32 iDestSchema; /* Original schema cookie in destination */
|
||||
public int bDestLocked; /* True once a write-transaction is open on pDest */
|
||||
|
||||
public Pgno iNext; /* Page number of the next source page to copy */
|
||||
public sqlite3 pSrcDb; /* Source database handle */
|
||||
public Btree pSrc; /* Source b-tree file */
|
||||
|
||||
public int rc; /* Backup process error code */
|
||||
|
||||
/* These two variables are set by every call to backup_step(). They are
|
||||
** read by calls to backup_remaining() and backup_pagecount().
|
||||
*/
|
||||
public Pgno nRemaining; /* Number of pages left to copy */
|
||||
public Pgno nPagecount; /* Total number of pages to copy */
|
||||
|
||||
public int isAttached; /* True once backup has been registered with pager */
|
||||
public sqlite3_backup pNext; /* Next backup associated with source pager */
|
||||
};
|
||||
|
||||
/*
|
||||
** THREAD SAFETY NOTES:
|
||||
**
|
||||
** Once it has been created using backup_init(), a single sqlite3_backup
|
||||
** structure may be accessed via two groups of thread-safe entry points:
|
||||
**
|
||||
** * Via the sqlite3_backup_XXX() API function backup_step() and
|
||||
** backup_finish(). Both these functions obtain the source database
|
||||
** handle mutex and the mutex associated with the source BtShared
|
||||
** structure, in that order.
|
||||
**
|
||||
** * Via the BackupUpdate() and BackupRestart() functions, which are
|
||||
** invoked by the pager layer to report various state changes in
|
||||
** the page cache associated with the source database. The mutex
|
||||
** associated with the source database BtShared structure will always
|
||||
** be held when either of these functions are invoked.
|
||||
**
|
||||
** The other sqlite3_backup_XXX() API functions, backup_remaining() and
|
||||
** backup_pagecount() are not thread-safe functions. If they are called
|
||||
** while some other thread is calling backup_step() or backup_finish(),
|
||||
** the values returned may be invalid. There is no way for a call to
|
||||
** BackupUpdate() or BackupRestart() to interfere with backup_remaining()
|
||||
** or backup_pagecount().
|
||||
**
|
||||
** Depending on the SQLite configuration, the database handles and/or
|
||||
** the Btree objects may have their own mutexes that require locking.
|
||||
** Non-sharable Btrees (in-memory databases for example), do not have
|
||||
** associated mutexes.
|
||||
*/
|
||||
|
||||
/*
|
||||
** Return a pointer corresponding to database zDb (i.e. "main", "temp")
|
||||
** in connection handle pDb. If such a database cannot be found, return
|
||||
** a NULL pointer and write an error message to pErrorDb.
|
||||
**
|
||||
** If the "temp" database is requested, it may need to be opened by this
|
||||
** function. If an error occurs while doing so, return 0 and write an
|
||||
** error message to pErrorDb.
|
||||
*/
|
||||
static Btree findBtree( sqlite3 pErrorDb, sqlite3 pDb, string zDb )
|
||||
{
|
||||
int i = sqlite3FindDbName( pDb, zDb );
|
||||
|
||||
if ( i == 1 )
|
||||
{
|
||||
Parse pParse;
|
||||
int rc = 0;
|
||||
pParse = new Parse();//sqlite3StackAllocZero(pErrorDb, sizeof(*pParse));
|
||||
if ( pParse == null )
|
||||
{
|
||||
sqlite3Error( pErrorDb, SQLITE_NOMEM, "out of memory" );
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
else
|
||||
{
|
||||
pParse.db = pDb;
|
||||
if ( sqlite3OpenTempDatabase( pParse ) != 0 )
|
||||
{
|
||||
sqlite3Error( pErrorDb, pParse.rc, "%s", pParse.zErrMsg );
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
sqlite3DbFree( pErrorDb, ref pParse.zErrMsg );
|
||||
//sqlite3StackFree( pErrorDb, pParse );
|
||||
}
|
||||
if ( rc != 0 )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if ( i < 0 )
|
||||
{
|
||||
sqlite3Error( pErrorDb, SQLITE_ERROR, "unknown database %s", zDb );
|
||||
return null;
|
||||
}
|
||||
|
||||
return pDb.aDb[i].pBt;
|
||||
}
|
||||
|
||||
/*
|
||||
** Attempt to set the page size of the destination to match the page size
|
||||
** of the source.
|
||||
*/
|
||||
static int setDestPgsz( sqlite3_backup p )
|
||||
{
|
||||
int rc;
|
||||
rc = sqlite3BtreeSetPageSize( p.pDest, sqlite3BtreeGetPageSize( p.pSrc ), -1, 0 );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Create an sqlite3_backup process to copy the contents of zSrcDb from
|
||||
** connection handle pSrcDb to zDestDb in pDestDb. If successful, return
|
||||
** a pointer to the new sqlite3_backup object.
|
||||
**
|
||||
** If an error occurs, NULL is returned and an error code and error message
|
||||
** stored in database handle pDestDb.
|
||||
*/
|
||||
static public sqlite3_backup sqlite3_backup_init(
|
||||
sqlite3 pDestDb, /* Database to write to */
|
||||
string zDestDb, /* Name of database within pDestDb */
|
||||
sqlite3 pSrcDb, /* Database connection to read from */
|
||||
string zSrcDb /* Name of database within pSrcDb */
|
||||
)
|
||||
{
|
||||
sqlite3_backup p; /* Value to return */
|
||||
|
||||
/* Lock the source database handle. The destination database
|
||||
** handle is not locked in this routine, but it is locked in
|
||||
** sqlite3_backup_step(). The user is required to ensure that no
|
||||
** other thread accesses the destination handle for the duration
|
||||
** of the backup operation. Any attempt to use the destination
|
||||
** database connection while a backup is in progress may cause
|
||||
** a malfunction or a deadlock.
|
||||
*/
|
||||
sqlite3_mutex_enter( pSrcDb.mutex );
|
||||
sqlite3_mutex_enter( pDestDb.mutex );
|
||||
|
||||
if ( pSrcDb == pDestDb )
|
||||
{
|
||||
sqlite3Error(
|
||||
pDestDb, SQLITE_ERROR, "source and destination must be distinct"
|
||||
);
|
||||
p = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Allocate space for a new sqlite3_backup object...
|
||||
** EVIDENCE-OF: R-64852-21591 The sqlite3_backup object is created by a
|
||||
** call to sqlite3_backup_init() and is destroyed by a call to
|
||||
** sqlite3_backup_finish(). */
|
||||
p = new sqlite3_backup();// (sqlite3_backup)sqlite3_malloc( sizeof( sqlite3_backup ) );
|
||||
//if ( null == p )
|
||||
//{
|
||||
// sqlite3Error( pDestDb, SQLITE_NOMEM, 0 );
|
||||
//}
|
||||
}
|
||||
|
||||
/* If the allocation succeeded, populate the new object. */
|
||||
if ( p != null )
|
||||
{
|
||||
// memset( p, 0, sizeof( sqlite3_backup ) );
|
||||
p.pSrc = findBtree( pDestDb, pSrcDb, zSrcDb );
|
||||
p.pDest = findBtree( pDestDb, pDestDb, zDestDb );
|
||||
p.pDestDb = pDestDb;
|
||||
p.pSrcDb = pSrcDb;
|
||||
p.iNext = 1;
|
||||
p.isAttached = 0;
|
||||
|
||||
if ( null == p.pSrc || null == p.pDest || setDestPgsz( p ) == SQLITE_NOMEM )
|
||||
{
|
||||
/* One (or both) of the named databases did not exist or an OOM
|
||||
** error was hit. The error has already been written into the
|
||||
** pDestDb handle. All that is left to do here is free the
|
||||
** sqlite3_backup structure.
|
||||
*/
|
||||
//sqlite3_free( ref p );
|
||||
p = null;
|
||||
}
|
||||
}
|
||||
|
||||
if ( p != null )
|
||||
{
|
||||
p.pSrc.nBackup++;
|
||||
}
|
||||
|
||||
sqlite3_mutex_leave( pDestDb.mutex );
|
||||
sqlite3_mutex_leave( pSrcDb.mutex );
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Argument rc is an SQLite error code. Return true if this error is
|
||||
** considered fatal if encountered during a backup operation. All errors
|
||||
** are considered fatal except for SQLITE_BUSY and SQLITE_LOCKED.
|
||||
*/
|
||||
static bool isFatalError( int rc )
|
||||
{
|
||||
return ( rc != SQLITE_OK && rc != SQLITE_BUSY && ALWAYS( rc != SQLITE_LOCKED ) );
|
||||
}
|
||||
|
||||
/*
|
||||
** Parameter zSrcData points to a buffer containing the data for
|
||||
** page iSrcPg from the source database. Copy this data into the
|
||||
** destination database.
|
||||
*/
|
||||
static int backupOnePage( sqlite3_backup p, Pgno iSrcPg, byte[] zSrcData )
|
||||
{
|
||||
Pager pDestPager = sqlite3BtreePager( p.pDest );
|
||||
int nSrcPgsz = sqlite3BtreeGetPageSize( p.pSrc );
|
||||
int nDestPgsz = sqlite3BtreeGetPageSize( p.pDest );
|
||||
int nCopy = MIN( nSrcPgsz, nDestPgsz );
|
||||
i64 iEnd = (i64)iSrcPg * (i64)nSrcPgsz;
|
||||
#if !NOT_SQLITE_HAS_CODEC //#if SQLITE_HAS_CODEC
|
||||
int nSrcReserve = sqlite3BtreeGetReserve(p.pSrc);
|
||||
int nDestReserve = sqlite3BtreeGetReserve(p.pDest);
|
||||
#endif
|
||||
|
||||
int rc = SQLITE_OK;
|
||||
i64 iOff;
|
||||
|
||||
Debug.Assert( p.bDestLocked != 0 );
|
||||
Debug.Assert( !isFatalError( p.rc ) );
|
||||
Debug.Assert( iSrcPg != PENDING_BYTE_PAGE( p.pSrc.pBt ) );
|
||||
Debug.Assert( zSrcData != null );
|
||||
|
||||
/* Catch the case where the destination is an in-memory database and the
|
||||
** page sizes of the source and destination differ.
|
||||
*/
|
||||
if ( nSrcPgsz != nDestPgsz && sqlite3PagerIsMemdb( pDestPager ) )
|
||||
{
|
||||
rc = SQLITE_READONLY;
|
||||
}
|
||||
|
||||
#if !NOT_SQLITE_HAS_CODEC //#if SQLITE_HAS_CODEC
|
||||
/* Backup is not possible if the page size of the destination is changing
|
||||
** and a codec is in use.
|
||||
*/
|
||||
if ( nSrcPgsz != nDestPgsz && sqlite3PagerGetCodec( pDestPager ) != null )
|
||||
{
|
||||
rc = SQLITE_READONLY;
|
||||
}
|
||||
|
||||
/* Backup is not possible if the number of bytes of reserve space differ
|
||||
** between source and destination. If there is a difference, try to
|
||||
** fix the destination to agree with the source. If that is not possible,
|
||||
** then the backup cannot proceed.
|
||||
*/
|
||||
if ( nSrcReserve != nDestReserve )
|
||||
{
|
||||
u32 newPgsz = (u32)nSrcPgsz;
|
||||
rc = sqlite3PagerSetPagesize( pDestPager, ref newPgsz, nSrcReserve );
|
||||
if ( rc == SQLITE_OK && newPgsz != nSrcPgsz )
|
||||
rc = SQLITE_READONLY;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* This loop runs once for each destination page spanned by the source
|
||||
** page. For each iteration, variable iOff is set to the byte offset
|
||||
** of the destination page.
|
||||
*/
|
||||
for ( iOff = iEnd - (i64)nSrcPgsz; rc == SQLITE_OK && iOff < iEnd; iOff += nDestPgsz )
|
||||
{
|
||||
DbPage pDestPg = null;
|
||||
u32 iDest = (u32)( iOff / nDestPgsz ) + 1;
|
||||
if ( iDest == PENDING_BYTE_PAGE( p.pDest.pBt ) )
|
||||
continue;
|
||||
if ( SQLITE_OK == ( rc = sqlite3PagerGet( pDestPager, iDest, ref pDestPg ) )
|
||||
&& SQLITE_OK == ( rc = sqlite3PagerWrite( pDestPg ) )
|
||||
)
|
||||
{
|
||||
//string zIn = &zSrcData[iOff%nSrcPgsz];
|
||||
byte[] zDestData = sqlite3PagerGetData( pDestPg );
|
||||
//string zOut = &zDestData[iOff % nDestPgsz];
|
||||
|
||||
/* Copy the data from the source page into the destination page.
|
||||
** Then clear the Btree layer MemPage.isInit flag. Both this module
|
||||
** and the pager code use this trick (clearing the first byte
|
||||
** of the page 'extra' space to invalidate the Btree layers
|
||||
** cached parse of the page). MemPage.isInit is marked
|
||||
** "MUST BE FIRST" for this purpose.
|
||||
*/
|
||||
Buffer.BlockCopy( zSrcData, (int)( iOff % nSrcPgsz ), zDestData, (int)( iOff % nDestPgsz ), nCopy );// memcpy( zOut, zIn, nCopy );
|
||||
sqlite3PagerGetExtra( pDestPg ).isInit = 0;// ( sqlite3PagerGetExtra( pDestPg ) )[0] = 0;
|
||||
}
|
||||
sqlite3PagerUnref( pDestPg );
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** If pFile is currently larger than iSize bytes, then truncate it to
|
||||
** exactly iSize bytes. If pFile is not larger than iSize bytes, then
|
||||
** this function is a no-op.
|
||||
**
|
||||
** Return SQLITE_OK if everything is successful, or an SQLite error
|
||||
** code if an error occurs.
|
||||
*/
|
||||
static int backupTruncateFile( sqlite3_file pFile, int iSize )
|
||||
{
|
||||
long iCurrent = 0;
|
||||
int rc = sqlite3OsFileSize( pFile, ref iCurrent );
|
||||
if ( rc == SQLITE_OK && iCurrent > iSize )
|
||||
{
|
||||
rc = sqlite3OsTruncate( pFile, iSize );
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Register this backup object with the associated source pager for
|
||||
** callbacks when pages are changed or the cache invalidated.
|
||||
*/
|
||||
static void attachBackupObject( sqlite3_backup p )
|
||||
{
|
||||
sqlite3_backup pp;
|
||||
Debug.Assert( sqlite3BtreeHoldsMutex( p.pSrc ) );
|
||||
pp = sqlite3PagerBackupPtr( sqlite3BtreePager( p.pSrc ) );
|
||||
p.pNext = pp;
|
||||
sqlite3BtreePager( p.pSrc ).pBackup = p; //*pp = p;
|
||||
p.isAttached = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Copy nPage pages from the source b-tree to the destination.
|
||||
*/
|
||||
static public int sqlite3_backup_step( sqlite3_backup p, int nPage )
|
||||
{
|
||||
int rc;
|
||||
int destMode; /* Destination journal mode */
|
||||
int pgszSrc = 0; /* Source page size */
|
||||
int pgszDest = 0; /* Destination page size */
|
||||
|
||||
sqlite3_mutex_enter( p.pSrcDb.mutex );
|
||||
sqlite3BtreeEnter( p.pSrc );
|
||||
if ( p.pDestDb != null )
|
||||
{
|
||||
sqlite3_mutex_enter( p.pDestDb.mutex );
|
||||
}
|
||||
|
||||
rc = p.rc;
|
||||
if ( !isFatalError( rc ) )
|
||||
{
|
||||
Pager pSrcPager = sqlite3BtreePager( p.pSrc ); /* Source pager */
|
||||
Pager pDestPager = sqlite3BtreePager( p.pDest ); /* Dest pager */
|
||||
int ii; /* Iterator variable */
|
||||
Pgno nSrcPage = 0; /* Size of source db in pages */
|
||||
int bCloseTrans = 0; /* True if src db requires unlocking */
|
||||
|
||||
/* If the source pager is currently in a write-transaction, return
|
||||
** SQLITE_BUSY immediately.
|
||||
*/
|
||||
if ( p.pDestDb != null && p.pSrc.pBt.inTransaction == TRANS_WRITE )
|
||||
{
|
||||
rc = SQLITE_BUSY;
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Lock the destination database, if it is not locked already. */
|
||||
if ( SQLITE_OK == rc && p.bDestLocked == 0
|
||||
&& SQLITE_OK == ( rc = sqlite3BtreeBeginTrans( p.pDest, 2 ) )
|
||||
)
|
||||
{
|
||||
p.bDestLocked = 1;
|
||||
sqlite3BtreeGetMeta( p.pDest, BTREE_SCHEMA_VERSION, ref p.iDestSchema );
|
||||
}
|
||||
|
||||
/* If there is no open read-transaction on the source database, open
|
||||
** one now. If a transaction is opened here, then it will be closed
|
||||
** before this function exits.
|
||||
*/
|
||||
if ( rc == SQLITE_OK && !sqlite3BtreeIsInReadTrans( p.pSrc ) )
|
||||
{
|
||||
rc = sqlite3BtreeBeginTrans( p.pSrc, 0 );
|
||||
bCloseTrans = 1;
|
||||
}
|
||||
|
||||
/* Do not allow backup if the destination database is in WAL mode
|
||||
** and the page sizes are different between source and destination */
|
||||
pgszSrc = sqlite3BtreeGetPageSize( p.pSrc );
|
||||
pgszDest = sqlite3BtreeGetPageSize( p.pDest );
|
||||
destMode = sqlite3PagerGetJournalMode( sqlite3BtreePager( p.pDest ) );
|
||||
if ( SQLITE_OK == rc && destMode == PAGER_JOURNALMODE_WAL && pgszSrc != pgszDest )
|
||||
{
|
||||
rc = SQLITE_READONLY;
|
||||
}
|
||||
|
||||
/* Now that there is a read-lock on the source database, query the
|
||||
** source pager for the number of pages in the database.
|
||||
*/
|
||||
nSrcPage = sqlite3BtreeLastPage( p.pSrc );
|
||||
Debug.Assert( nSrcPage >= 0 );
|
||||
|
||||
for ( ii = 0; ( nPage < 0 || ii < nPage ) && p.iNext <= nSrcPage && 0 == rc; ii++ )
|
||||
{
|
||||
Pgno iSrcPg = p.iNext; /* Source page number */
|
||||
if ( iSrcPg != PENDING_BYTE_PAGE( p.pSrc.pBt ) )
|
||||
{
|
||||
DbPage pSrcPg = null; /* Source page object */
|
||||
rc = sqlite3PagerGet( pSrcPager, (u32)iSrcPg, ref pSrcPg );
|
||||
if ( rc == SQLITE_OK )
|
||||
{
|
||||
rc = backupOnePage( p, iSrcPg, sqlite3PagerGetData( pSrcPg ) );
|
||||
sqlite3PagerUnref( pSrcPg );
|
||||
}
|
||||
}
|
||||
p.iNext++;
|
||||
}
|
||||
if ( rc == SQLITE_OK )
|
||||
{
|
||||
p.nPagecount = nSrcPage;
|
||||
p.nRemaining = ( nSrcPage + 1 - p.iNext );
|
||||
if ( p.iNext > nSrcPage )
|
||||
{
|
||||
rc = SQLITE_DONE;
|
||||
}
|
||||
else if ( 0 == p.isAttached )
|
||||
{
|
||||
attachBackupObject( p );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Update the schema version field in the destination database. This
|
||||
** is to make sure that the schema-version really does change in
|
||||
** the case where the source and destination databases have the
|
||||
** same schema version.
|
||||
*/
|
||||
if ( rc == SQLITE_DONE
|
||||
&& ( rc = sqlite3BtreeUpdateMeta( p.pDest, 1, p.iDestSchema + 1 ) ) == SQLITE_OK
|
||||
)
|
||||
{
|
||||
Pgno nDestTruncate;
|
||||
if ( p.pDestDb != null )
|
||||
{
|
||||
sqlite3ResetInternalSchema( p.pDestDb, -1 );
|
||||
}
|
||||
|
||||
/* Set nDestTruncate to the final number of pages in the destination
|
||||
** database. The complication here is that the destination page
|
||||
** size may be different to the source page size.
|
||||
**
|
||||
** If the source page size is smaller than the destination page size,
|
||||
** round up. In this case the call to sqlite3OsTruncate() below will
|
||||
** fix the size of the file. However it is important to call
|
||||
** sqlite3PagerTruncateImage() here so that any pages in the
|
||||
** destination file that lie beyond the nDestTruncate page mark are
|
||||
** journalled by PagerCommitPhaseOne() before they are destroyed
|
||||
** by the file truncation.
|
||||
*/
|
||||
Debug.Assert( pgszSrc == sqlite3BtreeGetPageSize( p.pSrc ) );
|
||||
Debug.Assert( pgszDest == sqlite3BtreeGetPageSize( p.pDest ) );
|
||||
if ( pgszSrc < pgszDest )
|
||||
{
|
||||
int ratio = pgszDest / pgszSrc;
|
||||
nDestTruncate = (Pgno)( ( nSrcPage + ratio - 1 ) / ratio );
|
||||
if ( nDestTruncate == (int)PENDING_BYTE_PAGE( p.pDest.pBt ) )
|
||||
{
|
||||
nDestTruncate--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
nDestTruncate = (Pgno)( nSrcPage * ( pgszSrc / pgszDest ) );
|
||||
}
|
||||
sqlite3PagerTruncateImage( pDestPager, nDestTruncate );
|
||||
|
||||
if ( pgszSrc < pgszDest )
|
||||
{
|
||||
/* If the source page-size is smaller than the destination page-size,
|
||||
** two extra things may need to happen:
|
||||
**
|
||||
** * The destination may need to be truncated, and
|
||||
**
|
||||
** * Data stored on the pages immediately following the
|
||||
** pending-byte page in the source database may need to be
|
||||
** copied into the destination database.
|
||||
*/
|
||||
int iSize = (int)( pgszSrc * nSrcPage );
|
||||
sqlite3_file pFile = sqlite3PagerFile( pDestPager );
|
||||
i64 iOff;
|
||||
i64 iEnd;
|
||||
|
||||
Debug.Assert( pFile != null );
|
||||
Debug.Assert( (i64)nDestTruncate * (i64)pgszDest >= iSize || (
|
||||
nDestTruncate == (int)( PENDING_BYTE_PAGE( p.pDest.pBt ) - 1 )
|
||||
&& iSize >= PENDING_BYTE && iSize <= PENDING_BYTE + pgszDest
|
||||
) );
|
||||
|
||||
/* This call ensures that all data required to recreate the original
|
||||
** database has been stored in the journal for pDestPager and the
|
||||
** journal synced to disk. So at this point we may safely modify
|
||||
** the database file in any way, knowing that if a power failure
|
||||
** occurs, the original database will be reconstructed from the
|
||||
** journal file. */
|
||||
rc = sqlite3PagerCommitPhaseOne( pDestPager, null, true );
|
||||
|
||||
/* Write the extra pages and truncate the database file as required. */
|
||||
iEnd = MIN( PENDING_BYTE + pgszDest, iSize );
|
||||
for (
|
||||
iOff = PENDING_BYTE + pgszSrc;
|
||||
rc == SQLITE_OK && iOff < iEnd;
|
||||
iOff += pgszSrc
|
||||
)
|
||||
{
|
||||
PgHdr pSrcPg = null;
|
||||
u32 iSrcPg = (u32)( ( iOff / pgszSrc ) + 1 );
|
||||
rc = sqlite3PagerGet( pSrcPager, iSrcPg, ref pSrcPg );
|
||||
if ( rc == SQLITE_OK )
|
||||
{
|
||||
byte[] zData = sqlite3PagerGetData( pSrcPg );
|
||||
rc = sqlite3OsWrite( pFile, zData, pgszSrc, iOff );
|
||||
}
|
||||
sqlite3PagerUnref( pSrcPg );
|
||||
}
|
||||
if ( rc == SQLITE_OK )
|
||||
{
|
||||
rc = backupTruncateFile( pFile, (int)iSize );
|
||||
}
|
||||
|
||||
/* Sync the database file to disk. */
|
||||
if ( rc == SQLITE_OK )
|
||||
{
|
||||
rc = sqlite3PagerSync( pDestPager );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = sqlite3PagerCommitPhaseOne( pDestPager, null, false );
|
||||
}
|
||||
|
||||
/* Finish committing the transaction to the destination database. */
|
||||
if ( SQLITE_OK == rc
|
||||
&& SQLITE_OK == ( rc = sqlite3BtreeCommitPhaseTwo( p.pDest, 0 ) )
|
||||
)
|
||||
{
|
||||
rc = SQLITE_DONE;
|
||||
}
|
||||
}
|
||||
|
||||
/* If bCloseTrans is true, then this function opened a read transaction
|
||||
** on the source database. Close the read transaction here. There is
|
||||
** no need to check the return values of the btree methods here, as
|
||||
** "committing" a read-only transaction cannot fail.
|
||||
*/
|
||||
if ( bCloseTrans != 0 )
|
||||
{
|
||||
#if !NDEBUG || SQLITE_COVERAGE_TEST
|
||||
//TESTONLY( int rc2 );
|
||||
//TESTONLY( rc2 = ) sqlite3BtreeCommitPhaseOne(p.pSrc, 0);
|
||||
//TESTONLY( rc2 |= ) sqlite3BtreeCommitPhaseTwo(p.pSrc);
|
||||
int rc2;
|
||||
rc2 = sqlite3BtreeCommitPhaseOne( p.pSrc, "" );
|
||||
rc2 |= sqlite3BtreeCommitPhaseTwo( p.pSrc, 0 );
|
||||
Debug.Assert( rc2 == SQLITE_OK );
|
||||
#else
|
||||
sqlite3BtreeCommitPhaseOne(p.pSrc, null);
|
||||
sqlite3BtreeCommitPhaseTwo(p.pSrc, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
if ( rc == SQLITE_IOERR_NOMEM )
|
||||
{
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
p.rc = rc;
|
||||
}
|
||||
if ( p.pDestDb != null )
|
||||
{
|
||||
sqlite3_mutex_leave( p.pDestDb.mutex );
|
||||
}
|
||||
sqlite3BtreeLeave( p.pSrc );
|
||||
sqlite3_mutex_leave( p.pSrcDb.mutex );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Release all resources associated with an sqlite3_backup* handle.
|
||||
*/
|
||||
static public int sqlite3_backup_finish( sqlite3_backup p )
|
||||
{
|
||||
sqlite3_backup pp; /* Ptr to head of pagers backup list */
|
||||
sqlite3_mutex mutex; /* Mutex to protect source database */
|
||||
int rc; /* Value to return */
|
||||
|
||||
/* Enter the mutexes */
|
||||
if ( p == null )
|
||||
return SQLITE_OK;
|
||||
sqlite3_mutex_enter( p.pSrcDb.mutex );
|
||||
sqlite3BtreeEnter( p.pSrc );
|
||||
mutex = p.pSrcDb.mutex;
|
||||
if ( p.pDestDb != null )
|
||||
{
|
||||
sqlite3_mutex_enter( p.pDestDb.mutex );
|
||||
}
|
||||
|
||||
/* Detach this backup from the source pager. */
|
||||
if ( p.pDestDb != null )
|
||||
{
|
||||
p.pSrc.nBackup--;
|
||||
}
|
||||
if ( p.isAttached != 0 )
|
||||
{
|
||||
pp = sqlite3PagerBackupPtr( sqlite3BtreePager( p.pSrc ) );
|
||||
while ( pp != p )
|
||||
{
|
||||
pp = ( pp ).pNext;
|
||||
}
|
||||
sqlite3BtreePager( p.pSrc ).pBackup = p.pNext;
|
||||
}
|
||||
|
||||
/* If a transaction is still open on the Btree, roll it back. */
|
||||
sqlite3BtreeRollback( p.pDest );
|
||||
|
||||
/* Set the error code of the destination database handle. */
|
||||
rc = ( p.rc == SQLITE_DONE ) ? SQLITE_OK : p.rc;
|
||||
sqlite3Error( p.pDestDb, rc, 0 );
|
||||
|
||||
/* Exit the mutexes and free the backup context structure. */
|
||||
if ( p.pDestDb != null )
|
||||
{
|
||||
sqlite3_mutex_leave( p.pDestDb.mutex );
|
||||
}
|
||||
sqlite3BtreeLeave( p.pSrc );
|
||||
if ( p.pDestDb != null )
|
||||
{
|
||||
/* EVIDENCE-OF: R-64852-21591 The sqlite3_backup object is created by a
|
||||
** call to sqlite3_backup_init() and is destroyed by a call to
|
||||
** sqlite3_backup_finish(). */
|
||||
//sqlite3_free( ref p );
|
||||
}
|
||||
sqlite3_mutex_leave( mutex );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the number of pages still to be backed up as of the most recent
|
||||
** call to sqlite3_backup_step().
|
||||
*/
|
||||
static int sqlite3_backup_remaining( sqlite3_backup p )
|
||||
{
|
||||
return (int)p.nRemaining;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the total number of pages in the source database as of the most
|
||||
** recent call to sqlite3_backup_step().
|
||||
*/
|
||||
static int sqlite3_backup_pagecount( sqlite3_backup p )
|
||||
{
|
||||
return (int)p.nPagecount;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is called after the contents of page iPage of the
|
||||
** source database have been modified. If page iPage has already been
|
||||
** copied into the destination database, then the data written to the
|
||||
** destination is now invalidated. The destination copy of iPage needs
|
||||
** to be updated with the new data before the backup operation is
|
||||
** complete.
|
||||
**
|
||||
** It is assumed that the mutex associated with the BtShared object
|
||||
** corresponding to the source database is held when this function is
|
||||
** called.
|
||||
*/
|
||||
static void sqlite3BackupUpdate( sqlite3_backup pBackup, Pgno iPage, byte[] aData )
|
||||
{
|
||||
sqlite3_backup p; /* Iterator variable */
|
||||
for ( p = pBackup; p != null; p = p.pNext )
|
||||
{
|
||||
Debug.Assert( sqlite3_mutex_held( p.pSrc.pBt.mutex ) );
|
||||
if ( !isFatalError( p.rc ) && iPage < p.iNext )
|
||||
{
|
||||
/* The backup process p has already copied page iPage. But now it
|
||||
** has been modified by a transaction on the source pager. Copy
|
||||
** the new data into the backup.
|
||||
*/
|
||||
int rc;
|
||||
Debug.Assert( p.pDestDb != null );
|
||||
sqlite3_mutex_enter( p.pDestDb.mutex );
|
||||
rc = backupOnePage( p, iPage, aData );
|
||||
sqlite3_mutex_leave( p.pDestDb.mutex );
|
||||
Debug.Assert( rc != SQLITE_BUSY && rc != SQLITE_LOCKED );
|
||||
if ( rc != SQLITE_OK )
|
||||
{
|
||||
p.rc = rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Restart the backup process. This is called when the pager layer
|
||||
** detects that the database has been modified by an external database
|
||||
** connection. In this case there is no way of knowing which of the
|
||||
** pages that have been copied into the destination database are still
|
||||
** valid and which are not, so the entire process needs to be restarted.
|
||||
**
|
||||
** It is assumed that the mutex associated with the BtShared object
|
||||
** corresponding to the source database is held when this function is
|
||||
** called.
|
||||
*/
|
||||
static void sqlite3BackupRestart( sqlite3_backup pBackup )
|
||||
{
|
||||
sqlite3_backup p; /* Iterator variable */
|
||||
for ( p = pBackup; p != null; p = p.pNext )
|
||||
{
|
||||
Debug.Assert( sqlite3_mutex_held( p.pSrc.pBt.mutex ) );
|
||||
p.iNext = 1;
|
||||
}
|
||||
}
|
||||
|
||||
#if !SQLITE_OMIT_VACUUM
|
||||
/*
|
||||
** Copy the complete content of pBtFrom into pBtTo. A transaction
|
||||
** must be active for both files.
|
||||
**
|
||||
** The size of file pTo may be reduced by this operation. If anything
|
||||
** goes wrong, the transaction on pTo is rolled back. If successful, the
|
||||
** transaction is committed before returning.
|
||||
*/
|
||||
static int sqlite3BtreeCopyFile( Btree pTo, Btree pFrom )
|
||||
{
|
||||
int rc;
|
||||
sqlite3_backup b;
|
||||
sqlite3BtreeEnter( pTo );
|
||||
sqlite3BtreeEnter( pFrom );
|
||||
|
||||
/* Set up an sqlite3_backup object. sqlite3_backup.pDestDb must be set
|
||||
** to 0. This is used by the implementations of sqlite3_backup_step()
|
||||
** and sqlite3_backup_finish() to detect that they are being called
|
||||
** from this function, not directly by the user.
|
||||
*/
|
||||
b = new sqlite3_backup();// memset( &b, 0, sizeof( b ) );
|
||||
b.pSrcDb = pFrom.db;
|
||||
b.pSrc = pFrom;
|
||||
b.pDest = pTo;
|
||||
b.iNext = 1;
|
||||
|
||||
/* 0x7FFFFFFF is the hard limit for the number of pages in a database
|
||||
** file. By passing this as the number of pages to copy to
|
||||
** sqlite3_backup_step(), we can guarantee that the copy finishes
|
||||
** within a single call (unless an error occurs). The Debug.Assert() statement
|
||||
** checks this assumption - (p.rc) should be set to either SQLITE_DONE
|
||||
** or an error code.
|
||||
*/
|
||||
sqlite3_backup_step( b, 0x7FFFFFFF );
|
||||
Debug.Assert( b.rc != SQLITE_OK );
|
||||
rc = sqlite3_backup_finish( b );
|
||||
if ( rc == SQLITE_OK )
|
||||
{
|
||||
pTo.pBt.pageSizeFixed = false;
|
||||
}
|
||||
|
||||
sqlite3BtreeLeave( pFrom );
|
||||
sqlite3BtreeLeave( pTo );
|
||||
return rc;
|
||||
}
|
||||
#endif //* SQLITE_OMIT_VACUUM */
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,534 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using Pgno = System.UInt32;
|
||||
using i64 = System.Int64;
|
||||
using u32 = System.UInt32;
|
||||
using BITVEC_TELEM = System.Byte;
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/*
|
||||
** 2008 February 16
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file implements an object that represents a fixed-length
|
||||
** bitmap. Bits are numbered starting with 1.
|
||||
**
|
||||
** A bitmap is used to record which pages of a database file have been
|
||||
** journalled during a transaction, or which pages have the "dont-write"
|
||||
** property. Usually only a few pages are meet either condition.
|
||||
** So the bitmap is usually sparse and has low cardinality.
|
||||
** But sometimes (for example when during a DROP of a large table) most
|
||||
** or all of the pages in a database can get journalled. In those cases,
|
||||
** the bitmap becomes dense with high cardinality. The algorithm needs
|
||||
** to handle both cases well.
|
||||
**
|
||||
** The size of the bitmap is fixed when the object is created.
|
||||
**
|
||||
** All bits are clear when the bitmap is created. Individual bits
|
||||
** may be set or cleared one at a time.
|
||||
**
|
||||
** Test operations are about 100 times more common that set operations.
|
||||
** Clear operations are exceedingly rare. There are usually between
|
||||
** 5 and 500 set operations per Bitvec object, though the number of sets can
|
||||
** sometimes grow into tens of thousands or larger. The size of the
|
||||
** Bitvec object is the number of pages in the database file at the
|
||||
** start of a transaction, and is thus usually less than a few thousand,
|
||||
** but can be as large as 2 billion for a really big database.
|
||||
*************************************************************************
|
||||
** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart
|
||||
** C#-SQLite is an independent reimplementation of the SQLite software library
|
||||
**
|
||||
** SQLITE_SOURCE_ID: 2010-08-23 18:52:01 42537b60566f288167f1b5864a5435986838e3a3
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
//#include "sqliteInt.h"
|
||||
|
||||
/* Size of the Bitvec structure in bytes. */
|
||||
static int BITVEC_SZ = 512;
|
||||
|
||||
|
||||
/* Round the union size down to the nearest pointer boundary, since that's how
|
||||
** it will be aligned within the Bitvec struct. */
|
||||
//#define BITVEC_USIZE (((BITVEC_SZ-(3*sizeof(u32)))/sizeof(Bitvec*))*sizeof(Bitvec*))
|
||||
static int BITVEC_USIZE = ( ( ( BITVEC_SZ - ( 3 * sizeof( u32 ) ) ) / 4 ) * 4 );
|
||||
|
||||
/* Type of the array "element" for the bitmap representation.
|
||||
** Should be a power of 2, and ideally, evenly divide into BITVEC_USIZE.
|
||||
** Setting this to the "natural word" size of your CPU may improve
|
||||
** performance. */
|
||||
//#define BITVEC_TELEM u8
|
||||
//using BITVEC_TELEM = System.Byte;
|
||||
|
||||
/* Size, in bits, of the bitmap element. */
|
||||
//#define BITVEC_SZELEM 8
|
||||
const int BITVEC_SZELEM = 8;
|
||||
|
||||
/* Number of elements in a bitmap array. */
|
||||
//#define BITVEC_NELEM (BITVEC_USIZE/sizeof(BITVEC_TELEM))
|
||||
static int BITVEC_NELEM = (int)( BITVEC_USIZE / sizeof( BITVEC_TELEM ) );
|
||||
|
||||
/* Number of bits in the bitmap array. */
|
||||
//#define BITVEC_NBIT (BITVEC_NELEM*BITVEC_SZELEM)
|
||||
static int BITVEC_NBIT = ( BITVEC_NELEM * BITVEC_SZELEM );
|
||||
|
||||
/* Number of u32 values in hash table. */
|
||||
//#define BITVEC_NINT (BITVEC_USIZE/sizeof(u32))
|
||||
static u32 BITVEC_NINT = (u32)( BITVEC_USIZE / sizeof( u32 ) );
|
||||
|
||||
/* Maximum number of entries in hash table before
|
||||
** sub-dividing and re-hashing. */
|
||||
//#define BITVEC_MXHASH (BITVEC_NINT/2)
|
||||
static int BITVEC_MXHASH = (int)( BITVEC_NINT / 2 );
|
||||
|
||||
/* Hashing function for the aHash representation.
|
||||
** Empirical testing showed that the *37 multiplier
|
||||
** (an arbitrary prime)in the hash function provided
|
||||
** no fewer collisions than the no-op *1. */
|
||||
//#define BITVEC_HASH(X) (((X)*1)%BITVEC_NINT)
|
||||
static u32 BITVEC_HASH( u32 X )
|
||||
{
|
||||
return (u32)( ( ( X ) * 1 ) % BITVEC_NINT );
|
||||
}
|
||||
|
||||
static int BITVEC_NPTR = (int)( BITVEC_USIZE / 4 );//sizeof(Bitvec *));
|
||||
|
||||
|
||||
/*
|
||||
** A bitmap is an instance of the following structure.
|
||||
**
|
||||
** This bitmap records the existence of zero or more bits
|
||||
** with values between 1 and iSize, inclusive.
|
||||
**
|
||||
** There are three possible representations of the bitmap.
|
||||
** If iSize<=BITVEC_NBIT, then Bitvec.u.aBitmap[] is a straight
|
||||
** bitmap. The least significant bit is bit 1.
|
||||
**
|
||||
** If iSize>BITVEC_NBIT and iDivisor==0 then Bitvec.u.aHash[] is
|
||||
** a hash table that will hold up to BITVEC_MXHASH distinct values.
|
||||
**
|
||||
** Otherwise, the value i is redirected into one of BITVEC_NPTR
|
||||
** sub-bitmaps pointed to by Bitvec.u.apSub[]. Each subbitmap
|
||||
** handles up to iDivisor separate values of i. apSub[0] holds
|
||||
** values between 1 and iDivisor. apSub[1] holds values between
|
||||
** iDivisor+1 and 2*iDivisor. apSub[N] holds values between
|
||||
** N*iDivisor+1 and (N+1)*iDivisor. Each subbitmap is normalized
|
||||
** to hold deal with values between 1 and iDivisor.
|
||||
*/
|
||||
public class _u
|
||||
{
|
||||
public BITVEC_TELEM[] aBitmap = new byte[BITVEC_NELEM]; /* Bitmap representation */
|
||||
public u32[] aHash = new u32[BITVEC_NINT]; /* Hash table representation */
|
||||
public Bitvec[] apSub = new Bitvec[BITVEC_NPTR]; /* Recursive representation */
|
||||
}
|
||||
public class Bitvec
|
||||
{
|
||||
public u32 iSize; /* Maximum bit index. Max iSize is 4,294,967,296. */
|
||||
public u32 nSet; /* Number of bits that are set - only valid for aHash
|
||||
** element. Max is BITVEC_NINT. For BITVEC_SZ of 512,
|
||||
** this would be 125. */
|
||||
public u32 iDivisor; /* Number of bits handled by each apSub[] entry. */
|
||||
/* Should >=0 for apSub element. */
|
||||
/* Max iDivisor is max(u32) / BITVEC_NPTR + 1. */
|
||||
/* For a BITVEC_SZ of 512, this would be 34,359,739. */
|
||||
public _u u = new _u();
|
||||
|
||||
public static implicit operator bool( Bitvec b )
|
||||
{
|
||||
return ( b != null );
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
** Create a new bitmap object able to handle bits between 0 and iSize,
|
||||
** inclusive. Return a pointer to the new object. Return NULL if
|
||||
** malloc fails.
|
||||
*/
|
||||
static Bitvec sqlite3BitvecCreate( u32 iSize )
|
||||
{
|
||||
Bitvec p;
|
||||
//Debug.Assert( sizeof(p)==BITVEC_SZ );
|
||||
p = new Bitvec();//sqlite3MallocZero( sizeof(p) );
|
||||
if ( p != null )
|
||||
{
|
||||
p.iSize = iSize;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Check to see if the i-th bit is set. Return true or false.
|
||||
** If p is NULL (if the bitmap has not been created) or if
|
||||
** i is out of range, then return false.
|
||||
*/
|
||||
static int sqlite3BitvecTest( Bitvec p, u32 i )
|
||||
{
|
||||
if ( p == null || i == 0 )
|
||||
return 0;
|
||||
if ( i > p.iSize )
|
||||
return 0;
|
||||
i--;
|
||||
while ( p.iDivisor != 0 )
|
||||
{
|
||||
u32 bin = i / p.iDivisor;
|
||||
i = i % p.iDivisor;
|
||||
p = p.u.apSub[bin];
|
||||
if ( null == p )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if ( p.iSize <= BITVEC_NBIT )
|
||||
{
|
||||
return ( ( p.u.aBitmap[i / BITVEC_SZELEM] & ( 1 << (int)( i & ( BITVEC_SZELEM - 1 ) ) ) ) != 0 ) ? 1 : 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 h = BITVEC_HASH( i++ );
|
||||
while ( p.u.aHash[h] != 0 )
|
||||
{
|
||||
if ( p.u.aHash[h] == i )
|
||||
return 1;
|
||||
h = ( h + 1 ) % BITVEC_NINT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Set the i-th bit. Return 0 on success and an error code if
|
||||
** anything goes wrong.
|
||||
**
|
||||
** This routine might cause sub-bitmaps to be allocated. Failing
|
||||
** to get the memory needed to hold the sub-bitmap is the only
|
||||
** that can go wrong with an insert, assuming p and i are valid.
|
||||
**
|
||||
** The calling function must ensure that p is a valid Bitvec object
|
||||
** and that the value for "i" is within range of the Bitvec object.
|
||||
** Otherwise the behavior is undefined.
|
||||
*/
|
||||
static int sqlite3BitvecSet( Bitvec p, u32 i )
|
||||
{
|
||||
u32 h;
|
||||
if ( p == null )
|
||||
return SQLITE_OK;
|
||||
Debug.Assert( i > 0 );
|
||||
Debug.Assert( i <= p.iSize );
|
||||
i--;
|
||||
while ( ( p.iSize > BITVEC_NBIT ) && p.iDivisor != 0 )
|
||||
{
|
||||
u32 bin = i / p.iDivisor;
|
||||
i = i % p.iDivisor;
|
||||
if ( p.u.apSub[bin] == null )
|
||||
{
|
||||
p.u.apSub[bin] = sqlite3BitvecCreate( p.iDivisor );
|
||||
//if ( p.u.apSub[bin] == null )
|
||||
// return SQLITE_NOMEM;
|
||||
}
|
||||
p = p.u.apSub[bin];
|
||||
}
|
||||
if ( p.iSize <= BITVEC_NBIT )
|
||||
{
|
||||
p.u.aBitmap[i / BITVEC_SZELEM] |= (byte)( 1 << (int)( i & ( BITVEC_SZELEM - 1 ) ) );
|
||||
return SQLITE_OK;
|
||||
}
|
||||
h = BITVEC_HASH( i++ );
|
||||
/* if there wasn't a hash collision, and this doesn't */
|
||||
/* completely fill the hash, then just add it without */
|
||||
/* worring about sub-dividing and re-hashing. */
|
||||
if ( 0 == p.u.aHash[h] )
|
||||
{
|
||||
if ( p.nSet < ( BITVEC_NINT - 1 ) )
|
||||
{
|
||||
goto bitvec_set_end;
|
||||
}
|
||||
else
|
||||
{
|
||||
goto bitvec_set_rehash;
|
||||
}
|
||||
}
|
||||
/* there was a collision, check to see if it's already */
|
||||
/* in hash, if not, try to find a spot for it */
|
||||
do
|
||||
{
|
||||
if ( p.u.aHash[h] == i )
|
||||
return SQLITE_OK;
|
||||
h++;
|
||||
if ( h >= BITVEC_NINT )
|
||||
h = 0;
|
||||
} while ( p.u.aHash[h] != 0 );
|
||||
/* we didn't find it in the hash. h points to the first */
|
||||
/* available free spot. check to see if this is going to */
|
||||
/* make our hash too "full". */
|
||||
bitvec_set_rehash:
|
||||
if ( p.nSet >= BITVEC_MXHASH )
|
||||
{
|
||||
u32 j;
|
||||
int rc;
|
||||
u32[] aiValues = new u32[BITVEC_NINT];// = sqlite3StackAllocRaw(0, sizeof(p->u.aHash));
|
||||
//if ( aiValues == null )
|
||||
//{
|
||||
// return SQLITE_NOMEM;
|
||||
//}
|
||||
//else
|
||||
{
|
||||
|
||||
Buffer.BlockCopy( p.u.aHash, 0, aiValues, 0, aiValues.Length * ( sizeof( u32 ) ) );// memcpy(aiValues, p->u.aHash, sizeof(p->u.aHash));
|
||||
p.u.apSub = new Bitvec[BITVEC_NPTR];//memset(p->u.apSub, 0, sizeof(p->u.apSub));
|
||||
p.iDivisor = (u32)( ( p.iSize + BITVEC_NPTR - 1 ) / BITVEC_NPTR );
|
||||
rc = sqlite3BitvecSet( p, i );
|
||||
for ( j = 0; j < BITVEC_NINT; j++ )
|
||||
{
|
||||
if ( aiValues[j] != 0 )
|
||||
rc |= sqlite3BitvecSet( p, aiValues[j] );
|
||||
}
|
||||
//sqlite3StackFree( null, aiValues );
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
bitvec_set_end:
|
||||
p.nSet++;
|
||||
p.u.aHash[h] = i;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Clear the i-th bit.
|
||||
**
|
||||
** pBuf must be a pointer to at least BITVEC_SZ bytes of temporary storage
|
||||
** that BitvecClear can use to rebuilt its hash table.
|
||||
*/
|
||||
static void sqlite3BitvecClear( Bitvec p, u32 i, u32[] pBuf )
|
||||
{
|
||||
if ( p == null )
|
||||
return;
|
||||
Debug.Assert( i > 0 );
|
||||
i--;
|
||||
while ( p.iDivisor != 0 )
|
||||
{
|
||||
u32 bin = i / p.iDivisor;
|
||||
i = i % p.iDivisor;
|
||||
p = p.u.apSub[bin];
|
||||
if ( null == p )
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ( p.iSize <= BITVEC_NBIT )
|
||||
{
|
||||
p.u.aBitmap[i / BITVEC_SZELEM] &= (byte)~( ( 1 << (int)( i & ( BITVEC_SZELEM - 1 ) ) ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 j;
|
||||
u32[] aiValues = pBuf;
|
||||
Array.Copy( p.u.aHash, aiValues, p.u.aHash.Length );//memcpy(aiValues, p->u.aHash, sizeof(p->u.aHash));
|
||||
p.u.aHash = new u32[aiValues.Length];// memset(p->u.aHash, 0, sizeof(p->u.aHash));
|
||||
p.nSet = 0;
|
||||
for ( j = 0; j < BITVEC_NINT; j++ )
|
||||
{
|
||||
if ( aiValues[j] != 0 && aiValues[j] != ( i + 1 ) )
|
||||
{
|
||||
u32 h = BITVEC_HASH( aiValues[j] - 1 );
|
||||
p.nSet++;
|
||||
while ( p.u.aHash[h] != 0 )
|
||||
{
|
||||
h++;
|
||||
if ( h >= BITVEC_NINT )
|
||||
h = 0;
|
||||
}
|
||||
p.u.aHash[h] = aiValues[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Destroy a bitmap object. Reclaim all memory used.
|
||||
*/
|
||||
static void sqlite3BitvecDestroy( ref Bitvec p )
|
||||
{
|
||||
if ( p == null )
|
||||
return;
|
||||
if ( p.iDivisor != 0 )
|
||||
{
|
||||
u32 i;
|
||||
for ( i = 0; i < BITVEC_NPTR; i++ )
|
||||
{
|
||||
sqlite3BitvecDestroy( ref p.u.apSub[i] );
|
||||
}
|
||||
}
|
||||
//sqlite3_free( ref p );
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the value of the iSize parameter specified when Bitvec *p
|
||||
** was created.
|
||||
*/
|
||||
static u32 sqlite3BitvecSize( Bitvec p )
|
||||
{
|
||||
return p.iSize;
|
||||
}
|
||||
|
||||
#if !SQLITE_OMIT_BUILTIN_TEST
|
||||
/*
|
||||
** Let V[] be an array of unsigned characters sufficient to hold
|
||||
** up to N bits. Let I be an integer between 0 and N. 0<=I<N.
|
||||
** Then the following macros can be used to set, clear, or test
|
||||
** individual bits within V.
|
||||
*/
|
||||
//#define SETBIT(V,I) V[I>>3] |= (1<<(I&7))
|
||||
static void SETBIT( byte[] V, int I )
|
||||
{
|
||||
V[I >> 3] |= (byte)( 1 << ( I & 7 ) );
|
||||
}
|
||||
|
||||
//#define CLEARBIT(V,I) V[I>>3] &= ~(1<<(I&7))
|
||||
static void CLEARBIT( byte[] V, int I )
|
||||
{
|
||||
V[I >> 3] &= (byte)~( 1 << ( I & 7 ) );
|
||||
}
|
||||
|
||||
//#define TESTBIT(V,I) (V[I>>3]&(1<<(I&7)))!=0
|
||||
static int TESTBIT( byte[] V, int I )
|
||||
{
|
||||
return ( V[I >> 3] & ( 1 << ( I & 7 ) ) ) != 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine runs an extensive test of the Bitvec code.
|
||||
**
|
||||
** The input is an array of integers that acts as a program
|
||||
** to test the Bitvec. The integers are opcodes followed
|
||||
** by 0, 1, or 3 operands, depending on the opcode. Another
|
||||
** opcode follows immediately after the last operand.
|
||||
**
|
||||
** There are 6 opcodes numbered from 0 through 5. 0 is the
|
||||
** "halt" opcode and causes the test to end.
|
||||
**
|
||||
** 0 Halt and return the number of errors
|
||||
** 1 N S X Set N bits beginning with S and incrementing by X
|
||||
** 2 N S X Clear N bits beginning with S and incrementing by X
|
||||
** 3 N Set N randomly chosen bits
|
||||
** 4 N Clear N randomly chosen bits
|
||||
** 5 N S X Set N bits from S increment X in array only, not in bitvec
|
||||
**
|
||||
** The opcodes 1 through 4 perform set and clear operations are performed
|
||||
** on both a Bitvec object and on a linear array of bits obtained from malloc.
|
||||
** Opcode 5 works on the linear array only, not on the Bitvec.
|
||||
** Opcode 5 is used to deliberately induce a fault in order to
|
||||
** confirm that error detection works.
|
||||
**
|
||||
** At the conclusion of the test the linear array is compared
|
||||
** against the Bitvec object. If there are any differences,
|
||||
** an error is returned. If they are the same, zero is returned.
|
||||
**
|
||||
** If a memory allocation error occurs, return -1.
|
||||
*/
|
||||
static int sqlite3BitvecBuiltinTest( u32 sz, int[] aOp )
|
||||
{
|
||||
Bitvec pBitvec = null;
|
||||
byte[] pV = null;
|
||||
int rc = -1;
|
||||
int i, nx, pc, op;
|
||||
u32[] pTmpSpace;
|
||||
|
||||
/* Allocate the Bitvec to be tested and a linear array of
|
||||
** bits to act as the reference */
|
||||
pBitvec = sqlite3BitvecCreate( sz );
|
||||
pV = sqlite3_malloc( (int)( sz + 7 ) / 8 + 1 );
|
||||
pTmpSpace = new u32[BITVEC_SZ];// sqlite3_malloc( BITVEC_SZ );
|
||||
if ( pBitvec == null || pV == null || pTmpSpace == null )
|
||||
goto bitvec_end;
|
||||
Array.Clear( pV, 0, (int)( sz + 7 ) / 8 + 1 );// memset( pV, 0, ( sz + 7 ) / 8 + 1 );
|
||||
|
||||
/* NULL pBitvec tests */
|
||||
sqlite3BitvecSet( null, (u32)1 );
|
||||
sqlite3BitvecClear( null, 1, pTmpSpace );
|
||||
|
||||
/* Run the program */
|
||||
pc = 0;
|
||||
while ( ( op = aOp[pc] ) != 0 )
|
||||
{
|
||||
switch ( op )
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
case 5:
|
||||
{
|
||||
nx = 4;
|
||||
i = aOp[pc + 2] - 1;
|
||||
aOp[pc + 2] += aOp[pc + 3];
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
case 4:
|
||||
default:
|
||||
{
|
||||
nx = 2;
|
||||
i64 i64Temp = 0;
|
||||
sqlite3_randomness( sizeof( i64 ), ref i64Temp );
|
||||
i = (int)i64Temp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( ( --aOp[pc + 1] ) > 0 )
|
||||
nx = 0;
|
||||
pc += nx;
|
||||
i = (int)( ( i & 0x7fffffff ) % sz );
|
||||
if ( ( op & 1 ) != 0 )
|
||||
{
|
||||
SETBIT( pV, ( i + 1 ) );
|
||||
if ( op != 5 )
|
||||
{
|
||||
if ( sqlite3BitvecSet( pBitvec, (u32)i + 1 ) != 0 )
|
||||
goto bitvec_end;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CLEARBIT( pV, ( i + 1 ) );
|
||||
sqlite3BitvecClear( pBitvec, (u32)i + 1, pTmpSpace );
|
||||
}
|
||||
}
|
||||
|
||||
/* Test to make sure the linear array exactly matches the
|
||||
** Bitvec object. Start with the assumption that they do
|
||||
** match (rc==0). Change rc to non-zero if a discrepancy
|
||||
** is found.
|
||||
*/
|
||||
rc = sqlite3BitvecTest( null, 0 ) + sqlite3BitvecTest( pBitvec, sz + 1 )
|
||||
+ sqlite3BitvecTest( pBitvec, 0 )
|
||||
+ (int)( sqlite3BitvecSize( pBitvec ) - sz );
|
||||
for ( i = 1; i <= sz; i++ )
|
||||
{
|
||||
if ( ( TESTBIT( pV, i ) ) != sqlite3BitvecTest( pBitvec, (u32)i ) )
|
||||
{
|
||||
rc = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Free allocated structure */
|
||||
bitvec_end:
|
||||
//sqlite3_free( ref pTmpSpace );
|
||||
//sqlite3_free( ref pV );
|
||||
sqlite3BitvecDestroy( ref pBitvec );
|
||||
return rc;
|
||||
}
|
||||
#endif //* SQLITE_OMIT_BUILTIN_TEST */
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,304 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/*
|
||||
** 2007 August 27
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** This file contains code used to implement mutexes on Btree objects.
|
||||
** This code really belongs in btree.c. But btree.c is getting too
|
||||
** big and we want to break it down some. This packaged seemed like
|
||||
** a good breakout.
|
||||
*************************************************************************
|
||||
** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart
|
||||
** C#-SQLite is an independent reimplementation of the SQLite software library
|
||||
**
|
||||
** SQLITE_SOURCE_ID: 2011-05-19 13:26:54 ed1da510a239ea767a01dc332b667119fa3c908e
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
//#include "btreeInt.h"
|
||||
#if NO_SQLITE_OMIT_SHARED_CACHE
|
||||
//#if !SQLITE_OMIT_SHARED_CACHE
|
||||
#if SQLITE_THREADSAFE
|
||||
|
||||
/*
|
||||
** Obtain the BtShared mutex associated with B-Tree handle p. Also,
|
||||
** set BtShared.db to the database handle associated with p and the
|
||||
** p->locked boolean to true.
|
||||
*/
|
||||
static void lockBtreeMutex(Btree *p){
|
||||
assert( p->locked==0 );
|
||||
assert( sqlite3_mutex_notheld(p->pBt->mutex) );
|
||||
assert( sqlite3_mutex_held(p->db->mutex) );
|
||||
|
||||
sqlite3_mutex_enter(p->pBt->mutex);
|
||||
p->pBt->db = p->db;
|
||||
p->locked = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Release the BtShared mutex associated with B-Tree handle p and
|
||||
** clear the p->locked boolean.
|
||||
*/
|
||||
static void unlockBtreeMutex(Btree *p){
|
||||
BtShared *pBt = p->pBt;
|
||||
assert( p->locked==1 );
|
||||
assert( sqlite3_mutex_held(pBt->mutex) );
|
||||
assert( sqlite3_mutex_held(p->db->mutex) );
|
||||
assert( p->db==pBt->db );
|
||||
|
||||
sqlite3_mutex_leave(pBt->mutex);
|
||||
p->locked = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Enter a mutex on the given BTree object.
|
||||
**
|
||||
** If the object is not sharable, then no mutex is ever required
|
||||
** and this routine is a no-op. The underlying mutex is non-recursive.
|
||||
** But we keep a reference count in Btree.wantToLock so the behavior
|
||||
** of this interface is recursive.
|
||||
**
|
||||
** To avoid deadlocks, multiple Btrees are locked in the same order
|
||||
** by all database connections. The p->pNext is a list of other
|
||||
** Btrees belonging to the same database connection as the p Btree
|
||||
** which need to be locked after p. If we cannot get a lock on
|
||||
** p, then first unlock all of the others on p->pNext, then wait
|
||||
** for the lock to become available on p, then relock all of the
|
||||
** subsequent Btrees that desire a lock.
|
||||
*/
|
||||
void sqlite3BtreeEnter(Btree *p){
|
||||
Btree *pLater;
|
||||
|
||||
/* Some basic sanity checking on the Btree. The list of Btrees
|
||||
** connected by pNext and pPrev should be in sorted order by
|
||||
** Btree.pBt value. All elements of the list should belong to
|
||||
** the same connection. Only shared Btrees are on the list. */
|
||||
assert( p->pNext==0 || p->pNext->pBt>p->pBt );
|
||||
assert( p->pPrev==0 || p->pPrev->pBt<p->pBt );
|
||||
assert( p->pNext==0 || p->pNext->db==p->db );
|
||||
assert( p->pPrev==0 || p->pPrev->db==p->db );
|
||||
assert( p->sharable || (p->pNext==0 && p->pPrev==0) );
|
||||
|
||||
/* Check for locking consistency */
|
||||
assert( !p->locked || p->wantToLock>0 );
|
||||
assert( p->sharable || p->wantToLock==0 );
|
||||
|
||||
/* We should already hold a lock on the database connection */
|
||||
assert( sqlite3_mutex_held(p->db->mutex) );
|
||||
|
||||
/* Unless the database is sharable and unlocked, then BtShared.db
|
||||
** should already be set correctly. */
|
||||
assert( (p->locked==0 && p->sharable) || p->pBt->db==p->db );
|
||||
|
||||
if( !p->sharable ) return;
|
||||
p->wantToLock++;
|
||||
if( p->locked ) return;
|
||||
|
||||
/* In most cases, we should be able to acquire the lock we
|
||||
** want without having to go throught the ascending lock
|
||||
** procedure that follows. Just be sure not to block.
|
||||
*/
|
||||
if( sqlite3_mutex_try(p->pBt->mutex)==SQLITE_OK ){
|
||||
p->pBt->db = p->db;
|
||||
p->locked = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
/* To avoid deadlock, first release all locks with a larger
|
||||
** BtShared address. Then acquire our lock. Then reacquire
|
||||
** the other BtShared locks that we used to hold in ascending
|
||||
** order.
|
||||
*/
|
||||
for(pLater=p->pNext; pLater; pLater=pLater->pNext){
|
||||
assert( pLater->sharable );
|
||||
assert( pLater->pNext==0 || pLater->pNext->pBt>pLater->pBt );
|
||||
assert( !pLater->locked || pLater->wantToLock>0 );
|
||||
if( pLater->locked ){
|
||||
unlockBtreeMutex(pLater);
|
||||
}
|
||||
}
|
||||
lockBtreeMutex(p);
|
||||
for(pLater=p->pNext; pLater; pLater=pLater->pNext){
|
||||
if( pLater->wantToLock ){
|
||||
lockBtreeMutex(pLater);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Exit the recursive mutex on a Btree.
|
||||
*/
|
||||
void sqlite3BtreeLeave(Btree *p){
|
||||
if( p->sharable ){
|
||||
assert( p->wantToLock>0 );
|
||||
p->wantToLock--;
|
||||
if( p->wantToLock==0 ){
|
||||
unlockBtreeMutex(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if NDEBUG
|
||||
/*
|
||||
** Return true if the BtShared mutex is held on the btree, or if the
|
||||
** B-Tree is not marked as sharable.
|
||||
**
|
||||
** This routine is used only from within assert() statements.
|
||||
*/
|
||||
int sqlite3BtreeHoldsMutex(Btree *p){
|
||||
assert( p->sharable==0 || p->locked==0 || p->wantToLock>0 );
|
||||
assert( p->sharable==0 || p->locked==0 || p->db==p->pBt->db );
|
||||
assert( p->sharable==0 || p->locked==0 || sqlite3_mutex_held(p->pBt->mutex) );
|
||||
assert( p->sharable==0 || p->locked==0 || sqlite3_mutex_held(p->db->mutex) );
|
||||
|
||||
return (p->sharable==0 || p->locked);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if !NOT_SQLITE_OMIT_INCRBLOB
|
||||
/*
|
||||
** Enter and leave a mutex on a Btree given a cursor owned by that
|
||||
** Btree. These entry points are used by incremental I/O and can be
|
||||
** omitted if that module is not used.
|
||||
*/
|
||||
void sqlite3BtreeEnterCursor(BtCursor *pCur){
|
||||
sqlite3BtreeEnter(pCur->pBtree);
|
||||
}
|
||||
void sqlite3BtreeLeaveCursor(BtCursor *pCur){
|
||||
sqlite3BtreeLeave(pCur->pBtree);
|
||||
}
|
||||
#endif //* SQLITE_OMIT_INCRBLOB */
|
||||
|
||||
|
||||
/*
|
||||
** Enter the mutex on every Btree associated with a database
|
||||
** connection. This is needed (for example) prior to parsing
|
||||
** a statement since we will be comparing table and column names
|
||||
** against all schemas and we do not want those schemas being
|
||||
** reset out from under us.
|
||||
**
|
||||
** There is a corresponding leave-all procedures.
|
||||
**
|
||||
** Enter the mutexes in accending order by BtShared pointer address
|
||||
** to avoid the possibility of deadlock when two threads with
|
||||
** two or more btrees in common both try to lock all their btrees
|
||||
** at the same instant.
|
||||
*/
|
||||
void sqlite3BtreeEnterAll(sqlite3 db){
|
||||
int i;
|
||||
Btree *p;
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
for(i=0; i<db->nDb; i++){
|
||||
p = db->aDb[i].pBt;
|
||||
if( p ) sqlite3BtreeEnter(p);
|
||||
}
|
||||
}
|
||||
void sqlite3BtreeLeaveAll(sqlite3 db){
|
||||
int i;
|
||||
Btree *p;
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
for(i=0; i<db->nDb; i++){
|
||||
p = db->aDb[i].pBt;
|
||||
if( p ) sqlite3BtreeLeave(p);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if a particular Btree requires a lock. Return FALSE if
|
||||
** no lock is ever required since it is not sharable.
|
||||
*/
|
||||
int sqlite3BtreeSharable(Btree *p){
|
||||
return p->sharable;
|
||||
}
|
||||
|
||||
#if NDEBUG
|
||||
/*
|
||||
** Return true if the current thread holds the database connection
|
||||
** mutex and all required BtShared mutexes.
|
||||
**
|
||||
** This routine is used inside assert() statements only.
|
||||
*/
|
||||
int sqlite3BtreeHoldsAllMutexes(sqlite3 db){
|
||||
int i;
|
||||
if( !sqlite3_mutex_held(db->mutex) ){
|
||||
return 0;
|
||||
}
|
||||
for(i=0; i<db->nDb; i++){
|
||||
Btree *p;
|
||||
p = db->aDb[i].pBt;
|
||||
if( p && p->sharable &&
|
||||
(p->wantToLock==0 || !sqlite3_mutex_held(p->pBt->mutex)) ){
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
#endif //* NDEBUG */
|
||||
|
||||
#if NDEBUG
|
||||
/*
|
||||
** Return true if the correct mutexes are held for accessing the
|
||||
** db->aDb[iDb].pSchema structure. The mutexes required for schema
|
||||
** access are:
|
||||
**
|
||||
** (1) The mutex on db
|
||||
** (2) if iDb!=1, then the mutex on db->aDb[iDb].pBt.
|
||||
**
|
||||
** If pSchema is not NULL, then iDb is computed from pSchema and
|
||||
** db using sqlite3SchemaToIndex().
|
||||
*/
|
||||
int sqlite3SchemaMutexHeld(sqlite3 db, int iDb, Schema *pSchema){
|
||||
Btree *p;
|
||||
assert( db!=0 );
|
||||
if( pSchema ) iDb = sqlite3SchemaToIndex(db, pSchema);
|
||||
assert( iDb>=0 && iDb<db->nDb );
|
||||
if( !sqlite3_mutex_held(db->mutex) ) return 0;
|
||||
if( iDb==1 ) return 1;
|
||||
p = db->aDb[iDb].pBt;
|
||||
assert( p!=0 );
|
||||
return p->sharable==0 || p->locked==1;
|
||||
}
|
||||
#endif //* NDEBUG */
|
||||
|
||||
#else //* SQLITE_THREADSAFE>0 above. SQLITE_THREADSAFE==0 below */
|
||||
/*
|
||||
** The following are special cases for mutex enter routines for use
|
||||
** in single threaded applications that use shared cache. Except for
|
||||
** these two routines, all mutex operations are no-ops in that case and
|
||||
** are null #defines in btree.h.
|
||||
**
|
||||
** If shared cache is disabled, then all btree mutex routines, including
|
||||
** the ones below, are no-ops and are null #defines in btree.h.
|
||||
*/
|
||||
|
||||
void sqlite3BtreeEnter(Btree *p){
|
||||
p->pBt->db = p->db;
|
||||
}
|
||||
void sqlite3BtreeEnterAll(sqlite3 db){
|
||||
int i;
|
||||
for(i=0; i<db->nDb; i++){
|
||||
Btree *p = db->aDb[i].pBt;
|
||||
if( p ){
|
||||
p->pBt->db = p->db;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif //* if SQLITE_THREADSAFE */
|
||||
#endif //* ifndef SQLITE_OMIT_SHARED_CACHE */
|
||||
|
||||
}
|
||||
}
|
||||
9567
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/btree_c.cs
Normal file
9567
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/btree_c.cs
Normal file
File diff suppressed because it is too large
Load Diff
4423
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/build_c.cs
Normal file
4423
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/build_c.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,559 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
using i16 = System.Int16;
|
||||
using u8 = System.Byte;
|
||||
using u16 = System.UInt16;
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
using sqlite3_value = Sqlite3.Mem;
|
||||
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/*
|
||||
** 2005 May 23
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** This file contains functions used to access the internal hash tables
|
||||
** of user defined functions and collation sequences.
|
||||
*************************************************************************
|
||||
** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart
|
||||
** C#-SQLite is an independent reimplementation of the SQLite software library
|
||||
**
|
||||
** SQLITE_SOURCE_ID: 2011-05-19 13:26:54 ed1da510a239ea767a01dc332b667119fa3c908e
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
|
||||
//#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** Invoke the 'collation needed' callback to request a collation sequence
|
||||
** in the encoding enc of name zName, length nName.
|
||||
*/
|
||||
static void callCollNeeded( sqlite3 db, int enc, string zName )
|
||||
{
|
||||
Debug.Assert( db.xCollNeeded == null || db.xCollNeeded16 == null );
|
||||
if ( db.xCollNeeded != null )
|
||||
{
|
||||
string zExternal = zName;// sqlite3DbStrDup(db, zName);
|
||||
if ( zExternal == null )
|
||||
return;
|
||||
db.xCollNeeded( db.pCollNeededArg, db, enc, zExternal );
|
||||
sqlite3DbFree( db, ref zExternal );
|
||||
}
|
||||
#if NO_SQLITE_OMIT_UTF16 //#if !SQLITE_OMIT_UTF16
|
||||
if( db.xCollNeeded16!=null ){
|
||||
string zExternal;
|
||||
sqlite3_value pTmp = sqlite3ValueNew(db);
|
||||
sqlite3ValueSetStr(pTmp, -1, zName, SQLITE_UTF8, SQLITE_STATIC);
|
||||
zExternal = sqlite3ValueText(pTmp, SQLITE_UTF16NATIVE);
|
||||
if( zExternal!="" ){
|
||||
db.xCollNeeded16( db.pCollNeededArg, db, db.aDbStatic[0].pSchema.enc, zExternal );//(int)ENC(db), zExternal);
|
||||
}
|
||||
sqlite3ValueFree(ref pTmp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine is called if the collation factory fails to deliver a
|
||||
** collation function in the best encoding but there may be other versions
|
||||
** of this collation function (for other text encodings) available. Use one
|
||||
** of these instead if they exist. Avoid a UTF-8 <. UTF-16 conversion if
|
||||
** possible.
|
||||
*/
|
||||
static int synthCollSeq( sqlite3 db, CollSeq pColl )
|
||||
{
|
||||
CollSeq pColl2;
|
||||
string z = pColl.zName;
|
||||
int i;
|
||||
byte[] aEnc = { SQLITE_UTF16BE, SQLITE_UTF16LE, SQLITE_UTF8 };
|
||||
for ( i = 0; i < 3; i++ )
|
||||
{
|
||||
pColl2 = sqlite3FindCollSeq( db, aEnc[i], z, 0 );
|
||||
if ( pColl2.xCmp != null )
|
||||
{
|
||||
pColl = pColl2.Copy(); //memcpy(pColl, pColl2, sizeof(CollSeq));
|
||||
pColl.xDel = null; /* Do not copy the destructor */
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is responsible for invoking the collation factory callback
|
||||
** or substituting a collation sequence of a different encoding when the
|
||||
** requested collation sequence is not available in the desired encoding.
|
||||
**
|
||||
** If it is not NULL, then pColl must point to the database native encoding
|
||||
** collation sequence with name zName, length nName.
|
||||
**
|
||||
** The return value is either the collation sequence to be used in database
|
||||
** db for collation type name zName, length nName, or NULL, if no collation
|
||||
** sequence can be found.
|
||||
**
|
||||
** See also: sqlite3LocateCollSeq(), sqlite3FindCollSeq()
|
||||
*/
|
||||
static CollSeq sqlite3GetCollSeq(
|
||||
sqlite3 db, /* The database connection */
|
||||
u8 enc, /* The desired encoding for the collating sequence */
|
||||
CollSeq pColl, /* Collating sequence with native encoding, or NULL */
|
||||
string zName /* Collating sequence name */
|
||||
)
|
||||
{
|
||||
CollSeq p;
|
||||
|
||||
p = pColl;
|
||||
if ( p == null )
|
||||
{
|
||||
p = sqlite3FindCollSeq( db, enc, zName, 0 );
|
||||
}
|
||||
if ( p == null || p.xCmp == null )
|
||||
{
|
||||
/* No collation sequence of this type for this encoding is registered.
|
||||
** Call the collation factory to see if it can supply us with one.
|
||||
*/
|
||||
callCollNeeded( db, enc, zName );
|
||||
p = sqlite3FindCollSeq( db, enc, zName, 0 );
|
||||
}
|
||||
if ( p != null && p.xCmp == null && synthCollSeq( db, p ) != 0 )
|
||||
{
|
||||
p = null;
|
||||
}
|
||||
Debug.Assert( p == null || p.xCmp != null );
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine is called on a collation sequence before it is used to
|
||||
** check that it is defined. An undefined collation sequence exists when
|
||||
** a database is loaded that contains references to collation sequences
|
||||
** that have not been defined by sqlite3_create_collation() etc.
|
||||
**
|
||||
** If required, this routine calls the 'collation needed' callback to
|
||||
** request a definition of the collating sequence. If this doesn't work,
|
||||
** an equivalent collating sequence that uses a text encoding different
|
||||
** from the main database is substituted, if one is available.
|
||||
*/
|
||||
static int sqlite3CheckCollSeq( Parse pParse, CollSeq pColl )
|
||||
{
|
||||
if ( pColl != null )
|
||||
{
|
||||
string zName = pColl.zName;
|
||||
sqlite3 db = pParse.db;
|
||||
CollSeq p = sqlite3GetCollSeq( db, ENC( db ), pColl, zName );
|
||||
if ( null == p )
|
||||
{
|
||||
sqlite3ErrorMsg( pParse, "no such collation sequence: %s", zName );
|
||||
pParse.nErr++;
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
//
|
||||
//Debug.Assert(p == pColl);
|
||||
if ( p != pColl ) // Had to lookup appropriate sequence
|
||||
{
|
||||
pColl.enc = p.enc;
|
||||
pColl.pUser = p.pUser;
|
||||
pColl.type = p.type;
|
||||
pColl.xCmp = p.xCmp;
|
||||
pColl.xDel = p.xDel;
|
||||
}
|
||||
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Locate and return an entry from the db.aCollSeq hash table. If the entry
|
||||
** specified by zName and nName is not found and parameter 'create' is
|
||||
** true, then create a new entry. Otherwise return NULL.
|
||||
**
|
||||
** Each pointer stored in the sqlite3.aCollSeq hash table contains an
|
||||
** array of three CollSeq structures. The first is the collation sequence
|
||||
** prefferred for UTF-8, the second UTF-16le, and the third UTF-16be.
|
||||
**
|
||||
** Stored immediately after the three collation sequences is a copy of
|
||||
** the collation sequence name. A pointer to this string is stored in
|
||||
** each collation sequence structure.
|
||||
*/
|
||||
static CollSeq[] findCollSeqEntry(
|
||||
sqlite3 db, /* Database connection */
|
||||
string zName, /* Name of the collating sequence */
|
||||
int create /* Create a new entry if true */
|
||||
)
|
||||
{
|
||||
CollSeq[] pColl;
|
||||
int nName = sqlite3Strlen30( zName );
|
||||
pColl = sqlite3HashFind( db.aCollSeq, zName, nName, (CollSeq[])null );
|
||||
|
||||
if ( ( null == pColl ) && create != 0 )
|
||||
{
|
||||
pColl = new CollSeq[3]; //sqlite3DbMallocZero(db, 3*sizeof(*pColl) + nName + 1 );
|
||||
if ( pColl != null )
|
||||
{
|
||||
CollSeq pDel = null;
|
||||
pColl[0] = new CollSeq();
|
||||
pColl[0].zName = zName;
|
||||
pColl[0].enc = SQLITE_UTF8;
|
||||
pColl[1] = new CollSeq();
|
||||
pColl[1].zName = zName;
|
||||
pColl[1].enc = SQLITE_UTF16LE;
|
||||
pColl[2] = new CollSeq();
|
||||
pColl[2].zName = zName;
|
||||
pColl[2].enc = SQLITE_UTF16BE;
|
||||
//memcpy(pColl[0].zName, zName, nName);
|
||||
//pColl[0].zName[nName] = 0;
|
||||
CollSeq[] pDelArray = sqlite3HashInsert( ref db.aCollSeq, pColl[0].zName, nName, pColl );
|
||||
if ( pDelArray != null )
|
||||
pDel = pDelArray[0];
|
||||
/* If a malloc() failure occurred in sqlite3HashInsert(), it will
|
||||
** return the pColl pointer to be deleted (because it wasn't added
|
||||
** to the hash table).
|
||||
*/
|
||||
Debug.Assert( pDel == null || pDel == pColl[0] );
|
||||
if ( pDel != null )
|
||||
{
|
||||
//// db.mallocFailed = 1;
|
||||
pDel = null; //was sqlite3DbFree(db,ref pDel);
|
||||
pColl = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return pColl;
|
||||
}
|
||||
|
||||
/*
|
||||
** Parameter zName points to a UTF-8 encoded string nName bytes long.
|
||||
** Return the CollSeq* pointer for the collation sequence named zName
|
||||
** for the encoding 'enc' from the database 'db'.
|
||||
**
|
||||
** If the entry specified is not found and 'create' is true, then create a
|
||||
** new entry. Otherwise return NULL.
|
||||
**
|
||||
** A separate function sqlite3LocateCollSeq() is a wrapper around
|
||||
** this routine. sqlite3LocateCollSeq() invokes the collation factory
|
||||
** if necessary and generates an error message if the collating sequence
|
||||
** cannot be found.
|
||||
**
|
||||
** See also: sqlite3LocateCollSeq(), sqlite3GetCollSeq()
|
||||
*/
|
||||
static CollSeq sqlite3FindCollSeq(
|
||||
sqlite3 db,
|
||||
u8 enc,
|
||||
string zName,
|
||||
u8 create
|
||||
)
|
||||
{
|
||||
CollSeq[] pColl;
|
||||
if ( zName != null )
|
||||
{
|
||||
pColl = findCollSeqEntry( db, zName, create );
|
||||
}
|
||||
else
|
||||
{
|
||||
pColl = new CollSeq[enc];
|
||||
pColl[enc - 1] = db.pDfltColl;
|
||||
}
|
||||
Debug.Assert( SQLITE_UTF8 == 1 && SQLITE_UTF16LE == 2 && SQLITE_UTF16BE == 3 );
|
||||
Debug.Assert( enc >= SQLITE_UTF8 && enc <= SQLITE_UTF16BE );
|
||||
if ( pColl != null )
|
||||
{
|
||||
enc -= 1; // if (pColl != null) pColl += enc - 1;
|
||||
return pColl[enc];
|
||||
}
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
/* During the search for the best function definition, this procedure
|
||||
** is called to test how well the function passed as the first argument
|
||||
** matches the request for a function with nArg arguments in a system
|
||||
** that uses encoding enc. The value returned indicates how well the
|
||||
** request is matched. A higher value indicates a better match.
|
||||
**
|
||||
** The returned value is always between 0 and 6, as follows:
|
||||
**
|
||||
** 0: Not a match, or if nArg<0 and the function is has no implementation.
|
||||
** 1: A variable arguments function that prefers UTF-8 when a UTF-16
|
||||
** encoding is requested, or vice versa.
|
||||
** 2: A variable arguments function that uses UTF-16BE when UTF-16LE is
|
||||
** requested, or vice versa.
|
||||
** 3: A variable arguments function using the same text encoding.
|
||||
** 4: A function with the exact number of arguments requested that
|
||||
** prefers UTF-8 when a UTF-16 encoding is requested, or vice versa.
|
||||
** 5: A function with the exact number of arguments requested that
|
||||
** prefers UTF-16LE when UTF-16BE is requested, or vice versa.
|
||||
** 6: An exact match.
|
||||
**
|
||||
*/
|
||||
static int matchQuality( FuncDef p, int nArg, int enc )
|
||||
{
|
||||
int match = 0;
|
||||
if ( p.nArg == -1 || p.nArg == nArg
|
||||
|| ( nArg == -1 && ( p.xFunc != null || p.xStep != null ) )
|
||||
)
|
||||
{
|
||||
match = 1;
|
||||
if ( p.nArg == nArg || nArg == -1 )
|
||||
{
|
||||
match = 4;
|
||||
}
|
||||
if ( enc == p.iPrefEnc )
|
||||
{
|
||||
match += 2;
|
||||
}
|
||||
else if ( ( enc == SQLITE_UTF16LE && p.iPrefEnc == SQLITE_UTF16BE ) ||
|
||||
( enc == SQLITE_UTF16BE && p.iPrefEnc == SQLITE_UTF16LE ) )
|
||||
{
|
||||
match += 1;
|
||||
}
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
/*
|
||||
** Search a FuncDefHash for a function with the given name. Return
|
||||
** a pointer to the matching FuncDef if found, or 0 if there is no match.
|
||||
*/
|
||||
static FuncDef functionSearch(
|
||||
FuncDefHash pHash, /* Hash table to search */
|
||||
int h, /* Hash of the name */
|
||||
string zFunc, /* Name of function */
|
||||
int nFunc /* Number of bytes in zFunc */
|
||||
)
|
||||
{
|
||||
FuncDef p;
|
||||
for ( p = pHash.a[h]; p != null; p = p.pHash )
|
||||
{
|
||||
if ( p.zName.Length == nFunc && p.zName.StartsWith( zFunc, StringComparison.InvariantCultureIgnoreCase ) )
|
||||
{
|
||||
return p;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
** Insert a new FuncDef into a FuncDefHash hash table.
|
||||
*/
|
||||
static void sqlite3FuncDefInsert(
|
||||
FuncDefHash pHash, /* The hash table into which to insert */
|
||||
FuncDef pDef /* The function definition to insert */
|
||||
)
|
||||
{
|
||||
FuncDef pOther;
|
||||
int nName = sqlite3Strlen30( pDef.zName );
|
||||
u8 c1 = (u8)pDef.zName[0];
|
||||
int h = ( sqlite3UpperToLower[c1] + nName ) % ArraySize( pHash.a );
|
||||
pOther = functionSearch( pHash, h, pDef.zName, nName );
|
||||
if ( pOther != null )
|
||||
{
|
||||
Debug.Assert( pOther != pDef && pOther.pNext != pDef );
|
||||
pDef.pNext = pOther.pNext;
|
||||
pOther.pNext = pDef;
|
||||
}
|
||||
else
|
||||
{
|
||||
pDef.pNext = null;
|
||||
pDef.pHash = pHash.a[h];
|
||||
pHash.a[h] = pDef;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Locate a user function given a name, a number of arguments and a flag
|
||||
** indicating whether the function prefers UTF-16 over UTF-8. Return a
|
||||
** pointer to the FuncDef structure that defines that function, or return
|
||||
** NULL if the function does not exist.
|
||||
**
|
||||
** If the createFlag argument is true, then a new (blank) FuncDef
|
||||
** structure is created and liked into the "db" structure if a
|
||||
** no matching function previously existed. When createFlag is true
|
||||
** and the nArg parameter is -1, then only a function that accepts
|
||||
** any number of arguments will be returned.
|
||||
**
|
||||
** If createFlag is false and nArg is -1, then the first valid
|
||||
** function found is returned. A function is valid if either xFunc
|
||||
** or xStep is non-zero.
|
||||
**
|
||||
** If createFlag is false, then a function with the required name and
|
||||
** number of arguments may be returned even if the eTextRep flag does not
|
||||
** match that requested.
|
||||
*/
|
||||
|
||||
static FuncDef sqlite3FindFunction(
|
||||
sqlite3 db, /* An open database */
|
||||
string zName, /* Name of the function. Not null-terminated */
|
||||
int nName, /* Number of characters in the name */
|
||||
int nArg, /* Number of arguments. -1 means any number */
|
||||
u8 enc, /* Preferred text encoding */
|
||||
u8 createFlag /* Create new entry if true and does not otherwise exist */
|
||||
)
|
||||
{
|
||||
FuncDef p; /* Iterator variable */
|
||||
FuncDef pBest = null; /* Best match found so far */
|
||||
int bestScore = 0;
|
||||
int h; /* Hash value */
|
||||
|
||||
Debug.Assert( enc == SQLITE_UTF8 || enc == SQLITE_UTF16LE || enc == SQLITE_UTF16BE );
|
||||
h = ( sqlite3UpperToLower[(u8)zName[0]] + nName ) % ArraySize( db.aFunc.a );
|
||||
|
||||
|
||||
/* First search for a match amongst the application-defined functions.
|
||||
*/
|
||||
p = functionSearch( db.aFunc, h, zName, nName );
|
||||
while ( p != null )
|
||||
{
|
||||
int score = matchQuality( p, nArg, enc );
|
||||
if ( score > bestScore )
|
||||
{
|
||||
pBest = p;
|
||||
bestScore = score;
|
||||
|
||||
}
|
||||
p = p.pNext;
|
||||
}
|
||||
|
||||
|
||||
/* If no match is found, search the built-in functions.
|
||||
**
|
||||
** If the SQLITE_PreferBuiltin flag is set, then search the built-in
|
||||
** functions even if a prior app-defined function was found. And give
|
||||
** priority to built-in functions.
|
||||
**
|
||||
** Except, if createFlag is true, that means that we are trying to
|
||||
** install a new function. Whatever FuncDef structure is returned it will
|
||||
** have fields overwritten with new information appropriate for the
|
||||
** new function. But the FuncDefs for built-in functions are read-only.
|
||||
** So we must not search for built-ins when creating a new function.
|
||||
*/
|
||||
if ( 0 == createFlag && ( pBest == null || ( db.flags & SQLITE_PreferBuiltin ) != 0 ) )
|
||||
{
|
||||
#if SQLITE_OMIT_WSD
|
||||
FuncDefHash pHash = GLOBAL( FuncDefHash, sqlite3GlobalFunctions );
|
||||
#else
|
||||
FuncDefHash pHash = sqlite3GlobalFunctions;
|
||||
#endif
|
||||
bestScore = 0;
|
||||
p = functionSearch( pHash, h, zName, nName );
|
||||
while ( p != null )
|
||||
{
|
||||
int score = matchQuality( p, nArg, enc );
|
||||
if ( score > bestScore )
|
||||
{
|
||||
pBest = p;
|
||||
bestScore = score;
|
||||
}
|
||||
p = p.pNext;
|
||||
}
|
||||
}
|
||||
|
||||
/* If the createFlag parameter is true and the search did not reveal an
|
||||
** exact match for the name, number of arguments and encoding, then add a
|
||||
** new entry to the hash table and return it.
|
||||
*/
|
||||
if ( createFlag != 0 && ( bestScore < 6 || pBest.nArg != nArg ) &&
|
||||
( pBest = new FuncDef() ) != null )
|
||||
{ //sqlite3DbMallocZero(db, sizeof(*pBest)+nName+1))!=0 ){
|
||||
//pBest.zName = (char *)&pBest[1];
|
||||
pBest.nArg = (i16)nArg;
|
||||
pBest.iPrefEnc = enc;
|
||||
pBest.zName = zName; //memcpy(pBest.zName, zName, nName);
|
||||
//pBest.zName[nName] = 0;
|
||||
sqlite3FuncDefInsert( db.aFunc, pBest );
|
||||
}
|
||||
|
||||
if ( pBest != null && ( pBest.xStep != null || pBest.xFunc != null || createFlag != 0 ) )
|
||||
{
|
||||
return pBest;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
** Free all resources held by the schema structure. The void* argument points
|
||||
** at a Schema struct. This function does not call sqlite3DbFree(db, ) on the
|
||||
** pointer itself, it just cleans up subsidiary resources (i.e. the contents
|
||||
** of the schema hash tables).
|
||||
**
|
||||
** The Schema.cache_size variable is not cleared.
|
||||
*/
|
||||
static void sqlite3SchemaClear( Schema p )
|
||||
{
|
||||
Hash temp1;
|
||||
Hash temp2;
|
||||
HashElem pElem;
|
||||
Schema pSchema = p;
|
||||
|
||||
temp1 = pSchema.tblHash;
|
||||
temp2 = pSchema.trigHash;
|
||||
sqlite3HashInit( pSchema.trigHash );
|
||||
sqlite3HashClear( pSchema.idxHash );
|
||||
for ( pElem = sqliteHashFirst( temp2 ); pElem != null; pElem = sqliteHashNext( pElem ) )
|
||||
{
|
||||
Trigger pTrigger = (Trigger)sqliteHashData( pElem );
|
||||
sqlite3DeleteTrigger( null, ref pTrigger );
|
||||
}
|
||||
sqlite3HashClear( temp2 );
|
||||
sqlite3HashInit( pSchema.trigHash );
|
||||
for ( pElem = temp1.first; pElem != null; pElem = pElem.next )//sqliteHashFirst(&temp1); pElem; pElem = sqliteHashNext(pElem))
|
||||
{
|
||||
Table pTab = (Table)pElem.data; //sqliteHashData(pElem);
|
||||
sqlite3DeleteTable( null, ref pTab );
|
||||
}
|
||||
sqlite3HashClear( temp1 );
|
||||
sqlite3HashClear( pSchema.fkeyHash );
|
||||
pSchema.pSeqTab = null;
|
||||
if ( ( pSchema.flags & DB_SchemaLoaded ) != 0 )
|
||||
{
|
||||
pSchema.iGeneration++;
|
||||
pSchema.flags = (u16)( pSchema.flags & ( ~DB_SchemaLoaded ) );
|
||||
}
|
||||
p.Clear();
|
||||
}
|
||||
|
||||
/*
|
||||
** Find and return the schema associated with a BTree. Create
|
||||
** a new one if necessary.
|
||||
*/
|
||||
static Schema sqlite3SchemaGet( sqlite3 db, Btree pBt )
|
||||
{
|
||||
Schema p;
|
||||
if ( pBt != null )
|
||||
{
|
||||
p = sqlite3BtreeSchema( pBt, -1, (dxFreeSchema)sqlite3SchemaClear );//Schema.Length, sqlite3SchemaFree);
|
||||
}
|
||||
else
|
||||
{
|
||||
p = new Schema(); // (Schema *)sqlite3DbMallocZero(0, sizeof(Schema));
|
||||
}
|
||||
if ( p == null )
|
||||
{
|
||||
//// db.mallocFailed = 1;
|
||||
}
|
||||
else if ( 0 == p.file_format )
|
||||
{
|
||||
sqlite3HashInit( p.tblHash );
|
||||
sqlite3HashInit( p.idxHash );
|
||||
sqlite3HashInit( p.trigHash );
|
||||
sqlite3HashInit( p.fkeyHash );
|
||||
p.enc = SQLITE_UTF8;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,362 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
|
||||
using u8 = System.Byte;
|
||||
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/*
|
||||
** 2001 September 15
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** An tokenizer for SQL
|
||||
**
|
||||
** This file contains C code that implements the sqlite3_complete() API.
|
||||
** This code used to be part of the tokenizer.c source file. But by
|
||||
** separating it out, the code will be automatically omitted from
|
||||
** static links that do not use it.
|
||||
*************************************************************************
|
||||
** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart
|
||||
** C#-SQLite is an independent reimplementation of the SQLite software library
|
||||
**
|
||||
** SQLITE_SOURCE_ID: 2010-08-23 18:52:01 42537b60566f288167f1b5864a5435986838e3a3
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
//#include "sqliteInt.h"
|
||||
#if !SQLITE_OMIT_COMPLETE
|
||||
|
||||
/*
|
||||
** This is defined in tokenize.c. We just have to import the definition.
|
||||
*/
|
||||
#if !SQLITE_AMALGAMATION
|
||||
#if !NO_SQLITE_ASCII
|
||||
//#define IdChar(C) ((sqlite3CtypeMap[(unsigned char)C]&0x46)!=0)
|
||||
static bool IdChar( u8 C )
|
||||
{
|
||||
return ( sqlite3CtypeMap[(char)C] & 0x46 ) != 0;
|
||||
}
|
||||
#endif
|
||||
//#if SQLITE_EBCDIC
|
||||
//extern const char sqlite3IsEbcdicIdChar[];
|
||||
//#define IdChar(C) (((c=C)>=0x42 && sqlite3IsEbcdicIdChar[c-0x40]))
|
||||
//#endif
|
||||
#endif // * SQLITE_AMALGAMATION */
|
||||
|
||||
|
||||
/*
|
||||
** Token types used by the sqlite3_complete() routine. See the header
|
||||
** comments on that procedure for additional information.
|
||||
*/
|
||||
const int tkSEMI = 0;
|
||||
const int tkWS = 1;
|
||||
const int tkOTHER = 2;
|
||||
#if !SQLITE_OMIT_TRIGGER
|
||||
const int tkEXPLAIN = 3;
|
||||
const int tkCREATE = 4;
|
||||
const int tkTEMP = 5;
|
||||
const int tkTRIGGER = 6;
|
||||
const int tkEND = 7;
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Return TRUE if the given SQL string ends in a semicolon.
|
||||
**
|
||||
** Special handling is require for CREATE TRIGGER statements.
|
||||
** Whenever the CREATE TRIGGER keywords are seen, the statement
|
||||
** must end with ";END;".
|
||||
**
|
||||
** This implementation uses a state machine with 8 states:
|
||||
**
|
||||
** (0) INVALID We have not yet seen a non-whitespace character.
|
||||
**
|
||||
** (1) START At the beginning or end of an SQL statement. This routine
|
||||
** returns 1 if it ends in the START state and 0 if it ends
|
||||
** in any other state.
|
||||
**
|
||||
** (2) NORMAL We are in the middle of statement which ends with a single
|
||||
** semicolon.
|
||||
**
|
||||
** (3) EXPLAIN The keyword EXPLAIN has been seen at the beginning of
|
||||
** a statement.
|
||||
**
|
||||
** (4) CREATE The keyword CREATE has been seen at the beginning of a
|
||||
** statement, possibly preceeded by EXPLAIN and/or followed by
|
||||
** TEMP or TEMPORARY
|
||||
**
|
||||
** (5) TRIGGER We are in the middle of a trigger definition that must be
|
||||
** ended by a semicolon, the keyword END, and another semicolon.
|
||||
**
|
||||
** (6) SEMI We've seen the first semicolon in the ";END;" that occurs at
|
||||
** the end of a trigger definition.
|
||||
**
|
||||
** (7) END We've seen the ";END" of the ";END;" that occurs at the end
|
||||
** of a trigger difinition.
|
||||
**
|
||||
** Transitions between states above are determined by tokens extracted
|
||||
** from the input. The following tokens are significant:
|
||||
**
|
||||
** (0) tkSEMI A semicolon.
|
||||
** (1) tkWS Whitespace.
|
||||
** (2) tkOTHER Any other SQL token.
|
||||
** (3) tkEXPLAIN The "explain" keyword.
|
||||
** (4) tkCREATE The "create" keyword.
|
||||
** (5) tkTEMP The "temp" or "temporary" keyword.
|
||||
** (6) tkTRIGGER The "trigger" keyword.
|
||||
** (7) tkEND The "end" keyword.
|
||||
**
|
||||
** Whitespace never causes a state transition and is always ignored.
|
||||
** This means that a SQL string of all whitespace is invalid.
|
||||
**
|
||||
** If we compile with SQLITE_OMIT_TRIGGER, all of the computation needed
|
||||
** to recognize the end of a trigger can be omitted. All we have to do
|
||||
** is look for a semicolon that is not part of an string or comment.
|
||||
*/
|
||||
|
||||
static public int sqlite3_complete( string zSql )
|
||||
{
|
||||
int state = 0; /* Current state, using numbers defined in header comment */
|
||||
int token; /* Value of the next token */
|
||||
|
||||
#if !SQLITE_OMIT_TRIGGER
|
||||
/* A complex statement machine used to detect the end of a CREATE TRIGGER
|
||||
** statement. This is the normal case.
|
||||
*/
|
||||
u8[][] trans = new u8[][] {
|
||||
/* Token: */
|
||||
/* State: ** SEMI WS OTHER EXPLAIN CREATE TEMP TRIGGER END */
|
||||
/* 0 INVALID: */ new u8[]{ 1, 0, 2, 3, 4, 2, 2, 2, },
|
||||
/* 1 START: */ new u8[]{ 1, 1, 2, 3, 4, 2, 2, 2, },
|
||||
/* 2 NORMAL: */ new u8[]{ 1, 2, 2, 2, 2, 2, 2, 2, },
|
||||
/* 3 EXPLAIN: */ new u8[]{ 1, 3, 3, 2, 4, 2, 2, 2, },
|
||||
/* 4 CREATE: */ new u8[]{ 1, 4, 2, 2, 2, 4, 5, 2, },
|
||||
/* 5 TRIGGER: */ new u8[]{ 6, 5, 5, 5, 5, 5, 5, 5, },
|
||||
/* 6 SEMI: */ new u8[]{ 6, 6, 5, 5, 5, 5, 5, 7, },
|
||||
/* 7 END: */ new u8[]{ 1, 7, 5, 5, 5, 5, 5, 5, },
|
||||
};
|
||||
#else
|
||||
/* If triggers are not supported by this compile then the statement machine
|
||||
** used to detect the end of a statement is much simplier
|
||||
*/
|
||||
u8[][] trans = new u8[][] {
|
||||
/* Token: */
|
||||
/* State: ** SEMI WS OTHER */
|
||||
/* 0 INVALID: */new u8[] { 1, 0, 2, },
|
||||
/* 1 START: */new u8[] { 1, 1, 2, },
|
||||
/* 2 NORMAL: */new u8[] { 1, 2, 2, },
|
||||
};
|
||||
#endif // * SQLITE_OMIT_TRIGGER */
|
||||
|
||||
int zIdx = 0;
|
||||
while ( zIdx < zSql.Length )
|
||||
{
|
||||
switch ( zSql[zIdx] )
|
||||
{
|
||||
case ';':
|
||||
{ /* A semicolon */
|
||||
token = tkSEMI;
|
||||
break;
|
||||
}
|
||||
case ' ':
|
||||
case '\r':
|
||||
case '\t':
|
||||
case '\n':
|
||||
case '\f':
|
||||
{ /* White space is ignored */
|
||||
token = tkWS;
|
||||
break;
|
||||
}
|
||||
case '/':
|
||||
{ /* C-style comments */
|
||||
if ( zSql[zIdx + 1] != '*' )
|
||||
{
|
||||
token = tkOTHER;
|
||||
break;
|
||||
}
|
||||
zIdx += 2;
|
||||
while ( zIdx < zSql.Length && zSql[zIdx] != '*' || zIdx < zSql.Length - 1 && zSql[zIdx + 1] != '/' )
|
||||
{
|
||||
zIdx++;
|
||||
}
|
||||
if ( zIdx == zSql.Length )
|
||||
return 0;
|
||||
zIdx++;
|
||||
token = tkWS;
|
||||
break;
|
||||
}
|
||||
case '-':
|
||||
{ /* SQL-style comments from "--" to end of line */
|
||||
if ( zSql[zIdx + 1] != '-' )
|
||||
{
|
||||
token = tkOTHER;
|
||||
break;
|
||||
}
|
||||
while ( zIdx < zSql.Length && zSql[zIdx] != '\n' )
|
||||
{
|
||||
zIdx++;
|
||||
}
|
||||
if ( zIdx == zSql.Length )
|
||||
return state == 1 ? 1 : 0;//if( *zSql==0 ) return state==1;
|
||||
token = tkWS;
|
||||
break;
|
||||
}
|
||||
case '[':
|
||||
{ /* Microsoft-style identifiers in [...] */
|
||||
zIdx++;
|
||||
while ( zIdx < zSql.Length && zSql[zIdx] != ']' )
|
||||
{
|
||||
zIdx++;
|
||||
}
|
||||
if ( zIdx == zSql.Length )
|
||||
return 0;
|
||||
token = tkOTHER;
|
||||
break;
|
||||
}
|
||||
case '`': /* Grave-accent quoted symbols used by MySQL */
|
||||
case '"': /* single- and double-quoted strings */
|
||||
case '\'':
|
||||
{
|
||||
int c = zSql[zIdx];
|
||||
zIdx++;
|
||||
while ( zIdx < zSql.Length && zSql[zIdx] != c )
|
||||
{
|
||||
zIdx++;
|
||||
}
|
||||
if ( zIdx == zSql.Length )
|
||||
return 0;
|
||||
token = tkOTHER;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
//#if SQLITE_EBCDIC
|
||||
// unsigned char c;
|
||||
//#endif
|
||||
if ( IdChar( (u8)zSql[zIdx] ) )
|
||||
{
|
||||
/* Keywords and unquoted identifiers */
|
||||
int nId;
|
||||
for ( nId = 1; ( zIdx + nId ) < zSql.Length && IdChar( (u8)zSql[zIdx + nId] ); nId++ )
|
||||
{
|
||||
}
|
||||
#if SQLITE_OMIT_TRIGGER
|
||||
token = tkOTHER;
|
||||
#else
|
||||
switch ( zSql[zIdx] )
|
||||
{
|
||||
case 'c':
|
||||
case 'C':
|
||||
{
|
||||
if ( nId == 6 && sqlite3StrNICmp( zSql, zIdx, "create", 6 ) == 0 )
|
||||
{
|
||||
token = tkCREATE;
|
||||
}
|
||||
else
|
||||
{
|
||||
token = tkOTHER;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 't':
|
||||
case 'T':
|
||||
{
|
||||
if ( nId == 7 && sqlite3StrNICmp( zSql, zIdx, "trigger", 7 ) == 0 )
|
||||
{
|
||||
token = tkTRIGGER;
|
||||
}
|
||||
else if ( nId == 4 && sqlite3StrNICmp( zSql, zIdx, "temp", 4 ) == 0 )
|
||||
{
|
||||
token = tkTEMP;
|
||||
}
|
||||
else if ( nId == 9 && sqlite3StrNICmp( zSql, zIdx, "temporary", 9 ) == 0 )
|
||||
{
|
||||
token = tkTEMP;
|
||||
}
|
||||
else
|
||||
{
|
||||
token = tkOTHER;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'e':
|
||||
case 'E':
|
||||
{
|
||||
if ( nId == 3 && sqlite3StrNICmp( zSql, zIdx, "end", 3 ) == 0 )
|
||||
{
|
||||
token = tkEND;
|
||||
}
|
||||
else
|
||||
#if !SQLITE_OMIT_EXPLAIN
|
||||
if ( nId == 7 && sqlite3StrNICmp( zSql, zIdx, "explain", 7 ) == 0 )
|
||||
{
|
||||
token = tkEXPLAIN;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
token = tkOTHER;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
token = tkOTHER;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif // * SQLITE_OMIT_TRIGGER */
|
||||
zIdx += nId - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Operators and special symbols */
|
||||
token = tkOTHER;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
state = trans[state][token];
|
||||
zIdx++;
|
||||
}
|
||||
return ( state == 1 ) ? 1 : 0;//return state==1;
|
||||
}
|
||||
|
||||
#if NO_SQLITE_OMIT_UTF16 //#if !SQLITE_OMIT_UTF16
|
||||
/*
|
||||
** This routine is the same as the sqlite3_complete() routine described
|
||||
** above, except that the parameter is required to be UTF-16 encoded, not
|
||||
** UTF-8.
|
||||
*/
|
||||
int sqlite3_complete16(const void *zSql){
|
||||
sqlite3_value pVal;
|
||||
char const *zSql8;
|
||||
int rc = SQLITE_NOMEM;
|
||||
|
||||
#if !SQLITE_OMIT_AUTOINIT
|
||||
rc = sqlite3_initialize();
|
||||
if( rc !=0) return rc;
|
||||
#endif
|
||||
pVal = sqlite3ValueNew(0);
|
||||
sqlite3ValueSetStr(pVal, -1, zSql, SQLITE_UTF16NATIVE, SQLITE_STATIC);
|
||||
zSql8 = sqlite3ValueText(pVal, SQLITE_UTF8);
|
||||
if( zSql8 ){
|
||||
rc = sqlite3_complete(zSql8);
|
||||
}else{
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
sqlite3ValueFree(pVal);
|
||||
return sqlite3ApiExit(0, rc);
|
||||
}
|
||||
#endif // * SQLITE_OMIT_UTF16 */
|
||||
#endif // * SQLITE_OMIT_COMPLETE */
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,763 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
using u8 = System.Byte;
|
||||
using u16 = System.UInt16;
|
||||
using Pgno = System.UInt32;
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
using sqlite3_int64 = System.Int64;
|
||||
using sqlite3_stmt = Sqlite3.Vdbe;
|
||||
using System.Security.Cryptography;
|
||||
using System.IO;
|
||||
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/*
|
||||
*************************************************************************
|
||||
** Included in SQLite3 port to C#-SQLite; 2010 Noah B Hart, Diego Torres
|
||||
** C#-SQLite is an independent reimplementation of the SQLite software library
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
|
||||
/*
|
||||
** SQLCipher
|
||||
** crypto.c developed by Stephen Lombardo (Zetetic LLC)
|
||||
** sjlombardo at zetetic dot net
|
||||
** http://zetetic.net
|
||||
**
|
||||
** Copyright (c) 2009, ZETETIC LLC
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** * Neither the name of the ZETETIC LLC nor the
|
||||
** names of its contributors may be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY ZETETIC LLC ''AS IS'' AND ANY
|
||||
** EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
** DISCLAIMED. IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY
|
||||
** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**
|
||||
*/
|
||||
/* BEGIN CRYPTO */
|
||||
#if !NOT_SQLITE_HAS_CODEC //#if SQLITE_HAS_CODEC
|
||||
|
||||
//#include <assert.h>
|
||||
//#include <openssl/evp.h>
|
||||
//#include <openssl/rand.h>
|
||||
//#include <openssl/hmac.h>
|
||||
//#include "sqliteInt.h"
|
||||
//#include "btreeInt.h"
|
||||
//#include "crypto.h"
|
||||
|
||||
#if CODEC_DEBUG || TRACE
|
||||
//#define CODEC_TRACE(X) {printf X;fflush(stdout);}
|
||||
static void CODEC_TRACE( string T, params object[] ap ) { if ( sqlite3PagerTrace )sqlite3DebugPrintf( T, ap ); }
|
||||
#else
|
||||
//#define CODEC_TRACE(X)
|
||||
static void CODEC_TRACE( string T, params object[] ap )
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
//void sqlite3FreeCodecArg(void *pCodecArg);
|
||||
|
||||
public class cipher_ctx
|
||||
{//typedef struct {
|
||||
public string pass;
|
||||
public int pass_sz;
|
||||
public bool derive_key;
|
||||
public byte[] key;
|
||||
public int key_sz;
|
||||
public byte[] iv;
|
||||
public int iv_sz;
|
||||
public ICryptoTransform encryptor;
|
||||
public ICryptoTransform decryptor;
|
||||
|
||||
public cipher_ctx Copy()
|
||||
{
|
||||
cipher_ctx c = new cipher_ctx();
|
||||
c.derive_key = derive_key;
|
||||
c.pass = pass;
|
||||
c.pass_sz = pass_sz;
|
||||
if ( key != null )
|
||||
{
|
||||
c.key = new byte[key.Length];
|
||||
key.CopyTo( c.key, 0 );
|
||||
}
|
||||
c.key_sz = key_sz;
|
||||
if ( iv != null )
|
||||
{
|
||||
c.iv = new byte[iv.Length];
|
||||
iv.CopyTo( c.iv, 0 );
|
||||
}
|
||||
c.iv_sz = iv_sz;
|
||||
c.encryptor = encryptor;
|
||||
c.decryptor = decryptor;
|
||||
return c;
|
||||
}
|
||||
|
||||
public void CopyTo( cipher_ctx ct )
|
||||
{
|
||||
ct.derive_key = derive_key;
|
||||
ct.pass = pass;
|
||||
ct.pass_sz = pass_sz;
|
||||
if ( key != null )
|
||||
{
|
||||
ct.key = new byte[key.Length];
|
||||
key.CopyTo( ct.key, 0 );
|
||||
}
|
||||
ct.key_sz = key_sz;
|
||||
if ( iv != null )
|
||||
{
|
||||
ct.iv = new byte[iv.Length];
|
||||
iv.CopyTo( ct.iv, 0 );
|
||||
}
|
||||
ct.iv_sz = iv_sz;
|
||||
ct.encryptor = encryptor;
|
||||
ct.decryptor = decryptor;
|
||||
}
|
||||
}
|
||||
|
||||
public class codec_ctx
|
||||
{//typedef struct {
|
||||
public int mode_rekey;
|
||||
public byte[] buffer;
|
||||
public Btree pBt;
|
||||
public cipher_ctx read_ctx;
|
||||
public cipher_ctx write_ctx;
|
||||
|
||||
public codec_ctx Copy()
|
||||
{
|
||||
codec_ctx c = new codec_ctx();
|
||||
c.mode_rekey = mode_rekey;
|
||||
c.buffer = sqlite3MemMalloc( buffer.Length );
|
||||
c.pBt = pBt;
|
||||
if ( read_ctx != null )
|
||||
c.read_ctx = read_ctx.Copy();
|
||||
if ( write_ctx != null )
|
||||
c.write_ctx = write_ctx.Copy();
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
const int FILE_HEADER_SZ = 16; //#define FILE_HEADER_SZ 16
|
||||
const string CIPHER = "aes-256-cbc"; //#define CIPHER "aes-256-cbc"
|
||||
const int CIPHER_DECRYPT = 0; //#define CIPHER_DECRYPT 0
|
||||
const int CIPHER_ENCRYPT = 1; //#define CIPHER_ENCRYPT 1
|
||||
|
||||
#if NET_2_0
|
||||
static RijndaelManaged Aes = new RijndaelManaged();
|
||||
#else
|
||||
static AesManaged Aes = new AesManaged();
|
||||
#endif
|
||||
|
||||
/* BEGIN CRYPTO */
|
||||
static void sqlite3pager_get_codec( Pager pPager, ref codec_ctx ctx )
|
||||
{
|
||||
ctx = pPager.pCodec;
|
||||
}
|
||||
|
||||
static int sqlite3pager_is_mj_pgno( Pager pPager, Pgno pgno )
|
||||
{
|
||||
return ( PAGER_MJ_PGNO( pPager ) == pgno ) ? 1 : 0;
|
||||
}
|
||||
|
||||
static sqlite3_file sqlite3Pager_get_fd( Pager pPager )
|
||||
{
|
||||
return ( isOpen( pPager.fd ) ) ? pPager.fd : null;
|
||||
}
|
||||
|
||||
static void sqlite3pager_sqlite3PagerSetCodec(
|
||||
Pager pPager,
|
||||
dxCodec xCodec,
|
||||
dxCodecSizeChng xCodecSizeChng,
|
||||
dxCodecFree xCodecFree,
|
||||
codec_ctx pCodec
|
||||
)
|
||||
{
|
||||
sqlite3PagerSetCodec( pPager, xCodec, xCodecSizeChng, xCodecFree, pCodec );
|
||||
}
|
||||
/* END CRYPTO */
|
||||
|
||||
//static void activate_openssl() {
|
||||
// if(EVP_get_cipherbyname(CIPHER) == null) {
|
||||
// OpenSSL_add_all_algorithms();
|
||||
// }
|
||||
//}
|
||||
|
||||
/**
|
||||
* Free and wipe memory
|
||||
* If ptr is not null memory will be freed.
|
||||
* If sz is greater than zero, the memory will be overwritten with zero before it is freed
|
||||
*/
|
||||
static void codec_free( ref byte[] ptr, int sz )
|
||||
{
|
||||
if ( ptr != null )
|
||||
{
|
||||
if ( sz > 0 )
|
||||
Array.Clear( ptr, 0, sz );//memset( ptr, 0, sz );
|
||||
sqlite3_free( ref ptr );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the raw password / key data for a cipher context
|
||||
*
|
||||
* returns SQLITE_OK if assignment was successfull
|
||||
* returns SQLITE_NOMEM if an error occured allocating memory
|
||||
* returns SQLITE_ERROR if the key couldn't be set because the pass was null or size was zero
|
||||
*/
|
||||
static int cipher_ctx_set_pass( cipher_ctx ctx, string zKey, int nKey )
|
||||
{
|
||||
ctx.pass = null; // codec_free( ctx.pass, ctx.pass_sz );
|
||||
ctx.pass_sz = nKey;
|
||||
if ( !String.IsNullOrEmpty( zKey ) && nKey > 0 )
|
||||
{
|
||||
//ctx.pass = sqlite3Malloc(nKey);
|
||||
//if(ctx.pass == null) return SQLITE_NOMEM;
|
||||
ctx.pass = zKey;//memcpy(ctx.pass, zKey, nKey);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a new cipher_ctx struct. This function will allocate memory
|
||||
* for the cipher context and for the key
|
||||
*
|
||||
* returns SQLITE_OK if initialization was successful
|
||||
* returns SQLITE_NOMEM if an error occured allocating memory
|
||||
*/
|
||||
static int cipher_ctx_init( ref cipher_ctx iCtx )
|
||||
{
|
||||
iCtx = new cipher_ctx();
|
||||
//iCtx = sqlite3Malloc( sizeof( cipher_ctx ) );
|
||||
//ctx = *iCtx;
|
||||
//if ( ctx == null ) return SQLITE_NOMEM;
|
||||
//memset( ctx, 0, sizeof( cipher_ctx ) );
|
||||
//ctx.key = sqlite3Malloc( EVP_MAX_KEY_LENGTH );
|
||||
//if ( ctx.key == null ) return SQLITE_NOMEM;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* free and wipe memory associated with a cipher_ctx
|
||||
*/
|
||||
static void cipher_ctx_free( ref cipher_ctx ictx )
|
||||
{
|
||||
cipher_ctx ctx = ictx;
|
||||
CODEC_TRACE( "cipher_ctx_free: entered ictx=%d\n", ictx );
|
||||
ctx.pass = null;//codec_free(ctx.pass, ctx.pass_sz);
|
||||
if ( ctx.key != null )
|
||||
Array.Clear( ctx.key, 0, ctx.key.Length );//codec_free(ctx.key, ctx.key_sz);
|
||||
if ( ctx.iv != null )
|
||||
Array.Clear( ctx.iv, 0, ctx.iv.Length );
|
||||
ictx = new cipher_ctx();// codec_free( ref ctx, sizeof( cipher_ctx ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy one cipher_ctx to another. For instance, assuming that read_ctx is a
|
||||
* fully initialized context, you could copy it to write_ctx and all yet data
|
||||
* and pass information across
|
||||
*
|
||||
* returns SQLITE_OK if initialization was successful
|
||||
* returns SQLITE_NOMEM if an error occured allocating memory
|
||||
*/
|
||||
static int cipher_ctx_copy( cipher_ctx target, cipher_ctx source )
|
||||
{
|
||||
//byte[] key = target.key;
|
||||
CODEC_TRACE( "cipher_ctx_copy: entered target=%d, source=%d\n", target, source );
|
||||
//codec_free(target.pass, target.pass_sz);
|
||||
source.CopyTo( target );//memcpy(target, source, sizeof(cipher_ctx);
|
||||
|
||||
//target.key = key; //restore pointer to previously allocated key data
|
||||
//memcpy(target.key, source.key, EVP_MAX_KEY_LENGTH);
|
||||
//target.pass = sqlite3Malloc(source.pass_sz);
|
||||
//if(target.pass == null) return SQLITE_NOMEM;
|
||||
//memcpy(target.pass, source.pass, source.pass_sz);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare one cipher_ctx to another.
|
||||
*
|
||||
* returns 0 if all the parameters (except the derived key data) are the same
|
||||
* returns 1 otherwise
|
||||
*/
|
||||
static int cipher_ctx_cmp( cipher_ctx c1, cipher_ctx c2 )
|
||||
{
|
||||
CODEC_TRACE( "cipher_ctx_cmp: entered c1=%d c2=%d\n", c1, c2 );
|
||||
|
||||
if ( c1.key_sz == c2.key_sz
|
||||
&& c1.pass_sz == c2.pass_sz
|
||||
&& c1.pass == c2.pass
|
||||
)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free and wipe memory associated with a cipher_ctx, including the allocated
|
||||
* read_ctx and write_ctx.
|
||||
*/
|
||||
static void codec_ctx_free( ref codec_ctx iCtx )
|
||||
{
|
||||
codec_ctx ctx = iCtx;
|
||||
CODEC_TRACE( "codec_ctx_free: entered iCtx=%d\n", iCtx );
|
||||
cipher_ctx_free( ref ctx.read_ctx );
|
||||
cipher_ctx_free( ref ctx.write_ctx );
|
||||
iCtx = new codec_ctx();//codec_free(ctx, sizeof(codec_ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive an encryption key for a cipher contex key based on the raw password.
|
||||
*
|
||||
* If the raw key data is formated as x'hex' and there are exactly enough hex chars to fill
|
||||
* the key space (i.e 64 hex chars for a 256 bit key) then the key data will be used directly.
|
||||
*
|
||||
* Otherwise, a key data will be derived using PBKDF2
|
||||
*
|
||||
* returns SQLITE_OK if initialization was successful
|
||||
* returns SQLITE_NOMEM if the key could't be derived (for instance if pass is null or pass_sz is 0)
|
||||
*/
|
||||
static int codec_key_derive( codec_ctx ctx, cipher_ctx c_ctx )
|
||||
{
|
||||
CODEC_TRACE( "codec_key_derive: entered c_ctx.pass=%s, c_ctx.pass_sz=%d ctx.iv=%d ctx.iv_sz=%d c_ctx.kdf_iter=%d c_ctx.key_sz=%d\n",
|
||||
c_ctx.pass, c_ctx.pass_sz, c_ctx.iv, c_ctx.iv_sz, c_ctx.key_sz );
|
||||
|
||||
if ( c_ctx.pass != null && c_ctx.pass_sz > 0 )
|
||||
{ // if pass is not null
|
||||
if ( ( c_ctx.pass_sz == ( c_ctx.key_sz * 2 ) + 3 ) && c_ctx.pass.StartsWith( "x'", StringComparison.InvariantCultureIgnoreCase ) )
|
||||
{
|
||||
int n = c_ctx.pass_sz - 3; /* adjust for leading x' and tailing ' */
|
||||
string z = c_ctx.pass.Substring( 2 );// + 2; /* adjust lead offset of x' */
|
||||
CODEC_TRACE( "codec_key_derive: deriving key from hex\n" );
|
||||
c_ctx.key = sqlite3HexToBlob( null, z, n );
|
||||
}
|
||||
else
|
||||
{
|
||||
CODEC_TRACE( "codec_key_derive: deriving key using AES256\n" );
|
||||
|
||||
Rfc2898DeriveBytes k1 = new Rfc2898DeriveBytes( c_ctx.pass, c_ctx.iv, 2010 );
|
||||
c_ctx.key_sz = 32;
|
||||
c_ctx.key = k1.GetBytes( c_ctx.key_sz );
|
||||
}
|
||||
#if NET_2_0
|
||||
Aes.BlockSize = 0x80;
|
||||
Aes.FeedbackSize = 8;
|
||||
Aes.KeySize = 0x100;
|
||||
Aes.Mode = CipherMode.CBC;
|
||||
#endif
|
||||
c_ctx.encryptor = Aes.CreateEncryptor( c_ctx.key, c_ctx.iv );
|
||||
c_ctx.decryptor = Aes.CreateDecryptor( c_ctx.key, c_ctx.iv );
|
||||
return SQLITE_OK;
|
||||
};
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* ctx - codec context
|
||||
* pgno - page number in database
|
||||
* size - size in bytes of input and output buffers
|
||||
* mode - 1 to encrypt, 0 to decrypt
|
||||
* in - pointer to input bytes
|
||||
* out - pouter to output bytes
|
||||
*/
|
||||
static int codec_cipher( cipher_ctx ctx, Pgno pgno, int mode, int size, byte[] bIn, byte[] bOut )
|
||||
{
|
||||
int iv;
|
||||
int tmp_csz, csz;
|
||||
|
||||
CODEC_TRACE( "codec_cipher:entered pgno=%d, mode=%d, size=%d\n", pgno, mode, size );
|
||||
|
||||
/* just copy raw data from in to out when key size is 0
|
||||
* i.e. during a rekey of a plaintext database */
|
||||
if ( ctx.key_sz == 0 )
|
||||
{
|
||||
Array.Copy( bIn, bOut, bIn.Length );//memcpy(out, in, size);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
MemoryStream dataStream = new MemoryStream();
|
||||
CryptoStream encryptionStream;
|
||||
if ( mode == CIPHER_ENCRYPT )
|
||||
{
|
||||
encryptionStream = new CryptoStream( dataStream, ctx.encryptor, CryptoStreamMode.Write );
|
||||
}
|
||||
else
|
||||
{
|
||||
encryptionStream = new CryptoStream( dataStream, ctx.decryptor, CryptoStreamMode.Write );
|
||||
}
|
||||
encryptionStream.Write( bIn, 0, size );
|
||||
encryptionStream.FlushFinalBlock();
|
||||
dataStream.Position = 0;
|
||||
|
||||
dataStream.Read( bOut, 0, (int)dataStream.Length );
|
||||
encryptionStream.Close();
|
||||
dataStream.Close();
|
||||
|
||||
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* when for_ctx == 0 then it will change for read
|
||||
* when for_ctx == 1 then it will change for write
|
||||
* when for_ctx == 2 then it will change for both
|
||||
*/
|
||||
static int codec_set_cipher_name( sqlite3 db, int nDb, string cipher_name, int for_ctx )
|
||||
{
|
||||
Db pDb = db.aDb[nDb];
|
||||
CODEC_TRACE( "codec_set_cipher_name: entered db=%d nDb=%d cipher_name=%s for_ctx=%d\n", db, nDb, cipher_name, for_ctx );
|
||||
|
||||
if ( pDb.pBt != null )
|
||||
{
|
||||
codec_ctx ctx = null;
|
||||
cipher_ctx c_ctx;
|
||||
sqlite3pager_get_codec( pDb.pBt.pBt.pPager, ref ctx );
|
||||
c_ctx = for_ctx != 0 ? ctx.write_ctx : ctx.read_ctx;
|
||||
|
||||
c_ctx.derive_key = true;
|
||||
|
||||
if ( for_ctx == 2 )
|
||||
cipher_ctx_copy( for_ctx != 0 ? ctx.read_ctx : ctx.write_ctx, c_ctx );
|
||||
return SQLITE_OK;
|
||||
}
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
static int codec_set_pass_key( sqlite3 db, int nDb, string zKey, int nKey, int for_ctx )
|
||||
{
|
||||
Db pDb = db.aDb[nDb];
|
||||
CODEC_TRACE( "codec_set_pass_key: entered db=%d nDb=%d cipher_name=%s nKey=%d for_ctx=%d\n", db, nDb, zKey, nKey, for_ctx );
|
||||
if ( pDb.pBt != null )
|
||||
{
|
||||
codec_ctx ctx = null;
|
||||
cipher_ctx c_ctx;
|
||||
sqlite3pager_get_codec( pDb.pBt.pBt.pPager, ref ctx );
|
||||
c_ctx = for_ctx != 0 ? ctx.write_ctx : ctx.read_ctx;
|
||||
|
||||
cipher_ctx_set_pass( c_ctx, zKey, nKey );
|
||||
c_ctx.derive_key = true;
|
||||
|
||||
if ( for_ctx == 2 )
|
||||
cipher_ctx_copy( for_ctx != 0 ? ctx.read_ctx : ctx.write_ctx, c_ctx );
|
||||
return SQLITE_OK;
|
||||
}
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* sqlite3Codec can be called in multiple modes.
|
||||
* encrypt mode - expected to return a pointer to the
|
||||
* encrypted data without altering pData.
|
||||
* decrypt mode - expected to return a pointer to pData, with
|
||||
* the data decrypted in the input buffer
|
||||
*/
|
||||
static byte[] sqlite3Codec( codec_ctx iCtx, byte[] data, Pgno pgno, int mode )
|
||||
{
|
||||
codec_ctx ctx = (codec_ctx)iCtx;
|
||||
int pg_sz = sqlite3BtreeGetPageSize( ctx.pBt );
|
||||
int offset = 0;
|
||||
byte[] pData = data;
|
||||
|
||||
CODEC_TRACE( "sqlite3Codec: entered pgno=%d, mode=%d, ctx.mode_rekey=%d, pg_sz=%d\n", pgno, mode, ctx.mode_rekey, pg_sz );
|
||||
|
||||
/* derive key on first use if necessary */
|
||||
if ( ctx.read_ctx.derive_key )
|
||||
{
|
||||
codec_key_derive( ctx, ctx.read_ctx );
|
||||
ctx.read_ctx.derive_key = false;
|
||||
}
|
||||
|
||||
if ( ctx.write_ctx.derive_key )
|
||||
{
|
||||
if ( cipher_ctx_cmp( ctx.write_ctx, ctx.read_ctx ) == 0 )
|
||||
{
|
||||
cipher_ctx_copy( ctx.write_ctx, ctx.read_ctx ); // the relevant parameters are the same, just copy read key
|
||||
}
|
||||
else
|
||||
{
|
||||
codec_key_derive( ctx, ctx.write_ctx );
|
||||
ctx.write_ctx.derive_key = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
CODEC_TRACE( "sqlite3Codec: switch mode=%d offset=%d\n", mode, offset );
|
||||
if ( ctx.buffer.Length != pg_sz )
|
||||
ctx.buffer = sqlite3MemMalloc( pg_sz );
|
||||
switch ( mode )
|
||||
{
|
||||
case SQLITE_DECRYPT:
|
||||
codec_cipher( ctx.read_ctx, pgno, CIPHER_DECRYPT, pg_sz, pData, ctx.buffer );
|
||||
if ( pgno == 1 )
|
||||
Buffer.BlockCopy( Encoding.UTF8.GetBytes( SQLITE_FILE_HEADER ), 0, ctx.buffer, 0, FILE_HEADER_SZ );// memcpy( ctx.buffer, SQLITE_FILE_HEADER, FILE_HEADER_SZ ); /* copy file header to the first 16 bytes of the page */
|
||||
Buffer.BlockCopy( ctx.buffer, 0, pData, 0, pg_sz ); //memcpy( pData, ctx.buffer, pg_sz ); /* copy buffer data back to pData and return */
|
||||
return pData;
|
||||
case SQLITE_ENCRYPT_WRITE_CTX: /* encrypt */
|
||||
if ( pgno == 1 )
|
||||
Buffer.BlockCopy( ctx.write_ctx.iv, 0, ctx.buffer, 0, FILE_HEADER_SZ );//memcpy( ctx.buffer, ctx.iv, FILE_HEADER_SZ ); /* copy salt to output buffer */
|
||||
codec_cipher( ctx.write_ctx, pgno, CIPHER_ENCRYPT, pg_sz, pData, ctx.buffer );
|
||||
return ctx.buffer; /* return persistent buffer data, pData remains intact */
|
||||
case SQLITE_ENCRYPT_READ_CTX:
|
||||
if ( pgno == 1 )
|
||||
Buffer.BlockCopy( ctx.read_ctx.iv, 0, ctx.buffer, 0, FILE_HEADER_SZ );//memcpy( ctx.buffer, ctx.iv, FILE_HEADER_SZ ); /* copy salt to output buffer */
|
||||
codec_cipher( ctx.read_ctx, pgno, CIPHER_ENCRYPT, pg_sz, pData, ctx.buffer );
|
||||
return ctx.buffer; /* return persistent buffer data, pData remains intact */
|
||||
default:
|
||||
return pData;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int sqlite3CodecAttach( sqlite3 db, int nDb, string zKey, int nKey )
|
||||
{
|
||||
Db pDb = db.aDb[nDb];
|
||||
|
||||
CODEC_TRACE( "sqlite3CodecAttach: entered nDb=%d zKey=%s, nKey=%d\n", nDb, zKey, nKey );
|
||||
//activate_openssl();
|
||||
|
||||
if ( zKey != null && pDb.pBt != null )
|
||||
{
|
||||
Aes.KeySize = 256;
|
||||
#if NO_SQLITE_SILVERLIGHT // #if !SQLITE_SILVERLIGHT
|
||||
Aes.Padding = PaddingMode.None;
|
||||
#endif
|
||||
codec_ctx ctx;
|
||||
int rc;
|
||||
Pager pPager = pDb.pBt.pBt.pPager;
|
||||
sqlite3_file fd;
|
||||
|
||||
ctx = new codec_ctx();//sqlite3Malloc(sizeof(codec_ctx);
|
||||
//if(ctx == null) return SQLITE_NOMEM;
|
||||
//memset(ctx, 0, sizeof(codec_ctx); /* initialize all pointers and values to 0 */
|
||||
|
||||
ctx.pBt = pDb.pBt; /* assign pointer to database btree structure */
|
||||
|
||||
if ( ( rc = cipher_ctx_init( ref ctx.read_ctx ) ) != SQLITE_OK )
|
||||
return rc;
|
||||
if ( ( rc = cipher_ctx_init( ref ctx.write_ctx ) ) != SQLITE_OK )
|
||||
return rc;
|
||||
|
||||
/* pre-allocate a page buffer of PageSize bytes. This will
|
||||
be used as a persistent buffer for encryption and decryption
|
||||
operations to avoid overhead of multiple memory allocations*/
|
||||
ctx.buffer = sqlite3MemMalloc( sqlite3BtreeGetPageSize( ctx.pBt ) );//sqlite3Malloc(sqlite3BtreeGetPageSize(ctx.pBt);
|
||||
//if(ctx.buffer == null) return SQLITE_NOMEM;
|
||||
|
||||
/* allocate space for salt data. Then read the first 16 bytes header as the salt for the key derivation */
|
||||
ctx.read_ctx.iv_sz = FILE_HEADER_SZ;
|
||||
ctx.read_ctx.iv = new byte[ctx.read_ctx.iv_sz];//sqlite3Malloc( ctx.iv_sz );
|
||||
|
||||
Buffer.BlockCopy( Encoding.UTF8.GetBytes( SQLITE_FILE_HEADER ), 0, ctx.read_ctx.iv, 0, FILE_HEADER_SZ );
|
||||
|
||||
sqlite3pager_sqlite3PagerSetCodec( sqlite3BtreePager( pDb.pBt ), sqlite3Codec, null, sqlite3FreeCodecArg, ctx );
|
||||
|
||||
codec_set_cipher_name( db, nDb, CIPHER, 0 );
|
||||
codec_set_pass_key( db, nDb, zKey, nKey, 0 );
|
||||
cipher_ctx_copy( ctx.write_ctx, ctx.read_ctx );
|
||||
|
||||
//sqlite3BtreeSetPageSize( ctx.pBt, sqlite3BtreeGetPageSize( ctx.pBt ), MAX_IV_LENGTH, 0 );
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static void sqlite3FreeCodecArg( ref codec_ctx pCodecArg )
|
||||
{
|
||||
if ( pCodecArg == null )
|
||||
return;
|
||||
codec_ctx_free( ref pCodecArg ); // wipe and free allocated memory for the context
|
||||
}
|
||||
|
||||
static void sqlite3_activate_see( string zPassword )
|
||||
{
|
||||
/* do nothing, security enhancements are always active */
|
||||
}
|
||||
|
||||
static public int sqlite3_key( sqlite3 db, string pKey, int nKey )
|
||||
{
|
||||
CODEC_TRACE( "sqlite3_key: entered db=%d pKey=%s nKey=%d\n", db, pKey, nKey );
|
||||
/* attach key if db and pKey are not null and nKey is > 0 */
|
||||
if ( db != null && pKey != null )
|
||||
{
|
||||
sqlite3CodecAttach( db, 0, pKey, nKey ); // operate only on the main db
|
||||
//
|
||||
// If we are reopening an existing database, redo the header information setup
|
||||
//
|
||||
BtShared pBt = db.aDb[0].pBt.pBt;
|
||||
byte[] zDbHeader = sqlite3MemMalloc( (int)pBt.pageSize );// pBt.pPager.pCodec.buffer;
|
||||
sqlite3PagerReadFileheader( pBt.pPager, zDbHeader.Length, zDbHeader );
|
||||
if ( sqlite3Get4byte( zDbHeader ) > 0 ) // Existing Database, need to reset some values
|
||||
{
|
||||
CODEC2( pBt.pPager, zDbHeader, 2, SQLITE_DECRYPT, ref zDbHeader );
|
||||
byte nReserve = zDbHeader[20];
|
||||
pBt.pageSize = (uint)( ( zDbHeader[16] << 8 ) | ( zDbHeader[17] << 16 ) );
|
||||
if ( pBt.pageSize < 512 || pBt.pageSize > SQLITE_MAX_PAGE_SIZE
|
||||
|| ( ( pBt.pageSize - 1 ) & pBt.pageSize ) != 0 )
|
||||
pBt.pageSize = 0;
|
||||
pBt.pageSizeFixed = true;
|
||||
#if !SQLITE_OMIT_AUTOVACUUM
|
||||
pBt.autoVacuum = sqlite3Get4byte( zDbHeader, 36 + 4 * 4 ) != 0;
|
||||
pBt.incrVacuum = sqlite3Get4byte( zDbHeader, 36 + 7 * 4 ) != 0;
|
||||
#endif
|
||||
sqlite3PagerSetPagesize( pBt.pPager, ref pBt.pageSize, nReserve );
|
||||
pBt.usableSize = (u16)( pBt.pageSize - nReserve );
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/* sqlite3_rekey
|
||||
** Given a database, this will reencrypt the database using a new key.
|
||||
** There are two possible modes of operation. The first is rekeying
|
||||
** an existing database that was not previously encrypted. The second
|
||||
** is to change the key on an existing database.
|
||||
**
|
||||
** The proposed logic for this function follows:
|
||||
** 1. Determine if there is already a key present
|
||||
** 2. If there is NOT already a key present, create one and attach a codec (key would be null)
|
||||
** 3. Initialize a ctx.rekey parameter of the codec
|
||||
**
|
||||
** Note: this will require modifications to the sqlite3Codec to support rekey
|
||||
**
|
||||
*/
|
||||
static int sqlite3_rekey( sqlite3 db, string pKey, int nKey )
|
||||
{
|
||||
CODEC_TRACE( "sqlite3_rekey: entered db=%d pKey=%s, nKey=%d\n", db, pKey, nKey );
|
||||
//activate_openssl();
|
||||
if ( db != null && pKey != null )
|
||||
{
|
||||
Db pDb = db.aDb[0];
|
||||
CODEC_TRACE( "sqlite3_rekey: database pDb=%d\n", pDb );
|
||||
if ( pDb.pBt != null )
|
||||
{
|
||||
codec_ctx ctx = null;
|
||||
int rc;
|
||||
Pgno page_count = 0;
|
||||
Pgno pgno;
|
||||
PgHdr page = null;
|
||||
Pager pPager = pDb.pBt.pBt.pPager;
|
||||
|
||||
sqlite3pager_get_codec( pDb.pBt.pBt.pPager, ref ctx );
|
||||
|
||||
if ( ctx == null )
|
||||
{
|
||||
CODEC_TRACE( "sqlite3_rekey: no codec attached to db, attaching now\n" );
|
||||
/* there was no codec attached to this database,so attach one now with a null password */
|
||||
sqlite3CodecAttach( db, 0, pKey, nKey );
|
||||
sqlite3pager_get_codec( pDb.pBt.pBt.pPager, ref ctx );
|
||||
|
||||
/* prepare this setup as if it had already been initialized */
|
||||
Buffer.BlockCopy( Encoding.UTF8.GetBytes( SQLITE_FILE_HEADER ), 0, ctx.read_ctx.iv, 0, FILE_HEADER_SZ );
|
||||
ctx.read_ctx.key_sz = ctx.read_ctx.iv_sz = ctx.read_ctx.pass_sz = 0;
|
||||
}
|
||||
|
||||
//if ( ctx.read_ctx.iv_sz != ctx.write_ctx.iv_sz )
|
||||
//{
|
||||
// string error = "";
|
||||
// CODEC_TRACE( "sqlite3_rekey: updating page size for iv_sz change from %d to %d\n", ctx.read_ctx.iv_sz, ctx.write_ctx.iv_sz );
|
||||
// db.nextPagesize = sqlite3BtreeGetPageSize( pDb.pBt );
|
||||
// pDb.pBt.pBt.pageSizeFixed = false; /* required for sqlite3BtreeSetPageSize to modify pagesize setting */
|
||||
// sqlite3BtreeSetPageSize( pDb.pBt, db.nextPagesize, MAX_IV_LENGTH, 0 );
|
||||
// sqlite3RunVacuum( ref error, db );
|
||||
//}
|
||||
|
||||
codec_set_pass_key( db, 0, pKey, nKey, 1 );
|
||||
ctx.mode_rekey = 1;
|
||||
/* do stuff here to rewrite the database
|
||||
** 1. Create a transaction on the database
|
||||
** 2. Iterate through each page, reading it and then writing it.
|
||||
** 3. If that goes ok then commit and put ctx.rekey into ctx.key
|
||||
** note: don't deallocate rekey since it may be used in a subsequent iteration
|
||||
*/
|
||||
rc = sqlite3BtreeBeginTrans( pDb.pBt, 1 ); /* begin write transaction */
|
||||
sqlite3PagerPagecount( pPager, out page_count );
|
||||
for ( pgno = 1; rc == SQLITE_OK && pgno <= page_count; pgno++ )
|
||||
{ /* pgno's start at 1 see pager.c:pagerAcquire */
|
||||
if ( 0 == sqlite3pager_is_mj_pgno( pPager, pgno ) )
|
||||
{ /* skip this page (see pager.c:pagerAcquire for reasoning) */
|
||||
rc = sqlite3PagerGet( pPager, pgno, ref page );
|
||||
if ( rc == SQLITE_OK )
|
||||
{ /* write page see pager_incr_changecounter for example */
|
||||
rc = sqlite3PagerWrite( page );
|
||||
//printf("sqlite3PagerWrite(%d)\n", pgno);
|
||||
if ( rc == SQLITE_OK )
|
||||
{
|
||||
sqlite3PagerUnref( page );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* if commit was successful commit and copy the rekey data to current key, else rollback to release locks */
|
||||
if ( rc == SQLITE_OK )
|
||||
{
|
||||
CODEC_TRACE( "sqlite3_rekey: committing\n" );
|
||||
db.nextPagesize = sqlite3BtreeGetPageSize( pDb.pBt );
|
||||
rc = sqlite3BtreeCommit( pDb.pBt );
|
||||
if ( ctx != null )
|
||||
cipher_ctx_copy( ctx.read_ctx, ctx.write_ctx );
|
||||
}
|
||||
else
|
||||
{
|
||||
CODEC_TRACE( "sqlite3_rekey: rollback\n" );
|
||||
sqlite3BtreeRollback( pDb.pBt );
|
||||
}
|
||||
|
||||
ctx.mode_rekey = 0;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
static void sqlite3CodecGetKey( sqlite3 db, int nDb, out string zKey, out int nKey )
|
||||
{
|
||||
Db pDb = db.aDb[nDb];
|
||||
CODEC_TRACE( "sqlite3CodecGetKey: entered db=%d, nDb=%d\n", db, nDb );
|
||||
|
||||
if ( pDb.pBt != null )
|
||||
{
|
||||
codec_ctx ctx = null;
|
||||
sqlite3pager_get_codec( pDb.pBt.pBt.pPager, ref ctx );
|
||||
|
||||
if ( ctx != null )
|
||||
{ /* if the codec has an attached codec_context user the raw key data */
|
||||
zKey = ctx.read_ctx.pass;
|
||||
nKey = ctx.read_ctx.pass_sz;
|
||||
return;
|
||||
}
|
||||
}
|
||||
zKey = null;
|
||||
nKey = 0;
|
||||
}
|
||||
/* END CRYPTO */
|
||||
#endif
|
||||
const int SQLITE_ENCRYPT_WRITE_CTX = 6; /* Encode page */
|
||||
const int SQLITE_ENCRYPT_READ_CTX = 7; /* Encode page */
|
||||
const int SQLITE_DECRYPT = 3; /* Decode page */
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,421 @@
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
using sqlite3_value = Sqlite3.Mem;
|
||||
using System;
|
||||
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/*
|
||||
** 2010 February 23
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** This file implements routines used to report what compile-time options
|
||||
** SQLite was built with.
|
||||
*************************************************************************
|
||||
** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart
|
||||
** C#-SQLite is an independent reimplementation of the SQLite software library
|
||||
**
|
||||
** SQLITE_SOURCE_ID: 2011-01-28 17:03:50 ed759d5a9edb3bba5f48f243df47be29e3fe8cd7
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
|
||||
#if !SQLITE_OMIT_COMPILEOPTION_DIAGS
|
||||
|
||||
//#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** An array of names of all compile-time options. This array should
|
||||
** be sorted A-Z.
|
||||
**
|
||||
** This array looks large, but in a typical installation actually uses
|
||||
** only a handful of compile-time options, so most times this array is usually
|
||||
** rather short and uses little memory space.
|
||||
*/
|
||||
static string[] azCompileOpt = {
|
||||
|
||||
/* These macros are provided to "stringify" the value of the define
|
||||
** for those options in which the value is meaningful. */
|
||||
//#define CTIMEOPT_VAL_(opt) #opt
|
||||
//#define CTIMEOPT_VAL(opt) CTIMEOPT_VAL_(opt)
|
||||
|
||||
#if SQLITE_32BIT_ROWID
|
||||
"32BIT_ROWID",
|
||||
#endif
|
||||
#if SQLITE_4_BYTE_ALIGNED_MALLOC
|
||||
"4_BYTE_ALIGNED_MALLOC",
|
||||
#endif
|
||||
#if SQLITE_CASE_SENSITIVE_LIKE
|
||||
"CASE_SENSITIVE_LIKE",
|
||||
#endif
|
||||
#if SQLITE_CHECK_PAGES
|
||||
"CHECK_PAGES",
|
||||
#endif
|
||||
#if SQLITE_COVERAGE_TEST
|
||||
"COVERAGE_TEST",
|
||||
#endif
|
||||
#if SQLITE_DEBUG
|
||||
"DEBUG",
|
||||
#endif
|
||||
#if SQLITE_DEFAULT_LOCKING_MODE
|
||||
"DEFAULT_LOCKING_MODE=" CTIMEOPT_VAL(SQLITE_DEFAULT_LOCKING_MODE),
|
||||
#endif
|
||||
#if SQLITE_DISABLE_DIRSYNC
|
||||
"DISABLE_DIRSYNC",
|
||||
#endif
|
||||
#if SQLITE_DISABLE_LFS
|
||||
"DISABLE_LFS",
|
||||
#endif
|
||||
#if SQLITE_ENABLE_ATOMIC_WRITE
|
||||
"ENABLE_ATOMIC_WRITE",
|
||||
#endif
|
||||
#if SQLITE_ENABLE_CEROD
|
||||
"ENABLE_CEROD",
|
||||
#endif
|
||||
#if SQLITE_ENABLE_COLUMN_METADATA
|
||||
"ENABLE_COLUMN_METADATA",
|
||||
#endif
|
||||
#if SQLITE_ENABLE_EXPENSIVE_ASSERT
|
||||
"ENABLE_EXPENSIVE_ASSERT",
|
||||
#endif
|
||||
#if SQLITE_ENABLE_FTS1
|
||||
"ENABLE_FTS1",
|
||||
#endif
|
||||
#if SQLITE_ENABLE_FTS2
|
||||
"ENABLE_FTS2",
|
||||
#endif
|
||||
#if SQLITE_ENABLE_FTS3
|
||||
"ENABLE_FTS3",
|
||||
#endif
|
||||
#if SQLITE_ENABLE_FTS3_PARENTHESIS
|
||||
"ENABLE_FTS3_PARENTHESIS",
|
||||
#endif
|
||||
#if SQLITE_ENABLE_FTS4
|
||||
"ENABLE_FTS4",
|
||||
#endif
|
||||
#if SQLITE_ENABLE_ICU
|
||||
"ENABLE_ICU",
|
||||
#endif
|
||||
#if SQLITE_ENABLE_IOTRACE
|
||||
"ENABLE_IOTRACE",
|
||||
#endif
|
||||
#if SQLITE_ENABLE_LOAD_EXTENSION
|
||||
"ENABLE_LOAD_EXTENSION",
|
||||
#endif
|
||||
#if SQLITE_ENABLE_LOCKING_STYLE
|
||||
"ENABLE_LOCKING_STYLE=" CTIMEOPT_VAL(SQLITE_ENABLE_LOCKING_STYLE),
|
||||
#endif
|
||||
#if SQLITE_ENABLE_MEMORY_MANAGEMENT
|
||||
"ENABLE_MEMORY_MANAGEMENT",
|
||||
#endif
|
||||
#if SQLITE_ENABLE_MEMSYS3
|
||||
"ENABLE_MEMSYS3",
|
||||
#endif
|
||||
#if SQLITE_ENABLE_MEMSYS5
|
||||
"ENABLE_MEMSYS5",
|
||||
#endif
|
||||
#if SQLITE_ENABLE_OVERSIZE_CELL_CHECK
|
||||
"ENABLE_OVERSIZE_CELL_CHECK",
|
||||
#endif
|
||||
#if SQLITE_ENABLE_RTREE
|
||||
"ENABLE_RTREE",
|
||||
#endif
|
||||
#if SQLITE_ENABLE_STAT2
|
||||
"ENABLE_STAT2",
|
||||
#endif
|
||||
#if SQLITE_ENABLE_UNLOCK_NOTIFY
|
||||
"ENABLE_UNLOCK_NOTIFY",
|
||||
#endif
|
||||
#if SQLITE_ENABLE_UPDATE_DELETE_LIMIT
|
||||
"ENABLE_UPDATE_DELETE_LIMIT",
|
||||
#endif
|
||||
#if !NOT_SQLITE_HAS_CODEC //#if !NOT_SQLITE_HAS_CODEC //#if SQLITE_HAS_CODEC
|
||||
"HAS_CODEC",
|
||||
#endif
|
||||
#if SQLITE_HAVE_ISNAN
|
||||
"HAVE_ISNAN",
|
||||
#endif
|
||||
#if SQLITE_HOMEGROWN_RECURSIVE_MUTEX
|
||||
"HOMEGROWN_RECURSIVE_MUTEX",
|
||||
#endif
|
||||
#if SQLITE_IGNORE_AFP_LOCK_ERRORS
|
||||
"IGNORE_AFP_LOCK_ERRORS",
|
||||
#endif
|
||||
#if SQLITE_IGNORE_FLOCK_LOCK_ERRORS
|
||||
"IGNORE_FLOCK_LOCK_ERRORS",
|
||||
#endif
|
||||
#if SQLITE_INT64_TYPE
|
||||
"INT64_TYPE",
|
||||
#endif
|
||||
#if SQLITE_LOCK_TRACE
|
||||
"LOCK_TRACE",
|
||||
#endif
|
||||
#if SQLITE_MEMDEBUG
|
||||
"MEMDEBUG",
|
||||
#endif
|
||||
#if SQLITE_MIXED_ENDIAN_64BIT_FLOAT
|
||||
"MIXED_ENDIAN_64BIT_FLOAT",
|
||||
#endif
|
||||
#if SQLITE_NO_SYNC
|
||||
"NO_SYNC",
|
||||
#endif
|
||||
#if SQLITE_OMIT_ALTERTABLE
|
||||
"OMIT_ALTERTABLE",
|
||||
#endif
|
||||
#if SQLITE_OMIT_ANALYZE
|
||||
"OMIT_ANALYZE",
|
||||
#endif
|
||||
#if SQLITE_OMIT_ATTACH
|
||||
"OMIT_ATTACH",
|
||||
#endif
|
||||
#if !NO_SQLITE_OMIT_AUTHORIZATION
|
||||
"OMIT_AUTHORIZATION",
|
||||
#endif
|
||||
#if SQLITE_OMIT_AUTOINCREMENT
|
||||
"OMIT_AUTOINCREMENT",
|
||||
#endif
|
||||
#if SQLITE_OMIT_AUTOINIT
|
||||
"OMIT_AUTOINIT",
|
||||
#endif
|
||||
#if SQLITE_OMIT_AUTOMATIC_INDEX
|
||||
"OMIT_AUTOMATIC_INDEX",
|
||||
#endif
|
||||
#if SQLITE_OMIT_AUTORESET
|
||||
"OMIT_AUTORESET",
|
||||
#endif
|
||||
#if SQLITE_OMIT_AUTOVACUUM
|
||||
"OMIT_AUTOVACUUM",
|
||||
#endif
|
||||
#if SQLITE_OMIT_BETWEEN_OPTIMIZATION
|
||||
"OMIT_BETWEEN_OPTIMIZATION",
|
||||
#endif
|
||||
#if SQLITE_OMIT_BLOB_LITERAL
|
||||
"OMIT_BLOB_LITERAL",
|
||||
#endif
|
||||
#if SQLITE_OMIT_BTREECOUNT
|
||||
"OMIT_BTREECOUNT",
|
||||
#endif
|
||||
#if SQLITE_OMIT_BUILTIN_TEST
|
||||
"OMIT_BUILTIN_TEST",
|
||||
#endif
|
||||
#if SQLITE_OMIT_CAST
|
||||
"OMIT_CAST",
|
||||
#endif
|
||||
#if SQLITE_OMIT_CHECK
|
||||
"OMIT_CHECK",
|
||||
#endif
|
||||
/* // redundant
|
||||
** #if SQLITE_OMIT_COMPILEOPTION_DIAGS
|
||||
** "OMIT_COMPILEOPTION_DIAGS",
|
||||
** #endif
|
||||
*/
|
||||
#if SQLITE_OMIT_COMPLETE
|
||||
"OMIT_COMPLETE",
|
||||
#endif
|
||||
#if SQLITE_OMIT_COMPOUND_SELECT
|
||||
"OMIT_COMPOUND_SELECT",
|
||||
#endif
|
||||
#if SQLITE_OMIT_DATETIME_FUNCS
|
||||
"OMIT_DATETIME_FUNCS",
|
||||
#endif
|
||||
#if SQLITE_OMIT_DECLTYPE
|
||||
"OMIT_DECLTYPE",
|
||||
#endif
|
||||
#if !NOT_SQLITE_OMIT_DEPRECATED
|
||||
"OMIT_DEPRECATED",
|
||||
#endif
|
||||
#if SQLITE_OMIT_DISKIO
|
||||
"OMIT_DISKIO",
|
||||
#endif
|
||||
#if SQLITE_OMIT_EXPLAIN
|
||||
"OMIT_EXPLAIN",
|
||||
#endif
|
||||
#if SQLITE_OMIT_FLAG_PRAGMAS
|
||||
"OMIT_FLAG_PRAGMAS",
|
||||
#endif
|
||||
#if SQLITE_OMIT_FLOATING_POINT
|
||||
"OMIT_FLOATING_POINT",
|
||||
#endif
|
||||
#if SQLITE_OMIT_FOREIGN_KEY
|
||||
"OMIT_FOREIGN_KEY",
|
||||
#endif
|
||||
#if !NO_SQLITE_OMIT_GET_TABLE
|
||||
"OMIT_GET_TABLE",
|
||||
#endif
|
||||
#if !NOT_SQLITE_OMIT_INCRBLOB
|
||||
"OMIT_INCRBLOB",
|
||||
#endif
|
||||
#if SQLITE_OMIT_INTEGRITY_CHECK
|
||||
"OMIT_INTEGRITY_CHECK",
|
||||
#endif
|
||||
#if SQLITE_OMIT_LIKE_OPTIMIZATION
|
||||
"OMIT_LIKE_OPTIMIZATION",
|
||||
#endif
|
||||
#if SQLITE_OMIT_LOAD_EXTENSION
|
||||
"OMIT_LOAD_EXTENSION",
|
||||
#endif
|
||||
#if SQLITE_OMIT_LOCALTIME
|
||||
"OMIT_LOCALTIME",
|
||||
#endif
|
||||
#if !NO_SQLITE_OMIT_LOOKASIDE
|
||||
"OMIT_LOOKASIDE",
|
||||
#endif
|
||||
#if SQLITE_OMIT_MEMORYDB
|
||||
"OMIT_MEMORYDB",
|
||||
#endif
|
||||
#if SQLITE_OMIT_OR_OPTIMIZATION
|
||||
"OMIT_OR_OPTIMIZATION",
|
||||
#endif
|
||||
#if SQLITE_OMIT_PAGER_PRAGMAS
|
||||
"OMIT_PAGER_PRAGMAS",
|
||||
#endif
|
||||
#if SQLITE_OMIT_PRAGMA
|
||||
"OMIT_PRAGMA",
|
||||
#endif
|
||||
#if SQLITE_OMIT_PROGRESS_CALLBACK
|
||||
"OMIT_PROGRESS_CALLBACK",
|
||||
#endif
|
||||
#if SQLITE_OMIT_QUICKBALANCE
|
||||
"OMIT_QUICKBALANCE",
|
||||
#endif
|
||||
#if SQLITE_OMIT_REINDEX
|
||||
"OMIT_REINDEX",
|
||||
#endif
|
||||
#if SQLITE_OMIT_SCHEMA_PRAGMAS
|
||||
"OMIT_SCHEMA_PRAGMAS",
|
||||
#endif
|
||||
#if SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS
|
||||
"OMIT_SCHEMA_VERSION_PRAGMAS",
|
||||
#endif
|
||||
#if !NO_SQLITE_OMIT_SHARED_CACHE
|
||||
//#if SQLITE_OMIT_SHARED_CACHE
|
||||
"OMIT_SHARED_CACHE",
|
||||
#endif
|
||||
#if SQLITE_OMIT_SUBQUERY
|
||||
"OMIT_SUBQUERY",
|
||||
#endif
|
||||
#if SQLITE_OMIT_TCL_VARIABLE
|
||||
"OMIT_TCL_VARIABLE",
|
||||
#endif
|
||||
#if SQLITE_OMIT_TEMPDB
|
||||
"OMIT_TEMPDB",
|
||||
#endif
|
||||
#if SQLITE_OMIT_TRACE
|
||||
"OMIT_TRACE",
|
||||
#endif
|
||||
#if SQLITE_OMIT_TRIGGER
|
||||
"OMIT_TRIGGER",
|
||||
#endif
|
||||
#if SQLITE_OMIT_TRUNCATE_OPTIMIZATION
|
||||
"OMIT_TRUNCATE_OPTIMIZATION",
|
||||
#endif
|
||||
#if !NO_SQLITE_OMIT_UTF16
|
||||
"OMIT_UTF16",
|
||||
#endif
|
||||
#if SQLITE_OMIT_VACUUM
|
||||
"OMIT_VACUUM",
|
||||
#endif
|
||||
#if SQLITE_OMIT_VIEW
|
||||
"OMIT_VIEW",
|
||||
#endif
|
||||
#if SQLITE_OMIT_VIRTUALTABLE
|
||||
"OMIT_VIRTUALTABLE",
|
||||
#endif
|
||||
#if SQLITE_OMIT_WAL
|
||||
"OMIT_WAL",
|
||||
#endif
|
||||
#if SQLITE_OMIT_WSD
|
||||
"OMIT_WSD",
|
||||
#endif
|
||||
#if SQLITE_OMIT_XFER_OPT
|
||||
"OMIT_XFER_OPT",
|
||||
#endif
|
||||
#if SQLITE_PERFORMANCE_TRACE
|
||||
"PERFORMANCE_TRACE",
|
||||
#endif
|
||||
#if SQLITE_PROXY_DEBUG
|
||||
"PROXY_DEBUG",
|
||||
#endif
|
||||
#if SQLITE_SECURE_DELETE
|
||||
"SECURE_DELETE",
|
||||
#endif
|
||||
#if SQLITE_SMALL_STACK
|
||||
"SMALL_STACK",
|
||||
#endif
|
||||
#if SQLITE_SOUNDEX
|
||||
"SOUNDEX",
|
||||
#endif
|
||||
#if SQLITE_TCL
|
||||
"TCL",
|
||||
#endif
|
||||
//#if SQLITE_TEMP_STORE
|
||||
"TEMP_STORE=1",//CTIMEOPT_VAL(SQLITE_TEMP_STORE),
|
||||
//#endif
|
||||
#if SQLITE_TEST
|
||||
"TEST",
|
||||
#endif
|
||||
#if SQLITE_THREADSAFE
|
||||
"THREADSAFE=2", // For C#, hardcode to = 2 CTIMEOPT_VAL(SQLITE_THREADSAFE),
|
||||
#else
|
||||
"THREADSAFE=0", // For C#, hardcode to = 0
|
||||
#endif
|
||||
#if SQLITE_USE_ALLOCA
|
||||
"USE_ALLOCA",
|
||||
#endif
|
||||
#if SQLITE_ZERO_MALLOC
|
||||
"ZERO_MALLOC"
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
** Given the name of a compile-time option, return true if that option
|
||||
** was used and false if not.
|
||||
**
|
||||
** The name can optionally begin with "SQLITE_" but the "SQLITE_" prefix
|
||||
** is not required for a match.
|
||||
*/
|
||||
static int sqlite3_compileoption_used( string zOptName )
|
||||
{
|
||||
if ( zOptName.EndsWith( "=" ) )
|
||||
return 0;
|
||||
int i, n = 0;
|
||||
if ( zOptName.StartsWith( "SQLITE_", System.StringComparison.InvariantCultureIgnoreCase ) )
|
||||
n = 7;
|
||||
//n = sqlite3Strlen30(zOptName);
|
||||
|
||||
/* Since ArraySize(azCompileOpt) is normally in single digits, a
|
||||
** linear search is adequate. No need for a binary search. */
|
||||
if ( !String.IsNullOrEmpty( zOptName ) )
|
||||
for ( i = 0; i < ArraySize( azCompileOpt ); i++ )
|
||||
{
|
||||
int n1 = ( zOptName.Length-n < azCompileOpt[i].Length ) ? zOptName.Length-n : azCompileOpt[i].Length;
|
||||
if ( String.Compare( zOptName, n, azCompileOpt[i], 0, n1, StringComparison.InvariantCultureIgnoreCase ) == 0 )
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the N-th compile-time option string. If N is out of range,
|
||||
** return a NULL pointer.
|
||||
*/
|
||||
static string sqlite3_compileoption_get( int N )
|
||||
{
|
||||
if ( N >= 0 && N < ArraySize( azCompileOpt ) )
|
||||
{
|
||||
return azCompileOpt[N];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
#endif //* SQLITE_OMIT_COMPILEOPTION_DIAGS */
|
||||
}
|
||||
}
|
||||
1410
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/date_c.cs
Normal file
1410
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/date_c.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,748 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
using u8 = System.Byte;
|
||||
using u32 = System.UInt32;
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/*
|
||||
** 2001 September 15
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file contains C code routines that are called by the parser
|
||||
** in order to generate code for DELETE FROM statements.
|
||||
*************************************************************************
|
||||
** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart
|
||||
** C#-SQLite is an independent reimplementation of the SQLite software library
|
||||
**
|
||||
** SQLITE_SOURCE_ID: 2011-06-23 19:49:22 4374b7e83ea0a3fbc3691f9c0c936272862f32f2
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
//#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** While a SrcList can in general represent multiple tables and subqueries
|
||||
** (as in the FROM clause of a SELECT statement) in this case it contains
|
||||
** the name of a single table, as one might find in an INSERT, DELETE,
|
||||
** or UPDATE statement. Look up that table in the symbol table and
|
||||
** return a pointer. Set an error message and return NULL if the table
|
||||
** name is not found or if any other error occurs.
|
||||
**
|
||||
** The following fields are initialized appropriate in pSrc:
|
||||
**
|
||||
** pSrc->a[0].pTab Pointer to the Table object
|
||||
** pSrc->a[0].pIndex Pointer to the INDEXED BY index, if there is one
|
||||
**
|
||||
*/
|
||||
static Table sqlite3SrcListLookup( Parse pParse, SrcList pSrc )
|
||||
{
|
||||
SrcList_item pItem = pSrc.a[0];
|
||||
Table pTab;
|
||||
Debug.Assert( pItem != null && pSrc.nSrc == 1 );
|
||||
pTab = sqlite3LocateTable( pParse, 0, pItem.zName, pItem.zDatabase );
|
||||
sqlite3DeleteTable( pParse.db, ref pItem.pTab );
|
||||
pItem.pTab = pTab;
|
||||
if ( pTab != null )
|
||||
{
|
||||
pTab.nRef++;
|
||||
}
|
||||
if ( sqlite3IndexedByLookup( pParse, pItem ) != 0 )
|
||||
{
|
||||
pTab = null;
|
||||
}
|
||||
return pTab;
|
||||
}
|
||||
|
||||
/*
|
||||
** Check to make sure the given table is writable. If it is not
|
||||
** writable, generate an error message and return 1. If it is
|
||||
** writable return 0;
|
||||
*/
|
||||
static bool sqlite3IsReadOnly( Parse pParse, Table pTab, int viewOk )
|
||||
{
|
||||
/* A table is not writable under the following circumstances:
|
||||
**
|
||||
** 1) It is a virtual table and no implementation of the xUpdate method
|
||||
** has been provided, or
|
||||
** 2) It is a system table (i.e. sqlite_master), this call is not
|
||||
** part of a nested parse and writable_schema pragma has not
|
||||
** been specified.
|
||||
**
|
||||
** In either case leave an error message in pParse and return non-zero.
|
||||
*/
|
||||
if (
|
||||
( IsVirtual( pTab )
|
||||
&& sqlite3GetVTable( pParse.db, pTab ).pMod.pModule.xUpdate == null )
|
||||
|| ( ( pTab.tabFlags & TF_Readonly ) != 0
|
||||
&& ( pParse.db.flags & SQLITE_WriteSchema ) == 0
|
||||
&& pParse.nested == 0 )
|
||||
)
|
||||
{
|
||||
sqlite3ErrorMsg( pParse, "table %s may not be modified", pTab.zName );
|
||||
return true;
|
||||
}
|
||||
|
||||
#if !SQLITE_OMIT_VIEW
|
||||
if ( viewOk == 0 && pTab.pSelect != null )
|
||||
{
|
||||
sqlite3ErrorMsg( pParse, "cannot modify %s because it is a view", pTab.zName );
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
#if !SQLITE_OMIT_VIEW && !SQLITE_OMIT_TRIGGER
|
||||
/*
|
||||
** Evaluate a view and store its result in an ephemeral table. The
|
||||
** pWhere argument is an optional WHERE clause that restricts the
|
||||
** set of rows in the view that are to be added to the ephemeral table.
|
||||
*/
|
||||
static void sqlite3MaterializeView(
|
||||
Parse pParse, /* Parsing context */
|
||||
Table pView, /* View definition */
|
||||
Expr pWhere, /* Optional WHERE clause to be added */
|
||||
int iCur /* VdbeCursor number for ephemerial table */
|
||||
)
|
||||
{
|
||||
SelectDest dest = new SelectDest();
|
||||
Select pDup;
|
||||
sqlite3 db = pParse.db;
|
||||
|
||||
pDup = sqlite3SelectDup( db, pView.pSelect, 0 );
|
||||
if ( pWhere != null )
|
||||
{
|
||||
SrcList pFrom;
|
||||
|
||||
pWhere = sqlite3ExprDup( db, pWhere, 0 );
|
||||
pFrom = sqlite3SrcListAppend( db, null, null, null );
|
||||
//if ( pFrom != null )
|
||||
//{
|
||||
Debug.Assert( pFrom.nSrc == 1 );
|
||||
pFrom.a[0].zAlias = pView.zName;// sqlite3DbStrDup( db, pView.zName );
|
||||
pFrom.a[0].pSelect = pDup;
|
||||
Debug.Assert( pFrom.a[0].pOn == null );
|
||||
Debug.Assert( pFrom.a[0].pUsing == null );
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// sqlite3SelectDelete( db, ref pDup );
|
||||
//}
|
||||
pDup = sqlite3SelectNew( pParse, null, pFrom, pWhere, null, null, null, 0, null, null );
|
||||
}
|
||||
sqlite3SelectDestInit( dest, SRT_EphemTab, iCur );
|
||||
sqlite3Select( pParse, pDup, ref dest );
|
||||
sqlite3SelectDelete( db, ref pDup );
|
||||
}
|
||||
#endif //* !SQLITE_OMIT_VIEW) && !SQLITE_OMIT_TRIGGER) */
|
||||
|
||||
#if (SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !(SQLITE_OMIT_SUBQUERY)
|
||||
/*
|
||||
** Generate an expression tree to implement the WHERE, ORDER BY,
|
||||
** and LIMIT/OFFSET portion of DELETE and UPDATE statements.
|
||||
**
|
||||
** DELETE FROM table_wxyz WHERE a<5 ORDER BY a LIMIT 1;
|
||||
** \__________________________/
|
||||
** pLimitWhere (pInClause)
|
||||
*/
|
||||
Expr sqlite3LimitWhere(
|
||||
Parse pParse, /* The parser context */
|
||||
SrcList pSrc, /* the FROM clause -- which tables to scan */
|
||||
Expr pWhere, /* The WHERE clause. May be null */
|
||||
ExprList pOrderBy, /* The ORDER BY clause. May be null */
|
||||
Expr pLimit, /* The LIMIT clause. May be null */
|
||||
Expr pOffset, /* The OFFSET clause. May be null */
|
||||
char zStmtType /* Either DELETE or UPDATE. For error messages. */
|
||||
){
|
||||
Expr pWhereRowid = null; /* WHERE rowid .. */
|
||||
Expr pInClause = null; /* WHERE rowid IN ( select ) */
|
||||
Expr pSelectRowid = null; /* SELECT rowid ... */
|
||||
ExprList pEList = null; /* Expression list contaning only pSelectRowid */
|
||||
SrcList pSelectSrc = null; /* SELECT rowid FROM x ... (dup of pSrc) */
|
||||
Select pSelect = null; /* Complete SELECT tree */
|
||||
|
||||
/* Check that there isn't an ORDER BY without a LIMIT clause.
|
||||
*/
|
||||
if( pOrderBy!=null && (pLimit == null) ) {
|
||||
sqlite3ErrorMsg(pParse, "ORDER BY without LIMIT on %s", zStmtType);
|
||||
pParse.parseError = 1;
|
||||
goto limit_where_cleanup_2;
|
||||
}
|
||||
|
||||
/* We only need to generate a select expression if there
|
||||
** is a limit/offset term to enforce.
|
||||
*/
|
||||
if ( pLimit == null )
|
||||
{
|
||||
/* if pLimit is null, pOffset will always be null as well. */
|
||||
Debug.Assert( pOffset == null );
|
||||
return pWhere;
|
||||
}
|
||||
|
||||
/* Generate a select expression tree to enforce the limit/offset
|
||||
** term for the DELETE or UPDATE statement. For example:
|
||||
** DELETE FROM table_a WHERE col1=1 ORDER BY col2 LIMIT 1 OFFSET 1
|
||||
** becomes:
|
||||
** DELETE FROM table_a WHERE rowid IN (
|
||||
** SELECT rowid FROM table_a WHERE col1=1 ORDER BY col2 LIMIT 1 OFFSET 1
|
||||
** );
|
||||
*/
|
||||
|
||||
pSelectRowid = sqlite3PExpr( pParse, TK_ROW, null, null, null );
|
||||
if( pSelectRowid == null ) goto limit_where_cleanup_2;
|
||||
pEList = sqlite3ExprListAppend( pParse, null, pSelectRowid);
|
||||
if( pEList == null ) goto limit_where_cleanup_2;
|
||||
|
||||
/* duplicate the FROM clause as it is needed by both the DELETE/UPDATE tree
|
||||
** and the SELECT subtree. */
|
||||
pSelectSrc = sqlite3SrcListDup(pParse.db, pSrc,0);
|
||||
if( pSelectSrc == null ) {
|
||||
sqlite3ExprListDelete(pParse.db, pEList);
|
||||
goto limit_where_cleanup_2;
|
||||
}
|
||||
|
||||
/* generate the SELECT expression tree. */
|
||||
pSelect = sqlite3SelectNew( pParse, pEList, pSelectSrc, pWhere, null, null,
|
||||
pOrderBy, 0, pLimit, pOffset );
|
||||
if( pSelect == null ) return null;
|
||||
|
||||
/* now generate the new WHERE rowid IN clause for the DELETE/UDPATE */
|
||||
pWhereRowid = sqlite3PExpr( pParse, TK_ROW, null, null, null );
|
||||
if( pWhereRowid == null ) goto limit_where_cleanup_1;
|
||||
pInClause = sqlite3PExpr( pParse, TK_IN, pWhereRowid, null, null );
|
||||
if( pInClause == null ) goto limit_where_cleanup_1;
|
||||
|
||||
pInClause->x.pSelect = pSelect;
|
||||
pInClause->flags |= EP_xIsSelect;
|
||||
sqlite3ExprSetHeight(pParse, pInClause);
|
||||
return pInClause;
|
||||
|
||||
/* something went wrong. clean up anything allocated. */
|
||||
limit_where_cleanup_1:
|
||||
sqlite3SelectDelete(pParse.db, pSelect);
|
||||
return null;
|
||||
|
||||
limit_where_cleanup_2:
|
||||
sqlite3ExprDelete(pParse.db, ref pWhere);
|
||||
sqlite3ExprListDelete(pParse.db, pOrderBy);
|
||||
sqlite3ExprDelete(pParse.db, ref pLimit);
|
||||
sqlite3ExprDelete(pParse.db, ref pOffset);
|
||||
return null;
|
||||
}
|
||||
#endif //* defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY) */
|
||||
|
||||
/*
|
||||
** Generate code for a DELETE FROM statement.
|
||||
**
|
||||
** DELETE FROM table_wxyz WHERE a<5 AND b NOT NULL;
|
||||
** \________/ \________________/
|
||||
** pTabList pWhere
|
||||
*/
|
||||
static void sqlite3DeleteFrom(
|
||||
Parse pParse, /* The parser context */
|
||||
SrcList pTabList, /* The table from which we should delete things */
|
||||
Expr pWhere /* The WHERE clause. May be null */
|
||||
)
|
||||
{
|
||||
Vdbe v; /* The virtual database engine */
|
||||
Table pTab; /* The table from which records will be deleted */
|
||||
string zDb; /* Name of database holding pTab */
|
||||
int end, addr = 0; /* A couple addresses of generated code */
|
||||
int i; /* Loop counter */
|
||||
WhereInfo pWInfo; /* Information about the WHERE clause */
|
||||
Index pIdx; /* For looping over indices of the table */
|
||||
int iCur; /* VDBE VdbeCursor number for pTab */
|
||||
sqlite3 db; /* Main database structure */
|
||||
AuthContext sContext; /* Authorization context */
|
||||
NameContext sNC; /* Name context to resolve expressions in */
|
||||
int iDb; /* Database number */
|
||||
int memCnt = -1; /* Memory cell used for change counting */
|
||||
int rcauth; /* Value returned by authorization callback */
|
||||
|
||||
#if !SQLITE_OMIT_TRIGGER
|
||||
bool isView; /* True if attempting to delete from a view */
|
||||
Trigger pTrigger; /* List of table triggers, if required */
|
||||
#endif
|
||||
sContext = new AuthContext();//memset(&sContext, 0, sizeof(sContext));
|
||||
|
||||
db = pParse.db;
|
||||
if ( pParse.nErr != 0 /*|| db.mallocFailed != 0 */ )
|
||||
{
|
||||
goto delete_from_cleanup;
|
||||
}
|
||||
Debug.Assert( pTabList.nSrc == 1 );
|
||||
|
||||
/* Locate the table which we want to delete. This table has to be
|
||||
** put in an SrcList structure because some of the subroutines we
|
||||
** will be calling are designed to work with multiple tables and expect
|
||||
** an SrcList* parameter instead of just a Table* parameter.
|
||||
*/
|
||||
pTab = sqlite3SrcListLookup( pParse, pTabList );
|
||||
if ( pTab == null )
|
||||
goto delete_from_cleanup;
|
||||
|
||||
/* Figure out if we have any triggers and if the table being
|
||||
** deleted from is a view
|
||||
*/
|
||||
#if !SQLITE_OMIT_TRIGGER
|
||||
int iDummy;
|
||||
pTrigger = sqlite3TriggersExist( pParse, pTab, TK_DELETE, null, out iDummy );
|
||||
isView = pTab.pSelect != null;
|
||||
#else
|
||||
const Trigger pTrigger = null;
|
||||
bool isView = false;
|
||||
#endif
|
||||
#if SQLITE_OMIT_VIEW
|
||||
//# undef isView
|
||||
isView = false;
|
||||
#endif
|
||||
|
||||
/* If pTab is really a view, make sure it has been initialized.
|
||||
*/
|
||||
if ( sqlite3ViewGetColumnNames( pParse, pTab ) != 0 )
|
||||
{
|
||||
goto delete_from_cleanup;
|
||||
}
|
||||
|
||||
if ( sqlite3IsReadOnly( pParse, pTab, ( pTrigger != null ? 1 : 0 ) ) )
|
||||
{
|
||||
goto delete_from_cleanup;
|
||||
}
|
||||
iDb = sqlite3SchemaToIndex( db, pTab.pSchema );
|
||||
Debug.Assert( iDb < db.nDb );
|
||||
zDb = db.aDb[iDb].zName;
|
||||
#if NO_SQLITE_OMIT_AUTHORIZATION //#if !SQLITE_OMIT_AUTHORIZATION
|
||||
rcauth = sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb);
|
||||
#else
|
||||
rcauth = SQLITE_OK;
|
||||
#endif
|
||||
Debug.Assert( rcauth == SQLITE_OK || rcauth == SQLITE_DENY || rcauth == SQLITE_IGNORE );
|
||||
if ( rcauth == SQLITE_DENY )
|
||||
{
|
||||
goto delete_from_cleanup;
|
||||
}
|
||||
Debug.Assert( !isView || pTrigger != null );
|
||||
|
||||
/* Assign cursor number to the table and all its indices.
|
||||
*/
|
||||
Debug.Assert( pTabList.nSrc == 1 );
|
||||
iCur = pTabList.a[0].iCursor = pParse.nTab++;
|
||||
for ( pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext )
|
||||
{
|
||||
pParse.nTab++;
|
||||
}
|
||||
|
||||
#if NO_SQLITE_OMIT_AUTHORIZATION //#if !SQLITE_OMIT_AUTHORIZATION
|
||||
/* Start the view context
|
||||
*/
|
||||
if( isView ){
|
||||
sqlite3AuthContextPush(pParse, sContext, pTab.zName);
|
||||
}
|
||||
#endif
|
||||
/* Begin generating code.
|
||||
*/
|
||||
v = sqlite3GetVdbe( pParse );
|
||||
if ( v == null )
|
||||
{
|
||||
goto delete_from_cleanup;
|
||||
}
|
||||
if ( pParse.nested == 0 )
|
||||
sqlite3VdbeCountChanges( v );
|
||||
sqlite3BeginWriteOperation( pParse, 1, iDb );
|
||||
|
||||
/* If we are trying to delete from a view, realize that view into
|
||||
** a ephemeral table.
|
||||
*/
|
||||
#if !(SQLITE_OMIT_VIEW) && !(SQLITE_OMIT_TRIGGER)
|
||||
if ( isView )
|
||||
{
|
||||
sqlite3MaterializeView( pParse, pTab, pWhere, iCur );
|
||||
}
|
||||
#endif
|
||||
/* Resolve the column names in the WHERE clause.
|
||||
*/
|
||||
sNC = new NameContext();// memset( &sNC, 0, sizeof( sNC ) );
|
||||
sNC.pParse = pParse;
|
||||
sNC.pSrcList = pTabList;
|
||||
if ( sqlite3ResolveExprNames( sNC, ref pWhere ) != 0 )
|
||||
{
|
||||
goto delete_from_cleanup;
|
||||
}
|
||||
|
||||
|
||||
/* Initialize the counter of the number of rows deleted, if
|
||||
** we are counting rows.
|
||||
*/
|
||||
if ( ( db.flags & SQLITE_CountRows ) != 0 )
|
||||
{
|
||||
memCnt = ++pParse.nMem;
|
||||
sqlite3VdbeAddOp2( v, OP_Integer, 0, memCnt );
|
||||
}
|
||||
|
||||
#if !SQLITE_OMIT_TRUNCATE_OPTIMIZATION
|
||||
/* Special case: A DELETE without a WHERE clause deletes everything.
|
||||
** It is easier just to erase the whole table. Prior to version 3.6.5,
|
||||
** this optimization caused the row change count (the value returned by
|
||||
** API function sqlite3_count_changes) to be set incorrectly. */
|
||||
if ( rcauth == SQLITE_OK && pWhere == null && null == pTrigger && !IsVirtual( pTab )
|
||||
&& 0 == sqlite3FkRequired( pParse, pTab, null, 0 )
|
||||
)
|
||||
{
|
||||
Debug.Assert( !isView );
|
||||
sqlite3VdbeAddOp4( v, OP_Clear, pTab.tnum, iDb, memCnt,
|
||||
pTab.zName, P4_STATIC );
|
||||
for ( pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext )
|
||||
{
|
||||
Debug.Assert( pIdx.pSchema == pTab.pSchema );
|
||||
sqlite3VdbeAddOp2( v, OP_Clear, pIdx.tnum, iDb );
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif //* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */
|
||||
/* The usual case: There is a WHERE clause so we have to scan through
|
||||
** the table and pick which records to delete.
|
||||
*/
|
||||
{
|
||||
int iRowSet = ++pParse.nMem; /* Register for rowset of rows to delete */
|
||||
int iRowid = ++pParse.nMem; /* Used for storing rowid values. */
|
||||
int regRowid; /* Actual register containing rowids */
|
||||
|
||||
/* Collect rowids of every row to be deleted.
|
||||
*/
|
||||
sqlite3VdbeAddOp2( v, OP_Null, 0, iRowSet );
|
||||
ExprList elDummy = null;
|
||||
pWInfo = sqlite3WhereBegin( pParse, pTabList, pWhere, ref elDummy, WHERE_DUPLICATES_OK );
|
||||
if ( pWInfo == null )
|
||||
goto delete_from_cleanup;
|
||||
regRowid = sqlite3ExprCodeGetColumn( pParse, pTab, -1, iCur, iRowid );
|
||||
sqlite3VdbeAddOp2( v, OP_RowSetAdd, iRowSet, regRowid );
|
||||
if ( ( db.flags & SQLITE_CountRows ) != 0 )
|
||||
{
|
||||
sqlite3VdbeAddOp2( v, OP_AddImm, memCnt, 1 );
|
||||
}
|
||||
|
||||
sqlite3WhereEnd( pWInfo );
|
||||
|
||||
/* Delete every item whose key was written to the list during the
|
||||
** database scan. We have to delete items after the scan is complete
|
||||
** because deleting an item can change the scan order. */
|
||||
end = sqlite3VdbeMakeLabel( v );
|
||||
|
||||
/* Unless this is a view, open cursors for the table we are
|
||||
** deleting from and all its indices. If this is a view, then the
|
||||
** only effect this statement has is to fire the INSTEAD OF
|
||||
** triggers. */
|
||||
if ( !isView )
|
||||
{
|
||||
sqlite3OpenTableAndIndices( pParse, pTab, iCur, OP_OpenWrite );
|
||||
}
|
||||
|
||||
addr = sqlite3VdbeAddOp3( v, OP_RowSetRead, iRowSet, end, iRowid );
|
||||
|
||||
/* Delete the row */
|
||||
#if !SQLITE_OMIT_VIRTUALTABLE
|
||||
if ( IsVirtual( pTab ) )
|
||||
{
|
||||
VTable pVTab = sqlite3GetVTable( db, pTab );
|
||||
sqlite3VtabMakeWritable( pParse, pTab );
|
||||
sqlite3VdbeAddOp4( v, OP_VUpdate, 0, 1, iRowid, pVTab, P4_VTAB );
|
||||
sqlite3VdbeChangeP5( v, OE_Abort );
|
||||
sqlite3MayAbort( pParse );
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
int count = ( pParse.nested == 0 ) ? 1 : 0; /* True to count changes */
|
||||
sqlite3GenerateRowDelete( pParse, pTab, iCur, iRowid, count, pTrigger, OE_Default );
|
||||
}
|
||||
|
||||
/* End of the delete loop */
|
||||
sqlite3VdbeAddOp2( v, OP_Goto, 0, addr );
|
||||
sqlite3VdbeResolveLabel( v, end );
|
||||
|
||||
/* Close the cursors open on the table and its indexes. */
|
||||
if ( !isView && !IsVirtual( pTab ) )
|
||||
{
|
||||
for ( i = 1, pIdx = pTab.pIndex; pIdx != null; i++, pIdx = pIdx.pNext )
|
||||
{
|
||||
sqlite3VdbeAddOp2( v, OP_Close, iCur + i, pIdx.tnum );
|
||||
}
|
||||
sqlite3VdbeAddOp1( v, OP_Close, iCur );
|
||||
}
|
||||
}
|
||||
|
||||
/* Update the sqlite_sequence table by storing the content of the
|
||||
** maximum rowid counter values recorded while inserting into
|
||||
** autoincrement tables.
|
||||
*/
|
||||
if ( pParse.nested == 0 && pParse.pTriggerTab == null )
|
||||
{
|
||||
sqlite3AutoincrementEnd( pParse );
|
||||
}
|
||||
|
||||
/* Return the number of rows that were deleted. If this routine is
|
||||
** generating code because of a call to sqlite3NestedParse(), do not
|
||||
** invoke the callback function.
|
||||
*/
|
||||
|
||||
if ( ( db.flags & SQLITE_CountRows ) != 0 && 0 == pParse.nested && null == pParse.pTriggerTab )
|
||||
{
|
||||
sqlite3VdbeAddOp2( v, OP_ResultRow, memCnt, 1 );
|
||||
sqlite3VdbeSetNumCols( v, 1 );
|
||||
sqlite3VdbeSetColName( v, 0, COLNAME_NAME, "rows deleted", SQLITE_STATIC );
|
||||
}
|
||||
|
||||
delete_from_cleanup:
|
||||
#if NO_SQLITE_OMIT_AUTHORIZATION //#if !SQLITE_OMIT_AUTHORIZATION
|
||||
sqlite3AuthContextPop(sContext);
|
||||
#endif
|
||||
sqlite3SrcListDelete( db, ref pTabList );
|
||||
sqlite3ExprDelete( db, ref pWhere );
|
||||
return;
|
||||
}
|
||||
|
||||
/* Make sure "isView" and other macros defined above are undefined. Otherwise
|
||||
** thely may interfere with compilation of other functions in this file
|
||||
** (or in another file, if this file becomes part of the amalgamation). */
|
||||
//#if isView
|
||||
// #undef isView
|
||||
//#endif
|
||||
//#if pTrigger
|
||||
// #undef pTrigger
|
||||
//#endif
|
||||
|
||||
/*
|
||||
** This routine generates VDBE code that causes a single row of a
|
||||
** single table to be deleted.
|
||||
**
|
||||
** The VDBE must be in a particular state when this routine is called.
|
||||
** These are the requirements:
|
||||
**
|
||||
** 1. A read/write cursor pointing to pTab, the table containing the row
|
||||
** to be deleted, must be opened as cursor number $iCur.
|
||||
**
|
||||
** 2. Read/write cursors for all indices of pTab must be open as
|
||||
** cursor number base+i for the i-th index.
|
||||
**
|
||||
** 3. The record number of the row to be deleted must be stored in
|
||||
** memory cell iRowid.
|
||||
**
|
||||
** This routine generates code to remove both the table record and all
|
||||
** index entries that point to that record.
|
||||
*/
|
||||
static void sqlite3GenerateRowDelete(
|
||||
Parse pParse, /* Parsing context */
|
||||
Table pTab, /* Table containing the row to be deleted */
|
||||
int iCur, /* VdbeCursor number for the table */
|
||||
int iRowid, /* Memory cell that contains the rowid to delete */
|
||||
int count, /* If non-zero, increment the row change counter */
|
||||
Trigger pTrigger, /* List of triggers to (potentially) fire */
|
||||
int onconf /* Default ON CONFLICT policy for triggers */
|
||||
)
|
||||
{
|
||||
Vdbe v = pParse.pVdbe; /* Vdbe */
|
||||
int iOld = 0; /* First register in OLD.* array */
|
||||
int iLabel; /* Label resolved to end of generated code */
|
||||
|
||||
/* Vdbe is guaranteed to have been allocated by this stage. */
|
||||
Debug.Assert( v != null );
|
||||
|
||||
/* Seek cursor iCur to the row to delete. If this row no longer exists
|
||||
** (this can happen if a trigger program has already deleted it), do
|
||||
** not attempt to delete it or fire any DELETE triggers. */
|
||||
iLabel = sqlite3VdbeMakeLabel( v );
|
||||
sqlite3VdbeAddOp3( v, OP_NotExists, iCur, iLabel, iRowid );
|
||||
|
||||
/* If there are any triggers to fire, allocate a range of registers to
|
||||
** use for the old.* references in the triggers. */
|
||||
if ( sqlite3FkRequired( pParse, pTab, null, 0 ) != 0 || pTrigger != null )
|
||||
{
|
||||
u32 mask; /* Mask of OLD.* columns in use */
|
||||
int iCol; /* Iterator used while populating OLD.* */
|
||||
|
||||
/* TODO: Could use temporary registers here. Also could attempt to
|
||||
** avoid copying the contents of the rowid register. */
|
||||
mask = sqlite3TriggerColmask(
|
||||
pParse, pTrigger, null, 0, TRIGGER_BEFORE | TRIGGER_AFTER, pTab, onconf
|
||||
);
|
||||
mask |= sqlite3FkOldmask( pParse, pTab );
|
||||
iOld = pParse.nMem + 1;
|
||||
pParse.nMem += ( 1 + pTab.nCol );
|
||||
|
||||
/* Populate the OLD.* pseudo-table register array. These values will be
|
||||
** used by any BEFORE and AFTER triggers that exist. */
|
||||
sqlite3VdbeAddOp2( v, OP_Copy, iRowid, iOld );
|
||||
for ( iCol = 0; iCol < pTab.nCol; iCol++ )
|
||||
{
|
||||
if ( mask == 0xffffffff || ( mask & ( 1 << iCol ) ) != 0 )
|
||||
{
|
||||
sqlite3ExprCodeGetColumnOfTable( v, pTab, iCur, iCol, iOld + iCol + 1 );
|
||||
}
|
||||
}
|
||||
|
||||
/* Invoke BEFORE DELETE trigger programs. */
|
||||
sqlite3CodeRowTrigger( pParse, pTrigger,
|
||||
TK_DELETE, null, TRIGGER_BEFORE, pTab, iOld, onconf, iLabel
|
||||
);
|
||||
|
||||
/* Seek the cursor to the row to be deleted again. It may be that
|
||||
** the BEFORE triggers coded above have already removed the row
|
||||
** being deleted. Do not attempt to delete the row a second time, and
|
||||
** do not fire AFTER triggers. */
|
||||
sqlite3VdbeAddOp3( v, OP_NotExists, iCur, iLabel, iRowid );
|
||||
|
||||
/* Do FK processing. This call checks that any FK constraints that
|
||||
** refer to this table (i.e. constraints attached to other tables)
|
||||
** are not violated by deleting this row. */
|
||||
sqlite3FkCheck( pParse, pTab, iOld, 0 );
|
||||
}
|
||||
|
||||
/* Delete the index and table entries. Skip this step if pTab is really
|
||||
** a view (in which case the only effect of the DELETE statement is to
|
||||
** fire the INSTEAD OF triggers). */
|
||||
if ( pTab.pSelect == null )
|
||||
{
|
||||
sqlite3GenerateRowIndexDelete( pParse, pTab, iCur, 0 );
|
||||
sqlite3VdbeAddOp2( v, OP_Delete, iCur, ( count != 0 ? (int)OPFLAG_NCHANGE : 0 ) );
|
||||
if ( count != 0 )
|
||||
{
|
||||
sqlite3VdbeChangeP4( v, -1, pTab.zName, P4_TRANSIENT );
|
||||
}
|
||||
}
|
||||
|
||||
/* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to
|
||||
** handle rows (possibly in other tables) that refer via a foreign key
|
||||
** to the row just deleted. */
|
||||
sqlite3FkActions( pParse, pTab, null, iOld );
|
||||
|
||||
/* Invoke AFTER DELETE trigger programs. */
|
||||
sqlite3CodeRowTrigger( pParse, pTrigger,
|
||||
TK_DELETE, null, TRIGGER_AFTER, pTab, iOld, onconf, iLabel
|
||||
);
|
||||
|
||||
/* Jump here if the row had already been deleted before any BEFORE
|
||||
** trigger programs were invoked. Or if a trigger program throws a
|
||||
** RAISE(IGNORE) exception. */
|
||||
sqlite3VdbeResolveLabel( v, iLabel );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** This routine generates VDBE code that causes the deletion of all
|
||||
** index entries associated with a single row of a single table.
|
||||
**
|
||||
** The VDBE must be in a particular state when this routine is called.
|
||||
** These are the requirements:
|
||||
**
|
||||
** 1. A read/write cursor pointing to pTab, the table containing the row
|
||||
** to be deleted, must be opened as cursor number "iCur".
|
||||
**
|
||||
** 2. Read/write cursors for all indices of pTab must be open as
|
||||
** cursor number iCur+i for the i-th index.
|
||||
**
|
||||
** 3. The "iCur" cursor must be pointing to the row that is to be
|
||||
** deleted.
|
||||
*/
|
||||
static void sqlite3GenerateRowIndexDelete(
|
||||
Parse pParse, /* Parsing and code generating context */
|
||||
Table pTab, /* Table containing the row to be deleted */
|
||||
int iCur, /* VdbeCursor number for the table */
|
||||
int nothing /* Only delete if aRegIdx!=0 && aRegIdx[i]>0 */
|
||||
)
|
||||
{
|
||||
int[] aRegIdx = null;
|
||||
sqlite3GenerateRowIndexDelete( pParse, pTab, iCur, aRegIdx );
|
||||
}
|
||||
static void sqlite3GenerateRowIndexDelete(
|
||||
Parse pParse, /* Parsing and code generating context */
|
||||
Table pTab, /* Table containing the row to be deleted */
|
||||
int iCur, /* VdbeCursor number for the table */
|
||||
int[] aRegIdx /* Only delete if aRegIdx!=0 && aRegIdx[i]>0 */
|
||||
)
|
||||
{
|
||||
int i;
|
||||
Index pIdx;
|
||||
int r1;
|
||||
|
||||
for ( i = 1, pIdx = pTab.pIndex; pIdx != null; i++, pIdx = pIdx.pNext )
|
||||
{
|
||||
if ( aRegIdx != null && aRegIdx[i - 1] == 0 )
|
||||
continue;
|
||||
r1 = sqlite3GenerateIndexKey( pParse, pIdx, iCur, 0, false );
|
||||
sqlite3VdbeAddOp3( pParse.pVdbe, OP_IdxDelete, iCur + i, r1, pIdx.nColumn + 1 );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code that will assemble an index key and put it in register
|
||||
** regOut. The key with be for index pIdx which is an index on pTab.
|
||||
** iCur is the index of a cursor open on the pTab table and pointing to
|
||||
** the entry that needs indexing.
|
||||
**
|
||||
** Return a register number which is the first in a block of
|
||||
** registers that holds the elements of the index key. The
|
||||
** block of registers has already been deallocated by the time
|
||||
** this routine returns.
|
||||
*/
|
||||
static int sqlite3GenerateIndexKey(
|
||||
Parse pParse, /* Parsing context */
|
||||
Index pIdx, /* The index for which to generate a key */
|
||||
int iCur, /* VdbeCursor number for the pIdx.pTable table */
|
||||
int regOut, /* Write the new index key to this register */
|
||||
bool doMakeRec /* Run the OP_MakeRecord instruction if true */
|
||||
)
|
||||
{
|
||||
Vdbe v = pParse.pVdbe;
|
||||
int j;
|
||||
Table pTab = pIdx.pTable;
|
||||
int regBase;
|
||||
int nCol;
|
||||
|
||||
nCol = pIdx.nColumn;
|
||||
regBase = sqlite3GetTempRange( pParse, nCol + 1 );
|
||||
sqlite3VdbeAddOp2( v, OP_Rowid, iCur, regBase + nCol );
|
||||
for ( j = 0; j < nCol; j++ )
|
||||
{
|
||||
int idx = pIdx.aiColumn[j];
|
||||
if ( idx == pTab.iPKey )
|
||||
{
|
||||
sqlite3VdbeAddOp2( v, OP_SCopy, regBase + nCol, regBase + j );
|
||||
}
|
||||
else
|
||||
{
|
||||
sqlite3VdbeAddOp3( v, OP_Column, iCur, idx, regBase + j );
|
||||
sqlite3ColumnDefault( v, pTab, idx, -1 );
|
||||
}
|
||||
}
|
||||
if ( doMakeRec )
|
||||
{
|
||||
string zAff;
|
||||
if ( pTab.pSelect != null|| ( pParse.db.flags & SQLITE_IdxRealAsInt ) != 0 )
|
||||
{
|
||||
zAff = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
zAff = sqlite3IndexAffinityStr( v, pIdx );
|
||||
}
|
||||
sqlite3VdbeAddOp3( v, OP_MakeRecord, regBase, nCol + 1, regOut );
|
||||
sqlite3VdbeChangeP4( v, -1, zAff, P4_TRANSIENT );
|
||||
}
|
||||
sqlite3ReleaseTempRange( pParse, regBase, nCol + 1 );
|
||||
return regBase;
|
||||
}
|
||||
}
|
||||
}
|
||||
4443
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/expr_c.cs
Normal file
4443
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/expr_c.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,116 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/*
|
||||
** 2008 Jan 22
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
*************************************************************************
|
||||
**
|
||||
** This file contains code to support the concept of "benign"
|
||||
** malloc failures (when the xMalloc() or xRealloc() method of the
|
||||
** sqlite3_mem_methods structure fails to allocate a block of memory
|
||||
** and returns 0).
|
||||
**
|
||||
** Most malloc failures are non-benign. After they occur, SQLite
|
||||
** abandons the current operation and returns an error code (usually
|
||||
** SQLITE_NOMEM) to the user. However, sometimes a fault is not necessarily
|
||||
** fatal. For example, if a malloc fails while resizing a hash table, this
|
||||
** is completely recoverable simply by not carrying out the resize. The
|
||||
** hash table will continue to function normally. So a malloc failure
|
||||
** during a hash table resize is a benign fault.
|
||||
*************************************************************************
|
||||
** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart
|
||||
** C#-SQLite is an independent reimplementation of the SQLite software library
|
||||
**
|
||||
** SQLITE_SOURCE_ID: 2010-08-23 18:52:01 42537b60566f288167f1b5864a5435986838e3a3
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
//#include "sqliteInt.h"
|
||||
|
||||
#if !SQLITE_OMIT_BUILTIN_TEST
|
||||
/*
|
||||
** Global variables.
|
||||
*/
|
||||
//typedef struct BenignMallocHooks BenignMallocHooks;
|
||||
public struct BenignMallocHooks//
|
||||
{
|
||||
public void_function xBenignBegin;//void (*xBenignBegin)(void);
|
||||
public void_function xBenignEnd; //void (*xBenignEnd)(void);
|
||||
public BenignMallocHooks( void_function xBenignBegin, void_function xBenignEnd )
|
||||
{
|
||||
this.xBenignBegin = xBenignBegin;
|
||||
this.xBenignEnd = xBenignEnd;
|
||||
}
|
||||
}
|
||||
static BenignMallocHooks sqlite3Hooks = new BenignMallocHooks( null, null );
|
||||
|
||||
/* The "wsdHooks" macro will resolve to the appropriate BenignMallocHooks
|
||||
** structure. If writable static data is unsupported on the target,
|
||||
** we have to locate the state vector at run-time. In the more common
|
||||
** case where writable static data is supported, wsdHooks can refer directly
|
||||
** to the "sqlite3Hooks" state vector declared above.
|
||||
*/
|
||||
#if SQLITE_OMIT_WSD
|
||||
//# define wsdHooksInit \
|
||||
BenignMallocHooks *x = &GLOBAL(BenignMallocHooks,sqlite3Hooks)
|
||||
//# define wsdHooks x[0]
|
||||
#else
|
||||
//# define wsdHooksInit
|
||||
static void wsdHooksInit()
|
||||
{
|
||||
}
|
||||
//# define wsdHooks sqlite3Hooks
|
||||
static BenignMallocHooks wsdHooks = sqlite3Hooks;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Register hooks to call when sqlite3BeginBenignMalloc() and
|
||||
** sqlite3EndBenignMalloc() are called, respectively.
|
||||
*/
|
||||
static void sqlite3BenignMallocHooks(
|
||||
void_function xBenignBegin, //void (*xBenignBegin)(void),
|
||||
void_function xBenignEnd //void (*xBenignEnd)(void)
|
||||
)
|
||||
{
|
||||
wsdHooksInit();
|
||||
wsdHooks.xBenignBegin = xBenignBegin;
|
||||
wsdHooks.xBenignEnd = xBenignEnd;
|
||||
}
|
||||
|
||||
/*
|
||||
** This (sqlite3EndBenignMalloc()) is called by SQLite code to indicate that
|
||||
** subsequent malloc failures are benign. A call to sqlite3EndBenignMalloc()
|
||||
** indicates that subsequent malloc failures are non-benign.
|
||||
*/
|
||||
static void sqlite3BeginBenignMalloc()
|
||||
{
|
||||
wsdHooksInit();
|
||||
if ( wsdHooks.xBenignBegin != null )
|
||||
{
|
||||
wsdHooks.xBenignBegin();
|
||||
}
|
||||
}
|
||||
static void sqlite3EndBenignMalloc()
|
||||
{
|
||||
wsdHooksInit();
|
||||
if ( wsdHooks.xBenignEnd != null )
|
||||
{
|
||||
wsdHooks.xBenignEnd();
|
||||
}
|
||||
}
|
||||
#endif //* SQLITE_OMIT_BUILTIN_TEST */
|
||||
}
|
||||
}
|
||||
1375
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/fkey_c.cs
Normal file
1375
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/fkey_c.cs
Normal file
File diff suppressed because it is too large
Load Diff
2147
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/func_c.cs
Normal file
2147
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/func_c.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,246 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
using sqlite3_value = Sqlite3.Mem;
|
||||
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/*
|
||||
** 2008 June 13
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** This file contains definitions of global variables and contants.
|
||||
*************************************************************************
|
||||
** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart
|
||||
** C#-SQLite is an independent reimplementation of the SQLite software library
|
||||
**
|
||||
** SQLITE_SOURCE_ID: 2011-06-23 19:49:22 4374b7e83ea0a3fbc3691f9c0c936272862f32f2
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
//#include "sqliteInt.h"
|
||||
|
||||
|
||||
/* An array to map all upper-case characters into their corresponding
|
||||
** lower-case character.
|
||||
**
|
||||
** SQLite only considers US-ASCII (or EBCDIC) characters. We do not
|
||||
** handle case conversions for the UTF character set since the tables
|
||||
** involved are nearly as big or bigger than SQLite itself.
|
||||
*/
|
||||
/* An array to map all upper-case characters into their corresponding
|
||||
** lower-case character.
|
||||
*/
|
||||
//
|
||||
// Replaced in C# with sqlite3UpperToLower class
|
||||
// static int[] sqlite3UpperToLower = new int[] {
|
||||
//#if SQLITE_ASCII
|
||||
//0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
|
||||
//18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
|
||||
//36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
|
||||
//54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99,100,101,102,103,
|
||||
//104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,
|
||||
//122, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103,104,105,106,107,
|
||||
//108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,
|
||||
//126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
|
||||
//144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,
|
||||
//162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,
|
||||
//180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,
|
||||
//198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,
|
||||
//216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,
|
||||
//234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,
|
||||
//252,253,254,255
|
||||
//#endif
|
||||
//#if SQLITE_EBCDIC
|
||||
//0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 0x */
|
||||
//16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* 1x */
|
||||
//32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, /* 2x */
|
||||
//48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, /* 3x */
|
||||
//64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, /* 4x */
|
||||
//80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, /* 5x */
|
||||
//96, 97, 66, 67, 68, 69, 70, 71, 72, 73,106,107,108,109,110,111, /* 6x */
|
||||
//112, 81, 82, 83, 84, 85, 86, 87, 88, 89,122,123,124,125,126,127, /* 7x */
|
||||
//128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, /* 8x */
|
||||
//144,145,146,147,148,149,150,151,152,153,154,155,156,157,156,159, /* 9x */
|
||||
//160,161,162,163,164,165,166,167,168,169,170,171,140,141,142,175, /* Ax */
|
||||
//176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, /* Bx */
|
||||
//192,129,130,131,132,133,134,135,136,137,202,203,204,205,206,207, /* Cx */
|
||||
//208,145,146,147,148,149,150,151,152,153,218,219,220,221,222,223, /* Dx */
|
||||
//224,225,162,163,164,165,166,167,168,169,232,203,204,205,206,207, /* Ex */
|
||||
//239,240,241,242,243,244,245,246,247,248,249,219,220,221,222,255, /* Fx */
|
||||
//#endif
|
||||
//};
|
||||
|
||||
/*
|
||||
** The following 256 byte lookup table is used to support SQLites built-in
|
||||
** equivalents to the following standard library functions:
|
||||
**
|
||||
** isspace() 0x01
|
||||
** isalpha() 0x02
|
||||
** isdigit() 0x04
|
||||
** isalnum() 0x06
|
||||
** isxdigit() 0x08
|
||||
** toupper() 0x20
|
||||
** SQLite identifier character 0x40
|
||||
**
|
||||
** Bit 0x20 is set if the mapped character requires translation to upper
|
||||
** case. i.e. if the character is a lower-case ASCII character.
|
||||
** If x is a lower-case ASCII character, then its upper-case equivalent
|
||||
** is (x - 0x20). Therefore toupper() can be implemented as:
|
||||
**
|
||||
** (x & ~(map[x]&0x20))
|
||||
**
|
||||
** Standard function tolower() is implemented using the sqlite3UpperToLower[]
|
||||
** array. tolower() is used more often than toupper() by SQLite.
|
||||
**
|
||||
** Bit 0x40 is set if the character non-alphanumeric and can be used in an
|
||||
** SQLite identifier. Identifiers are alphanumerics, "_", "$", and any
|
||||
** non-ASCII UTF character. Hence the test for whether or not a character is
|
||||
** part of an identifier is 0x46.
|
||||
**
|
||||
** SQLite's versions are identical to the standard versions assuming a
|
||||
** locale of "C". They are implemented as macros in sqliteInt.h.
|
||||
*/
|
||||
#if !NO_SQLITE_ASCII
|
||||
static byte[] sqlite3CtypeMap = new byte[] {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00..07 ........ */
|
||||
0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, /* 08..0f ........ */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 10..17 ........ */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 18..1f ........ */
|
||||
0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, /* 20..27 !"#$%&' */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 28..2f ()*+,-./ */
|
||||
0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, /* 30..37 01234567 */
|
||||
0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 38..3f 89:;<=>? */
|
||||
|
||||
0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x02, /* 40..47 @ABCDEFG */
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 48..4f HIJKLMNO */
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 50..57 PQRSTUVW */
|
||||
0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x40, /* 58..5f XYZ[\]^_ */
|
||||
0x00, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x22, /* 60..67 `abcdefg */
|
||||
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* 68..6f hijklmno */
|
||||
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* 70..77 pqrstuvw */
|
||||
0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, /* 78..7f xyz{|}~. */
|
||||
|
||||
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 80..87 ........ */
|
||||
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 88..8f ........ */
|
||||
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 90..97 ........ */
|
||||
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 98..9f ........ */
|
||||
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* a0..a7 ........ */
|
||||
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* a8..af ........ */
|
||||
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* b0..b7 ........ */
|
||||
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* b8..bf ........ */
|
||||
|
||||
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* c0..c7 ........ */
|
||||
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* c8..cf ........ */
|
||||
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* d0..d7 ........ */
|
||||
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* d8..df ........ */
|
||||
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* e0..e7 ........ */
|
||||
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* e8..ef ........ */
|
||||
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* f0..f7 ........ */
|
||||
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 /* f8..ff ........ */
|
||||
};
|
||||
#endif
|
||||
|
||||
#if SQLITE_USE_URI
|
||||
const bool SQLITE_USE_URI = true;
|
||||
#else
|
||||
//# define SQLITE_USE_URI 0
|
||||
const bool SQLITE_USE_URI = false;
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The following singleton contains the global configuration for
|
||||
** the SQLite library.
|
||||
*/
|
||||
static Sqlite3Config sqlite3Config = new Sqlite3Config(
|
||||
SQLITE_DEFAULT_MEMSTATUS, /* bMemstat */
|
||||
1, /* bCoreMutex */
|
||||
SQLITE_THREADSAFE != 0, /* bFullMutex */
|
||||
SQLITE_USE_URI, /* bOpenUri */
|
||||
0x7ffffffe, /* mxStrlen */
|
||||
100, /* szLookaside */
|
||||
500, /* nLookaside */
|
||||
new sqlite3_mem_methods(), /* m */
|
||||
new sqlite3_mutex_methods( null, null, null, null, null, null, null, null, null ), /* mutex */
|
||||
new sqlite3_pcache_methods(),/* pcache */
|
||||
null, /* pHeap */
|
||||
0, /* nHeap */
|
||||
0, 0, /* mnHeap, mxHeap */
|
||||
null, /* pScratch */
|
||||
0, /* szScratch */
|
||||
0, /* nScratch */
|
||||
null, /* pPage */
|
||||
SQLITE_DEFAULT_PAGE_SIZE, /* szPage */
|
||||
0, /* nPage */
|
||||
0, /* mxParserStack */
|
||||
false, /* sharedCacheEnabled */
|
||||
/* All the rest should always be initialized to zero */
|
||||
0, /* isInit */
|
||||
0, /* inProgress */
|
||||
0, /* isMutexInit */
|
||||
0, /* isMallocInit */
|
||||
0, /* isPCacheInit */
|
||||
null, /* pInitMutex */
|
||||
0, /* nRefInitMutex */
|
||||
null, /* xLog */
|
||||
0, /* pLogArg */
|
||||
false /* bLocaltimeFault */
|
||||
);
|
||||
|
||||
/*
|
||||
** Hash table for global functions - functions common to all
|
||||
** database connections. After initialization, this table is
|
||||
** read-only.
|
||||
*/
|
||||
static FuncDefHash sqlite3GlobalFunctions;
|
||||
/*
|
||||
** Constant tokens for values 0 and 1.
|
||||
*/
|
||||
static Token[] sqlite3IntTokens = {
|
||||
new Token( "0", 1 ),
|
||||
new Token( "1", 1 )
|
||||
};
|
||||
|
||||
/*
|
||||
** The value of the "pending" byte must be 0x40000000 (1 byte past the
|
||||
** 1-gibabyte boundary) in a compatible database. SQLite never uses
|
||||
** the database page that contains the pending byte. It never attempts
|
||||
** to read or write that page. The pending byte page is set assign
|
||||
** for use by the VFS layers as space for managing file locks.
|
||||
**
|
||||
** During testing, it is often desirable to move the pending byte to
|
||||
** a different position in the file. This allows code that has to
|
||||
** deal with the pending byte to run on files that are much smaller
|
||||
** than 1 GiB. The sqlite3_test_control() interface can be used to
|
||||
** move the pending byte.
|
||||
**
|
||||
** IMPORTANT: Changing the pending byte to any value other than
|
||||
** 0x40000000 results in an incompatible database file format!
|
||||
** Changing the pending byte during operating results in undefined
|
||||
** and dileterious behavior.
|
||||
*/
|
||||
#if !SQLITE_OMIT_WSD
|
||||
static int sqlite3PendingByte = 0x40000000;
|
||||
#endif
|
||||
|
||||
//#include "opcodes.h"
|
||||
/*
|
||||
** Properties of opcodes. The OPFLG_INITIALIZER macro is
|
||||
** created by mkopcodeh.awk during compilation. Data is obtained
|
||||
** from the comments following the "case OP_xxxx:" statements in
|
||||
** the vdbe.c file.
|
||||
*/
|
||||
public static int[] sqlite3OpcodeProperty;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,363 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
using u8 = System.Byte;
|
||||
using u32 = System.UInt32;
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/*
|
||||
** 2001 September 22
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This is the implementation of generic hash-tables
|
||||
** used in SQLite.
|
||||
*************************************************************************
|
||||
** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart
|
||||
** C#-SQLite is an independent reimplementation of the SQLite software library
|
||||
**
|
||||
** SQLITE_SOURCE_ID: 2010-08-23 18:52:01 42537b60566f288167f1b5864a5435986838e3a3
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
//#include "sqliteInt.h"
|
||||
//#include <assert.h>
|
||||
|
||||
/* Turn bulk memory into a hash table object by initializing the
|
||||
** fields of the Hash structure.
|
||||
**
|
||||
** "pNew" is a pointer to the hash table that is to be initialized.
|
||||
*/
|
||||
static void sqlite3HashInit( Hash pNew )
|
||||
{
|
||||
Debug.Assert( pNew != null );
|
||||
pNew.first = null;
|
||||
pNew.count = 0;
|
||||
pNew.htsize = 0;
|
||||
pNew.ht = null;
|
||||
}
|
||||
|
||||
/* Remove all entries from a hash table. Reclaim all memory.
|
||||
** Call this routine to delete a hash table or to reset a hash table
|
||||
** to the empty state.
|
||||
*/
|
||||
static void sqlite3HashClear( Hash pH )
|
||||
{
|
||||
HashElem elem; /* For looping over all elements of the table */
|
||||
|
||||
Debug.Assert( pH != null );
|
||||
elem = pH.first;
|
||||
pH.first = null;
|
||||
//sqlite3_free( ref pH.ht );
|
||||
pH.ht = null;
|
||||
pH.htsize = 0;
|
||||
while ( elem != null )
|
||||
{
|
||||
HashElem next_elem = elem.next;
|
||||
////sqlite3_free(ref elem );
|
||||
elem = next_elem;
|
||||
}
|
||||
pH.count = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** The hashing function.
|
||||
*/
|
||||
static u32 strHash( string z, int nKey )
|
||||
{
|
||||
int h = 0;
|
||||
Debug.Assert( nKey >= 0 );
|
||||
int _z = 0;
|
||||
while ( nKey > 0 )
|
||||
{
|
||||
h = ( h << 3 ) ^ h ^ ( ( _z < z.Length ) ? (int)sqlite3UpperToLower[(byte)z[_z++]] : 0 );
|
||||
nKey--;
|
||||
}
|
||||
return (u32)h;
|
||||
}
|
||||
|
||||
/* Link pNew element into the hash table pH. If pEntry!=0 then also
|
||||
** insert pNew into the pEntry hash bucket.
|
||||
*/
|
||||
static void insertElement(
|
||||
Hash pH, /* The complete hash table */
|
||||
_ht pEntry, /* The entry into which pNew is inserted */
|
||||
HashElem pNew /* The element to be inserted */
|
||||
)
|
||||
{
|
||||
HashElem pHead; /* First element already in pEntry */
|
||||
if ( pEntry != null )
|
||||
{
|
||||
pHead = pEntry.count != 0 ? pEntry.chain : null;
|
||||
pEntry.count++;
|
||||
pEntry.chain = pNew;
|
||||
}
|
||||
else
|
||||
{
|
||||
pHead = null;
|
||||
}
|
||||
if ( pHead != null )
|
||||
{
|
||||
pNew.next = pHead;
|
||||
pNew.prev = pHead.prev;
|
||||
if ( pHead.prev != null )
|
||||
{
|
||||
pHead.prev.next = pNew;
|
||||
}
|
||||
else
|
||||
{
|
||||
pH.first = pNew;
|
||||
}
|
||||
pHead.prev = pNew;
|
||||
}
|
||||
else
|
||||
{
|
||||
pNew.next = pH.first;
|
||||
if ( pH.first != null )
|
||||
{
|
||||
pH.first.prev = pNew;
|
||||
}
|
||||
pNew.prev = null;
|
||||
pH.first = pNew;
|
||||
}
|
||||
}
|
||||
|
||||
/* Resize the hash table so that it cantains "new_size" buckets.
|
||||
**
|
||||
** The hash table might fail to resize if sqlite3_malloc() fails or
|
||||
** if the new size is the same as the prior size.
|
||||
** Return TRUE if the resize occurs and false if not.
|
||||
*/
|
||||
static bool rehash( ref Hash pH, u32 new_size )
|
||||
{
|
||||
_ht[] new_ht; /* The new hash table */
|
||||
HashElem elem;
|
||||
HashElem next_elem; /* For looping over existing elements */
|
||||
|
||||
#if SQLITE_MALLOC_SOFT_LIMIT
|
||||
if( new_size*sizeof(struct _ht)>SQLITE_MALLOC_SOFT_LIMIT ){
|
||||
new_size = SQLITE_MALLOC_SOFT_LIMIT/sizeof(struct _ht);
|
||||
}
|
||||
if( new_size==pH->htsize ) return false;
|
||||
#endif
|
||||
|
||||
/* There is a call to sqlite3Malloc() inside rehash(). If there is
|
||||
** already an allocation at pH.ht, then if this malloc() fails it
|
||||
** is benign (since failing to resize a hash table is a performance
|
||||
** hit only, not a fatal error).
|
||||
*/
|
||||
sqlite3BeginBenignMalloc();
|
||||
new_ht = new _ht[new_size]; //(struct _ht )sqlite3Malloc( new_size*sizeof(struct _ht) );
|
||||
for ( int i = 0; i < new_size; i++ )
|
||||
new_ht[i] = new _ht();
|
||||
sqlite3EndBenignMalloc();
|
||||
|
||||
if ( new_ht == null )
|
||||
return false;
|
||||
//sqlite3_free( ref pH.ht );
|
||||
pH.ht = new_ht;
|
||||
// pH.htsize = new_size = sqlite3MallocSize(new_ht)/sizeof(struct _ht);
|
||||
//memset(new_ht, 0, new_size*sizeof(struct _ht));
|
||||
pH.htsize = new_size;
|
||||
|
||||
for ( elem = pH.first, pH.first = null; elem != null; elem = next_elem )
|
||||
{
|
||||
u32 h = strHash( elem.pKey, elem.nKey ) % new_size;
|
||||
next_elem = elem.next;
|
||||
insertElement( pH, new_ht[h], elem );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* This function (for internal use only) locates an element in an
|
||||
** hash table that matches the given key. The hash for this key has
|
||||
** already been computed and is passed as the 4th parameter.
|
||||
*/
|
||||
static HashElem findElementGivenHash(
|
||||
Hash pH, /* The pH to be searched */
|
||||
string pKey, /* The key we are searching for */
|
||||
int nKey, /* Bytes in key (not counting zero terminator) */
|
||||
u32 h /* The hash for this key. */
|
||||
)
|
||||
{
|
||||
HashElem elem; /* Used to loop thru the element list */
|
||||
int count; /* Number of elements left to test */
|
||||
|
||||
if ( pH.ht != null && pH.ht[h] != null )
|
||||
{
|
||||
_ht pEntry = pH.ht[h];
|
||||
elem = pEntry.chain;
|
||||
count = (int)pEntry.count;
|
||||
}
|
||||
else
|
||||
{
|
||||
elem = pH.first;
|
||||
count = (int)pH.count;
|
||||
}
|
||||
while ( count-- > 0 && ALWAYS( elem ) )
|
||||
{
|
||||
if ( elem.nKey == nKey && elem.pKey.Equals( pKey, StringComparison.InvariantCultureIgnoreCase ) )
|
||||
{
|
||||
return elem;
|
||||
}
|
||||
elem = elem.next;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/* Remove a single entry from the hash table given a pointer to that
|
||||
** element and a hash on the element's key.
|
||||
*/
|
||||
static void removeElementGivenHash(
|
||||
Hash pH, /* The pH containing "elem" */
|
||||
ref HashElem elem, /* The element to be removed from the pH */
|
||||
u32 h /* Hash value for the element */
|
||||
)
|
||||
{
|
||||
_ht pEntry;
|
||||
if ( elem.prev != null )
|
||||
{
|
||||
elem.prev.next = elem.next;
|
||||
}
|
||||
else
|
||||
{
|
||||
pH.first = elem.next;
|
||||
}
|
||||
if ( elem.next != null )
|
||||
{
|
||||
elem.next.prev = elem.prev;
|
||||
}
|
||||
if ( pH.ht != null && pH.ht[h] != null )
|
||||
{
|
||||
pEntry = pH.ht[h];
|
||||
if ( pEntry.chain == elem )
|
||||
{
|
||||
pEntry.chain = elem.next;
|
||||
}
|
||||
pEntry.count--;
|
||||
Debug.Assert( pEntry.count >= 0 );
|
||||
}
|
||||
//sqlite3_free( ref elem );
|
||||
pH.count--;
|
||||
if ( pH.count <= 0 )
|
||||
{
|
||||
Debug.Assert( pH.first == null );
|
||||
Debug.Assert( pH.count == 0 );
|
||||
sqlite3HashClear( pH );
|
||||
}
|
||||
}
|
||||
|
||||
/* Attempt to locate an element of the hash table pH with a key
|
||||
** that matches pKey,nKey. Return the data for this element if it is
|
||||
** found, or NULL if there is no match.
|
||||
*/
|
||||
static T sqlite3HashFind<T>( Hash pH, string pKey, int nKey, T nullType ) where T : class
|
||||
{
|
||||
HashElem elem; /* The element that matches key */
|
||||
u32 h; /* A hash on key */
|
||||
|
||||
Debug.Assert( pH != null );
|
||||
Debug.Assert( pKey != null );
|
||||
Debug.Assert( nKey >= 0 );
|
||||
if ( pH.ht != null )
|
||||
{
|
||||
h = strHash( pKey, nKey ) % pH.htsize;
|
||||
}
|
||||
else
|
||||
{
|
||||
h = 0;
|
||||
}
|
||||
elem = findElementGivenHash( pH, pKey, nKey, h );
|
||||
return elem != null ? (T)elem.data : nullType;
|
||||
}
|
||||
|
||||
/* Insert an element into the hash table pH. The key is pKey,nKey
|
||||
** and the data is "data".
|
||||
**
|
||||
** If no element exists with a matching key, then a new
|
||||
** element is created and NULL is returned.
|
||||
**
|
||||
** If another element already exists with the same key, then the
|
||||
** new data replaces the old data and the old data is returned.
|
||||
** The key is not copied in this instance. If a malloc fails, then
|
||||
** the new data is returned and the hash table is unchanged.
|
||||
**
|
||||
** If the "data" parameter to this function is NULL, then the
|
||||
** element corresponding to "key" is removed from the hash table.
|
||||
*/
|
||||
static T sqlite3HashInsert<T>( ref Hash pH, string pKey, int nKey, T data ) where T : class
|
||||
{
|
||||
u32 h; /* the hash of the key modulo hash table size */
|
||||
|
||||
HashElem elem; /* Used to loop thru the element list */
|
||||
HashElem new_elem; /* New element added to the pH */
|
||||
|
||||
Debug.Assert( pH != null );
|
||||
Debug.Assert( pKey != null );
|
||||
Debug.Assert( nKey >= 0 );
|
||||
|
||||
if ( pH.htsize != 0 )
|
||||
{
|
||||
h = strHash( pKey, nKey ) % pH.htsize;
|
||||
}
|
||||
else
|
||||
{
|
||||
h = 0;
|
||||
}
|
||||
elem = findElementGivenHash( pH, pKey, nKey, h );
|
||||
if ( elem != null )
|
||||
{
|
||||
T old_data = (T)elem.data;
|
||||
if ( data == null )
|
||||
{
|
||||
removeElementGivenHash( pH, ref elem, h );
|
||||
}
|
||||
else
|
||||
{
|
||||
elem.data = data;
|
||||
elem.pKey = pKey;
|
||||
Debug.Assert( nKey == elem.nKey );
|
||||
}
|
||||
return old_data;
|
||||
}
|
||||
if ( data == null )
|
||||
return data;
|
||||
new_elem = new HashElem();//(HashElem)sqlite3Malloc( sizeof(HashElem) );
|
||||
if ( new_elem == null )
|
||||
return data;
|
||||
new_elem.pKey = pKey;
|
||||
new_elem.nKey = nKey;
|
||||
new_elem.data = data;
|
||||
pH.count++;
|
||||
if ( pH.count >= 10 && pH.count > 2 * pH.htsize )
|
||||
{
|
||||
if ( rehash( ref pH, pH.count * 2 ) )
|
||||
{
|
||||
Debug.Assert( pH.htsize > 0 );
|
||||
h = strHash( pKey, nKey ) % pH.htsize;
|
||||
}
|
||||
}
|
||||
if ( pH.ht != null )
|
||||
{
|
||||
insertElement( pH, pH.ht[h], new_elem );
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
insertElement( pH, null, new_elem );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
using sqlite_u3264 = System.UInt64;
|
||||
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/*
|
||||
** 2008 May 27
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
******************************************************************************
|
||||
**
|
||||
** This file contains inline asm code for retrieving "high-performance"
|
||||
** counters for x86 class CPUs.
|
||||
**
|
||||
** $Id: hwtime.h,v 1.3 2008/08/01 14:33:15 shane Exp $
|
||||
**
|
||||
*************************************************************************
|
||||
** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart
|
||||
** C#-SQLite is an independent reimplementation of the SQLite software library
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
//#if !_HWTIME_H_
|
||||
//#define _HWTIME_H_
|
||||
|
||||
/*
|
||||
** The following routine only works on pentium-class (or newer) processors.
|
||||
** It uses the RDTSC opcode to read the cycle count value out of the
|
||||
** processor and returns that value. This can be used for high-res
|
||||
** profiling.
|
||||
*/
|
||||
#if ((__GNUC__) || (_MSC_VER)) && ((i386) || (__i386__) || (_M_IX86))
|
||||
|
||||
#if (__GNUC__)
|
||||
|
||||
__inline__ sqlite_u3264 sqlite3Hwtime(void){
|
||||
unsigned int lo, hi;
|
||||
__asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
|
||||
return (sqlite_u3264)hi << 32 | lo;
|
||||
}
|
||||
|
||||
#elif (_MSC_VER)
|
||||
|
||||
__declspec(naked) __inline sqlite_u3264 __cdecl sqlite3Hwtime(void){
|
||||
__asm {
|
||||
rdtsc
|
||||
ret ; return value at EDX:EAX
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#elif ((__GNUC__) && (__x86_64__))
|
||||
|
||||
__inline__ sqlite_u3264 sqlite3Hwtime(void){
|
||||
unsigned long val;
|
||||
__asm__ __volatile__ ("rdtsc" : "=A" (val));
|
||||
return val;
|
||||
}
|
||||
|
||||
#elif ( (__GNUC__) && (__ppc__))
|
||||
|
||||
__inline__ sqlite_u3264 sqlite3Hwtime(void){
|
||||
unsigned long long retval;
|
||||
unsigned long junk;
|
||||
__asm__ __volatile__ ("\n\
|
||||
1: mftbu %1\n\
|
||||
mftb %L0\n\
|
||||
mftbu %0\n\
|
||||
cmpw %0,%1\n\
|
||||
bne 1b"
|
||||
: "=r" (retval), "=r" (junk));
|
||||
return retval;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
//#error Need implementation of sqlite3Hwtime() for your platform.
|
||||
|
||||
/*
|
||||
** To compile without implementing sqlite3Hwtime() for your platform,
|
||||
** you can remove the above #error and use the following
|
||||
** stub function. You will lose timing support for many
|
||||
** of the debugging and testing utilities, but it should at
|
||||
** least compile and run.
|
||||
*/
|
||||
static sqlite_u3264 sqlite3Hwtime()
|
||||
{
|
||||
return (sqlite_u3264)System.DateTime.Now.Ticks;
|
||||
}// (sqlite_u3264)0 ); }
|
||||
|
||||
#endif
|
||||
|
||||
//#endif //* !_HWTIME_H_) */
|
||||
}
|
||||
}
|
||||
2173
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/insert_c.cs
Normal file
2173
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/insert_c.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,250 @@
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/*
|
||||
** 2007 August 22
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** This file implements a special kind of sqlite3_file object used
|
||||
** by SQLite to create journal files if the atomic-write optimization
|
||||
** is enabled.
|
||||
**
|
||||
** The distinctive characteristic of this sqlite3_file is that the
|
||||
** actual on disk file is created lazily. When the file is created,
|
||||
** the caller specifies a buffer size for an in-memory buffer to
|
||||
** be used to service read() and write() requests. The actual file
|
||||
** on disk is not created or populated until either:
|
||||
**
|
||||
** 1) The in-memory representation grows too large for the allocated
|
||||
** buffer, or
|
||||
** 2) The sqlite3JournalCreate() function is called.
|
||||
*************************************************************************
|
||||
** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart
|
||||
** C#-SQLite is an independent reimplementation of the SQLite software library
|
||||
**
|
||||
** SQLITE_SOURCE_ID: 2010-08-23 18:52:01 42537b60566f288167f1b5864a5435986838e3a3
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
#if SQLITE_ENABLE_ATOMIC_WRITE
|
||||
//#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** A JournalFile object is a subclass of sqlite3_file used by
|
||||
** as an open file handle for journal files.
|
||||
*/
|
||||
struct JournalFile {
|
||||
sqlite3_io_methods pMethod; /* I/O methods on journal files */
|
||||
int nBuf; /* Size of zBuf[] in bytes */
|
||||
string zBuf; /* Space to buffer journal writes */
|
||||
int iSize; /* Amount of zBuf[] currently used */
|
||||
int flags; /* xOpen flags */
|
||||
sqlite3_vfs pVfs; /* The "real" underlying VFS */
|
||||
sqlite3_file pReal; /* The "real" underlying file descriptor */
|
||||
string zJournal; /* Name of the journal file */
|
||||
};
|
||||
typedef struct JournalFile JournalFile;
|
||||
|
||||
/*
|
||||
** If it does not already exists, create and populate the on-disk file
|
||||
** for JournalFile p.
|
||||
*/
|
||||
static int createFile(JournalFile p){
|
||||
int rc = SQLITE_OK;
|
||||
if( null==p.pReal ){
|
||||
sqlite3_file pReal = (sqlite3_file )&p[1];
|
||||
rc = sqlite3OsOpen(p.pVfs, p.zJournal, pReal, p.flags, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
p.pReal = pReal;
|
||||
if( p.iSize>0 ){
|
||||
Debug.Assert(p.iSize<=p.nBuf);
|
||||
rc = sqlite3OsWrite(p.pReal, p.zBuf, p.iSize, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close the file.
|
||||
*/
|
||||
static int jrnlClose(sqlite3_file pJfd){
|
||||
JournalFile p = (JournalFile )pJfd;
|
||||
if( p.pReal ){
|
||||
sqlite3OsClose(p.pReal);
|
||||
}
|
||||
sqlite3DbFree(db,p.zBuf);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Read data from the file.
|
||||
*/
|
||||
static int jrnlRead(
|
||||
sqlite3_file *pJfd, /* The journal file from which to read */
|
||||
void *zBuf, /* Put the results here */
|
||||
int iAmt, /* Number of bytes to read */
|
||||
sqlite_int64 iOfst /* Begin reading at this offset */
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
JournalFile *p = (JournalFile )pJfd;
|
||||
if( p->pReal ){
|
||||
rc = sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst);
|
||||
}else if( (iAmt+iOfst)>p->iSize ){
|
||||
rc = SQLITE_IOERR_SHORT_READ;
|
||||
}else{
|
||||
memcpy(zBuf, &p->zBuf[iOfst], iAmt);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Write data to the file.
|
||||
*/
|
||||
static int jrnlWrite(
|
||||
sqlite3_file pJfd, /* The journal file into which to write */
|
||||
string zBuf, /* Take data to be written from here */
|
||||
int iAmt, /* Number of bytes to write */
|
||||
sqlite_int64 iOfst /* Begin writing at this offset into the file */
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
JournalFile p = (JournalFile )pJfd;
|
||||
if( null==p.pReal && (iOfst+iAmt)>p.nBuf ){
|
||||
rc = createFile(p);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
if( p.pReal ){
|
||||
rc = sqlite3OsWrite(p.pReal, zBuf, iAmt, iOfst);
|
||||
}else{
|
||||
memcpy(p.zBuf[iOfst], zBuf, iAmt);
|
||||
if( p.iSize<(iOfst+iAmt) ){
|
||||
p.iSize = (iOfst+iAmt);
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Truncate the file.
|
||||
*/
|
||||
static int jrnlTruncate(sqlite3_file pJfd, sqlite_int64 size){
|
||||
int rc = SQLITE_OK;
|
||||
JournalFile p = (JournalFile )pJfd;
|
||||
if( p.pReal ){
|
||||
rc = sqlite3OsTruncate(p.pReal, size);
|
||||
}else if( size<p.iSize ){
|
||||
p.iSize = size;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Sync the file.
|
||||
*/
|
||||
static int jrnlSync(sqlite3_file pJfd, int flags){
|
||||
int rc;
|
||||
JournalFile p = (JournalFile )pJfd;
|
||||
if( p.pReal ){
|
||||
rc = sqlite3OsSync(p.pReal, flags);
|
||||
}else{
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Query the size of the file in bytes.
|
||||
*/
|
||||
static int jrnlFileSize(sqlite3_file pJfd, sqlite_int64 pSize){
|
||||
int rc = SQLITE_OK;
|
||||
JournalFile p = (JournalFile )pJfd;
|
||||
if( p.pReal ){
|
||||
rc = sqlite3OsFileSize(p.pReal, pSize);
|
||||
}else{
|
||||
pSize = (sqlite_int64) p.iSize;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Table of methods for JournalFile sqlite3_file object.
|
||||
*/
|
||||
static struct sqlite3_io_methods JournalFileMethods = {
|
||||
1, /* iVersion */
|
||||
jrnlClose, /* xClose */
|
||||
jrnlRead, /* xRead */
|
||||
jrnlWrite, /* xWrite */
|
||||
jrnlTruncate, /* xTruncate */
|
||||
jrnlSync, /* xSync */
|
||||
jrnlFileSize, /* xFileSize */
|
||||
0, /* xLock */
|
||||
0, /* xUnlock */
|
||||
0, /* xCheckReservedLock */
|
||||
0, /* xFileControl */
|
||||
0, /* xSectorSize */
|
||||
0, /* xDeviceCharacteristics */
|
||||
0, /* xShmMap */
|
||||
0, /* xShmLock */
|
||||
0, /* xShmBarrier */
|
||||
0 /* xShmUnmap */
|
||||
};
|
||||
|
||||
/*
|
||||
** Open a journal file.
|
||||
*/
|
||||
int sqlite3JournalOpen(
|
||||
sqlite3_vfs pVfs, /* The VFS to use for actual file I/O */
|
||||
string zName, /* Name of the journal file */
|
||||
sqlite3_file pJfd, /* Preallocated, blank file handle */
|
||||
int flags, /* Opening flags */
|
||||
int nBuf /* Bytes buffered before opening the file */
|
||||
){
|
||||
JournalFile p = (JournalFile )pJfd;
|
||||
memset(p, 0, sqlite3JournalSize(pVfs));
|
||||
if( nBuf>0 ){
|
||||
p.zBuf = sqlite3MallocZero(nBuf);
|
||||
if( null==p.zBuf ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
}else{
|
||||
return sqlite3OsOpen(pVfs, zName, pJfd, flags, 0);
|
||||
}
|
||||
p.pMethod = JournalFileMethods;
|
||||
p.nBuf = nBuf;
|
||||
p.flags = flags;
|
||||
p.zJournal = zName;
|
||||
p.pVfs = pVfs;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** If the argument p points to a JournalFile structure, and the underlying
|
||||
** file has not yet been created, create it now.
|
||||
*/
|
||||
int sqlite3JournalCreate(sqlite3_file p){
|
||||
if( p.pMethods!=&JournalFileMethods ){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
return createFile((JournalFile )p);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the number of bytes required to store a JournalFile that uses vfs
|
||||
** pVfs to create the underlying on-disk files.
|
||||
*/
|
||||
int sqlite3JournalSize(sqlite3_vfs pVfs){
|
||||
return (pVfs->szOsFile+sizeof(JournalFile));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,303 @@
|
||||
using System.Diagnostics;
|
||||
using System;
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/***** This file contains automatically generated code ******
|
||||
**
|
||||
** The code in this file has been automatically generated by
|
||||
**
|
||||
** sqlite/tool/mkkeywordhash.c
|
||||
**
|
||||
** The code in this file implements a function that determines whether
|
||||
** or not a given identifier is really an SQL keyword. The same thing
|
||||
** might be implemented more directly using a hand-written hash table.
|
||||
** But by using this automatically generated code, the size of the code
|
||||
** is substantially reduced. This is important for embedded applications
|
||||
** on platforms with limited memory.
|
||||
*************************************************************************
|
||||
** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart
|
||||
** C#-SQLite is an independent reimplementation of the SQLite software library
|
||||
**
|
||||
** SQLITE_SOURCE_ID: 2010-08-23 18:52:01 42537b60566f288167f1b5864a5435986838e3a3
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
/* Hash score: 175 */
|
||||
/* zText[] encodes 811 bytes of keywords in 541 bytes */
|
||||
/* REINDEXEDESCAPEACHECKEYBEFOREIGNOREGEXPLAINSTEADDATABASELECT */
|
||||
/* ABLEFTHENDEFERRABLELSEXCEPTRANSACTIONATURALTERAISEXCLUSIVE */
|
||||
/* XISTSAVEPOINTERSECTRIGGEREFERENCESCONSTRAINTOFFSETEMPORARY */
|
||||
/* UNIQUERYATTACHAVINGROUPDATEBEGINNERELEASEBETWEENOTNULLIKE */
|
||||
/* CASCADELETECASECOLLATECREATECURRENT_DATEDETACHIMMEDIATEJOIN */
|
||||
/* SERTMATCHPLANALYZEPRAGMABORTVALUESVIRTUALIMITWHENWHERENAME */
|
||||
/* AFTEREPLACEANDEFAULTAUTOINCREMENTCASTCOLUMNCOMMITCONFLICTCROSS */
|
||||
/* CURRENT_TIMESTAMPRIMARYDEFERREDISTINCTDROPFAILFROMFULLGLOBYIF */
|
||||
/* ISNULLORDERESTRICTOUTERIGHTROLLBACKROWUNIONUSINGVACUUMVIEW */
|
||||
/* INITIALLY */
|
||||
static string zText = new string( new char[540] {
|
||||
'R','E','I','N','D','E','X','E','D','E','S','C','A','P','E','A','C','H',
|
||||
'E','C','K','E','Y','B','E','F','O','R','E','I','G','N','O','R','E','G',
|
||||
'E','X','P','L','A','I','N','S','T','E','A','D','D','A','T','A','B','A',
|
||||
'S','E','L','E','C','T','A','B','L','E','F','T','H','E','N','D','E','F',
|
||||
'E','R','R','A','B','L','E','L','S','E','X','C','E','P','T','R','A','N',
|
||||
'S','A','C','T','I','O','N','A','T','U','R','A','L','T','E','R','A','I',
|
||||
'S','E','X','C','L','U','S','I','V','E','X','I','S','T','S','A','V','E',
|
||||
'P','O','I','N','T','E','R','S','E','C','T','R','I','G','G',
|
||||
#if !SQLITE_OMIT_TRIGGER
|
||||
'E',
|
||||
#else
|
||||
'\0',
|
||||
#endif
|
||||
'R',
|
||||
#if !SQLITE_OMIT_FOREIGN_KEY
|
||||
'E',
|
||||
#else
|
||||
'\0',
|
||||
#endif
|
||||
'F','E','R','E','N','C','E','S','C','O','N','S','T','R','A','I','N','T',
|
||||
'O','F','F','S','E','T','E','M','P','O','R','A','R','Y','U','N','I','Q',
|
||||
'U','E','R','Y','A','T','T','A','C','H','A','V','I','N','G','R','O','U',
|
||||
'P','D','A','T','E','B','E','G','I','N','N','E','R','E','L','E','A','S',
|
||||
'E','B','E','T','W','E','E','N','O','T','N','U','L','L','I','K','E','C',
|
||||
'A','S','C','A','D','E','L','E','T','E','C','A','S','E','C','O','L','L',
|
||||
'A','T','E','C','R','E','A','T','E','C','U','R','R','E','N','T','_','D',
|
||||
'A','T','E','D','E','T','A','C','H','I','M','M','E','D','I','A','T','E',
|
||||
'J','O','I','N','S','E','R','T','M','A','T','C','H','P','L','A','N','A',
|
||||
'L','Y','Z','E','P','R','A','G','M','A','B','O','R','T','V','A','L','U',
|
||||
'E','S','V','I','R','T','U','A','L','I','M','I','T','W','H','E','N','W',
|
||||
'H','E','R','E','N','A','M','E','A','F','T','E','R','E','P','L','A','C',
|
||||
'E','A','N','D','E','F','A','U','L','T','A','U','T','O','I','N','C','R',
|
||||
'E','M','E','N','T','C','A','S','T','C','O','L','U','M','N','C','O','M',
|
||||
'M','I','T','C','O','N','F','L','I','C','T','C','R','O','S','S','C','U',
|
||||
'R','R','E','N','T','_','T','I','M','E','S','T','A','M','P','R','I','M',
|
||||
'A','R','Y','D','E','F','E','R','R','E','D','I','S','T','I','N','C','T',
|
||||
'D','R','O','P','F','A','I','L','F','R','O','M','F','U','L','L','G','L',
|
||||
'O','B','Y','I','F','I','S','N','U','L','L','O','R','D','E','R','E','S',
|
||||
'T','R','I','C','T','O','U','T','E','R','I','G','H','T','R','O','L','L',
|
||||
'B','A','C','K','R','O','W','U','N','I','O','N','U','S','I','N','G','V',
|
||||
'A','C','U','U','M','V','I','E','W','I','N','I','T','I','A','L','L','Y',
|
||||
} );
|
||||
|
||||
static byte[] aHash = { //aHash[127]
|
||||
72, 101, 114, 70, 0, 45, 0, 0, 78, 0, 73, 0, 0,
|
||||
42, 12, 74, 15, 0, 113, 81, 50, 108, 0, 19, 0, 0,
|
||||
118, 0, 116, 111, 0, 22, 89, 0, 9, 0, 0, 66, 67,
|
||||
0, 65, 6, 0, 48, 86, 98, 0, 115, 97, 0, 0, 44,
|
||||
0, 99, 24, 0, 17, 0, 119, 49, 23, 0, 5, 106, 25,
|
||||
92, 0, 0, 121, 102, 56, 120, 53, 28, 51, 0, 87, 0,
|
||||
96, 26, 0, 95, 0, 0, 0, 91, 88, 93, 84, 105, 14,
|
||||
39, 104, 0, 77, 0, 18, 85, 107, 32, 0, 117, 76, 109,
|
||||
58, 46, 80, 0, 0, 90, 40, 0, 112, 0, 36, 0, 0,
|
||||
29, 0, 82, 59, 60, 0, 20, 57, 0, 52,
|
||||
};
|
||||
static byte[] aNext = { //aNext[121]
|
||||
0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 2, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0,
|
||||
0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 33, 0, 21, 0, 0, 0, 43, 3, 47,
|
||||
0, 0, 0, 0, 30, 0, 54, 0, 38, 0, 0, 0, 1,
|
||||
62, 0, 0, 63, 0, 41, 0, 0, 0, 0, 0, 0, 0,
|
||||
61, 0, 0, 0, 0, 31, 55, 16, 34, 10, 0, 0, 0,
|
||||
0, 0, 0, 0, 11, 68, 75, 0, 8, 0, 100, 94, 0,
|
||||
103, 0, 83, 0, 71, 0, 0, 110, 27, 37, 69, 79, 0,
|
||||
35, 64, 0, 0,
|
||||
};
|
||||
static byte[] aLen = { //aLen[121]
|
||||
7, 7, 5, 4, 6, 4, 5, 3, 6, 7, 3, 6, 6,
|
||||
7, 7, 3, 8, 2, 6, 5, 4, 4, 3, 10, 4, 6,
|
||||
11, 6, 2, 7, 5, 5, 9, 6, 9, 9, 7, 10, 10,
|
||||
4, 6, 2, 3, 9, 4, 2, 6, 5, 6, 6, 5, 6,
|
||||
5, 5, 7, 7, 7, 3, 2, 4, 4, 7, 3, 6, 4,
|
||||
7, 6, 12, 6, 9, 4, 6, 5, 4, 7, 6, 5, 6,
|
||||
7, 5, 4, 5, 6, 5, 7, 3, 7, 13, 2, 2, 4,
|
||||
6, 6, 8, 5, 17, 12, 7, 8, 8, 2, 4, 4, 4,
|
||||
4, 4, 2, 2, 6, 5, 8, 5, 5, 8, 3, 5, 5,
|
||||
6, 4, 9, 3,
|
||||
};
|
||||
static int[] aOffset = { //aOffset[121]
|
||||
0, 2, 2, 8, 9, 14, 16, 20, 23, 25, 25, 29, 33,
|
||||
36, 41, 46, 48, 53, 54, 59, 62, 65, 67, 69, 78, 81,
|
||||
86, 91, 95, 96, 101, 105, 109, 117, 122, 128, 136, 142, 152,
|
||||
159, 162, 162, 165, 167, 167, 171, 176, 179, 184, 189, 194, 197,
|
||||
203, 206, 210, 217, 223, 223, 223, 226, 229, 233, 234, 238, 244,
|
||||
248, 255, 261, 273, 279, 288, 290, 296, 301, 303, 310, 315, 320,
|
||||
326, 332, 337, 341, 344, 350, 354, 361, 363, 370, 372, 374, 383,
|
||||
387, 393, 399, 407, 412, 412, 428, 435, 442, 443, 450, 454, 458,
|
||||
462, 466, 469, 471, 473, 479, 483, 491, 495, 500, 508, 511, 516,
|
||||
521, 527, 531, 536,
|
||||
};
|
||||
static byte[] aCode = { //aCode[121
|
||||
TK_REINDEX, TK_INDEXED, TK_INDEX, TK_DESC, TK_ESCAPE,
|
||||
TK_EACH, TK_CHECK, TK_KEY, TK_BEFORE, TK_FOREIGN,
|
||||
TK_FOR, TK_IGNORE, TK_LIKE_KW, TK_EXPLAIN, TK_INSTEAD,
|
||||
TK_ADD, TK_DATABASE, TK_AS, TK_SELECT, TK_TABLE,
|
||||
TK_JOIN_KW, TK_THEN, TK_END, TK_DEFERRABLE, TK_ELSE,
|
||||
TK_EXCEPT, TK_TRANSACTION,TK_ACTION, TK_ON, TK_JOIN_KW,
|
||||
TK_ALTER, TK_RAISE, TK_EXCLUSIVE, TK_EXISTS, TK_SAVEPOINT,
|
||||
TK_INTERSECT, TK_TRIGGER, TK_REFERENCES, TK_CONSTRAINT, TK_INTO,
|
||||
TK_OFFSET, TK_OF, TK_SET, TK_TEMP, TK_TEMP,
|
||||
TK_OR, TK_UNIQUE, TK_QUERY, TK_ATTACH, TK_HAVING,
|
||||
TK_GROUP, TK_UPDATE, TK_BEGIN, TK_JOIN_KW, TK_RELEASE,
|
||||
TK_BETWEEN, TK_NOTNULL, TK_NOT, TK_NO, TK_NULL,
|
||||
TK_LIKE_KW, TK_CASCADE, TK_ASC, TK_DELETE, TK_CASE,
|
||||
TK_COLLATE, TK_CREATE, TK_CTIME_KW, TK_DETACH, TK_IMMEDIATE,
|
||||
TK_JOIN, TK_INSERT, TK_MATCH, TK_PLAN, TK_ANALYZE,
|
||||
TK_PRAGMA, TK_ABORT, TK_VALUES, TK_VIRTUAL, TK_LIMIT,
|
||||
TK_WHEN, TK_WHERE, TK_RENAME, TK_AFTER, TK_REPLACE,
|
||||
TK_AND, TK_DEFAULT, TK_AUTOINCR, TK_TO, TK_IN,
|
||||
TK_CAST, TK_COLUMNKW, TK_COMMIT, TK_CONFLICT, TK_JOIN_KW,
|
||||
TK_CTIME_KW, TK_CTIME_KW, TK_PRIMARY, TK_DEFERRED, TK_DISTINCT,
|
||||
TK_IS, TK_DROP, TK_FAIL, TK_FROM, TK_JOIN_KW,
|
||||
TK_LIKE_KW, TK_BY, TK_IF, TK_ISNULL, TK_ORDER,
|
||||
TK_RESTRICT, TK_JOIN_KW, TK_JOIN_KW, TK_ROLLBACK, TK_ROW,
|
||||
TK_UNION, TK_USING, TK_VACUUM, TK_VIEW, TK_INITIALLY,
|
||||
TK_ALL,
|
||||
};
|
||||
static int keywordCode( string z, int iOffset, int n )
|
||||
{
|
||||
int h, i;
|
||||
if ( n < 2 )
|
||||
return TK_ID;
|
||||
h = ( ( sqlite3UpperToLower[z[iOffset + 0]] ) * 4 ^//(charMap(z[iOffset+0]) * 4) ^
|
||||
( sqlite3UpperToLower[z[iOffset + n - 1]] * 3 ) ^ //(charMap(z[iOffset+n - 1]) * 3) ^
|
||||
n ) % 127;
|
||||
for ( i = ( aHash[h] ) - 1; i >= 0; i = ( aNext[i] ) - 1 )
|
||||
{
|
||||
if ( aLen[i] == n && String.Compare( zText, aOffset[i], z, iOffset, n, StringComparison.InvariantCultureIgnoreCase ) == 0 )
|
||||
{
|
||||
testcase( i == 0 ); /* REINDEX */
|
||||
testcase( i == 1 ); /* INDEXED */
|
||||
testcase( i == 2 ); /* INDEX */
|
||||
testcase( i == 3 ); /* DESC */
|
||||
testcase( i == 4 ); /* ESCAPE */
|
||||
testcase( i == 5 ); /* EACH */
|
||||
testcase( i == 6 ); /* CHECK */
|
||||
testcase( i == 7 ); /* KEY */
|
||||
testcase( i == 8 ); /* BEFORE */
|
||||
testcase( i == 9 ); /* FOREIGN */
|
||||
testcase( i == 10 ); /* FOR */
|
||||
testcase( i == 11 ); /* IGNORE */
|
||||
testcase( i == 12 ); /* REGEXP */
|
||||
testcase( i == 13 ); /* EXPLAIN */
|
||||
testcase( i == 14 ); /* INSTEAD */
|
||||
testcase( i == 15 ); /* ADD */
|
||||
testcase( i == 16 ); /* DATABASE */
|
||||
testcase( i == 17 ); /* AS */
|
||||
testcase( i == 18 ); /* SELECT */
|
||||
testcase( i == 19 ); /* TABLE */
|
||||
testcase( i == 20 ); /* LEFT */
|
||||
testcase( i == 21 ); /* THEN */
|
||||
testcase( i == 22 ); /* END */
|
||||
testcase( i == 23 ); /* DEFERRABLE */
|
||||
testcase( i == 24 ); /* ELSE */
|
||||
testcase( i == 25 ); /* EXCEPT */
|
||||
testcase( i == 26 ); /* TRANSACTION */
|
||||
testcase( i == 27 ); /* ACTION */
|
||||
testcase( i == 28 ); /* ON */
|
||||
testcase( i == 29 ); /* NATURAL */
|
||||
testcase( i == 30 ); /* ALTER */
|
||||
testcase( i == 31 ); /* RAISE */
|
||||
testcase( i == 32 ); /* EXCLUSIVE */
|
||||
testcase( i == 33 ); /* EXISTS */
|
||||
testcase( i == 34 ); /* SAVEPOINT */
|
||||
testcase( i == 35 ); /* INTERSECT */
|
||||
testcase( i == 36 ); /* TRIGGER */
|
||||
testcase( i == 37 ); /* REFERENCES */
|
||||
testcase( i == 38 ); /* CONSTRAINT */
|
||||
testcase( i == 39 ); /* INTO */
|
||||
testcase( i == 40 ); /* OFFSET */
|
||||
testcase( i == 41 ); /* OF */
|
||||
testcase( i == 42 ); /* SET */
|
||||
testcase( i == 43 ); /* TEMPORARY */
|
||||
testcase( i == 44 ); /* TEMP */
|
||||
testcase( i == 45 ); /* OR */
|
||||
testcase( i == 46 ); /* UNIQUE */
|
||||
testcase( i == 47 ); /* QUERY */
|
||||
testcase( i == 48 ); /* ATTACH */
|
||||
testcase( i == 49 ); /* HAVING */
|
||||
testcase( i == 50 ); /* GROUP */
|
||||
testcase( i == 51 ); /* UPDATE */
|
||||
testcase( i == 52 ); /* BEGIN */
|
||||
testcase( i == 53 ); /* INNER */
|
||||
testcase( i == 54 ); /* RELEASE */
|
||||
testcase( i == 55 ); /* BETWEEN */
|
||||
testcase( i == 56 ); /* NOTNULL */
|
||||
testcase( i == 57 ); /* NOT */
|
||||
testcase( i == 58 ); /* NO */
|
||||
testcase( i == 59 ); /* NULL */
|
||||
testcase( i == 60 ); /* LIKE */
|
||||
testcase( i == 61 ); /* CASCADE */
|
||||
testcase( i == 62 ); /* ASC */
|
||||
testcase( i == 63 ); /* DELETE */
|
||||
testcase( i == 64 ); /* CASE */
|
||||
testcase( i == 65 ); /* COLLATE */
|
||||
testcase( i == 66 ); /* CREATE */
|
||||
testcase( i == 67 ); /* CURRENT_DATE */
|
||||
testcase( i == 68 ); /* DETACH */
|
||||
testcase( i == 69 ); /* IMMEDIATE */
|
||||
testcase( i == 70 ); /* JOIN */
|
||||
testcase( i == 71 ); /* INSERT */
|
||||
testcase( i == 72 ); /* MATCH */
|
||||
testcase( i == 73 ); /* PLAN */
|
||||
testcase( i == 74 ); /* ANALYZE */
|
||||
testcase( i == 75 ); /* PRAGMA */
|
||||
testcase( i == 76 ); /* ABORT */
|
||||
testcase( i == 77 ); /* VALUES */
|
||||
testcase( i == 78 ); /* VIRTUAL */
|
||||
testcase( i == 79 ); /* LIMIT */
|
||||
testcase( i == 80 ); /* WHEN */
|
||||
testcase( i == 81 ); /* WHERE */
|
||||
testcase( i == 82 ); /* RENAME */
|
||||
testcase( i == 83 ); /* AFTER */
|
||||
testcase( i == 84 ); /* REPLACE */
|
||||
testcase( i == 85 ); /* AND */
|
||||
testcase( i == 86 ); /* DEFAULT */
|
||||
testcase( i == 87 ); /* AUTOINCREMENT */
|
||||
testcase( i == 88 ); /* TO */
|
||||
testcase( i == 89 ); /* IN */
|
||||
testcase( i == 90 ); /* CAST */
|
||||
testcase( i == 91 ); /* COLUMN */
|
||||
testcase( i == 92 ); /* COMMIT */
|
||||
testcase( i == 93 ); /* CONFLICT */
|
||||
testcase( i == 94 ); /* CROSS */
|
||||
testcase( i == 95 ); /* CURRENT_TIMESTAMP */
|
||||
testcase( i == 96 ); /* CURRENT_TIME */
|
||||
testcase( i == 97 ); /* PRIMARY */
|
||||
testcase( i == 98 ); /* DEFERRED */
|
||||
testcase( i == 99 ); /* DISTINCT */
|
||||
testcase( i == 100 ); /* IS */
|
||||
testcase( i == 101 ); /* DROP */
|
||||
testcase( i == 102 ); /* FAIL */
|
||||
testcase( i == 103 ); /* FROM */
|
||||
testcase( i == 104 ); /* FULL */
|
||||
testcase( i == 105 ); /* GLOB */
|
||||
testcase( i == 106 ); /* BY */
|
||||
testcase( i == 107 ); /* IF */
|
||||
testcase( i == 108 ); /* ISNULL */
|
||||
testcase( i == 109 ); /* ORDER */
|
||||
testcase( i == 110 ); /* RESTRICT */
|
||||
testcase( i == 111 ); /* OUTER */
|
||||
testcase( i == 112 ); /* RIGHT */
|
||||
testcase( i == 113 ); /* ROLLBACK */
|
||||
testcase( i == 114 ); /* ROW */
|
||||
testcase( i == 115 ); /* UNION */
|
||||
testcase( i == 116 ); /* USING */
|
||||
testcase( i == 117 ); /* VACUUM */
|
||||
testcase( i == 118 ); /* VIEW */
|
||||
testcase( i == 119 ); /* INITIALLY */
|
||||
testcase( i == 120 ); /* ALL */
|
||||
return aCode[i];
|
||||
}
|
||||
}
|
||||
return TK_ID;
|
||||
}
|
||||
static int sqlite3KeywordCode( string z, int n )
|
||||
{
|
||||
return keywordCode( z, 0, n );
|
||||
}
|
||||
public const int SQLITE_N_KEYWORD = 121;//#define SQLITE_N_KEYWORD 121
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
using sqlite3_callback = Sqlite3.dxCallback;
|
||||
using sqlite3_stmt = Sqlite3.Vdbe;
|
||||
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/*
|
||||
** 2001 September 15
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** Main file for the SQLite library. The routines in this file
|
||||
** implement the programmer interface to the library. Routines in
|
||||
** other files are for internal use by SQLite and should not be
|
||||
** accessed by users of the library.
|
||||
*************************************************************************
|
||||
** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart
|
||||
** C#-SQLite is an independent reimplementation of the SQLite software library
|
||||
**
|
||||
** SQLITE_SOURCE_ID: 2010-08-23 18:52:01 42537b60566f288167f1b5864a5435986838e3a3
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
|
||||
//#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** Execute SQL code. Return one of the SQLITE_ success/failure
|
||||
** codes. Also write an error message into memory obtained from
|
||||
** malloc() and make pzErrMsg point to that message.
|
||||
**
|
||||
** If the SQL is a query, then for each row in the query result
|
||||
** the xCallback() function is called. pArg becomes the first
|
||||
** argument to xCallback(). If xCallback=NULL then no callback
|
||||
** is invoked, even for queries.
|
||||
*/
|
||||
//C# Alias
|
||||
static public int exec( sqlite3 db, /* The database on which the SQL executes */ string zSql, /* The SQL to be executed */ int NoCallback, int NoArgs, int NoErrors )
|
||||
{
|
||||
string Errors = "";
|
||||
return sqlite3_exec( db, zSql, null, null, ref Errors );
|
||||
}
|
||||
|
||||
static public int exec( sqlite3 db, /* The database on which the SQL executes */ string zSql, /* The SQL to be executed */ sqlite3_callback xCallback, /* Invoke this callback routine */ object pArg, /* First argument to xCallback() */ int NoErrors )
|
||||
{
|
||||
string Errors = "";
|
||||
return sqlite3_exec( db, zSql, xCallback, pArg, ref Errors );
|
||||
}
|
||||
static public int exec( sqlite3 db, /* The database on which the SQL executes */ string zSql, /* The SQL to be executed */ sqlite3_callback xCallback, /* Invoke this callback routine */ object pArg, /* First argument to xCallback() */ ref string pzErrMsg /* Write error messages here */)
|
||||
{
|
||||
return sqlite3_exec( db, zSql, xCallback, pArg, ref pzErrMsg );
|
||||
}
|
||||
|
||||
//OVERLOADS
|
||||
static public int sqlite3_exec(
|
||||
sqlite3 db, /* The database on which the SQL executes */
|
||||
string zSql, /* The SQL to be executed */
|
||||
int NoCallback, int NoArgs, int NoErrors
|
||||
)
|
||||
{
|
||||
string Errors = "";
|
||||
return sqlite3_exec( db, zSql, null, null, ref Errors );
|
||||
}
|
||||
|
||||
static public int sqlite3_exec(
|
||||
sqlite3 db, /* The database on which the SQL executes */
|
||||
string zSql, /* The SQL to be executed */
|
||||
sqlite3_callback xCallback, /* Invoke this callback routine */
|
||||
object pArg, /* First argument to xCallback() */
|
||||
int NoErrors
|
||||
)
|
||||
{
|
||||
string Errors = "";
|
||||
return sqlite3_exec( db, zSql, xCallback, pArg, ref Errors );
|
||||
}
|
||||
static public int sqlite3_exec(
|
||||
sqlite3 db, /* The database on which the SQL executes */
|
||||
string zSql, /* The SQL to be executed */
|
||||
sqlite3_callback xCallback, /* Invoke this callback routine */
|
||||
object pArg, /* First argument to xCallback() */
|
||||
ref string pzErrMsg /* Write error messages here */
|
||||
)
|
||||
{
|
||||
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
string zLeftover = ""; /* Tail of unprocessed SQL */
|
||||
sqlite3_stmt pStmt = null; /* The current SQL statement */
|
||||
string[] azCols = null; /* Names of result columns */
|
||||
int nRetry = 0; /* Number of retry attempts */
|
||||
int callbackIsInit; /* True if callback data is initialized */
|
||||
|
||||
if ( !sqlite3SafetyCheckOk( db ) )
|
||||
return SQLITE_MISUSE_BKPT();
|
||||
|
||||
if ( zSql == null )
|
||||
zSql = "";
|
||||
|
||||
sqlite3_mutex_enter( db.mutex );
|
||||
sqlite3Error( db, SQLITE_OK, 0 );
|
||||
while ( ( rc == SQLITE_OK || ( rc == SQLITE_SCHEMA && ( ++nRetry ) < 2 ) ) && zSql != "" )
|
||||
{
|
||||
int nCol;
|
||||
string[] azVals = null;
|
||||
|
||||
pStmt = null;
|
||||
rc = sqlite3_prepare( db, zSql, -1, ref pStmt, ref zLeftover );
|
||||
Debug.Assert( rc == SQLITE_OK || pStmt == null );
|
||||
if ( rc != SQLITE_OK )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if ( pStmt == null )
|
||||
{
|
||||
/* this happens for a comment or white-space */
|
||||
zSql = zLeftover;
|
||||
continue;
|
||||
}
|
||||
|
||||
callbackIsInit = 0;
|
||||
nCol = sqlite3_column_count( pStmt );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
int i;
|
||||
rc = sqlite3_step( pStmt );
|
||||
|
||||
/* Invoke the callback function if required */
|
||||
if ( xCallback != null && ( SQLITE_ROW == rc ||
|
||||
( SQLITE_DONE == rc && callbackIsInit == 0
|
||||
&& ( db.flags & SQLITE_NullCallback ) != 0 ) ) )
|
||||
{
|
||||
if ( 0 == callbackIsInit )
|
||||
{
|
||||
azCols = new string[nCol];//sqlite3DbMallocZero(db, 2*nCol*sizeof(const char*) + 1);
|
||||
//if ( azCols == null )
|
||||
//{
|
||||
// goto exec_out;
|
||||
//}
|
||||
for ( i = 0; i < nCol; i++ )
|
||||
{
|
||||
azCols[i] = sqlite3_column_name( pStmt, i );
|
||||
/* sqlite3VdbeSetColName() installs column names as UTF8
|
||||
** strings so there is no way for sqlite3_column_name() to fail. */
|
||||
Debug.Assert( azCols[i] != null );
|
||||
}
|
||||
callbackIsInit = 1;
|
||||
}
|
||||
if ( rc == SQLITE_ROW )
|
||||
{
|
||||
azVals = new string[nCol];// azCols[nCol];
|
||||
for ( i = 0; i < nCol; i++ )
|
||||
{
|
||||
azVals[i] = sqlite3_column_text( pStmt, i );
|
||||
if ( azVals[i] == null && sqlite3_column_type( pStmt, i ) != SQLITE_NULL )
|
||||
{
|
||||
//db.mallocFailed = 1;
|
||||
//goto exec_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( xCallback( pArg, nCol, azVals, azCols ) != 0 )
|
||||
{
|
||||
rc = SQLITE_ABORT;
|
||||
sqlite3VdbeFinalize( ref pStmt );
|
||||
pStmt = null;
|
||||
sqlite3Error( db, SQLITE_ABORT, 0 );
|
||||
goto exec_out;
|
||||
}
|
||||
}
|
||||
|
||||
if ( rc != SQLITE_ROW )
|
||||
{
|
||||
rc = sqlite3VdbeFinalize( ref pStmt );
|
||||
pStmt = null;
|
||||
if ( rc != SQLITE_SCHEMA )
|
||||
{
|
||||
nRetry = 0;
|
||||
if ( ( zSql = zLeftover ) != "" )
|
||||
{
|
||||
int zindex = 0;
|
||||
while ( zindex < zSql.Length && sqlite3Isspace( zSql[zindex] ) )
|
||||
zindex++;
|
||||
if ( zindex != 0 )
|
||||
zSql = zindex < zSql.Length ? zSql.Substring( zindex ) : "";
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3DbFree( db, ref azCols );
|
||||
azCols = null;
|
||||
}
|
||||
|
||||
exec_out:
|
||||
if ( pStmt != null )
|
||||
sqlite3VdbeFinalize( ref pStmt );
|
||||
sqlite3DbFree( db, ref azCols );
|
||||
|
||||
rc = sqlite3ApiExit( db, rc );
|
||||
if ( rc != SQLITE_OK && ALWAYS( rc == sqlite3_errcode( db ) ) && pzErrMsg != null )
|
||||
{
|
||||
//int nErrMsg = 1 + sqlite3Strlen30(sqlite3_errmsg(db));
|
||||
//pzErrMsg = sqlite3Malloc(nErrMsg);
|
||||
//if (pzErrMsg)
|
||||
//{
|
||||
// memcpy(pzErrMsg, sqlite3_errmsg(db), nErrMsg);
|
||||
//}else{
|
||||
//rc = SQLITE_NOMEM;
|
||||
//sqlite3Error(db, SQLITE_NOMEM, 0);
|
||||
//}
|
||||
pzErrMsg = sqlite3_errmsg( db );
|
||||
}
|
||||
else if ( pzErrMsg != "" )
|
||||
{
|
||||
pzErrMsg = "";
|
||||
}
|
||||
|
||||
Debug.Assert( ( rc & db.errMask ) == rc );
|
||||
sqlite3_mutex_leave( db.mutex );
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,744 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using HANDLE = System.IntPtr;
|
||||
using System.Text;
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/*
|
||||
** 2006 June 7
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file contains code used to dynamically load extensions into
|
||||
** the SQLite library.
|
||||
*************************************************************************
|
||||
** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart
|
||||
** C#-SQLite is an independent reimplementation of the SQLite software library
|
||||
**
|
||||
** SQLITE_SOURCE_ID: 2011-05-19 13:26:54 ed1da510a239ea767a01dc332b667119fa3c908e
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
#if !SQLITE_CORE
|
||||
//#define SQLITE_CORE 1 /* Disable the API redefinition in sqlite3ext.h */
|
||||
const int SQLITE_CORE = 1;
|
||||
#endif
|
||||
//#include "sqlite3ext.h"
|
||||
//#include "sqliteInt.h"
|
||||
//#include <string.h>
|
||||
|
||||
#if !SQLITE_OMIT_LOAD_EXTENSION
|
||||
|
||||
/*
|
||||
** Some API routines are omitted when various features are
|
||||
** excluded from a build of SQLite. Substitute a NULL pointer
|
||||
** for any missing APIs.
|
||||
*/
|
||||
#if !SQLITE_ENABLE_COLUMN_METADATA
|
||||
//# define sqlite3_column_database_name 0
|
||||
//# define sqlite3_column_database_name16 0
|
||||
//# define sqlite3_column_table_name 0
|
||||
//# define sqlite3_column_table_name16 0
|
||||
//# define sqlite3_column_origin_name 0
|
||||
//# define sqlite3_column_origin_name16 0
|
||||
//# define sqlite3_table_column_metadata 0
|
||||
#endif
|
||||
|
||||
//#if SQLITE_OMIT_AUTHORIZATION
|
||||
//# define sqlite3_set_authorizer 0
|
||||
//#endif
|
||||
|
||||
#if !NO_SQLITE_OMIT_UTF16
|
||||
//# define sqlite3_bind_text16 0
|
||||
//# define sqlite3_collation_needed16 0
|
||||
//# define sqlite3_column_decltype16 0
|
||||
//# define sqlite3_column_name16 0
|
||||
//# define sqlite3_column_text16 0
|
||||
//# define sqlite3_complete16 0
|
||||
//# define sqlite3_create_collation16 0
|
||||
//# define sqlite3_create_function16 0
|
||||
//# define sqlite3_errmsg16 0
|
||||
static string sqlite3_errmsg16( sqlite3 db )
|
||||
{
|
||||
return "";
|
||||
}
|
||||
//# define sqlite3_open16 0
|
||||
//# define sqlite3_prepare16 0
|
||||
//# define sqlite3_prepare16_v2 0
|
||||
//# define sqlite3_result_error16 0
|
||||
//# define sqlite3_result_text16 0
|
||||
static void sqlite3_result_text16( sqlite3_context pCtx, string z, int n, dxDel xDel )
|
||||
{
|
||||
}
|
||||
//# define sqlite3_result_text16be 0
|
||||
//# define sqlite3_result_text16le 0
|
||||
//# define sqlite3_value_text16 0
|
||||
//# define sqlite3_value_text16be 0
|
||||
//# define sqlite3_value_text16le 0
|
||||
//# define sqlite3_column_database_name16 0
|
||||
//# define sqlite3_column_table_name16 0
|
||||
//# define sqlite3_column_origin_name16 0
|
||||
#endif
|
||||
|
||||
#if SQLITE_OMIT_COMPLETE
|
||||
//# define sqlite3_complete 0
|
||||
//# define sqlite3_complete16 0
|
||||
#endif
|
||||
|
||||
#if SQLITE_OMIT_DECLTYPE
|
||||
//# define sqlite3_column_decltype16 0
|
||||
//# define sqlite3_column_decltype 0
|
||||
#endif
|
||||
|
||||
#if SQLITE_OMIT_PROGRESS_CALLBACK
|
||||
//# define sqlite3_progress_handler 0
|
||||
static void sqlite3_progress_handler (sqlite3 db, int nOps, dxProgress xProgress, object pArg){}
|
||||
#endif
|
||||
|
||||
#if SQLITE_OMIT_VIRTUALTABLE
|
||||
//# define sqlite3_create_module 0
|
||||
//# define sqlite3_create_module_v2 0
|
||||
//# define sqlite3_declare_vtab 0
|
||||
#endif
|
||||
|
||||
//#if SQLITE_OMIT_SHARED_CACHE
|
||||
//# define sqlite3_enable_shared_cache 0
|
||||
//#endif
|
||||
|
||||
#if SQLITE_OMIT_TRACE
|
||||
//# define sqlite3_profile 0
|
||||
//# define sqlite3_trace 0
|
||||
#endif
|
||||
|
||||
#if !NO_SQLITE_OMIT_GET_TABLE
|
||||
//# define //sqlite3_free_table 0
|
||||
//# define sqlite3_get_table 0
|
||||
static public int sqlite3_get_table(
|
||||
sqlite3 db, /* An open database */
|
||||
string zSql, /* SQL to be evaluated */
|
||||
ref string[] pazResult, /* Results of the query */
|
||||
ref int pnRow, /* Number of result rows written here */
|
||||
ref int pnColumn, /* Number of result columns written here */
|
||||
ref string pzErrmsg /* Error msg written here */
|
||||
)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
//#if SQLITE_OMIT_INCRBLOB
|
||||
//#define sqlite3_bind_zeroblob 0
|
||||
//#define sqlite3_blob_bytes 0
|
||||
//#define sqlite3_blob_close 0
|
||||
//#define sqlite3_blob_open 0
|
||||
//#define sqlite3_blob_read 0
|
||||
//#define sqlite3_blob_write 0
|
||||
//#endif
|
||||
|
||||
/*
|
||||
** The following structure contains pointers to all SQLite API routines.
|
||||
** A pointer to this structure is passed into extensions when they are
|
||||
** loaded so that the extension can make calls back into the SQLite
|
||||
** library.
|
||||
**
|
||||
** When adding new APIs, add them to the bottom of this structure
|
||||
** in order to preserve backwards compatibility.
|
||||
**
|
||||
** Extensions that use newer APIs should first call the
|
||||
** sqlite3_libversion_number() to make sure that the API they
|
||||
** intend to use is supported by the library. Extensions should
|
||||
** also check to make sure that the pointer to the function is
|
||||
** not NULL before calling it.
|
||||
*/
|
||||
public class sqlite3_api_routines
|
||||
{
|
||||
public sqlite3 context_db_handle;
|
||||
};
|
||||
|
||||
static sqlite3_api_routines sqlite3Apis = new sqlite3_api_routines();
|
||||
//{
|
||||
// sqlite3_aggregate_context,
|
||||
#if NOT_SQLITE_OMIT_DEPRECATED //#if !SQLITE_OMIT_DEPRECATED
|
||||
/ sqlite3_aggregate_count,
|
||||
#else
|
||||
// 0,
|
||||
#endif
|
||||
// sqlite3_bind_blob,
|
||||
// sqlite3_bind_double,
|
||||
// sqlite3_bind_int,
|
||||
// sqlite3_bind_int64,
|
||||
// sqlite3_bind_null,
|
||||
// sqlite3_bind_parameter_count,
|
||||
// sqlite3_bind_parameter_index,
|
||||
// sqlite3_bind_parameter_name,
|
||||
// sqlite3_bind_text,
|
||||
// sqlite3_bind_text16,
|
||||
// sqlite3_bind_value,
|
||||
// sqlite3_busy_handler,
|
||||
// sqlite3_busy_timeout,
|
||||
// sqlite3_changes,
|
||||
// sqlite3_close,
|
||||
// sqlite3_collation_needed,
|
||||
// sqlite3_collation_needed16,
|
||||
// sqlite3_column_blob,
|
||||
// sqlite3_column_bytes,
|
||||
// sqlite3_column_bytes16,
|
||||
// sqlite3_column_count,
|
||||
// sqlite3_column_database_name,
|
||||
// sqlite3_column_database_name16,
|
||||
// sqlite3_column_decltype,
|
||||
// sqlite3_column_decltype16,
|
||||
// sqlite3_column_double,
|
||||
// sqlite3_column_int,
|
||||
// sqlite3_column_int64,
|
||||
// sqlite3_column_name,
|
||||
// sqlite3_column_name16,
|
||||
// sqlite3_column_origin_name,
|
||||
// sqlite3_column_origin_name16,
|
||||
// sqlite3_column_table_name,
|
||||
// sqlite3_column_table_name16,
|
||||
// sqlite3_column_text,
|
||||
// sqlite3_column_text16,
|
||||
// sqlite3_column_type,
|
||||
// sqlite3_column_value,
|
||||
// sqlite3_commit_hook,
|
||||
// sqlite3_complete,
|
||||
// sqlite3_complete16,
|
||||
// sqlite3_create_collation,
|
||||
// sqlite3_create_collation16,
|
||||
// sqlite3_create_function,
|
||||
// sqlite3_create_function16,
|
||||
// sqlite3_create_module,
|
||||
// sqlite3_data_count,
|
||||
// sqlite3_db_handle,
|
||||
// sqlite3_declare_vtab,
|
||||
// sqlite3_enable_shared_cache,
|
||||
// sqlite3_errcode,
|
||||
// sqlite3_errmsg,
|
||||
// sqlite3_errmsg16,
|
||||
// sqlite3_exec,
|
||||
#if NOT_SQLITE_OMIT_DEPRECATED //#if !SQLITE_OMIT_DEPRECATED
|
||||
//sqlite3_expired,
|
||||
#else
|
||||
//0,
|
||||
#endif
|
||||
// sqlite3_finalize,
|
||||
// //sqlite3_free,
|
||||
// //sqlite3_free_table,
|
||||
// sqlite3_get_autocommit,
|
||||
// sqlite3_get_auxdata,
|
||||
// sqlite3_get_table,
|
||||
// 0, /* Was sqlite3_global_recover(), but that function is deprecated */
|
||||
// sqlite3_interrupt,
|
||||
// sqlite3_last_insert_rowid,
|
||||
// sqlite3_libversion,
|
||||
// sqlite3_libversion_number,
|
||||
// sqlite3_malloc,
|
||||
// sqlite3_mprintf,
|
||||
// sqlite3_open,
|
||||
// sqlite3_open16,
|
||||
// sqlite3_prepare,
|
||||
// sqlite3_prepare16,
|
||||
// sqlite3_profile,
|
||||
// sqlite3_progress_handler,
|
||||
// sqlite3_realloc,
|
||||
// sqlite3_reset,
|
||||
// sqlite3_result_blob,
|
||||
// sqlite3_result_double,
|
||||
// sqlite3_result_error,
|
||||
// sqlite3_result_error16,
|
||||
// sqlite3_result_int,
|
||||
// sqlite3_result_int64,
|
||||
// sqlite3_result_null,
|
||||
// sqlite3_result_text,
|
||||
// sqlite3_result_text16,
|
||||
// sqlite3_result_text16be,
|
||||
// sqlite3_result_text16le,
|
||||
// sqlite3_result_value,
|
||||
// sqlite3_rollback_hook,
|
||||
// sqlite3_set_authorizer,
|
||||
// sqlite3_set_auxdata,
|
||||
// sqlite3_snprintf,
|
||||
// sqlite3_step,
|
||||
// sqlite3_table_column_metadata,
|
||||
#if NOT_SQLITE_OMIT_DEPRECATED //#if !SQLITE_OMIT_DEPRECATED
|
||||
//sqlite3_thread_cleanup,
|
||||
#else
|
||||
// 0,
|
||||
#endif
|
||||
// sqlite3_total_changes,
|
||||
// sqlite3_trace,
|
||||
#if NOT_SQLITE_OMIT_DEPRECATED //#if !SQLITE_OMIT_DEPRECATED
|
||||
//sqlite3_transfer_bindings,
|
||||
#else
|
||||
// 0,
|
||||
#endif
|
||||
// sqlite3_update_hook,
|
||||
// sqlite3_user_data,
|
||||
// sqlite3_value_blob,
|
||||
// sqlite3_value_bytes,
|
||||
// sqlite3_value_bytes16,
|
||||
// sqlite3_value_double,
|
||||
// sqlite3_value_int,
|
||||
// sqlite3_value_int64,
|
||||
// sqlite3_value_numeric_type,
|
||||
// sqlite3_value_text,
|
||||
// sqlite3_value_text16,
|
||||
// sqlite3_value_text16be,
|
||||
// sqlite3_value_text16le,
|
||||
// sqlite3_value_type,
|
||||
// sqlite3_vmprintf,
|
||||
// /*
|
||||
// ** The original API set ends here. All extensions can call any
|
||||
// ** of the APIs above provided that the pointer is not NULL. But
|
||||
// ** before calling APIs that follow, extension should check the
|
||||
// ** sqlite3_libversion_number() to make sure they are dealing with
|
||||
// ** a library that is new enough to support that API.
|
||||
// *************************************************************************
|
||||
// */
|
||||
// sqlite3_overload_function,
|
||||
|
||||
// /*
|
||||
// ** Added after 3.3.13
|
||||
// */
|
||||
// sqlite3_prepare_v2,
|
||||
// sqlite3_prepare16_v2,
|
||||
// sqlite3_clear_bindings,
|
||||
|
||||
// /*
|
||||
// ** Added for 3.4.1
|
||||
// */
|
||||
// sqlite3_create_module_v2,
|
||||
|
||||
// /*
|
||||
// ** Added for 3.5.0
|
||||
// */
|
||||
// sqlite3_bind_zeroblob,
|
||||
// sqlite3_blob_bytes,
|
||||
// sqlite3_blob_close,
|
||||
// sqlite3_blob_open,
|
||||
// sqlite3_blob_read,
|
||||
// sqlite3_blob_write,
|
||||
// sqlite3_create_collation_v2,
|
||||
// sqlite3_file_control,
|
||||
// sqlite3_memory_highwater,
|
||||
// sqlite3_memory_used,
|
||||
#if SQLITE_MUTEX_OMIT
|
||||
// 0,
|
||||
// 0,
|
||||
// 0,
|
||||
// 0,
|
||||
// 0,
|
||||
#else
|
||||
// sqlite3MutexAlloc,
|
||||
// sqlite3_mutex_enter,
|
||||
// sqlite3_mutex_free,
|
||||
// sqlite3_mutex_leave,
|
||||
// sqlite3_mutex_try,
|
||||
#endif
|
||||
// sqlite3_open_v2,
|
||||
// sqlite3_release_memory,
|
||||
// sqlite3_result_error_nomem,
|
||||
// sqlite3_result_error_toobig,
|
||||
// sqlite3_sleep,
|
||||
// sqlite3_soft_heap_limit,
|
||||
// sqlite3_vfs_find,
|
||||
// sqlite3_vfs_register,
|
||||
// sqlite3_vfs_unregister,
|
||||
|
||||
// /*
|
||||
// ** Added for 3.5.8
|
||||
// */
|
||||
// sqlite3_threadsafe,
|
||||
// sqlite3_result_zeroblob,
|
||||
// sqlite3_result_error_code,
|
||||
// sqlite3_test_control,
|
||||
// sqlite3_randomness,
|
||||
// sqlite3_context_db_handle,
|
||||
|
||||
// /*
|
||||
// ** Added for 3.6.0
|
||||
// */
|
||||
// sqlite3_extended_result_codes,
|
||||
// sqlite3_limit,
|
||||
// sqlite3_next_stmt,
|
||||
// sqlite3_sql,
|
||||
// sqlite3_status,
|
||||
|
||||
// /*
|
||||
// ** Added for 3.7.4
|
||||
// */
|
||||
// sqlite3_backup_finish,
|
||||
// sqlite3_backup_init,
|
||||
// sqlite3_backup_pagecount,
|
||||
// sqlite3_backup_remaining,
|
||||
// sqlite3_backup_step,
|
||||
//#if !SQLITE_OMIT_COMPILEOPTION_DIAGS
|
||||
// sqlite3_compileoption_get,
|
||||
// sqlite3_compileoption_used,
|
||||
//#else
|
||||
// 0,
|
||||
// 0,
|
||||
//#endif
|
||||
// sqlite3_create_function_v2,
|
||||
// sqlite3_db_config,
|
||||
// sqlite3_db_mutex,
|
||||
// sqlite3_db_status,
|
||||
// sqlite3_extended_errcode,
|
||||
// sqlite3_log,
|
||||
// sqlite3_soft_heap_limit64,
|
||||
// sqlite3_sourceid,
|
||||
// sqlite3_stmt_status,
|
||||
// sqlite3_strnicmp,
|
||||
//#if SQLITE_ENABLE_UNLOCK_NOTIFY
|
||||
// sqlite3_unlock_notify,
|
||||
//#else
|
||||
// 0,
|
||||
//#endif
|
||||
//#if !SQLITE_OMIT_WAL
|
||||
// sqlite3_wal_autocheckpoint,
|
||||
// sqlite3_wal_checkpoint,
|
||||
// sqlite3_wal_hook,
|
||||
//#else
|
||||
// 0,
|
||||
// 0,
|
||||
// 0,
|
||||
//#endif
|
||||
//};
|
||||
|
||||
/*
|
||||
** Attempt to load an SQLite extension library contained in the file
|
||||
** zFile. The entry point is zProc. zProc may be 0 in which case a
|
||||
** default entry point name (sqlite3_extension_init) is used. Use
|
||||
** of the default name is recommended.
|
||||
**
|
||||
** Return SQLITE_OK on success and SQLITE_ERROR if something goes wrong.
|
||||
**
|
||||
** If an error occurs and pzErrMsg is not 0, then fill pzErrMsg with
|
||||
** error message text. The calling function should free this memory
|
||||
** by calling sqlite3DbFree(db, ).
|
||||
*/
|
||||
static int sqlite3LoadExtension(
|
||||
sqlite3 db, /* Load the extension into this database connection */
|
||||
string zFile, /* Name of the shared library containing extension */
|
||||
string zProc, /* Entry point. Use "sqlite3_extension_init" if 0 */
|
||||
ref string pzErrMsg /* Put error message here if not 0 */
|
||||
)
|
||||
{
|
||||
sqlite3_vfs pVfs = db.pVfs;
|
||||
HANDLE handle;
|
||||
dxInit xInit; //int (*xInit)(sqlite3*,char**,const sqlite3_api_routines);
|
||||
StringBuilder zErrmsg = new StringBuilder( 100 );
|
||||
//object aHandle;
|
||||
const int nMsg = 300;
|
||||
if ( pzErrMsg != null )
|
||||
pzErrMsg = null;
|
||||
|
||||
|
||||
/* Ticket #1863. To avoid a creating security problems for older
|
||||
** applications that relink against newer versions of SQLite, the
|
||||
** ability to run load_extension is turned off by default. One
|
||||
** must call sqlite3_enable_load_extension() to turn on extension
|
||||
** loading. Otherwise you get the following error.
|
||||
*/
|
||||
if ( ( db.flags & SQLITE_LoadExtension ) == 0 )
|
||||
{
|
||||
//if( pzErrMsg != null){
|
||||
pzErrMsg = sqlite3_mprintf( "not authorized" );
|
||||
//}
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
if ( zProc == null || zProc == "" )
|
||||
{
|
||||
zProc = "sqlite3_extension_init";
|
||||
}
|
||||
|
||||
handle = sqlite3OsDlOpen( pVfs, zFile );
|
||||
if ( handle == IntPtr.Zero )
|
||||
{
|
||||
// if( pzErrMsg ){
|
||||
pzErrMsg = "";//*pzErrMsg = zErrmsg = sqlite3_malloc(nMsg);
|
||||
//if( zErrmsg !=null){
|
||||
sqlite3_snprintf( nMsg, zErrmsg,
|
||||
"unable to open shared library [%s]", zFile );
|
||||
sqlite3OsDlError( pVfs, nMsg - 1, zErrmsg.ToString() );
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
//xInit = (int()(sqlite3*,char**,const sqlite3_api_routines))
|
||||
// sqlite3OsDlSym(pVfs, handle, zProc);
|
||||
xInit = (dxInit)sqlite3OsDlSym( pVfs, handle, ref zProc );
|
||||
Debugger.Break(); // TODO --
|
||||
//if( xInit==0 ){
|
||||
// if( pzErrMsg ){
|
||||
// *pzErrMsg = zErrmsg = sqlite3_malloc(nMsg);
|
||||
// if( zErrmsg ){
|
||||
// sqlite3_snprintf(nMsg, zErrmsg,
|
||||
// "no entry point [%s] in shared library [%s]", zProc,zFile);
|
||||
// sqlite3OsDlError(pVfs, nMsg-1, zErrmsg);
|
||||
// }
|
||||
// sqlite3OsDlClose(pVfs, handle);
|
||||
// }
|
||||
// return SQLITE_ERROR;
|
||||
// }else if( xInit(db, ref zErrmsg, sqlite3Apis) ){
|
||||
//// if( pzErrMsg !=null){
|
||||
// pzErrMsg = sqlite3_mprintf("error during initialization: %s", zErrmsg);
|
||||
// //}
|
||||
// sqlite3DbFree(db,ref zErrmsg);
|
||||
// sqlite3OsDlClose(pVfs, ref handle);
|
||||
// return SQLITE_ERROR;
|
||||
// }
|
||||
|
||||
// /* Append the new shared library handle to the db.aExtension array. */
|
||||
// aHandle = sqlite3DbMallocZero(db, sizeof(handle)*db.nExtension+1);
|
||||
// if( aHandle==null ){
|
||||
// return SQLITE_NOMEM;
|
||||
// }
|
||||
// if( db.nExtension>0 ){
|
||||
// memcpy(aHandle, db.aExtension, sizeof(handle)*(db.nExtension));
|
||||
// }
|
||||
// sqlite3DbFree(db,ref db.aExtension);
|
||||
// db.aExtension = aHandle;
|
||||
|
||||
// db.aExtension[db.nExtension++] = handle;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static public int sqlite3_load_extension(
|
||||
sqlite3 db, /* Load the extension into this database connection */
|
||||
string zFile, /* Name of the shared library containing extension */
|
||||
string zProc, /* Entry point. Use "sqlite3_extension_init" if 0 */
|
||||
ref string pzErrMsg /* Put error message here if not 0 */
|
||||
)
|
||||
{
|
||||
int rc;
|
||||
sqlite3_mutex_enter( db.mutex );
|
||||
rc = sqlite3LoadExtension( db, zFile, zProc, ref pzErrMsg );
|
||||
rc = sqlite3ApiExit( db, rc );
|
||||
sqlite3_mutex_leave( db.mutex );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Call this routine when the database connection is closing in order
|
||||
** to clean up loaded extensions
|
||||
*/
|
||||
static void sqlite3CloseExtensions( sqlite3 db )
|
||||
{
|
||||
int i;
|
||||
Debug.Assert( sqlite3_mutex_held( db.mutex ) );
|
||||
for ( i = 0; i < db.nExtension; i++ )
|
||||
{
|
||||
sqlite3OsDlClose( db.pVfs, (HANDLE)db.aExtension[i] );
|
||||
}
|
||||
sqlite3DbFree( db, ref db.aExtension );
|
||||
}
|
||||
|
||||
/*
|
||||
** Enable or disable extension loading. Extension loading is disabled by
|
||||
** default so as not to open security holes in older applications.
|
||||
*/
|
||||
static public int sqlite3_enable_load_extension( sqlite3 db, int onoff )
|
||||
{
|
||||
sqlite3_mutex_enter( db.mutex );
|
||||
if ( onoff != 0 )
|
||||
{
|
||||
db.flags |= SQLITE_LoadExtension;
|
||||
}
|
||||
else
|
||||
{
|
||||
db.flags &= ~SQLITE_LoadExtension;
|
||||
}
|
||||
sqlite3_mutex_leave( db.mutex );
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
#endif //* SQLITE_OMIT_LOAD_EXTENSION */
|
||||
|
||||
/*
|
||||
** The auto-extension code added regardless of whether or not extension
|
||||
** loading is supported. We need a dummy sqlite3Apis pointer for that
|
||||
** code if regular extension loading is not available. This is that
|
||||
** dummy pointer.
|
||||
*/
|
||||
#if SQLITE_OMIT_LOAD_EXTENSION
|
||||
const sqlite3_api_routines sqlite3Apis = null;
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** The following object holds the list of automatically loaded
|
||||
** extensions.
|
||||
**
|
||||
** This list is shared across threads. The SQLITE_MUTEX_STATIC_MASTER
|
||||
** mutex must be held while accessing this list.
|
||||
*/
|
||||
//typedef struct sqlite3AutoExtList sqlite3AutoExtList;
|
||||
public class sqlite3AutoExtList
|
||||
{
|
||||
public int nExt = 0; /* Number of entries in aExt[] */
|
||||
public dxInit[] aExt = null; /* Pointers to the extension init functions */
|
||||
public sqlite3AutoExtList( int nExt, dxInit[] aExt )
|
||||
{
|
||||
this.nExt = nExt;
|
||||
this.aExt = aExt;
|
||||
}
|
||||
}
|
||||
static sqlite3AutoExtList sqlite3Autoext = new sqlite3AutoExtList( 0, null );
|
||||
/* The "wsdAutoext" macro will resolve to the autoextension
|
||||
** state vector. If writable static data is unsupported on the target,
|
||||
** we have to locate the state vector at run-time. In the more common
|
||||
** case where writable static data is supported, wsdStat can refer directly
|
||||
** to the "sqlite3Autoext" state vector declared above.
|
||||
*/
|
||||
#if SQLITE_OMIT_WSD
|
||||
//# define wsdAutoextInit \
|
||||
sqlite3AutoExtList *x = &GLOBAL(sqlite3AutoExtList,sqlite3Autoext)
|
||||
//# define wsdAutoext x[0]
|
||||
#else
|
||||
//# define wsdAutoextInit
|
||||
static void wsdAutoextInit()
|
||||
{
|
||||
}
|
||||
//# define wsdAutoext sqlite3Autoext
|
||||
static sqlite3AutoExtList wsdAutoext = sqlite3Autoext;
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Register a statically linked extension that is automatically
|
||||
** loaded by every new database connection.
|
||||
*/
|
||||
static int sqlite3_auto_extension( dxInit xInit )
|
||||
{
|
||||
int rc = SQLITE_OK;
|
||||
#if !SQLITE_OMIT_AUTOINIT
|
||||
rc = sqlite3_initialize();
|
||||
if ( rc != 0 )
|
||||
{
|
||||
return rc;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
int i;
|
||||
#if SQLITE_THREADSAFE
|
||||
sqlite3_mutex mutex = sqlite3MutexAlloc( SQLITE_MUTEX_STATIC_MASTER );
|
||||
#else
|
||||
sqlite3_mutex mutex = sqlite3MutexAlloc( SQLITE_MUTEX_STATIC_MASTER );
|
||||
#endif
|
||||
wsdAutoextInit();
|
||||
sqlite3_mutex_enter( mutex );
|
||||
for ( i = 0; i < wsdAutoext.nExt; i++ )
|
||||
{
|
||||
if ( wsdAutoext.aExt[i] == xInit )
|
||||
break;
|
||||
}
|
||||
//if( i==wsdAutoext.nExt ){
|
||||
// int nByte = (wsdAutoext.nExt+1)*sizeof(wsdAutoext.aExt[0]);
|
||||
// void **aNew;
|
||||
// aNew = sqlite3_realloc(wsdAutoext.aExt, nByte);
|
||||
// if( aNew==0 ){
|
||||
// rc = SQLITE_NOMEM;
|
||||
// }else{
|
||||
Array.Resize( ref wsdAutoext.aExt, wsdAutoext.nExt + 1 );// wsdAutoext.aExt = aNew;
|
||||
wsdAutoext.aExt[wsdAutoext.nExt] = xInit;
|
||||
wsdAutoext.nExt++;
|
||||
//}
|
||||
sqlite3_mutex_leave( mutex );
|
||||
Debug.Assert( ( rc & 0xff ) == rc );
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Reset the automatic extension loading mechanism.
|
||||
*/
|
||||
static void sqlite3_reset_auto_extension()
|
||||
{
|
||||
#if !SQLITE_OMIT_AUTOINIT
|
||||
if ( sqlite3_initialize() == SQLITE_OK )
|
||||
#endif
|
||||
{
|
||||
#if SQLITE_THREADSAFE
|
||||
sqlite3_mutex mutex = sqlite3MutexAlloc( SQLITE_MUTEX_STATIC_MASTER );
|
||||
#else
|
||||
sqlite3_mutex mutex = sqlite3MutexAlloc( SQLITE_MUTEX_STATIC_MASTER );
|
||||
#endif
|
||||
wsdAutoextInit();
|
||||
sqlite3_mutex_enter( mutex );
|
||||
#if SQLITE_OMIT_WSD
|
||||
//sqlite3_free( ref wsdAutoext.aExt );
|
||||
wsdAutoext.aExt = null;
|
||||
wsdAutoext.nExt = 0;
|
||||
#else
|
||||
//sqlite3_free( ref sqlite3Autoext.aExt );
|
||||
sqlite3Autoext.aExt = null;
|
||||
sqlite3Autoext.nExt = 0;
|
||||
#endif
|
||||
sqlite3_mutex_leave( mutex );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Load all automatic extensions.
|
||||
**
|
||||
** If anything goes wrong, set an error in the database connection.
|
||||
*/
|
||||
static void sqlite3AutoLoadExtensions( sqlite3 db )
|
||||
{
|
||||
int i;
|
||||
bool go = true;
|
||||
dxInit xInit;//)(sqlite3*,char**,const sqlite3_api_routines);
|
||||
|
||||
wsdAutoextInit();
|
||||
#if SQLITE_OMIT_WSD
|
||||
if ( wsdAutoext.nExt == 0 )
|
||||
#else
|
||||
if ( sqlite3Autoext.nExt == 0 )
|
||||
#endif
|
||||
{
|
||||
/* Common case: early out without every having to acquire a mutex */
|
||||
return;
|
||||
}
|
||||
for ( i = 0; go; i++ )
|
||||
{
|
||||
string zErrmsg = "";
|
||||
#if SQLITE_THREADSAFE
|
||||
sqlite3_mutex mutex = sqlite3MutexAlloc( SQLITE_MUTEX_STATIC_MASTER );
|
||||
#else
|
||||
sqlite3_mutex mutex = sqlite3MutexAlloc( SQLITE_MUTEX_STATIC_MASTER );
|
||||
#endif
|
||||
sqlite3_mutex_enter( mutex );
|
||||
if ( i >= wsdAutoext.nExt )
|
||||
{
|
||||
xInit = null;
|
||||
go = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
xInit = (dxInit)
|
||||
wsdAutoext.aExt[i];
|
||||
}
|
||||
sqlite3_mutex_leave( mutex );
|
||||
zErrmsg = "";
|
||||
if ( xInit != null && xInit( db, ref zErrmsg, (sqlite3_api_routines)sqlite3Apis ) != 0 )
|
||||
{
|
||||
sqlite3Error( db, SQLITE_ERROR,
|
||||
"automatic extension loading failed: %s", zErrmsg );
|
||||
go = false;
|
||||
}
|
||||
sqlite3DbFree( db, ref zErrmsg );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
3430
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/main_c.cs
Normal file
3430
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/main_c.cs
Normal file
File diff suppressed because it is too large
Load Diff
1116
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/malloc_c.cs
Normal file
1116
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/malloc_c.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,440 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using sqlite3_int64 = System.Int64;
|
||||
using u32 = System.UInt32;
|
||||
using System.Text;
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/*
|
||||
*************************************************************************
|
||||
**
|
||||
** This file contains low-level memory & pool allocation drivers
|
||||
**
|
||||
** This file contains implementations of the low-level memory allocation
|
||||
** routines specified in the sqlite3_mem_methods object.
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
|
||||
/*
|
||||
** Like malloc(), but remember the size of the allocation
|
||||
** so that we can find it later using sqlite3MemSize().
|
||||
**
|
||||
** For this low-level routine, we are guaranteed that nByte>0 because
|
||||
** cases of nByte<=0 will be intercepted and dealt with by higher level
|
||||
** routines.
|
||||
*/
|
||||
#if SQLITE_POOL_MEM
|
||||
#if !NO_TRUE
|
||||
static byte[] sqlite3MemMalloc( u32 nByte )
|
||||
{
|
||||
return new byte[nByte];
|
||||
}
|
||||
static byte[] sqlite3MemMalloc( int nByte )
|
||||
{
|
||||
return new byte[nByte];
|
||||
}
|
||||
static int[] sqlite3MemMallocInt( int nInt )
|
||||
{
|
||||
return new int[nInt];
|
||||
}
|
||||
#else
|
||||
static byte[] sqlite3MemMalloc(int nByte)
|
||||
{
|
||||
byte[] pByte = null;
|
||||
int savej = -1;
|
||||
int BestSize = int.MaxValue;
|
||||
if (nByte > mem0.aByteSize[0])
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < mem0.aByteSize.Length - 1; i++) if (nByte <= mem0.aByteSize[i]) break;
|
||||
mem0.mr_Byte++;
|
||||
for (int j = 0; j <= mem0.aByte_used[i]; j++)
|
||||
if (mem0.aByte[i][j] != null)
|
||||
{
|
||||
if (mem0.aByte[i][j].Length == nByte)
|
||||
{
|
||||
pByte = mem0.aByte[i][j]; mem0.aByte[i][j] = null; mem0.cf_Byte++;
|
||||
if (j == mem0.aByte_used[i]) mem0.aByte_used[i]--;
|
||||
break;
|
||||
}
|
||||
if (mem0.aByte[i][j].Length > nByte)
|
||||
{
|
||||
if (mem0.aByte[i][j].Length < BestSize)
|
||||
{
|
||||
BestSize = mem0.aByte[i][j].Length;
|
||||
savej = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pByte == null && savej >= 0)
|
||||
{
|
||||
pByte = mem0.aByte[i][savej]; mem0.aByte[i][savej] = null; mem0.cf_Byte++;
|
||||
Array.Resize(ref pByte, nByte);
|
||||
}
|
||||
if (mem0.aByte_used[i] >=0 && mem0.aByte[i][mem0.aByte_used[i]] == null) mem0.aByte_used[i]--;
|
||||
}
|
||||
if (pByte == null) pByte = new byte[nByte];
|
||||
return pByte;
|
||||
}
|
||||
static int[] sqlite3MemMallocInt(int nInt)
|
||||
{
|
||||
int[] pInt = null;
|
||||
int savei = -1;
|
||||
int BestSize = int.MaxValue;
|
||||
if (nInt >=10)
|
||||
{
|
||||
mem0.mr_Int++;
|
||||
int i;
|
||||
for (i = 0; i < mem0.hw_Int; i++)
|
||||
if (mem0.aInt[i] != null)
|
||||
{
|
||||
if (mem0.aInt[i].Length == nInt)
|
||||
{
|
||||
pInt = mem0.aInt[i]; mem0.aInt[i] = null; mem0.cf_Int++; break;
|
||||
}
|
||||
if (mem0.aInt[i].Length > nInt)
|
||||
{
|
||||
if (mem0.aInt[i].Length < BestSize)
|
||||
{
|
||||
BestSize = mem0.aInt[i].Length;
|
||||
savei = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pInt == null && savei >= 0)
|
||||
{
|
||||
pInt = mem0.aInt[savei]; mem0.aInt[savei] = null; mem0.cf_Int++;
|
||||
}
|
||||
}
|
||||
if (pInt == null) pInt = new int[nInt];
|
||||
return pInt;
|
||||
}
|
||||
#endif
|
||||
|
||||
static Mem sqlite3MemMallocMem( Mem dummy )
|
||||
{
|
||||
Mem pMem;
|
||||
mem0.msMem.alloc++;
|
||||
if ( mem0.msMem.next > 0 && mem0.aMem[mem0.msMem.next] != null )
|
||||
{
|
||||
pMem = mem0.aMem[mem0.msMem.next];
|
||||
mem0.aMem[mem0.msMem.next] = null;
|
||||
mem0.msMem.cached++;
|
||||
mem0.msMem.next--;
|
||||
}
|
||||
else
|
||||
pMem = new Mem();
|
||||
return pMem;
|
||||
}
|
||||
static BtCursor sqlite3MemMallocBtCursor( BtCursor dummy )
|
||||
{
|
||||
BtCursor pBtCursor;
|
||||
mem0.msBtCursor.alloc++;
|
||||
if ( mem0.msBtCursor.next > 0 && mem0.aBtCursor[mem0.msBtCursor.next] != null )
|
||||
{
|
||||
pBtCursor = mem0.aBtCursor[mem0.msBtCursor.next];
|
||||
Debug.Assert( pBtCursor.pNext == null && pBtCursor.pPrev == null && pBtCursor.wrFlag == 0 );
|
||||
mem0.aBtCursor[mem0.msBtCursor.next] = null;
|
||||
mem0.msBtCursor.cached++;
|
||||
mem0.msBtCursor.next--;
|
||||
}
|
||||
else
|
||||
pBtCursor = new BtCursor();
|
||||
return pBtCursor;
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
** Free memory.
|
||||
*/
|
||||
// -- overloads ---------------------------------------
|
||||
static void sqlite3MemFree<T>( ref T x ) where T : class
|
||||
{
|
||||
x = null;
|
||||
}
|
||||
static void sqlite3MemFree( ref string x )
|
||||
{
|
||||
x = null;
|
||||
}
|
||||
//
|
||||
|
||||
/*
|
||||
** Like free() but works for allocations obtained from sqlite3MemMalloc()
|
||||
** or sqlite3MemRealloc().
|
||||
**
|
||||
** For this low-level routine, we already know that pPrior!=0 since
|
||||
** cases where pPrior==0 will have been intecepted and dealt with
|
||||
** by higher-level routines.
|
||||
*/
|
||||
#if SQLITE_POOL_MEM
|
||||
#if !NO_TRUE
|
||||
static void sqlite3MemFreeInt( ref int[] pPrior )
|
||||
{
|
||||
pPrior = null;
|
||||
}
|
||||
#else
|
||||
static void sqlite3MemFree(ref byte[] pPrior)
|
||||
{
|
||||
if (pPrior == null) return;
|
||||
if (pPrior.Length > mem0.aByteSize[0])
|
||||
{
|
||||
int savej = -1;
|
||||
int Smallest = int.MaxValue;
|
||||
int i;
|
||||
for (i = 0; i < mem0.aByteSize.Length - 1; i++) if (pPrior.Length <= mem0.aByteSize[i]) break;
|
||||
#if DEBUG
|
||||
for (int j = 0; j < mem0.aByte[i].Length; j++) if (mem0.aByte[i][j] != null && mem0.aByte[i][j] == pPrior) Debugger.Break();
|
||||
#endif
|
||||
mem0.mf_Byte++;
|
||||
for (int j = 0; j <= mem0.aByte_used[i]; j++)
|
||||
{
|
||||
if (mem0.aByte[i][j] == null)
|
||||
{
|
||||
mem0.aByte[i][j] = pPrior;
|
||||
pPrior = null;
|
||||
return;
|
||||
}
|
||||
if (mem0.aByte[i][j].Length < Smallest)
|
||||
{
|
||||
savej = j;
|
||||
Smallest = mem0.aByte[i][j].Length;
|
||||
}
|
||||
}
|
||||
|
||||
if (mem0.aByte_used[i] < mem0.aByte[i].Length - 1) mem0.aByte[i][++mem0.aByte_used[i]] = pPrior;
|
||||
else if (savej >= 0) mem0.aByte[i][savej] = pPrior;
|
||||
}
|
||||
pPrior = null;
|
||||
return;
|
||||
}
|
||||
static void sqlite3MemFreeInt(ref int[] pPrior)
|
||||
{
|
||||
if (pPrior == null) return;
|
||||
if (pPrior.Length >= 10)
|
||||
{
|
||||
int savei = -1;
|
||||
int Smallest = int.MaxValue;
|
||||
#if DEBUG
|
||||
for (int i = 0; i < mem0.aInt.Length; i++) if (mem0.aInt[i] != null && mem0.aInt[i] == pPrior) Debugger.Break();
|
||||
#endif
|
||||
mem0.mf_Int++;
|
||||
for (int i = 0; i < mem0.aInt.Length; i++)
|
||||
{
|
||||
if (mem0.aInt[i] == null)
|
||||
{
|
||||
mem0.aInt[i] = pPrior;
|
||||
pPrior = null;
|
||||
return;
|
||||
}
|
||||
if (mem0.aInt[i].Length < Smallest)
|
||||
{
|
||||
savei = i;
|
||||
Smallest = mem0.aInt[i].Length;
|
||||
}
|
||||
}
|
||||
if (savei >= 0) mem0.aInt[savei] = pPrior;
|
||||
}
|
||||
pPrior = null;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
static void sqlite3MemFreeMem( ref Mem pPrior )
|
||||
{
|
||||
if ( pPrior == null )
|
||||
return;
|
||||
#if FALSE && DEBUG
|
||||
for (int i = mem0.msMem.next - 1; i >= 0; i--) if (mem0.aMem[i] != null && mem0.aMem[i] == pPrior) Debugger.Break();
|
||||
#endif
|
||||
mem0.msMem.dealloc++;
|
||||
if ( mem0.msMem.next < mem0.aMem.Length - 1 )
|
||||
{
|
||||
pPrior.db = null;
|
||||
pPrior._SumCtx = null;
|
||||
pPrior._MD5Context = null;
|
||||
pPrior._SubProgram = null;
|
||||
pPrior.flags = MEM_Null;
|
||||
pPrior.r = 0;
|
||||
pPrior.u.i = 0;
|
||||
pPrior.n = 0;
|
||||
if ( pPrior.zBLOB != null )
|
||||
sqlite3MemFree( ref pPrior.zBLOB );
|
||||
mem0.aMem[++mem0.msMem.next] = pPrior;
|
||||
if ( mem0.msMem.next > mem0.msMem.max )
|
||||
mem0.msMem.max = mem0.msMem.next;
|
||||
|
||||
}
|
||||
//Array.Resize( ref mem0.aMem, (int)(mem0.hw_Mem * 1.5 + 1 ));
|
||||
//mem0.aMem[mem0.hw_Mem] = pPrior;
|
||||
//mem0.hw_Mem = mem0.aMem.Length;
|
||||
pPrior = null;
|
||||
return;
|
||||
}
|
||||
static void sqlite3MemFreeBtCursor( ref BtCursor pPrior )
|
||||
{
|
||||
if ( pPrior == null )
|
||||
return;
|
||||
#if FALSE && DEBUG
|
||||
for ( int i = mem0.msBtCursor.next - 1; i >= 0; i-- ) if ( mem0.aBtCursor[i] != null && mem0.aBtCursor[i] == pPrior ) Debugger.Break();
|
||||
#endif
|
||||
mem0.msBtCursor.dealloc++;
|
||||
if ( mem0.msBtCursor.next < mem0.aBtCursor.Length - 1 )
|
||||
{
|
||||
mem0.aBtCursor[++mem0.msBtCursor.next] = pPrior;
|
||||
if ( mem0.msBtCursor.next > mem0.msBtCursor.max )
|
||||
mem0.msBtCursor.max = mem0.msBtCursor.next;
|
||||
}
|
||||
//Array.Resize( ref mem0.aBtCursor, (int)(mem0.hw_BtCursor * 1.5 + 1 ));
|
||||
//mem0.aBtCursor[mem0.hw_BtCursor] = pPrior;
|
||||
//mem0.hw_BtCursor = mem0.aBtCursor.Length;
|
||||
pPrior = null;
|
||||
return;
|
||||
}
|
||||
/*
|
||||
** Like realloc(). Resize an allocation previously obtained from
|
||||
** sqlite3MemMalloc().
|
||||
**
|
||||
** For this low-level interface, we know that pPrior!=0. Cases where
|
||||
** pPrior==0 while have been intercepted by higher-level routine and
|
||||
** redirected to xMalloc. Similarly, we know that nByte>0 becauses
|
||||
** cases where nByte<=0 will have been intercepted by higher-level
|
||||
** routines and redirected to xFree.
|
||||
*/
|
||||
static byte[] sqlite3MemRealloc( ref byte[] pPrior, int nByte )
|
||||
{
|
||||
// sqlite3_int64 p = (sqlite3_int64*)pPrior;
|
||||
// Debug.Assert(pPrior!=0 && nByte>0 );
|
||||
// nByte = ROUND8( nByte );
|
||||
// p = (sqlite3_int64*)pPrior;
|
||||
// p--;
|
||||
// p = realloc(p, nByte+8 );
|
||||
// if( p ){
|
||||
// p[0] = nByte;
|
||||
// p++;
|
||||
// }
|
||||
// return (void*)p;
|
||||
Array.Resize( ref pPrior, nByte );
|
||||
return pPrior;
|
||||
}
|
||||
#else
|
||||
/*
|
||||
** No-op versions of all memory allocation routines
|
||||
*/
|
||||
static byte[] sqlite3MemMalloc(int nByte) { return new byte[nByte]; }
|
||||
static int[] sqlite3MemMallocInt(int nInt) { return new int[nInt]; }
|
||||
static Mem sqlite3MemMallocMem( Mem pMem) { return new Mem(); }
|
||||
static void sqlite3MemFree( ref byte[] pPrior ) { pPrior = null; }
|
||||
static void sqlite3MemFreeInt( ref int[] pPrior ) { pPrior = null; }
|
||||
static void sqlite3MemFreeMem(ref Mem pPrior) { pPrior = null; }
|
||||
static int sqlite3MemInit() { return SQLITE_OK; }
|
||||
static void sqlite3MemShutdown() { }
|
||||
static BtCursor sqlite3MemMallocBtCursor( BtCursor dummy ){return new BtCursor();}
|
||||
static void sqlite3MemFreeBtCursor(ref BtCursor pPrior) { pPrior = null; }
|
||||
#endif
|
||||
|
||||
static byte[] sqlite3MemRealloc( byte[] pPrior, int nByte )
|
||||
{
|
||||
Array.Resize( ref pPrior, nByte );
|
||||
return pPrior;
|
||||
}
|
||||
|
||||
/*
|
||||
** Report the allocated size of a prior return from xMalloc()
|
||||
** or xRealloc().
|
||||
*/
|
||||
static int sqlite3MemSize( byte[] pPrior )
|
||||
{
|
||||
// sqlite3_int64 p;
|
||||
// if( pPrior==0 ) return 0;
|
||||
// p = (sqlite3_int64*)pPrior;
|
||||
// p--;
|
||||
// return p[0];
|
||||
return pPrior == null ? 0 : (int)pPrior.Length;
|
||||
}
|
||||
|
||||
/*
|
||||
** Round up a request size to the next valid allocation size.
|
||||
*/
|
||||
static int sqlite3MemRoundup( int n )
|
||||
{
|
||||
return n;// ROUND8( n );
|
||||
}
|
||||
|
||||
/*
|
||||
** Initialize this module.
|
||||
*/
|
||||
static int sqlite3MemInit( object NotUsed )
|
||||
{
|
||||
UNUSED_PARAMETER( NotUsed );
|
||||
if ( !sqlite3GlobalConfig.bMemstat )
|
||||
{
|
||||
/* If memory status is enabled, then the malloc.c wrapper will already
|
||||
** hold the STATIC_MEM mutex when the routines here are invoked. */
|
||||
mem0.mutex = sqlite3MutexAlloc( SQLITE_MUTEX_STATIC_MEM );
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Deinitialize this module.
|
||||
*/
|
||||
static void sqlite3MemShutdown( object NotUsed )
|
||||
{
|
||||
|
||||
UNUSED_PARAMETER( NotUsed );
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine is the only routine in this file with external linkage.
|
||||
**
|
||||
** Populate the low-level memory allocation function pointers in
|
||||
** sqlite3GlobalConfig.m with pointers to the routines in this file.
|
||||
*/
|
||||
static void sqlite3MemSetDefault()
|
||||
{
|
||||
sqlite3_mem_methods defaultMethods = new sqlite3_mem_methods(
|
||||
sqlite3MemMalloc,
|
||||
sqlite3MemMallocInt,
|
||||
sqlite3MemMallocMem,
|
||||
sqlite3MemFree,
|
||||
sqlite3MemFreeInt,
|
||||
sqlite3MemFreeMem,
|
||||
sqlite3MemRealloc,
|
||||
sqlite3MemSize,
|
||||
sqlite3MemRoundup,
|
||||
(dxMemInit)sqlite3MemInit,
|
||||
(dxMemShutdown)sqlite3MemShutdown,
|
||||
0
|
||||
);
|
||||
sqlite3_config( SQLITE_CONFIG_MALLOC, defaultMethods );
|
||||
}
|
||||
|
||||
static void sqlite3DbFree( sqlite3 db, ref int[] pPrior )
|
||||
{
|
||||
if ( pPrior != null )
|
||||
sqlite3MemFreeInt( ref pPrior );
|
||||
}
|
||||
static void sqlite3DbFree( sqlite3 db, ref Mem pPrior )
|
||||
{
|
||||
if ( pPrior != null )
|
||||
sqlite3MemFreeMem( ref pPrior );
|
||||
}
|
||||
static void sqlite3DbFree( sqlite3 db, ref Mem[] pPrior )
|
||||
{
|
||||
if ( pPrior != null )
|
||||
for ( int i = 0; i < pPrior.Length; i++ )
|
||||
sqlite3MemFreeMem( ref pPrior[i] );
|
||||
}
|
||||
static void sqlite3DbFree<T>( sqlite3 db, ref T pT ) where T : class
|
||||
{
|
||||
}
|
||||
static void sqlite3DbFree( sqlite3 db, ref string pString )
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user