Sqlite-Plugin, DB-Copy plugin added und integriert
This commit is contained in:
@@ -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 )
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,322 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
using Bitmask = System.UInt64;
|
||||
using u8 = System.Byte;
|
||||
using u16 = System.UInt16;
|
||||
using u32 = System.UInt32;
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
using sqlite3_int64 = System.Int64;
|
||||
using MemJournal = Sqlite3.sqlite3_file;
|
||||
|
||||
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 contains code use to implement an in-memory rollback journal.
|
||||
** The in-memory rollback journal is used to journal transactions for
|
||||
** ":memory:" databases and when the journal_mode=MEMORY pragma is used.
|
||||
*************************************************************************
|
||||
** 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-12-07 20:14:09 a586a4deeb25330037a49df295b36aaf624d0f45
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
|
||||
//#include "sqliteInt.h"
|
||||
|
||||
/* Forward references to internal structures */
|
||||
//typedef struct MemJournal MemJournal;
|
||||
//typedef struct FilePoint FilePoint;
|
||||
//typedef struct FileChunk FileChunk;
|
||||
|
||||
/* Space to hold the rollback journal is allocated in increments of
|
||||
** this many bytes.
|
||||
**
|
||||
** The size chosen is a little less than a power of two. That way,
|
||||
** the FileChunk object will have a size that almost exactly fills
|
||||
** a power-of-two allocation. This mimimizes wasted space in power-of-two
|
||||
** memory allocators.
|
||||
*/
|
||||
//#define JOURNAL_CHUNKSIZE ((int)(1024-sizeof(FileChunk*)))
|
||||
const int JOURNAL_CHUNKSIZE = 4096;
|
||||
|
||||
/* Macro to find the minimum of two numeric values.
|
||||
*/
|
||||
//#if !MIN
|
||||
//# define MIN(x,y) ((x)<(y)?(x):(y))
|
||||
//#endif
|
||||
static int MIN( int x, int y )
|
||||
{
|
||||
return ( x < y ) ? x : y;
|
||||
}
|
||||
static int MIN( int x, u32 y )
|
||||
{
|
||||
return ( x < y ) ? x : (int)y;
|
||||
}
|
||||
|
||||
/*
|
||||
** The rollback journal is composed of a linked list of these structures.
|
||||
*/
|
||||
public class FileChunk
|
||||
{
|
||||
public FileChunk pNext; /* Next chunk in the journal */
|
||||
public byte[] zChunk = new byte[JOURNAL_CHUNKSIZE]; /* Content of this chunk */
|
||||
};
|
||||
|
||||
/*
|
||||
** An instance of this object serves as a cursor into the rollback journal.
|
||||
** The cursor can be either for reading or writing.
|
||||
*/
|
||||
public class FilePoint
|
||||
{
|
||||
public long iOffset; /* Offset from the beginning of the file */
|
||||
public FileChunk pChunk; /* Specific chunk into which cursor points */
|
||||
};
|
||||
|
||||
/*
|
||||
** This subclass is a subclass of sqlite3_file. Each open memory-journal
|
||||
** is an instance of this class.
|
||||
*/
|
||||
public partial class sqlite3_file
|
||||
{
|
||||
//public sqlite3_io_methods pMethods; /* Parent class. MUST BE FIRST */
|
||||
public FileChunk pFirst; /* Head of in-memory chunk-list */
|
||||
public FilePoint endpoint; /* Pointer to the end of the file */
|
||||
public FilePoint readpoint; /* Pointer to the end of the last xRead() */
|
||||
};
|
||||
|
||||
/*
|
||||
** Read data from the in-memory journal file. This is the implementation
|
||||
** of the sqlite3_vfs.xRead method.
|
||||
*/
|
||||
static int memjrnlRead(
|
||||
sqlite3_file pJfd, /* The journal file from which to read */
|
||||
byte[] zBuf, /* Put the results here */
|
||||
int iAmt, /* Number of bytes to read */
|
||||
sqlite3_int64 iOfst /* Begin reading at this offset */
|
||||
)
|
||||
{
|
||||
MemJournal p = (MemJournal)pJfd;
|
||||
byte[] zOut = zBuf;
|
||||
int nRead = iAmt;
|
||||
int iChunkOffset;
|
||||
FileChunk pChunk;
|
||||
|
||||
/* SQLite never tries to read past the end of a rollback journal file */
|
||||
Debug.Assert( iOfst + iAmt <= p.endpoint.iOffset );
|
||||
|
||||
if ( p.readpoint.iOffset != iOfst || iOfst == 0 )
|
||||
{
|
||||
int iOff = 0;
|
||||
for ( pChunk = p.pFirst;
|
||||
ALWAYS( pChunk != null ) && ( iOff + JOURNAL_CHUNKSIZE ) <= iOfst;
|
||||
pChunk = pChunk.pNext
|
||||
)
|
||||
{
|
||||
iOff += JOURNAL_CHUNKSIZE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pChunk = p.readpoint.pChunk;
|
||||
}
|
||||
|
||||
iChunkOffset = (int)( iOfst % JOURNAL_CHUNKSIZE );
|
||||
int izOut = 0;
|
||||
do
|
||||
{
|
||||
int iSpace = JOURNAL_CHUNKSIZE - iChunkOffset;
|
||||
int nCopy = MIN( nRead, ( JOURNAL_CHUNKSIZE - iChunkOffset ) );
|
||||
Buffer.BlockCopy( pChunk.zChunk, iChunkOffset, zOut, izOut, nCopy ); //memcpy( zOut, pChunk.zChunk[iChunkOffset], nCopy );
|
||||
izOut += nCopy;// zOut += nCopy;
|
||||
nRead -= iSpace;
|
||||
iChunkOffset = 0;
|
||||
} while ( nRead >= 0 && ( pChunk = pChunk.pNext ) != null && nRead > 0 );
|
||||
p.readpoint.iOffset = (int)( iOfst + iAmt );
|
||||
p.readpoint.pChunk = pChunk;
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Write data to the file.
|
||||
*/
|
||||
static int memjrnlWrite(
|
||||
sqlite3_file pJfd, /* The journal file into which to write */
|
||||
byte[] zBuf, /* Take data to be written from here */
|
||||
int iAmt, /* Number of bytes to write */
|
||||
sqlite3_int64 iOfst /* Begin writing at this offset into the file */
|
||||
)
|
||||
{
|
||||
MemJournal p = (MemJournal)pJfd;
|
||||
int nWrite = iAmt;
|
||||
byte[] zWrite = zBuf;
|
||||
int izWrite = 0;
|
||||
|
||||
/* An in-memory journal file should only ever be appended to. Random
|
||||
** access writes are not required by sqlite.
|
||||
*/
|
||||
Debug.Assert( iOfst == p.endpoint.iOffset );
|
||||
UNUSED_PARAMETER( iOfst );
|
||||
|
||||
while ( nWrite > 0 )
|
||||
{
|
||||
FileChunk pChunk = p.endpoint.pChunk;
|
||||
int iChunkOffset = (int)( p.endpoint.iOffset % JOURNAL_CHUNKSIZE );
|
||||
int iSpace = MIN( nWrite, JOURNAL_CHUNKSIZE - iChunkOffset );
|
||||
|
||||
if ( iChunkOffset == 0 )
|
||||
{
|
||||
/* New chunk is required to extend the file. */
|
||||
FileChunk pNew = new FileChunk();// sqlite3_malloc( sizeof( FileChunk ) );
|
||||
if ( null == pNew )
|
||||
{
|
||||
return SQLITE_IOERR_NOMEM;
|
||||
}
|
||||
pNew.pNext = null;
|
||||
if ( pChunk != null )
|
||||
{
|
||||
Debug.Assert( p.pFirst != null );
|
||||
pChunk.pNext = pNew;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert( null == p.pFirst );
|
||||
p.pFirst = pNew;
|
||||
}
|
||||
p.endpoint.pChunk = pNew;
|
||||
}
|
||||
|
||||
Buffer.BlockCopy( zWrite, izWrite, p.endpoint.pChunk.zChunk, iChunkOffset, iSpace ); //memcpy( &p.endpoint.pChunk.zChunk[iChunkOffset], zWrite, iSpace );
|
||||
izWrite += iSpace;//zWrite += iSpace;
|
||||
nWrite -= iSpace;
|
||||
p.endpoint.iOffset += iSpace;
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Truncate the file.
|
||||
*/
|
||||
static int memjrnlTruncate( sqlite3_file pJfd, sqlite3_int64 size )
|
||||
{
|
||||
MemJournal p = (MemJournal)pJfd;
|
||||
FileChunk pChunk;
|
||||
Debug.Assert( size == 0 );
|
||||
UNUSED_PARAMETER( size );
|
||||
pChunk = p.pFirst;
|
||||
while ( pChunk != null )
|
||||
{
|
||||
FileChunk pTmp = pChunk;
|
||||
pChunk = pChunk.pNext;
|
||||
//sqlite3_free( ref pTmp );
|
||||
}
|
||||
sqlite3MemJournalOpen( pJfd );
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close the file.
|
||||
*/
|
||||
static int memjrnlClose( MemJournal pJfd )
|
||||
{
|
||||
memjrnlTruncate( pJfd, 0 );
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Sync the file.
|
||||
**
|
||||
** Syncing an in-memory journal is a no-op. And, in fact, this routine
|
||||
** is never called in a working implementation. This implementation
|
||||
** exists purely as a contingency, in case some malfunction in some other
|
||||
** part of SQLite causes Sync to be called by mistake.
|
||||
*/
|
||||
static int memjrnlSync( sqlite3_file NotUsed, int NotUsed2 )
|
||||
{
|
||||
UNUSED_PARAMETER2( NotUsed, NotUsed2 );
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Query the size of the file in bytes.
|
||||
*/
|
||||
static int memjrnlFileSize( sqlite3_file pJfd, ref long pSize )
|
||||
{
|
||||
MemJournal p = (MemJournal)pJfd;
|
||||
pSize = p.endpoint.iOffset;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Table of methods for MemJournal sqlite3_file object.
|
||||
*/
|
||||
static sqlite3_io_methods MemJournalMethods = new sqlite3_io_methods(
|
||||
1, /* iVersion */
|
||||
(dxClose)memjrnlClose, /* xClose */
|
||||
(dxRead)memjrnlRead, /* xRead */
|
||||
(dxWrite)memjrnlWrite, /* xWrite */
|
||||
(dxTruncate)memjrnlTruncate, /* xTruncate */
|
||||
(dxSync)memjrnlSync, /* xSync */
|
||||
(dxFileSize)memjrnlFileSize, /* xFileSize */
|
||||
null, /* xLock */
|
||||
null, /* xUnlock */
|
||||
null, /* xCheckReservedLock */
|
||||
null, /* xFileControl */
|
||||
null, /* xSectorSize */
|
||||
null, /* xDeviceCharacteristics */
|
||||
null, /* xShmMap */
|
||||
null, /* xShmLock */
|
||||
null, /* xShmBarrier */
|
||||
null /* xShmUnlock */
|
||||
);
|
||||
|
||||
/*
|
||||
** Open a journal file.
|
||||
*/
|
||||
static void sqlite3MemJournalOpen( sqlite3_file pJfd )
|
||||
{
|
||||
MemJournal p = (MemJournal)pJfd;
|
||||
//memset( p, 0, sqlite3MemJournalSize() );
|
||||
p.pFirst = null;
|
||||
p.endpoint = new FilePoint();
|
||||
p.readpoint = new FilePoint();
|
||||
p.pMethods = MemJournalMethods;//(sqlite3_io_methods*)&MemJournalMethods;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if the file-handle passed as an argument is
|
||||
** an in-memory journal
|
||||
*/
|
||||
static bool sqlite3IsMemJournal( sqlite3_file pJfd )
|
||||
{
|
||||
return pJfd.pMethods == MemJournalMethods;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the number of bytes required to store a MemJournal file descriptor.
|
||||
*/
|
||||
static int sqlite3MemJournalSize()
|
||||
{
|
||||
return 3096; // sizeof( MemJournal );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
#define SQLITE_OS_WIN
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/*
|
||||
** 2007 August 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 common header for all mutex implementations.
|
||||
** The sqliteInt.h header #includes this file so that it is available
|
||||
** to all source files. We break it out in an effort to keep the code
|
||||
** better organized.
|
||||
**
|
||||
** NOTE: source files should *not* #include this header file directly.
|
||||
** Source files should #include the sqliteInt.h file and let that file
|
||||
** include this one indirectly.
|
||||
*************************************************************************
|
||||
** 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-12-07 20:14:09 a586a4deeb25330037a49df295b36aaf624d0f45
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
** Figure out what version of the code to use. The choices are
|
||||
**
|
||||
** SQLITE_MUTEX_OMIT No mutex logic. Not even stubs. The
|
||||
** mutexes implemention cannot be overridden
|
||||
** at start-time.
|
||||
**
|
||||
** SQLITE_MUTEX_NOOP For single-threaded applications. No
|
||||
** mutual exclusion is provided. But this
|
||||
** implementation can be overridden at
|
||||
** start-time.
|
||||
**
|
||||
** SQLITE_MUTEX_PTHREADS For multi-threaded applications on Unix.
|
||||
**
|
||||
** SQLITE_MUTEX_W32 For multi-threaded applications on Win32.
|
||||
**
|
||||
** SQLITE_MUTEX_OS2 For multi-threaded applications on OS/2.
|
||||
*/
|
||||
|
||||
//#if !SQLITE_THREADSAFE
|
||||
//# define SQLITE_MUTEX_OMIT
|
||||
//#endif
|
||||
//#if SQLITE_THREADSAFE && !defined(SQLITE_MUTEX_NOOP)
|
||||
//# if SQLITE_OS_UNIX
|
||||
//# define SQLITE_MUTEX_PTHREADS
|
||||
//# elif SQLITE_OS_WIN
|
||||
//# define SQLITE_MUTEX_W32
|
||||
//# elif SQLITE_OS_OS2
|
||||
//# define SQLITE_MUTEX_OS2
|
||||
//# else
|
||||
//# define SQLITE_MUTEX_NOOP
|
||||
//# endif
|
||||
//#endif
|
||||
|
||||
//#if WINDOWS_PHONE && SQLITE_THREADSAFE
|
||||
//#error Cannot compile with both WINDOWS_PHONE and SQLITE_THREADSAFE
|
||||
//#endif
|
||||
|
||||
#if SQLITE_SILVERLIGHT && SQLITE_THREADSAFE
|
||||
#error Cannot compile with both SQLITE_SILVERLIGHT and SQLITE_THREADSAFE
|
||||
#endif
|
||||
|
||||
#if SQLITE_THREADSAFE && SQLITE_MUTEX_NOOP
|
||||
#error Cannot compile with both SQLITE_THREADSAFE and SQLITE_MUTEX_NOOP
|
||||
#endif
|
||||
|
||||
#if SQLITE_THREADSAFE && SQLITE_MUTEX_OMIT
|
||||
#error Cannot compile with both SQLITE_THREADSAFE and SQLITE_MUTEX_OMIT
|
||||
#endif
|
||||
|
||||
#if SQLITE_MUTEX_OMIT && SQLITE_MUTEX_NOOP
|
||||
#error Cannot compile with both SQLITE_MUTEX_OMIT and SQLITE_MUTEX_NOOP
|
||||
#endif
|
||||
|
||||
#if SQLITE_MUTEX_OMIT && SQLITE_MUTEX_W32
|
||||
#error Cannot compile with both SQLITE_MUTEX_OMIT and SQLITE_MUTEX_W32
|
||||
#endif
|
||||
|
||||
#if SQLITE_MUTEX_NOOP && SQLITE_MUTEX_W32
|
||||
#error Cannot compile with both SQLITE_MUTEX_NOOP and SQLITE_MUTEX_W32
|
||||
#endif
|
||||
|
||||
#if !NO_SQLITE_MUTEX_OMIT
|
||||
/*
|
||||
** If this is a no-op implementation, implement everything as macros.
|
||||
*/
|
||||
public class sqlite3_mutex
|
||||
{
|
||||
}
|
||||
static sqlite3_mutex mutex = null; //sqlite3_mutex sqlite3_mutex;
|
||||
static sqlite3_mutex sqlite3MutexAlloc( int iType )
|
||||
{
|
||||
return new sqlite3_mutex();
|
||||
}//#define sqlite3MutexAlloc(X) ((sqlite3_mutex*)8)
|
||||
static sqlite3_mutex sqlite3_mutex_alloc( int iType )
|
||||
{
|
||||
return new sqlite3_mutex();
|
||||
}//#define sqlite3_mutex_alloc(X) ((sqlite3_mutex*)8)
|
||||
static void sqlite3_mutex_free( sqlite3_mutex m )
|
||||
{
|
||||
} //#define sqlite3_mutex_free(X)
|
||||
static void sqlite3_mutex_enter( sqlite3_mutex m )
|
||||
{
|
||||
} //#define sqlite3_mutex_enter(X)
|
||||
static int sqlite3_mutex_try( int iType )
|
||||
{
|
||||
return SQLITE_OK;
|
||||
} //#define sqlite3_mutex_try(X) SQLITE_OK
|
||||
static void sqlite3_mutex_leave( sqlite3_mutex m )
|
||||
{
|
||||
} //#define sqlite3_mutex_leave(X)
|
||||
static bool sqlite3_mutex_held( sqlite3_mutex m )
|
||||
{
|
||||
return true;
|
||||
}//#define sqlite3_mutex_held(X) ((void)(X),1)
|
||||
static bool sqlite3_mutex_notheld( sqlite3_mutex m )
|
||||
{
|
||||
return true;
|
||||
} //#define sqlite3_mutex_notheld(X) ((void)(X),1)
|
||||
static int sqlite3MutexInit()
|
||||
{
|
||||
return SQLITE_OK;
|
||||
} //#define sqlite3MutexInit() SQLITE_OK
|
||||
static void sqlite3MutexEnd()
|
||||
{
|
||||
} //#define sqlite3MutexEnd()
|
||||
#endif //* defined(SQLITE_MUTEX_OMIT) */
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/*
|
||||
** 2008 October 07
|
||||
**
|
||||
** 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 C functions that implement mutexes.
|
||||
**
|
||||
** This implementation in this file does not provide any mutual
|
||||
** exclusion and is thus suitable for use only in applications
|
||||
** that use SQLite in a single thread. The routines defined
|
||||
** here are place-holders. Applications can substitute working
|
||||
** mutex routines at start-time using the
|
||||
**
|
||||
** sqlite3_config(SQLITE_CONFIG_MUTEX,...)
|
||||
**
|
||||
** interface.
|
||||
**
|
||||
** If compiled with SQLITE_DEBUG, then additional logic is inserted
|
||||
** that does error checking on mutexes to make sure they are being
|
||||
** called correctly.
|
||||
*************************************************************************
|
||||
** 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: 2009-12-07 16:39:13 1ed88e9d01e9eda5cbc622e7614277f29bcc551c
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
//#include "sqliteInt.h"
|
||||
|
||||
|
||||
#if !SQLITE_DEBUG
|
||||
/*
|
||||
** Stub routines for all mutex methods.
|
||||
**
|
||||
** This routines provide no mutual exclusion or error checking.
|
||||
*/
|
||||
static int noopMutexHeld(sqlite3_mutex p){ return 1; }
|
||||
static int noopMutexNotheld(sqlite3_mutex p){ return 1; }
|
||||
static int noopMutexInit(){ return SQLITE_OK; }
|
||||
static int noopMutexEnd(){ return SQLITE_OK; }
|
||||
static sqlite3_mutex noopMutexAlloc(int id){ return new sqlite3_mutex(); }
|
||||
static void noopMutexFree(sqlite3_mutex p){ }
|
||||
static void noopMutexEnter(sqlite3_mutex p){ }
|
||||
static int noopMutexTry(sqlite3_mutex p){ return SQLITE_OK; }
|
||||
static void noopMutexLeave(sqlite3_mutex p){ }
|
||||
|
||||
sqlite3_mutex_methods sqlite3DefaultMutex(){
|
||||
sqlite3_mutex_methods sMutex = new sqlite3_mutex_methods(
|
||||
(dxMutexInit)noopMutexInit,
|
||||
(dxMutexEnd)noopMutexEnd,
|
||||
(dxMutexAlloc)noopMutexAlloc,
|
||||
(dxMutexFree)noopMutexFree,
|
||||
(dxMutexEnter)noopMutexEnter,
|
||||
(dxMutexTry)noopMutexTry,
|
||||
(dxMutexLeave)noopMutexLeave,
|
||||
#if SQLITE_DEBUG
|
||||
(dxMutexHeld)noopMutexHeld,
|
||||
(dxMutexNotheld)noopMutexNotheld
|
||||
#else
|
||||
null,
|
||||
null
|
||||
#endif
|
||||
);
|
||||
|
||||
return sMutex;
|
||||
}
|
||||
#endif //* !SQLITE_DEBUG */
|
||||
|
||||
#if SQLITE_DEBUG && !SQLITE_MUTEX_OMIT
|
||||
/*
|
||||
** In this implementation, error checking is provided for testing
|
||||
** and debugging purposes. The mutexes still do not provide any
|
||||
** mutual exclusion.
|
||||
*/
|
||||
|
||||
/*
|
||||
** The mutex object
|
||||
*/
|
||||
public class sqlite3_debug_mutex : sqlite3_mutex
|
||||
{
|
||||
//public int id; /* The mutex type */
|
||||
public int cnt; /* Number of entries without a matching leave */
|
||||
};
|
||||
|
||||
/*
|
||||
** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are
|
||||
** intended for use inside Debug.Assert() statements.
|
||||
*/
|
||||
static bool debugMutexHeld( sqlite3_mutex pX )
|
||||
{
|
||||
sqlite3_debug_mutex p = (sqlite3_debug_mutex)pX;
|
||||
return p == null || p.cnt > 0;
|
||||
}
|
||||
static bool debugMutexNotheld( sqlite3_mutex pX )
|
||||
{
|
||||
sqlite3_debug_mutex p = (sqlite3_debug_mutex)pX;
|
||||
return p == null || p.cnt == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Initialize and deinitialize the mutex subsystem.
|
||||
*/
|
||||
static int debugMutexInit()
|
||||
{
|
||||
return SQLITE_OK;
|
||||
}
|
||||
static int debugMutexEnd()
|
||||
{
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** The sqlite3_mutex_alloc() routine allocates a new
|
||||
** mutex and returns a pointer to it. If it returns NULL
|
||||
** that means that a mutex could not be allocated.
|
||||
*/
|
||||
static sqlite3_mutex debugMutexAlloc( int id )
|
||||
{
|
||||
sqlite3_debug_mutex[] aStatic = new sqlite3_debug_mutex[6];
|
||||
sqlite3_debug_mutex pNew = null;
|
||||
switch ( id )
|
||||
{
|
||||
case SQLITE_MUTEX_FAST:
|
||||
case SQLITE_MUTEX_RECURSIVE:
|
||||
{
|
||||
pNew = new sqlite3_debug_mutex();//sqlite3Malloc(sizeof(*pNew));
|
||||
if ( pNew != null )
|
||||
{
|
||||
pNew.id = id;
|
||||
pNew.cnt = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
Debug.Assert( id - 2 >= 0 );
|
||||
Debug.Assert( id - 2 < aStatic.Length );//(int)(sizeof(aStatic)/sizeof(aStatic[0])) );
|
||||
pNew = aStatic[id - 2];
|
||||
pNew.id = id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return pNew;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine deallocates a previously allocated mutex.
|
||||
*/
|
||||
static void debugMutexFree( sqlite3_mutex pX )
|
||||
{
|
||||
sqlite3_debug_mutex p = (sqlite3_debug_mutex)pX;
|
||||
Debug.Assert( p.cnt == 0 );
|
||||
Debug.Assert( p.id == SQLITE_MUTEX_FAST || p.id == SQLITE_MUTEX_RECURSIVE );
|
||||
//sqlite3_free(ref p);
|
||||
}
|
||||
|
||||
/*
|
||||
** The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt
|
||||
** to enter a mutex. If another thread is already within the mutex,
|
||||
** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return
|
||||
** SQLITE_BUSY. The sqlite3_mutex_try() interface returns SQLITE_OK
|
||||
** upon successful entry. Mutexes created using SQLITE_MUTEX_RECURSIVE can
|
||||
** be entered multiple times by the same thread. In such cases the,
|
||||
** mutex must be exited an equal number of times before another thread
|
||||
** can enter. If the same thread tries to enter any other kind of mutex
|
||||
** more than once, the behavior is undefined.
|
||||
*/
|
||||
static void debugMutexEnter( sqlite3_mutex pX )
|
||||
{
|
||||
sqlite3_debug_mutex p = (sqlite3_debug_mutex)pX;
|
||||
Debug.Assert( p.id == SQLITE_MUTEX_RECURSIVE || debugMutexNotheld( p ) );
|
||||
p.cnt++;
|
||||
}
|
||||
|
||||
static int debugMutexTry( sqlite3_mutex pX )
|
||||
{
|
||||
sqlite3_debug_mutex p = (sqlite3_debug_mutex)pX;
|
||||
Debug.Assert( p.id == SQLITE_MUTEX_RECURSIVE || debugMutexNotheld( p ) );
|
||||
p.cnt++;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** The sqlite3_mutex_leave() routine exits a mutex that was
|
||||
** previously entered by the same thread. The behavior
|
||||
** is undefined if the mutex is not currently entered or
|
||||
** is not currently allocated. SQLite will never do either.
|
||||
*/
|
||||
static void debugMutexLeave( sqlite3_mutex pX )
|
||||
{
|
||||
sqlite3_debug_mutex p = (sqlite3_debug_mutex)pX;
|
||||
Debug.Assert( debugMutexHeld( p ) );
|
||||
p.cnt--;
|
||||
Debug.Assert( p.id == SQLITE_MUTEX_RECURSIVE || debugMutexNotheld( p ) );
|
||||
}
|
||||
|
||||
static sqlite3_mutex_methods sqlite3NoopMutex()
|
||||
{
|
||||
sqlite3_mutex_methods sMutex = new sqlite3_mutex_methods(
|
||||
(dxMutexInit)debugMutexInit,
|
||||
(dxMutexEnd)debugMutexEnd,
|
||||
(dxMutexAlloc)debugMutexAlloc,
|
||||
(dxMutexFree)debugMutexFree,
|
||||
(dxMutexEnter)debugMutexEnter,
|
||||
(dxMutexTry)debugMutexTry,
|
||||
(dxMutexLeave)debugMutexLeave,
|
||||
|
||||
(dxMutexHeld)debugMutexHeld,
|
||||
(dxMutexNotheld)debugMutexNotheld
|
||||
);
|
||||
|
||||
return sMutex;
|
||||
}
|
||||
#endif //* SQLITE_DEBUG */
|
||||
|
||||
/*
|
||||
** If compiled with SQLITE_MUTEX_NOOP, then the no-op mutex implementation
|
||||
** is used regardless of the run-time threadsafety setting.
|
||||
*/
|
||||
#if SQLITE_MUTEX_NOOP
|
||||
sqlite3_mutex_methods const sqlite3DefaultMutex(void){
|
||||
return sqlite3NoopMutex();
|
||||
}
|
||||
#endif //* SQLITE_MUTEX_NOOP */
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/*
|
||||
** 2009 March 3
|
||||
**
|
||||
** 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_unlock_notify()
|
||||
** API method and its associated functionality.
|
||||
*************************************************************************
|
||||
** 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: 2009-12-07 16:39:13 1ed88e9d01e9eda5cbc622e7614277f29bcc551c
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
//#include "sqliteInt.h"
|
||||
//#include "btreeInt.h"
|
||||
|
||||
/* Omit this entire file if SQLITE_ENABLE_UNLOCK_NOTIFY is not defined. */
|
||||
#if SQLITE_ENABLE_UNLOCK_NOTIFY
|
||||
|
||||
/*
|
||||
** Public interfaces:
|
||||
**
|
||||
** sqlite3ConnectionBlocked()
|
||||
** sqlite3ConnectionUnlocked()
|
||||
** sqlite3ConnectionClosed()
|
||||
** sqlite3_unlock_notify()
|
||||
*/
|
||||
|
||||
//#define assertMutexHeld() \
|
||||
assert( sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)) )
|
||||
|
||||
/*
|
||||
** Head of a linked list of all sqlite3 objects created by this process
|
||||
** for which either sqlite3.pBlockingConnection or sqlite3.pUnlockConnection
|
||||
** is not NULL. This variable may only accessed while the STATIC_MASTER
|
||||
** mutex is held.
|
||||
*/
|
||||
static sqlite3 *SQLITE_WSD sqlite3BlockedList = 0;
|
||||
|
||||
#if !NDEBUG
|
||||
/*
|
||||
** This function is a complex assert() that verifies the following
|
||||
** properties of the blocked connections list:
|
||||
**
|
||||
** 1) Each entry in the list has a non-NULL value for either
|
||||
** pUnlockConnection or pBlockingConnection, or both.
|
||||
**
|
||||
** 2) All entries in the list that share a common value for
|
||||
** xUnlockNotify are grouped together.
|
||||
**
|
||||
** 3) If the argument db is not NULL, then none of the entries in the
|
||||
** blocked connections list have pUnlockConnection or pBlockingConnection
|
||||
** set to db. This is used when closing connection db.
|
||||
*/
|
||||
static void checkListProperties(sqlite3 *db){
|
||||
sqlite3 *p;
|
||||
for(p=sqlite3BlockedList; p; p=p->pNextBlocked){
|
||||
int seen = 0;
|
||||
sqlite3 *p2;
|
||||
|
||||
/* Verify property (1) */
|
||||
assert( p->pUnlockConnection || p->pBlockingConnection );
|
||||
|
||||
/* Verify property (2) */
|
||||
for(p2=sqlite3BlockedList; p2!=p; p2=p2->pNextBlocked){
|
||||
if( p2->xUnlockNotify==p->xUnlockNotify ) seen = 1;
|
||||
assert( p2->xUnlockNotify==p->xUnlockNotify || !seen );
|
||||
assert( db==0 || p->pUnlockConnection!=db );
|
||||
assert( db==0 || p->pBlockingConnection!=db );
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
//# define checkListProperties(x)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Remove connection db from the blocked connections list. If connection
|
||||
** db is not currently a part of the list, this function is a no-op.
|
||||
*/
|
||||
static void removeFromBlockedList(sqlite3 *db){
|
||||
sqlite3 **pp;
|
||||
assertMutexHeld();
|
||||
for(pp=&sqlite3BlockedList; *pp; pp = &(*pp)->pNextBlocked){
|
||||
if( *pp==db ){
|
||||
*pp = (*pp)->pNextBlocked;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Add connection db to the blocked connections list. It is assumed
|
||||
** that it is not already a part of the list.
|
||||
*/
|
||||
static void addToBlockedList(sqlite3 *db){
|
||||
sqlite3 **pp;
|
||||
assertMutexHeld();
|
||||
for(
|
||||
pp=&sqlite3BlockedList;
|
||||
*pp && (*pp)->xUnlockNotify!=db->xUnlockNotify;
|
||||
pp=&(*pp)->pNextBlocked
|
||||
);
|
||||
db->pNextBlocked = *pp;
|
||||
*pp = db;
|
||||
}
|
||||
|
||||
/*
|
||||
** Obtain the STATIC_MASTER mutex.
|
||||
*/
|
||||
static void enterMutex(){
|
||||
sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
|
||||
checkListProperties(0);
|
||||
}
|
||||
|
||||
/*
|
||||
** Release the STATIC_MASTER mutex.
|
||||
*/
|
||||
static void leaveMutex(){
|
||||
assertMutexHeld();
|
||||
checkListProperties(0);
|
||||
sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
|
||||
}
|
||||
|
||||
/*
|
||||
** Register an unlock-notify callback.
|
||||
**
|
||||
** This is called after connection "db" has attempted some operation
|
||||
** but has received an SQLITE_LOCKED error because another connection
|
||||
** (call it pOther) in the same process was busy using the same shared
|
||||
** cache. pOther is found by looking at db->pBlockingConnection.
|
||||
**
|
||||
** If there is no blocking connection, the callback is invoked immediately,
|
||||
** before this routine returns.
|
||||
**
|
||||
** If pOther is already blocked on db, then report SQLITE_LOCKED, to indicate
|
||||
** a deadlock.
|
||||
**
|
||||
** Otherwise, make arrangements to invoke xNotify when pOther drops
|
||||
** its locks.
|
||||
**
|
||||
** Each call to this routine overrides any prior callbacks registered
|
||||
** on the same "db". If xNotify==0 then any prior callbacks are immediately
|
||||
** cancelled.
|
||||
*/
|
||||
int sqlite3_unlock_notify(
|
||||
sqlite3 *db,
|
||||
void (*xNotify)(void **, int),
|
||||
void *pArg
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
enterMutex();
|
||||
|
||||
if( xNotify==0 ){
|
||||
removeFromBlockedList(db);
|
||||
db->pUnlockConnection = 0;
|
||||
db->xUnlockNotify = 0;
|
||||
db->pUnlockArg = 0;
|
||||
}else if( 0==db->pBlockingConnection ){
|
||||
/* The blocking transaction has been concluded. Or there never was a
|
||||
** blocking transaction. In either case, invoke the notify callback
|
||||
** immediately.
|
||||
*/
|
||||
xNotify(&pArg, 1);
|
||||
}else{
|
||||
sqlite3 *p;
|
||||
|
||||
for(p=db->pBlockingConnection; p && p!=db; p=p->pUnlockConnection){}
|
||||
if( p ){
|
||||
rc = SQLITE_LOCKED; /* Deadlock detected. */
|
||||
}else{
|
||||
db->pUnlockConnection = db->pBlockingConnection;
|
||||
db->xUnlockNotify = xNotify;
|
||||
db->pUnlockArg = pArg;
|
||||
removeFromBlockedList(db);
|
||||
addToBlockedList(db);
|
||||
}
|
||||
}
|
||||
|
||||
leaveMutex();
|
||||
assert( !db->mallocFailed );
|
||||
sqlite3Error(db, rc, (rc?"database is deadlocked":0));
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is called while stepping or preparing a statement
|
||||
** associated with connection db. The operation will return SQLITE_LOCKED
|
||||
** to the user because it requires a lock that will not be available
|
||||
** until connection pBlocker concludes its current transaction.
|
||||
*/
|
||||
void sqlite3ConnectionBlocked(sqlite3 *db, sqlite3 *pBlocker){
|
||||
enterMutex();
|
||||
if( db->pBlockingConnection==0 && db->pUnlockConnection==0 ){
|
||||
addToBlockedList(db);
|
||||
}
|
||||
db->pBlockingConnection = pBlocker;
|
||||
leaveMutex();
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is called when
|
||||
** the transaction opened by database db has just finished. Locks held
|
||||
** by database connection db have been released.
|
||||
**
|
||||
** This function loops through each entry in the blocked connections
|
||||
** list and does the following:
|
||||
**
|
||||
** 1) If the sqlite3.pBlockingConnection member of a list entry is
|
||||
** set to db, then set pBlockingConnection=0.
|
||||
**
|
||||
** 2) If the sqlite3.pUnlockConnection member of a list entry is
|
||||
** set to db, then invoke the configured unlock-notify callback and
|
||||
** set pUnlockConnection=0.
|
||||
**
|
||||
** 3) If the two steps above mean that pBlockingConnection==0 and
|
||||
** pUnlockConnection==0, remove the entry from the blocked connections
|
||||
** list.
|
||||
*/
|
||||
void sqlite3ConnectionUnlocked(sqlite3 *db){
|
||||
void (*xUnlockNotify)(void **, int) = 0; /* Unlock-notify cb to invoke */
|
||||
int nArg = 0; /* Number of entries in aArg[] */
|
||||
sqlite3 **pp; /* Iterator variable */
|
||||
void **aArg; /* Arguments to the unlock callback */
|
||||
void **aDyn = 0; /* Dynamically allocated space for aArg[] */
|
||||
void *aStatic[16]; /* Starter space for aArg[]. No malloc required */
|
||||
|
||||
aArg = aStatic;
|
||||
enterMutex(); /* Enter STATIC_MASTER mutex */
|
||||
|
||||
/* This loop runs once for each entry in the blocked-connections list. */
|
||||
for(pp=&sqlite3BlockedList; *pp; /* no-op */ ){
|
||||
sqlite3 *p = *pp;
|
||||
|
||||
/* Step 1. */
|
||||
if( p->pBlockingConnection==db ){
|
||||
p->pBlockingConnection = 0;
|
||||
}
|
||||
|
||||
/* Step 2. */
|
||||
if( p->pUnlockConnection==db ){
|
||||
assert( p->xUnlockNotify );
|
||||
if( p->xUnlockNotify!=xUnlockNotify && nArg!=0 ){
|
||||
xUnlockNotify(aArg, nArg);
|
||||
nArg = 0;
|
||||
}
|
||||
|
||||
sqlite3BeginBenignMalloc();
|
||||
assert( aArg==aDyn || (aDyn==0 && aArg==aStatic) );
|
||||
assert( nArg<=(int)ArraySize(aStatic) || aArg==aDyn );
|
||||
if( (!aDyn && nArg==(int)ArraySize(aStatic))
|
||||
|| (aDyn && nArg==(int)(sqlite3DbMallocSize(db, aDyn)/sizeof(void*)))
|
||||
){
|
||||
/* The aArg[] array needs to grow. */
|
||||
void **pNew = (void **)sqlite3Malloc(nArg*sizeof(void *)*2);
|
||||
if( pNew ){
|
||||
memcpy(pNew, aArg, nArg*sizeof(void *));
|
||||
//sqlite3_free(aDyn);
|
||||
aDyn = aArg = pNew;
|
||||
}else{
|
||||
/* This occurs when the array of context pointers that need to
|
||||
** be passed to the unlock-notify callback is larger than the
|
||||
** aStatic[] array allocated on the stack and the attempt to
|
||||
** allocate a larger array from the heap has failed.
|
||||
**
|
||||
** This is a difficult situation to handle. Returning an error
|
||||
** code to the caller is insufficient, as even if an error code
|
||||
** is returned the transaction on connection db will still be
|
||||
** closed and the unlock-notify callbacks on blocked connections
|
||||
** will go unissued. This might cause the application to wait
|
||||
** indefinitely for an unlock-notify callback that will never
|
||||
** arrive.
|
||||
**
|
||||
** Instead, invoke the unlock-notify callback with the context
|
||||
** array already accumulated. We can then clear the array and
|
||||
** begin accumulating any further context pointers without
|
||||
** requiring any dynamic allocation. This is sub-optimal because
|
||||
** it means that instead of one callback with a large array of
|
||||
** context pointers the application will receive two or more
|
||||
** callbacks with smaller arrays of context pointers, which will
|
||||
** reduce the applications ability to prioritize multiple
|
||||
** connections. But it is the best that can be done under the
|
||||
** circumstances.
|
||||
*/
|
||||
xUnlockNotify(aArg, nArg);
|
||||
nArg = 0;
|
||||
}
|
||||
}
|
||||
sqlite3EndBenignMalloc();
|
||||
|
||||
aArg[nArg++] = p->pUnlockArg;
|
||||
xUnlockNotify = p->xUnlockNotify;
|
||||
p->pUnlockConnection = 0;
|
||||
p->xUnlockNotify = 0;
|
||||
p->pUnlockArg = 0;
|
||||
}
|
||||
|
||||
/* Step 3. */
|
||||
if( p->pBlockingConnection==0 && p->pUnlockConnection==0 ){
|
||||
/* Remove connection p from the blocked connections list. */
|
||||
*pp = p->pNextBlocked;
|
||||
p->pNextBlocked = 0;
|
||||
}else{
|
||||
pp = &p->pNextBlocked;
|
||||
}
|
||||
}
|
||||
|
||||
if( nArg!=0 ){
|
||||
xUnlockNotify(aArg, nArg);
|
||||
}
|
||||
//sqlite3_free(aDyn);
|
||||
leaveMutex(); /* Leave STATIC_MASTER mutex */
|
||||
}
|
||||
|
||||
/*
|
||||
** This is called when the database connection passed as an argument is
|
||||
** being closed. The connection is removed from the blocked list.
|
||||
*/
|
||||
void sqlite3ConnectionClosed(sqlite3 *db){
|
||||
sqlite3ConnectionUnlocked(db);
|
||||
enterMutex();
|
||||
removeFromBlockedList(db);
|
||||
checkListProperties(db);
|
||||
leaveMutex();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
*************************************************************************
|
||||
** 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
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/* Automatically generated. Do not edit */
|
||||
/* See the mkopcodec.awk script for details. */
|
||||
#if !SQLITE_OMIT_EXPLAIN || !NDEBUG || VDBE_PROFILE || SQLITE_DEBUG
|
||||
static string sqlite3OpcodeName( int i )
|
||||
{
|
||||
string[] azName = { "?",
|
||||
/* 1 */ "Goto",
|
||||
/* 2 */ "Gosub",
|
||||
/* 3 */ "Return",
|
||||
/* 4 */ "Yield",
|
||||
/* 5 */ "HaltIfNull",
|
||||
/* 6 */ "Halt",
|
||||
/* 7 */ "Integer",
|
||||
/* 8 */ "Int64",
|
||||
/* 9 */ "String",
|
||||
/* 10 */ "Null",
|
||||
/* 11 */ "Blob",
|
||||
/* 12 */ "Variable",
|
||||
/* 13 */ "Move",
|
||||
/* 14 */ "Copy",
|
||||
/* 15 */ "SCopy",
|
||||
/* 16 */ "ResultRow",
|
||||
/* 17 */ "CollSeq",
|
||||
/* 18 */ "Function",
|
||||
/* 19 */ "Not",
|
||||
/* 20 */ "AddImm",
|
||||
/* 21 */ "MustBeInt",
|
||||
/* 22 */ "RealAffinity",
|
||||
/* 23 */ "Permutation",
|
||||
/* 24 */ "Compare",
|
||||
/* 25 */ "Jump",
|
||||
/* 26 */ "If",
|
||||
/* 27 */ "IfNot",
|
||||
/* 28 */ "Column",
|
||||
/* 29 */ "Affinity",
|
||||
/* 30 */ "MakeRecord",
|
||||
/* 31 */ "Count",
|
||||
/* 32 */ "Savepoint",
|
||||
/* 33 */ "AutoCommit",
|
||||
/* 34 */ "Transaction",
|
||||
/* 35 */ "ReadCookie",
|
||||
/* 36 */ "SetCookie",
|
||||
/* 37 */ "VerifyCookie",
|
||||
/* 38 */ "OpenRead",
|
||||
/* 39 */ "OpenWrite",
|
||||
/* 40 */ "OpenAutoindex",
|
||||
/* 41 */ "OpenEphemeral",
|
||||
/* 42 */ "OpenPseudo",
|
||||
/* 43 */ "Close",
|
||||
/* 44 */ "SeekLt",
|
||||
/* 45 */ "SeekLe",
|
||||
/* 46 */ "SeekGe",
|
||||
/* 47 */ "SeekGt",
|
||||
/* 48 */ "Seek",
|
||||
/* 49 */ "NotFound",
|
||||
/* 50 */ "Found",
|
||||
/* 51 */ "IsUnique",
|
||||
/* 52 */ "NotExists",
|
||||
/* 53 */ "Sequence",
|
||||
/* 54 */ "NewRowid",
|
||||
/* 55 */ "Insert",
|
||||
/* 56 */ "InsertInt",
|
||||
/* 57 */ "Delete",
|
||||
/* 58 */ "ResetCount",
|
||||
/* 59 */ "RowKey",
|
||||
/* 60 */ "RowData",
|
||||
/* 61 */ "Rowid",
|
||||
/* 62 */ "NullRow",
|
||||
/* 63 */ "Last",
|
||||
/* 64 */ "Sort",
|
||||
/* 65 */ "Rewind",
|
||||
/* 66 */ "Prev",
|
||||
/* 67 */ "Next",
|
||||
/* 68 */ "Or",
|
||||
/* 69 */ "And",
|
||||
/* 70 */ "IdxInsert",
|
||||
/* 71 */ "IdxDelete",
|
||||
/* 72 */ "IdxRowid",
|
||||
/* 73 */ "IsNull",
|
||||
/* 74 */ "NotNull",
|
||||
/* 75 */ "Ne",
|
||||
/* 76 */ "Eq",
|
||||
/* 77 */ "Gt",
|
||||
/* 78 */ "Le",
|
||||
/* 79 */ "Lt",
|
||||
/* 80 */ "Ge",
|
||||
/* 81 */ "IdxLT",
|
||||
/* 82 */ "BitAnd",
|
||||
/* 83 */ "BitOr",
|
||||
/* 84 */ "ShiftLeft",
|
||||
/* 85 */ "ShiftRight",
|
||||
/* 86 */ "Add",
|
||||
/* 87 */ "Subtract",
|
||||
/* 88 */ "Multiply",
|
||||
/* 89 */ "Divide",
|
||||
/* 90 */ "Remainder",
|
||||
/* 91 */ "Concat",
|
||||
/* 92 */ "IdxGE",
|
||||
/* 93 */ "BitNot",
|
||||
/* 94 */ "String8",
|
||||
/* 95 */ "Destroy",
|
||||
/* 96 */ "Clear",
|
||||
/* 97 */ "CreateIndex",
|
||||
/* 98 */ "CreateTable",
|
||||
/* 99 */ "ParseSchema",
|
||||
/* 100 */ "LoadAnalysis",
|
||||
/* 101 */ "DropTable",
|
||||
/* 102 */ "DropIndex",
|
||||
/* 103 */ "DropTrigger",
|
||||
/* 104 */ "IntegrityCk",
|
||||
/* 105 */ "RowSetAdd",
|
||||
/* 106 */ "RowSetRead",
|
||||
/* 107 */ "RowSetTest",
|
||||
/* 108 */ "Program",
|
||||
/* 109 */ "Param",
|
||||
/* 110 */ "FkCounter",
|
||||
/* 111 */ "FkIfZero",
|
||||
/* 112 */ "MemMax",
|
||||
/* 113 */ "IfPos",
|
||||
/* 114 */ "IfNeg",
|
||||
/* 115 */ "IfZero",
|
||||
/* 116 */ "AggStep",
|
||||
/* 117 */ "AggFinal",
|
||||
/* 118 */ "Checkpoint",
|
||||
/* 119 */ "JournalMode",
|
||||
/* 120 */ "Vacuum",
|
||||
/* 121 */ "IncrVacuum",
|
||||
/* 122 */ "Expire",
|
||||
/* 123 */ "TableLock",
|
||||
/* 124 */ "VBegin",
|
||||
/* 125 */ "VCreate",
|
||||
/* 126 */ "VDestroy",
|
||||
/* 127 */ "VOpen",
|
||||
/* 128 */ "VFilter",
|
||||
/* 129 */ "VColumn",
|
||||
/* 130 */ "Real",
|
||||
/* 131 */ "VNext",
|
||||
/* 132 */ "VRename",
|
||||
/* 133 */ "VUpdate",
|
||||
/* 134 */ "Pagecount",
|
||||
/* 135 */ "MaxPgcnt",
|
||||
/* 136 */ "Trace",
|
||||
/* 137 */ "Noop",
|
||||
/* 138 */ "Explain",
|
||||
/* 139 */ "NotUsed_139",
|
||||
/* 140 */ "NotUsed_140",
|
||||
/* 141 */ "ToText",
|
||||
/* 142 */ "ToBlob",
|
||||
/* 143 */ "ToNumeric",
|
||||
/* 144 */ "ToInt",
|
||||
/* 145 */ "ToReal",
|
||||
};
|
||||
return azName[i];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,348 @@
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/* Automatically generated. Do not edit */
|
||||
/* See the mkopcodeh.awk script for details */
|
||||
/* Automatically generated. Do not edit */
|
||||
/* See the mkopcodeh.awk script for details */
|
||||
//#define OP_Goto 1
|
||||
//#define OP_Gosub 2
|
||||
//#define OP_Return 3
|
||||
//#define OP_Yield 4
|
||||
//#define OP_HaltIfNull 5
|
||||
//#define OP_Halt 6
|
||||
//#define OP_Integer 7
|
||||
//#define OP_Int64 8
|
||||
//#define OP_Real 130 /* same as TK_FLOAT */
|
||||
//#define OP_String8 94 /* same as TK_STRING */
|
||||
//#define OP_String 9
|
||||
//#define OP_Null 10
|
||||
//#define OP_Blob 11
|
||||
//#define OP_Variable 12
|
||||
//#define OP_Move 13
|
||||
//#define OP_Copy 14
|
||||
//#define OP_SCopy 15
|
||||
//#define OP_ResultRow 16
|
||||
//#define OP_Concat 91 /* same as TK_CONCAT */
|
||||
//#define OP_Add 86 /* same as TK_PLUS */
|
||||
//#define OP_Subtract 87 /* same as TK_MINUS */
|
||||
//#define OP_Multiply 88 /* same as TK_STAR */
|
||||
//#define OP_Divide 89 /* same as TK_SLASH */
|
||||
//#define OP_Remainder 90 /* same as TK_REM */
|
||||
//#define OP_CollSeq 17
|
||||
//#define OP_Function 18
|
||||
//#define OP_BitAnd 82 /* same as TK_BITAND */
|
||||
//#define OP_BitOr 83 /* same as TK_BITOR */
|
||||
//#define OP_ShiftLeft 84 /* same as TK_LSHIFT */
|
||||
//#define OP_ShiftRight 85 /* same as TK_RSHIFT */
|
||||
//#define OP_AddImm 20
|
||||
//#define OP_MustBeInt 21
|
||||
//#define OP_RealAffinity 22
|
||||
//#define OP_ToText 141 /* same as TK_TO_TEXT */
|
||||
//#define OP_ToBlob 142 /* same as TK_TO_BLOB */
|
||||
//#define OP_ToNumeric 143 /* same as TK_TO_NUMERIC*/
|
||||
//#define OP_ToInt 144 /* same as TK_TO_INT */
|
||||
//#define OP_ToReal 145 /* same as TK_TO_REAL */
|
||||
//#define OP_Eq 76 /* same as TK_EQ */
|
||||
//#define OP_Ne 75 /* same as TK_NE */
|
||||
//#define OP_Lt 79 /* same as TK_LT */
|
||||
//#define OP_Le 78 /* same as TK_LE */
|
||||
//#define OP_Gt 77 /* same as TK_GT */
|
||||
//#define OP_Ge 80 /* same as TK_GE */
|
||||
//#define OP_Permutation 23
|
||||
//#define OP_Compare 24
|
||||
//#define OP_Jump 25
|
||||
//#define OP_And 69 /* same as TK_AND */
|
||||
//#define OP_Or 68 /* same as TK_OR */
|
||||
//#define OP_Not 19 /* same as TK_NOT */
|
||||
//#define OP_BitNot 93 /* same as TK_BITNOT */
|
||||
//#define OP_If 26
|
||||
//#define OP_IfNot 27
|
||||
//#define OP_IsNull 73 /* same as TK_ISNULL */
|
||||
//#define OP_NotNull 74 /* same as TK_NOTNULL */
|
||||
//#define OP_Column 28
|
||||
//#define OP_Affinity 29
|
||||
//#define OP_MakeRecord 30
|
||||
//#define OP_Count 31
|
||||
//#define OP_Savepoint 32
|
||||
//#define OP_AutoCommit 33
|
||||
//#define OP_Transaction 34
|
||||
//#define OP_ReadCookie 35
|
||||
//#define OP_SetCookie 36
|
||||
//#define OP_VerifyCookie 37
|
||||
//#define OP_OpenRead 38
|
||||
//#define OP_OpenWrite 39
|
||||
//#define OP_OpenAutoindex 40
|
||||
//#define OP_OpenEphemeral 41
|
||||
//#define OP_OpenPseudo 42
|
||||
//#define OP_Close 43
|
||||
//#define OP_SeekLt 44
|
||||
//#define OP_SeekLe 45
|
||||
//#define OP_SeekGe 46
|
||||
//#define OP_SeekGt 47
|
||||
//#define OP_Seek 48
|
||||
//#define OP_NotFound 49
|
||||
//#define OP_Found 50
|
||||
//#define OP_IsUnique 51
|
||||
//#define OP_NotExists 52
|
||||
//#define OP_Sequence 53
|
||||
//#define OP_NewRowid 54
|
||||
//#define OP_Insert 55
|
||||
//#define OP_InsertInt 56
|
||||
//#define OP_Delete 57
|
||||
//#define OP_ResetCount 58
|
||||
//#define OP_RowKey 59
|
||||
//#define OP_RowData 60
|
||||
//#define OP_Rowid 61
|
||||
//#define OP_NullRow 62
|
||||
//#define OP_Last 63
|
||||
//#define OP_Sort 64
|
||||
//#define OP_Rewind 65
|
||||
//#define OP_Prev 66
|
||||
//#define OP_Next 67
|
||||
//#define OP_IdxInsert 70
|
||||
//#define OP_IdxDelete 71
|
||||
//#define OP_IdxRowid 72
|
||||
//#define OP_IdxLT 81
|
||||
//#define OP_IdxGE 92
|
||||
//#define OP_Destroy 95
|
||||
//#define OP_Clear 96
|
||||
//#define OP_CreateIndex 97
|
||||
//#define OP_CreateTable 98
|
||||
//#define OP_ParseSchema 99
|
||||
//#define OP_LoadAnalysis 100
|
||||
//#define OP_DropTable 101
|
||||
//#define OP_DropIndex 102
|
||||
//#define OP_DropTrigger 103
|
||||
//#define OP_IntegrityCk 104
|
||||
//#define OP_RowSetAdd 105
|
||||
//#define OP_RowSetRead 106
|
||||
//#define OP_RowSetTest 107
|
||||
//#define OP_Program 108
|
||||
//#define OP_Param 109
|
||||
//#define OP_FkCounter 110
|
||||
//#define OP_FkIfZero 111
|
||||
//#define OP_MemMax 112
|
||||
//#define OP_IfPos 113
|
||||
//#define OP_IfNeg 114
|
||||
//#define OP_IfZero 115
|
||||
//#define OP_AggStep 116
|
||||
//#define OP_AggFinal 117
|
||||
//#define OP_Checkpoint 118
|
||||
//#define OP_JournalMode 119
|
||||
//#define OP_Vacuum 120
|
||||
//#define OP_IncrVacuum 121
|
||||
//#define OP_Expire 122
|
||||
//#define OP_TableLock 123
|
||||
//#define OP_VBegin 124
|
||||
//#define OP_VCreate 125
|
||||
//#define OP_VDestroy 126
|
||||
//#define OP_VOpen 127
|
||||
//#define OP_VFilter 128
|
||||
//#define OP_VColumn 129
|
||||
//#define OP_VNext 131
|
||||
//#define OP_VRename 132
|
||||
//#define OP_VUpdate 133
|
||||
//#define OP_Pagecount 134
|
||||
//#define OP_MaxPgcnt 135
|
||||
//#define OP_Trace 136
|
||||
//#define OP_Noop 137
|
||||
//#define OP_Explain 138
|
||||
|
||||
public const int OP_Goto = 1;
|
||||
public const int OP_Gosub = 2;
|
||||
public const int OP_Return = 3;
|
||||
public const int OP_Yield = 4;
|
||||
public const int OP_HaltIfNull = 5;
|
||||
public const int OP_Halt = 6;
|
||||
public const int OP_Integer = 7;
|
||||
public const int OP_Int64 = 8;
|
||||
public const int OP_Real = 130 /* same as TK_FLOAT */;
|
||||
public const int OP_String8 = 94 /* same as TK_STRING */;
|
||||
public const int OP_String = 9;
|
||||
public const int OP_Null = 10;
|
||||
public const int OP_Blob = 11;
|
||||
public const int OP_Variable = 12;
|
||||
public const int OP_Move = 13;
|
||||
public const int OP_Copy = 14;
|
||||
public const int OP_SCopy = 15;
|
||||
public const int OP_ResultRow = 16;
|
||||
public const int OP_Concat = 91 /* same as TK_CONCAT */;
|
||||
public const int OP_Add = 86 /* same as TK_PLUS */;
|
||||
public const int OP_Subtract = 87 /* same as TK_MINUS */;
|
||||
public const int OP_Multiply = 88 /* same as TK_STAR */;
|
||||
public const int OP_Divide = 89 /* same as TK_SLASH */;
|
||||
public const int OP_Remainder = 90 /* same as TK_REM */;
|
||||
public const int OP_CollSeq = 17;
|
||||
public const int OP_Function = 18;
|
||||
public const int OP_BitAnd = 82 /* same as TK_BITAND */;
|
||||
public const int OP_BitOr = 83 /* same as TK_BITOR */;
|
||||
public const int OP_ShiftLeft = 84 /* same as TK_LSHIFT */;
|
||||
public const int OP_ShiftRight = 85 /* same as TK_RSHIFT */;
|
||||
public const int OP_AddImm = 20;
|
||||
public const int OP_MustBeInt = 21;
|
||||
public const int OP_RealAffinity = 22;
|
||||
public const int OP_ToText = 141 /* same as TK_TO_TEXT */;
|
||||
public const int OP_ToBlob = 142 /* same as TK_TO_BLOB */;
|
||||
public const int OP_ToNumeric = 143 /* same as TK_TO_NUMERIC*/;
|
||||
public const int OP_ToInt = 144 /* same as TK_TO_INT */;
|
||||
public const int OP_ToReal = 145 /* same as TK_TO_REAL */;
|
||||
public const int OP_Eq = 76 /* same as TK_EQ */;
|
||||
public const int OP_Ne = 75 /* same as TK_NE */;
|
||||
public const int OP_Lt = 79 /* same as TK_LT */;
|
||||
public const int OP_Le = 78 /* same as TK_LE */;
|
||||
public const int OP_Gt = 77 /* same as TK_GT */;
|
||||
public const int OP_Ge = 80 /* same as TK_GE */;
|
||||
public const int OP_Permutation = 23;
|
||||
public const int OP_Compare = 24;
|
||||
public const int OP_Jump = 25;
|
||||
public const int OP_And = 69 /* same as TK_AND */;
|
||||
public const int OP_Or = 68 /* same as TK_OR */;
|
||||
public const int OP_Not = 19 /* same as TK_NOT */;
|
||||
public const int OP_BitNot = 93 /* same as TK_BITNOT */;
|
||||
public const int OP_If = 26;
|
||||
public const int OP_IfNot = 27;
|
||||
public const int OP_IsNull = 73 /* same as TK_ISNULL */;
|
||||
public const int OP_NotNull = 74 /* same as TK_NOTNULL */;
|
||||
public const int OP_Column = 28;
|
||||
public const int OP_Affinity = 29;
|
||||
public const int OP_MakeRecord = 30;
|
||||
public const int OP_Count = 31;
|
||||
public const int OP_Savepoint = 32;
|
||||
public const int OP_AutoCommit = 33;
|
||||
public const int OP_Transaction = 34;
|
||||
public const int OP_ReadCookie = 35;
|
||||
public const int OP_SetCookie = 36;
|
||||
public const int OP_VerifyCookie = 37;
|
||||
public const int OP_OpenRead = 38;
|
||||
public const int OP_OpenWrite = 39;
|
||||
public const int OP_OpenAutoindex = 40;
|
||||
public const int OP_OpenEphemeral = 41;
|
||||
public const int OP_OpenPseudo = 42;
|
||||
public const int OP_Close = 43;
|
||||
public const int OP_SeekLt = 44;
|
||||
public const int OP_SeekLe = 45;
|
||||
public const int OP_SeekGe = 46;
|
||||
public const int OP_SeekGt = 47;
|
||||
public const int OP_Seek = 48;
|
||||
public const int OP_NotFound = 49;
|
||||
public const int OP_Found = 50;
|
||||
public const int OP_IsUnique = 51;
|
||||
public const int OP_NotExists = 52;
|
||||
public const int OP_Sequence = 53;
|
||||
public const int OP_NewRowid = 54;
|
||||
public const int OP_Insert = 55;
|
||||
public const int OP_InsertInt = 56;
|
||||
public const int OP_Delete = 57;
|
||||
public const int OP_ResetCount = 58;
|
||||
public const int OP_RowKey = 59;
|
||||
public const int OP_RowData = 60;
|
||||
public const int OP_Rowid = 61;
|
||||
public const int OP_NullRow = 62;
|
||||
public const int OP_Last = 63;
|
||||
public const int OP_Sort = 64;
|
||||
public const int OP_Rewind = 65;
|
||||
public const int OP_Prev = 66;
|
||||
public const int OP_Next = 67;
|
||||
public const int OP_IdxInsert = 70;
|
||||
public const int OP_IdxDelete = 71;
|
||||
public const int OP_IdxRowid = 72;
|
||||
public const int OP_IdxLT = 81;
|
||||
public const int OP_IdxGE = 92;
|
||||
public const int OP_Destroy = 95;
|
||||
public const int OP_Clear = 96;
|
||||
public const int OP_CreateIndex = 97;
|
||||
public const int OP_CreateTable = 98;
|
||||
public const int OP_ParseSchema = 99;
|
||||
public const int OP_LoadAnalysis = 100;
|
||||
public const int OP_DropTable = 101;
|
||||
public const int OP_DropIndex = 102;
|
||||
public const int OP_DropTrigger = 103;
|
||||
public const int OP_IntegrityCk = 104;
|
||||
public const int OP_RowSetAdd = 105;
|
||||
public const int OP_RowSetRead = 106;
|
||||
public const int OP_RowSetTest = 107;
|
||||
public const int OP_Program = 108;
|
||||
public const int OP_Param = 109;
|
||||
public const int OP_FkCounter = 110;
|
||||
public const int OP_FkIfZero = 111;
|
||||
public const int OP_MemMax = 112;
|
||||
public const int OP_IfPos = 113;
|
||||
public const int OP_IfNeg = 114;
|
||||
public const int OP_IfZero = 115;
|
||||
public const int OP_AggStep = 116;
|
||||
public const int OP_AggFinal = 117;
|
||||
public const int OP_Checkpoint = 118;
|
||||
public const int OP_JournalMode = 119;
|
||||
public const int OP_Vacuum = 120;
|
||||
public const int OP_IncrVacuum = 121;
|
||||
public const int OP_Expire = 122;
|
||||
public const int OP_TableLock = 123;
|
||||
public const int OP_VBegin = 124;
|
||||
public const int OP_VCreate = 125;
|
||||
public const int OP_VDestroy = 126;
|
||||
public const int OP_VOpen = 127;
|
||||
public const int OP_VFilter = 128;
|
||||
public const int OP_VColumn = 129;
|
||||
public const int OP_VNext = 131;
|
||||
public const int OP_VRename = 132;
|
||||
public const int OP_VUpdate = 133;
|
||||
public const int OP_Pagecount = 134;
|
||||
public const int OP_MaxPgcnt = 135;
|
||||
public const int OP_Trace = 136;
|
||||
public const int OP_Noop = 137;
|
||||
public const int OP_Explain = 138;
|
||||
|
||||
/* The following opcode values are never used */
|
||||
//#define OP_NotUsed_139 139
|
||||
//#define OP_NotUsed_140 140
|
||||
|
||||
/* The following opcode values are never used */
|
||||
public const int OP_NotUsed_138 = 138;
|
||||
public const int OP_NotUsed_139 = 139;
|
||||
public const int OP_NotUsed_140 = 140;
|
||||
|
||||
|
||||
/* Properties such as "out2" or "jump" that are specified in
|
||||
** comments following the "case" for each opcode in the vdbe.c
|
||||
** are encoded into bitvectors as follows:
|
||||
*/
|
||||
//#define OPFLG_JUMP 0x0001 /* jump: P2 holds jmp target */
|
||||
//#define OPFLG_OUT2_PRERELEASE 0x0002 /* out2-prerelease: */
|
||||
//#define OPFLG_IN1 0x0004 /* in1: P1 is an input */
|
||||
//#define OPFLG_IN2 0x0008 /* in2: P2 is an input */
|
||||
//#define OPFLG_IN3 0x0010 /* in3: P3 is an input */
|
||||
//#define OPFLG_OUT2 0x0020 /* out2: P2 is an output */
|
||||
//#define OPFLG_OUT3 0x0040 /* out3: P3 is an output */
|
||||
|
||||
public const int OPFLG_JUMP = 0x0001; /* jump: P2 holds jmp target */
|
||||
public const int OPFLG_OUT2_PRERELEASE = 0x0002; /* out2-prerelease: */
|
||||
public const int OPFLG_IN1 = 0x0004; /* in1: P1 is an input */
|
||||
public const int OPFLG_IN2 = 0x0008; /* in2: P2 is an input */
|
||||
public const int OPFLG_IN3 = 0x0010; /* in3: P3 is an input */
|
||||
public const int OPFLG_OUT2 = 0x0020; /* out2: P2 is an output */
|
||||
public const int OPFLG_OUT3 = 0x0040; /* out3: P3 is an output */
|
||||
public static int[] OPFLG_INITIALIZER = new int[]{
|
||||
/* 0 */ 0x00, 0x01, 0x05, 0x04, 0x04, 0x10, 0x00, 0x02,
|
||||
/* 8 */ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x24, 0x24,
|
||||
/* 16 */ 0x00, 0x00, 0x00, 0x24, 0x04, 0x05, 0x04, 0x00,
|
||||
/* 24 */ 0x00, 0x01, 0x05, 0x05, 0x00, 0x00, 0x00, 0x02,
|
||||
/* 32 */ 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00,
|
||||
/* 40 */ 0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11,
|
||||
/* 48 */ 0x08, 0x11, 0x11, 0x11, 0x11, 0x02, 0x02, 0x00,
|
||||
/* 56 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01,
|
||||
/* 64 */ 0x01, 0x01, 0x01, 0x01, 0x4c, 0x4c, 0x08, 0x00,
|
||||
/* 72 */ 0x02, 0x05, 0x05, 0x15, 0x15, 0x15, 0x15, 0x15,
|
||||
/* 80 */ 0x15, 0x01, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,
|
||||
/* 88 */ 0x4c, 0x4c, 0x4c, 0x4c, 0x01, 0x24, 0x02, 0x02,
|
||||
/* 96 */ 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 104 */ 0x00, 0x0c, 0x45, 0x15, 0x01, 0x02, 0x00, 0x01,
|
||||
/* 112 */ 0x08, 0x05, 0x05, 0x05, 0x00, 0x00, 0x00, 0x02,
|
||||
/* 120 */ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 128 */ 0x01, 0x00, 0x02, 0x01, 0x00, 0x00, 0x02, 0x02,
|
||||
/* 136 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04,
|
||||
/* 144 */ 0x04, 0x04
|
||||
};
|
||||
}
|
||||
}
|
||||
419
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/os_c.cs
Normal file
419
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/os_c.cs
Normal file
@@ -0,0 +1,419 @@
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
using HANDLE = System.IntPtr;
|
||||
using i64 = System.Int64;
|
||||
using u32 = System.UInt32;
|
||||
using sqlite3_int64 = System.Int64;
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
public partial class Sqlite3
|
||||
{
|
||||
|
||||
/*
|
||||
** 2005 November 29
|
||||
**
|
||||
** 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 OS interface code that is common to all
|
||||
** architectures.
|
||||
*************************************************************************
|
||||
** 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-12-07 20:14:09 a586a4deeb25330037a49df295b36aaf624d0f45
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
//#define _SQLITE_OS_C_ 1
|
||||
//#include "sqliteInt.h"
|
||||
//#undef _SQLITE_OS_C_
|
||||
|
||||
/*
|
||||
** The default SQLite sqlite3_vfs implementations do not allocate
|
||||
** memory (actually, os_unix.c allocates a small amount of memory
|
||||
** from within OsOpen()), but some third-party implementations may.
|
||||
** So we test the effects of a malloc() failing and the sqlite3OsXXX()
|
||||
** function returning SQLITE_IOERR_NOMEM using the DO_OS_MALLOC_TEST macro.
|
||||
**
|
||||
** The following functions are instrumented for malloc() failure
|
||||
** testing:
|
||||
**
|
||||
** sqlite3OsOpen()
|
||||
** sqlite3OsRead()
|
||||
** sqlite3OsWrite()
|
||||
** sqlite3OsSync()
|
||||
** sqlite3OsLock()
|
||||
**
|
||||
*/
|
||||
#if (SQLITE_TEST)
|
||||
static int sqlite3_memdebug_vfs_oom_test = 1;
|
||||
|
||||
//#define DO_OS_MALLOC_TEST(x) \
|
||||
//if (sqlite3_memdebug_vfs_oom_test && (!x || !sqlite3IsMemJournal(x))) { \
|
||||
// void *pTstAlloc = sqlite3Malloc(10); \
|
||||
// if (!pTstAlloc) return SQLITE_IOERR_NOMEM; \
|
||||
// sqlite3_free(pTstAlloc); \
|
||||
//}
|
||||
static void DO_OS_MALLOC_TEST( sqlite3_file x )
|
||||
{
|
||||
}
|
||||
#else
|
||||
//#define DO_OS_MALLOC_TEST(x)
|
||||
static void DO_OS_MALLOC_TEST( sqlite3_file x ) { }
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** The following routines are convenience wrappers around methods
|
||||
** of the sqlite3_file object. This is mostly just syntactic sugar. All
|
||||
** of this would be completely automatic if SQLite were coded using
|
||||
** C++ instead of plain old C.
|
||||
*/
|
||||
static int sqlite3OsClose( sqlite3_file pId )
|
||||
{
|
||||
int rc = SQLITE_OK;
|
||||
if ( pId.pMethods != null )
|
||||
{
|
||||
rc = pId.pMethods.xClose( pId );
|
||||
pId.pMethods = null;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
static int sqlite3OsRead( sqlite3_file id, byte[] pBuf, int amt, i64 offset )
|
||||
{
|
||||
DO_OS_MALLOC_TEST( id );
|
||||
if ( pBuf == null )
|
||||
pBuf = sqlite3Malloc( amt );
|
||||
return id.pMethods.xRead( id, pBuf, amt, offset );
|
||||
}
|
||||
static int sqlite3OsWrite( sqlite3_file id, byte[] pBuf, int amt, i64 offset )
|
||||
{
|
||||
DO_OS_MALLOC_TEST( id );
|
||||
return id.pMethods.xWrite( id, pBuf, amt, offset );
|
||||
}
|
||||
static int sqlite3OsTruncate( sqlite3_file id, i64 size )
|
||||
{
|
||||
return id.pMethods.xTruncate( id, size );
|
||||
}
|
||||
static int sqlite3OsSync( sqlite3_file id, int flags )
|
||||
{
|
||||
DO_OS_MALLOC_TEST( id );
|
||||
return id.pMethods.xSync( id, flags );
|
||||
}
|
||||
static int sqlite3OsFileSize( sqlite3_file id, ref long pSize )
|
||||
{
|
||||
return id.pMethods.xFileSize( id, ref pSize );
|
||||
}
|
||||
static int sqlite3OsLock( sqlite3_file id, int lockType )
|
||||
{
|
||||
DO_OS_MALLOC_TEST( id );
|
||||
return id.pMethods.xLock( id, lockType );
|
||||
}
|
||||
static int sqlite3OsUnlock( sqlite3_file id, int lockType )
|
||||
{
|
||||
return id.pMethods.xUnlock( id, lockType );
|
||||
}
|
||||
static int sqlite3OsCheckReservedLock( sqlite3_file id, ref int pResOut )
|
||||
{
|
||||
DO_OS_MALLOC_TEST( id );
|
||||
return id.pMethods.xCheckReservedLock( id, ref pResOut );
|
||||
}
|
||||
static int sqlite3OsFileControl( sqlite3_file id, u32 op, ref sqlite3_int64 pArg )
|
||||
{
|
||||
return id.pMethods.xFileControl( id, (int)op, ref pArg );
|
||||
}
|
||||
|
||||
static int sqlite3OsSectorSize( sqlite3_file id )
|
||||
{
|
||||
dxSectorSize xSectorSize = id.pMethods.xSectorSize;
|
||||
return ( xSectorSize != null ? xSectorSize( id ) : SQLITE_DEFAULT_SECTOR_SIZE );
|
||||
}
|
||||
static int sqlite3OsDeviceCharacteristics( sqlite3_file id )
|
||||
{
|
||||
return id.pMethods.xDeviceCharacteristics( id );
|
||||
}
|
||||
|
||||
static int sqlite3OsShmLock( sqlite3_file id, int offset, int n, int flags )
|
||||
{
|
||||
return id.pMethods.xShmLock( id, offset, n, flags );
|
||||
}
|
||||
static void sqlite3OsShmBarrier( sqlite3_file id )
|
||||
{
|
||||
id.pMethods.xShmBarrier( id );
|
||||
}
|
||||
|
||||
static int sqlite3OsShmUnmap( sqlite3_file id, int deleteFlag )
|
||||
{
|
||||
return id.pMethods.xShmUnmap( id, deleteFlag );
|
||||
}
|
||||
static int sqlite3OsShmMap(
|
||||
sqlite3_file id, /* Database file handle */
|
||||
int iPage,
|
||||
int pgsz,
|
||||
int bExtend, /* True to extend file if necessary */
|
||||
out object pp /* OUT: Pointer to mapping */
|
||||
)
|
||||
{
|
||||
return id.pMethods.xShmMap( id, iPage, pgsz, bExtend, out pp );
|
||||
}
|
||||
|
||||
/*
|
||||
** The next group of routines are convenience wrappers around the
|
||||
** VFS methods.
|
||||
*/
|
||||
static int sqlite3OsOpen(
|
||||
sqlite3_vfs pVfs,
|
||||
string zPath,
|
||||
sqlite3_file pFile,
|
||||
int flags,
|
||||
ref int pFlagsOut
|
||||
)
|
||||
{
|
||||
int rc;
|
||||
DO_OS_MALLOC_TEST( null );
|
||||
/* 0x87f3f is a mask of SQLITE_OPEN_ flags that are valid to be passed
|
||||
** down into the VFS layer. Some SQLITE_OPEN_ flags (for example,
|
||||
** SQLITE_OPEN_FULLMUTEX or SQLITE_OPEN_SHAREDCACHE) are blocked before
|
||||
** reaching the VFS. */
|
||||
rc = pVfs.xOpen( pVfs, zPath, pFile, flags & 0x87f3f, out pFlagsOut );
|
||||
Debug.Assert( rc == SQLITE_OK || pFile.pMethods == null );
|
||||
return rc;
|
||||
}
|
||||
static int sqlite3OsDelete( sqlite3_vfs pVfs, string zPath, int dirSync )
|
||||
{
|
||||
return pVfs.xDelete( pVfs, zPath, dirSync );
|
||||
}
|
||||
static int sqlite3OsAccess( sqlite3_vfs pVfs, string zPath, int flags, ref int pResOut )
|
||||
{
|
||||
DO_OS_MALLOC_TEST( null );
|
||||
return pVfs.xAccess( pVfs, zPath, flags, out pResOut );
|
||||
}
|
||||
static int sqlite3OsFullPathname(
|
||||
sqlite3_vfs pVfs,
|
||||
string zPath,
|
||||
int nPathOut,
|
||||
StringBuilder zPathOut
|
||||
)
|
||||
{
|
||||
zPathOut.Length = 0;//zPathOut[0] = 0;
|
||||
return pVfs.xFullPathname( pVfs, zPath, nPathOut, zPathOut );
|
||||
}
|
||||
#if !SQLITE_OMIT_LOAD_EXTENSION
|
||||
static HANDLE sqlite3OsDlOpen( sqlite3_vfs pVfs, string zPath )
|
||||
{
|
||||
return pVfs.xDlOpen( pVfs, zPath );
|
||||
}
|
||||
|
||||
static void sqlite3OsDlError( sqlite3_vfs pVfs, int nByte, string zBufOut )
|
||||
{
|
||||
pVfs.xDlError( pVfs, nByte, zBufOut );
|
||||
}
|
||||
static object sqlite3OsDlSym( sqlite3_vfs pVfs, HANDLE pHdle, ref string zSym )
|
||||
{
|
||||
return pVfs.xDlSym( pVfs, pHdle, zSym );
|
||||
}
|
||||
static void sqlite3OsDlClose( sqlite3_vfs pVfs, HANDLE pHandle )
|
||||
{
|
||||
pVfs.xDlClose( pVfs, pHandle );
|
||||
}
|
||||
#endif
|
||||
static int sqlite3OsRandomness( sqlite3_vfs pVfs, int nByte, byte[] zBufOut )
|
||||
{
|
||||
return pVfs.xRandomness( pVfs, nByte, zBufOut );
|
||||
}
|
||||
static int sqlite3OsSleep( sqlite3_vfs pVfs, int nMicro )
|
||||
{
|
||||
return pVfs.xSleep( pVfs, nMicro );
|
||||
}
|
||||
|
||||
static int sqlite3OsCurrentTimeInt64( sqlite3_vfs pVfs, ref sqlite3_int64 pTimeOut )
|
||||
{
|
||||
int rc;
|
||||
/* IMPLEMENTATION-OF: R-49045-42493 SQLite will use the xCurrentTimeInt64()
|
||||
** method to get the current date and time if that method is available
|
||||
** (if iVersion is 2 or greater and the function pointer is not NULL) and
|
||||
** will fall back to xCurrentTime() if xCurrentTimeInt64() is
|
||||
** unavailable.
|
||||
*/
|
||||
if ( pVfs.iVersion >= 2 && pVfs.xCurrentTimeInt64 != null )
|
||||
{
|
||||
rc = pVfs.xCurrentTimeInt64( pVfs, ref pTimeOut );
|
||||
}
|
||||
else
|
||||
{
|
||||
double r = 0;
|
||||
rc = pVfs.xCurrentTime( pVfs, ref r );
|
||||
pTimeOut = (sqlite3_int64)( r * 86400000.0 );
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int sqlite3OsOpenMalloc(
|
||||
ref sqlite3_vfs pVfs,
|
||||
string zFile,
|
||||
ref sqlite3_file ppFile,
|
||||
int flags,
|
||||
ref int pOutFlags
|
||||
)
|
||||
{
|
||||
int rc = SQLITE_NOMEM;
|
||||
sqlite3_file pFile;
|
||||
pFile = new sqlite3_file(); //sqlite3Malloc(ref pVfs.szOsFile);
|
||||
if ( pFile != null )
|
||||
{
|
||||
rc = sqlite3OsOpen( pVfs, zFile, pFile, flags, ref pOutFlags );
|
||||
if ( rc != SQLITE_OK )
|
||||
{
|
||||
pFile = null; // was sqlite3DbFree(db,ref pFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
ppFile = pFile;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
static int sqlite3OsCloseFree( sqlite3_file pFile )
|
||||
{
|
||||
int rc = SQLITE_OK;
|
||||
Debug.Assert( pFile != null );
|
||||
rc = sqlite3OsClose( pFile );
|
||||
//sqlite3_free( ref pFile );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is a wrapper around the OS specific implementation of
|
||||
** sqlite3_os_init(). The purpose of the wrapper is to provide the
|
||||
** ability to simulate a malloc failure, so that the handling of an
|
||||
** error in sqlite3_os_init() by the upper layers can be tested.
|
||||
*/
|
||||
static int sqlite3OsInit()
|
||||
{
|
||||
//void *p = sqlite3_malloc(10);
|
||||
//if( p==null ) return SQLITE_NOMEM;
|
||||
//sqlite3_free(ref p);
|
||||
return sqlite3_os_init();
|
||||
}
|
||||
/*
|
||||
** The list of all registered VFS implementations.
|
||||
*/
|
||||
static sqlite3_vfs vfsList;
|
||||
//#define vfsList GLOBAL(sqlite3_vfs *, vfsList)
|
||||
|
||||
/*
|
||||
** Locate a VFS by name. If no name is given, simply return the
|
||||
** first VFS on the list.
|
||||
*/
|
||||
static bool isInit = false;
|
||||
|
||||
static sqlite3_vfs sqlite3_vfs_find( string zVfs )
|
||||
{
|
||||
sqlite3_vfs pVfs = null;
|
||||
#if SQLITE_THREADSAFE
|
||||
sqlite3_mutex mutex;
|
||||
#endif
|
||||
#if !SQLITE_OMIT_AUTOINIT
|
||||
int rc = sqlite3_initialize();
|
||||
if ( rc != 0 )
|
||||
return null;
|
||||
#endif
|
||||
#if SQLITE_THREADSAFE
|
||||
mutex = sqlite3MutexAlloc( SQLITE_MUTEX_STATIC_MASTER );
|
||||
#endif
|
||||
sqlite3_mutex_enter( mutex );
|
||||
for ( pVfs = vfsList; pVfs != null; pVfs = pVfs.pNext )
|
||||
{
|
||||
if ( zVfs == null || zVfs == "" )
|
||||
break;
|
||||
if ( zVfs == pVfs.zName )
|
||||
break; //strcmp(zVfs, pVfs.zName) == null) break;
|
||||
}
|
||||
sqlite3_mutex_leave( mutex );
|
||||
return pVfs;
|
||||
}
|
||||
|
||||
/*
|
||||
** Unlink a VFS from the linked list
|
||||
*/
|
||||
static void vfsUnlink( sqlite3_vfs pVfs )
|
||||
{
|
||||
Debug.Assert( sqlite3_mutex_held( sqlite3MutexAlloc( SQLITE_MUTEX_STATIC_MASTER ) ) );
|
||||
if ( pVfs == null )
|
||||
{
|
||||
/* No-op */
|
||||
}
|
||||
else if ( vfsList == pVfs )
|
||||
{
|
||||
vfsList = pVfs.pNext;
|
||||
}
|
||||
else if ( vfsList != null )
|
||||
{
|
||||
sqlite3_vfs p = vfsList;
|
||||
while ( p.pNext != null && p.pNext != pVfs )
|
||||
{
|
||||
p = p.pNext;
|
||||
}
|
||||
if ( p.pNext == pVfs )
|
||||
{
|
||||
p.pNext = pVfs.pNext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Register a VFS with the system. It is harmless to register the same
|
||||
** VFS multiple times. The new VFS becomes the default if makeDflt is
|
||||
** true.
|
||||
*/
|
||||
static int sqlite3_vfs_register( sqlite3_vfs pVfs, int makeDflt )
|
||||
{
|
||||
sqlite3_mutex mutex;
|
||||
#if !SQLITE_OMIT_AUTOINIT
|
||||
int rc = sqlite3_initialize();
|
||||
if ( rc != 0 )
|
||||
return rc;
|
||||
#endif
|
||||
mutex = sqlite3MutexAlloc( SQLITE_MUTEX_STATIC_MASTER );
|
||||
sqlite3_mutex_enter( mutex );
|
||||
vfsUnlink( pVfs );
|
||||
if ( makeDflt != 0 || vfsList == null )
|
||||
{
|
||||
pVfs.pNext = vfsList;
|
||||
vfsList = pVfs;
|
||||
}
|
||||
else
|
||||
{
|
||||
pVfs.pNext = vfsList.pNext;
|
||||
vfsList.pNext = pVfs;
|
||||
}
|
||||
Debug.Assert( vfsList != null );
|
||||
sqlite3_mutex_leave( mutex );
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Unregister a VFS so that it is no longer accessible.
|
||||
*/
|
||||
static int sqlite3_vfs_unregister( sqlite3_vfs pVfs )
|
||||
{
|
||||
#if SQLITE_THREADSAFE
|
||||
sqlite3_mutex mutex = sqlite3MutexAlloc( SQLITE_MUTEX_STATIC_MASTER );
|
||||
#endif
|
||||
sqlite3_mutex_enter( mutex );
|
||||
vfsUnlink( pVfs );
|
||||
sqlite3_mutex_leave( mutex );
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
using System.Diagnostics;
|
||||
using va_list = System.Object;
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/*
|
||||
** 2004 May 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 macros and a little bit of code that is common to
|
||||
** all of the platform-specific files (os_*.c) and is #included into those
|
||||
** files.
|
||||
**
|
||||
** This file should be #included by the os_*.c files only. It is not a
|
||||
** general purpose header file.
|
||||
*************************************************************************
|
||||
** 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 !_OS_COMMON_H_
|
||||
//#define _OS_COMMON_H_
|
||||
/*
|
||||
** At least two bugs have slipped in because we changed the MEMORY_DEBUG
|
||||
** macro to SQLITE_DEBUG and some older makefiles have not yet made the
|
||||
** switch. The following code should catch this problem at compile-time.
|
||||
*/
|
||||
#if MEMORY_DEBUG
|
||||
//# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead."
|
||||
#endif
|
||||
|
||||
#if SQLITE_DEBUG || TRACE
|
||||
static bool sqlite3OsTrace = false;
|
||||
//#define OSTRACE(X) if( sqlite3OSTrace ) sqlite3DebugPrintf X
|
||||
static void OSTRACE( string X, params va_list[] ap )
|
||||
{
|
||||
if ( sqlite3OsTrace )
|
||||
sqlite3DebugPrintf( X, ap );
|
||||
}
|
||||
#else
|
||||
//#define OSTRACE(X)
|
||||
static void OSTRACE( string X, params object[] ap) { }
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Macros for performance tracing. Normally turned off. Only works
|
||||
** on i486 hardware.
|
||||
*/
|
||||
#if SQLITE_PERFORMANCE_TRACE
|
||||
|
||||
/*
|
||||
** hwtime.h contains inline assembler code for implementing
|
||||
** high-performance timing routines.
|
||||
*/
|
||||
//#include "hwtime.h"
|
||||
|
||||
static sqlite_u3264 g_start;
|
||||
static sqlite_u3264 g_elapsed;
|
||||
//#define TIMER_START g_start=sqlite3Hwtime()
|
||||
//#define TIMER_END g_elapsed=sqlite3Hwtime()-g_start
|
||||
//#define TIMER_ELAPSED g_elapsed
|
||||
#else
|
||||
const int TIMER_START = 0; //#define TIMER_START
|
||||
const int TIMER_END = 0; //#define TIMER_END
|
||||
const int TIMER_ELAPSED = 0; //#define TIMER_ELAPSED ((sqlite_u3264)0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** If we compile with the SQLITE_TEST macro set, then the following block
|
||||
** of code will give us the ability to simulate a disk I/O error. This
|
||||
** is used for testing the I/O recovery logic.
|
||||
*/
|
||||
#if SQLITE_TEST
|
||||
|
||||
#if !TCLSH
|
||||
static int sqlite3_io_error_hit = 0; /* Total number of I/O Errors */
|
||||
static int sqlite3_io_error_hardhit = 0; /* Number of non-benign errors */
|
||||
static int sqlite3_io_error_pending = 0; /* Count down to first I/O error */
|
||||
static int sqlite3_io_error_persist = 0; /* True if I/O errors persist */
|
||||
static int sqlite3_io_error_benign = 0; /* True if errors are benign */
|
||||
static int sqlite3_diskfull_pending = 0;
|
||||
static int sqlite3_diskfull = 0;
|
||||
#else
|
||||
static tcl.lang.Var.SQLITE3_GETSET sqlite3_io_error_hit = new tcl.lang.Var.SQLITE3_GETSET( "sqlite_io_error_hit" );
|
||||
static tcl.lang.Var.SQLITE3_GETSET sqlite3_io_error_hardhit = new tcl.lang.Var.SQLITE3_GETSET( "sqlite_io_error_hardhit" );
|
||||
static tcl.lang.Var.SQLITE3_GETSET sqlite3_io_error_pending = new tcl.lang.Var.SQLITE3_GETSET( "sqlite_io_error_pending" );
|
||||
static tcl.lang.Var.SQLITE3_GETSET sqlite3_io_error_persist = new tcl.lang.Var.SQLITE3_GETSET( "sqlite_io_error_persist" );
|
||||
static tcl.lang.Var.SQLITE3_GETSET sqlite3_io_error_benign = new tcl.lang.Var.SQLITE3_GETSET( "sqlite_io_error_benign" );
|
||||
static tcl.lang.Var.SQLITE3_GETSET sqlite3_diskfull_pending = new tcl.lang.Var.SQLITE3_GETSET( "sqlite_diskfull_pending" );
|
||||
static tcl.lang.Var.SQLITE3_GETSET sqlite3_diskfull = new tcl.lang.Var.SQLITE3_GETSET( "sqlite_diskfull" );
|
||||
#endif
|
||||
static void SimulateIOErrorBenign( int X )
|
||||
{
|
||||
#if !TCLSH
|
||||
sqlite3_io_error_benign = ( X );
|
||||
#else
|
||||
sqlite3_io_error_benign.iValue = ( X );
|
||||
#endif
|
||||
}
|
||||
//#define SimulateIOError(CODE) \
|
||||
// if( (sqlite3_io_error_persist && sqlite3_io_error_hit) \
|
||||
// || sqlite3_io_error_pending-- == 1 ) \
|
||||
// { local_ioerr(); CODE; }
|
||||
static bool SimulateIOError()
|
||||
{
|
||||
#if !TCLSH
|
||||
if ( ( sqlite3_io_error_persist != 0 && sqlite3_io_error_hit != 0 )
|
||||
|| sqlite3_io_error_pending-- == 1 )
|
||||
#else
|
||||
if ( ( sqlite3_io_error_persist.iValue != 0 && sqlite3_io_error_hit.iValue != 0 )
|
||||
|| sqlite3_io_error_pending.iValue-- == 1 )
|
||||
#endif
|
||||
{
|
||||
local_ioerr();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void local_ioerr()
|
||||
{
|
||||
#if TRACE
|
||||
IOTRACE( "IOERR\n" );
|
||||
#endif
|
||||
#if !TCLSH
|
||||
sqlite3_io_error_hit++;
|
||||
if ( sqlite3_io_error_benign == 0 )
|
||||
sqlite3_io_error_hardhit++;
|
||||
#else
|
||||
sqlite3_io_error_hit.iValue++;
|
||||
if ( sqlite3_io_error_benign.iValue == 0 )
|
||||
sqlite3_io_error_hardhit.iValue++;
|
||||
#endif
|
||||
}
|
||||
//#define SimulateDiskfullError(CODE) \
|
||||
// if( sqlite3_diskfull_pending ){ \
|
||||
// if( sqlite3_diskfull_pending == 1 ){ \
|
||||
// local_ioerr(); \
|
||||
// sqlite3_diskfull = 1; \
|
||||
// sqlite3_io_error_hit = 1; \
|
||||
// CODE; \
|
||||
// }else{ \
|
||||
// sqlite3_diskfull_pending--; \
|
||||
// } \
|
||||
// }
|
||||
static bool SimulateDiskfullError()
|
||||
{
|
||||
#if !TCLSH
|
||||
if ( sqlite3_diskfull_pending != 0 )
|
||||
{
|
||||
if ( sqlite3_diskfull_pending == 1 )
|
||||
{
|
||||
#else
|
||||
if ( sqlite3_diskfull_pending.iValue != 0 )
|
||||
{
|
||||
if ( sqlite3_diskfull_pending.iValue == 1 )
|
||||
{
|
||||
#endif
|
||||
local_ioerr();
|
||||
#if !TCLSH
|
||||
sqlite3_diskfull = 1;
|
||||
sqlite3_io_error_hit = 1;
|
||||
#else
|
||||
sqlite3_diskfull.iValue = 1;
|
||||
sqlite3_io_error_hit.iValue = 1;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if !TCLSH
|
||||
sqlite3_diskfull_pending--;
|
||||
#else
|
||||
sqlite3_diskfull_pending.iValue--;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
static bool SimulateIOError() { return false; }
|
||||
//#define SimulateIOErrorBenign(X)
|
||||
static void SimulateIOErrorBenign( int x ) { }
|
||||
|
||||
//#define SimulateIOError(A)
|
||||
//#define SimulateDiskfullError(A)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** When testing, keep a count of the number of open files.
|
||||
*/
|
||||
#if SQLITE_TEST
|
||||
#if !TCLSH
|
||||
static int sqlite3_open_file_count = 0;
|
||||
#else
|
||||
static tcl.lang.Var.SQLITE3_GETSET sqlite3_open_file_count = new tcl.lang.Var.SQLITE3_GETSET( "sqlite3_open_file_count" );
|
||||
#endif
|
||||
|
||||
static void OpenCounter( int X )
|
||||
{
|
||||
#if !TCLSH
|
||||
sqlite3_open_file_count += ( X );
|
||||
#else
|
||||
sqlite3_open_file_count.iValue += ( X );
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
//#define OpenCounter(X)
|
||||
#endif
|
||||
//#endif //* !_OS_COMMON_H_) */
|
||||
}
|
||||
}
|
||||
304
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/os_h.cs
Normal file
304
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/os_h.cs
Normal file
@@ -0,0 +1,304 @@
|
||||
#define SQLITE_OS_WIN
|
||||
using u32 = System.UInt32;
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/*
|
||||
** 2001 September 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 header file (together with is companion C source-code file
|
||||
** "os.c") attempt to abstract the underlying operating system so that
|
||||
** the SQLite library will work on both POSIX and windows systems.
|
||||
**
|
||||
** This header file is #include-ed by sqliteInt.h and thus ends up
|
||||
** being included by every source file.
|
||||
*************************************************************************
|
||||
** 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_OS_H_
|
||||
//#define _SQLITE_OS_H_
|
||||
|
||||
/*
|
||||
** Figure out if we are dealing with Unix, Windows, or some other
|
||||
** operating system. After the following block of preprocess macros,
|
||||
** all of SQLITE_OS_UNIX, SQLITE_OS_WIN, SQLITE_OS_OS2, and SQLITE_OS_OTHER
|
||||
** will defined to either 1 or 0. One of the four will be 1. The other
|
||||
** three will be 0.
|
||||
*/
|
||||
//#if (SQLITE_OS_OTHER)
|
||||
//# if SQLITE_OS_OTHER==1
|
||||
//# undef SQLITE_OS_UNIX
|
||||
//# define SQLITE_OS_UNIX 0
|
||||
//# undef SQLITE_OS_WIN
|
||||
//# define SQLITE_OS_WIN 0
|
||||
//# undef SQLITE_OS_OS2
|
||||
//# define SQLITE_OS_OS2 0
|
||||
//# else
|
||||
//# undef SQLITE_OS_OTHER
|
||||
//# endif
|
||||
//#endif
|
||||
//#if !(SQLITE_OS_UNIX) && !SQLITE_OS_OTHER)
|
||||
//# define SQLITE_OS_OTHER 0
|
||||
//# ifndef SQLITE_OS_WIN
|
||||
//# if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__BORLANDC__)
|
||||
//# define SQLITE_OS_WIN 1
|
||||
//# define SQLITE_OS_UNIX 0
|
||||
//# define SQLITE_OS_OS2 0
|
||||
//# elif defined(__EMX__) || defined(_OS2) || defined(OS2) || defined(_OS2_) || defined(__OS2__)
|
||||
//# define SQLITE_OS_WIN 0
|
||||
//# define SQLITE_OS_UNIX 0
|
||||
//# define SQLITE_OS_OS2 1
|
||||
//# else
|
||||
//# define SQLITE_OS_WIN 0
|
||||
//# define SQLITE_OS_UNIX 1
|
||||
//# define SQLITE_OS_OS2 0
|
||||
//# endif
|
||||
//# else
|
||||
//# define SQLITE_OS_UNIX 0
|
||||
//# define SQLITE_OS_OS2 0
|
||||
//# endif
|
||||
//#else
|
||||
//# ifndef SQLITE_OS_WIN
|
||||
//# define SQLITE_OS_WIN 0
|
||||
//# endif
|
||||
//#endif
|
||||
|
||||
const bool SQLITE_OS_WIN = true;
|
||||
const bool SQLITE_OS_UNIX = false;
|
||||
const bool SQLITE_OS_OS2 = false;
|
||||
|
||||
/*
|
||||
** Determine if we are dealing with WindowsCE - which has a much
|
||||
** reduced API.
|
||||
*/
|
||||
//#if (_WIN32_WCE)
|
||||
//# define SQLITE_OS_WINCE 1
|
||||
//#else
|
||||
//# define SQLITE_OS_WINCE 0
|
||||
//#endif
|
||||
|
||||
/*
|
||||
** Define the maximum size of a temporary filename
|
||||
*/
|
||||
#if SQLITE_OS_WIN
|
||||
//# include <windows.h>
|
||||
const int MAX_PATH = 260;
|
||||
const int SQLITE_TEMPNAME_SIZE = ( MAX_PATH + 50 ); //# define SQLITE_TEMPNAME_SIZE (MAX_PATH+50)
|
||||
#elif SQLITE_OS_OS2
|
||||
# if FALSE //(__GNUC__ > 3 || __GNUC__ == 3 && __GNUC_MINOR__ >= 3) && OS2_HIGH_MEMORY)
|
||||
//# include <os2safe.h> /* has to be included before os2.h for linking to work */
|
||||
# endif
|
||||
//# define INCL_DOSDATETIME
|
||||
//# define INCL_DOSFILEMGR
|
||||
//# define INCL_DOSERRORS
|
||||
//# define INCL_DOSMISC
|
||||
//# define INCL_DOSPROCESS
|
||||
//# define INCL_DOSMODULEMGR
|
||||
//# define INCL_DOSSEMAPHORES
|
||||
//# include <os2.h>
|
||||
//# include <uconv.h>
|
||||
//# define SQLITE_TEMPNAME_SIZE (CCHMAXPATHCOMP)
|
||||
//#else
|
||||
//# define SQLITE_TEMPNAME_SIZE 200
|
||||
#endif
|
||||
|
||||
/* If the SET_FULLSYNC macro is not defined above, then make it
|
||||
** a no-op
|
||||
*/
|
||||
//#if !SET_FULLSYNC
|
||||
//# define SET_FULLSYNC(x,y)
|
||||
//#endif
|
||||
|
||||
/*
|
||||
** The default size of a disk sector
|
||||
*/
|
||||
#if !SQLITE_DEFAULT_SECTOR_SIZE
|
||||
const int SQLITE_DEFAULT_SECTOR_SIZE = 512;//# define SQLITE_DEFAULT_SECTOR_SIZE 512
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Temporary files are named starting with this prefix followed by 16 random
|
||||
** alphanumeric characters, and no file extension. They are stored in the
|
||||
** OS's standard temporary file directory, and are deleted prior to exit.
|
||||
** If sqlite is being embedded in another program, you may wish to change the
|
||||
** prefix to reflect your program's name, so that if your program exits
|
||||
** prematurely, old temporary files can be easily identified. This can be done
|
||||
** using -DSQLITE_TEMP_FILE_PREFIX=myprefix_ on the compiler command line.
|
||||
**
|
||||
** 2006-10-31: The default prefix used to be "sqlite_". But then
|
||||
** Mcafee started using SQLite in their anti-virus product and it
|
||||
** started putting files with the "sqlite" name in the c:/temp folder.
|
||||
** This annoyed many windows users. Those users would then do a
|
||||
** Google search for "sqlite", find the telephone numbers of the
|
||||
** developers and call to wake them up at night and complain.
|
||||
** For this reason, the default name prefix is changed to be "sqlite"
|
||||
** spelled backwards. So the temp files are still identified, but
|
||||
** anybody smart enough to figure out the code is also likely smart
|
||||
** enough to know that calling the developer will not help get rid
|
||||
** of the file.
|
||||
*/
|
||||
#if !SQLITE_TEMP_FILE_PREFIX
|
||||
const string SQLITE_TEMP_FILE_PREFIX = "etilqs_"; //# define SQLITE_TEMP_FILE_PREFIX "etilqs_"
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The following values may be passed as the second argument to
|
||||
** sqlite3OsLock(). The various locks exhibit the following semantics:
|
||||
**
|
||||
** SHARED: Any number of processes may hold a SHARED lock simultaneously.
|
||||
** RESERVED: A single process may hold a RESERVED lock on a file at
|
||||
** any time. Other processes may hold and obtain new SHARED locks.
|
||||
** PENDING: A single process may hold a PENDING lock on a file at
|
||||
** any one time. Existing SHARED locks may persist, but no new
|
||||
** SHARED locks may be obtained by other processes.
|
||||
** EXCLUSIVE: An EXCLUSIVE lock precludes all other locks.
|
||||
**
|
||||
** PENDING_LOCK may not be passed directly to sqlite3OsLock(). Instead, a
|
||||
** process that requests an EXCLUSIVE lock may actually obtain a PENDING
|
||||
** lock. This can be upgraded to an EXCLUSIVE lock by a subsequent call to
|
||||
** sqlite3OsLock().
|
||||
*/
|
||||
const int NO_LOCK = 0;
|
||||
const int SHARED_LOCK = 1;
|
||||
const int RESERVED_LOCK = 2;
|
||||
const int PENDING_LOCK = 3;
|
||||
const int EXCLUSIVE_LOCK = 4;
|
||||
|
||||
/*
|
||||
** File Locking Notes: (Mostly about windows but also some info for Unix)
|
||||
**
|
||||
** We cannot use LockFileEx() or UnlockFileEx() on Win95/98/ME because
|
||||
** those functions are not available. So we use only LockFile() and
|
||||
** UnlockFile().
|
||||
**
|
||||
** LockFile() prevents not just writing but also reading by other processes.
|
||||
** A SHARED_LOCK is obtained by locking a single randomly-chosen
|
||||
** byte out of a specific range of bytes. The lock byte is obtained at
|
||||
** random so two separate readers can probably access the file at the
|
||||
** same time, unless they are unlucky and choose the same lock byte.
|
||||
** An EXCLUSIVE_LOCK is obtained by locking all bytes in the range.
|
||||
** There can only be one writer. A RESERVED_LOCK is obtained by locking
|
||||
** a single byte of the file that is designated as the reserved lock byte.
|
||||
** A PENDING_LOCK is obtained by locking a designated byte different from
|
||||
** the RESERVED_LOCK byte.
|
||||
**
|
||||
** On WinNT/2K/XP systems, LockFileEx() and UnlockFileEx() are available,
|
||||
** which means we can use reader/writer locks. When reader/writer locks
|
||||
** are used, the lock is placed on the same range of bytes that is used
|
||||
** for probabilistic locking in Win95/98/ME. Hence, the locking scheme
|
||||
** will support two or more Win95 readers or two or more WinNT readers.
|
||||
** But a single Win95 reader will lock out all WinNT readers and a single
|
||||
** WinNT reader will lock out all other Win95 readers.
|
||||
**
|
||||
** The following #defines specify the range of bytes used for locking.
|
||||
** SHARED_SIZE is the number of bytes available in the pool from which
|
||||
** a random byte is selected for a shared lock. The pool of bytes for
|
||||
** shared locks begins at SHARED_FIRST.
|
||||
**
|
||||
** The same locking strategy and
|
||||
** byte ranges are used for Unix. This leaves open the possiblity of having
|
||||
** clients on win95, winNT, and unix all talking to the same shared file
|
||||
** and all locking correctly. To do so would require that samba (or whatever
|
||||
** tool is being used for file sharing) implements locks correctly between
|
||||
** windows and unix. I'm guessing that isn't likely to happen, but by
|
||||
** using the same locking range we are at least open to the possibility.
|
||||
**
|
||||
** Locking in windows is manditory. For this reason, we cannot store
|
||||
** actual data in the bytes used for locking. The pager never allocates
|
||||
** the pages involved in locking therefore. SHARED_SIZE is selected so
|
||||
** that all locks will fit on a single page even at the minimum page size.
|
||||
** PENDING_BYTE defines the beginning of the locks. By default PENDING_BYTE
|
||||
** is set high so that we don't have to allocate an unused page except
|
||||
** for very large databases. But one should test the page skipping logic
|
||||
** by setting PENDING_BYTE low and running the entire regression suite.
|
||||
**
|
||||
** Changing the value of PENDING_BYTE results in a subtly incompatible
|
||||
** file format. Depending on how it is changed, you might not notice
|
||||
** the incompatibility right away, even running a full regression test.
|
||||
** The default location of PENDING_BYTE is the first byte past the
|
||||
** 1GB boundary.
|
||||
**
|
||||
*/
|
||||
#if SQLITE_OMIT_WSD
|
||||
//# define PENDING_BYTE (0x40000000)
|
||||
static int PENDING_BYTE = 0x40000000;
|
||||
#else
|
||||
//# define PENDING_BYTE sqlite3PendingByte
|
||||
static int PENDING_BYTE = 0x40000000;
|
||||
#endif
|
||||
|
||||
static int RESERVED_BYTE = ( PENDING_BYTE + 1 );
|
||||
static int SHARED_FIRST = ( PENDING_BYTE + 2 );
|
||||
static int SHARED_SIZE = 510;
|
||||
|
||||
/*
|
||||
** Wrapper around OS specific sqlite3_os_init() function.
|
||||
*/
|
||||
//int sqlite3OsInit(void);
|
||||
|
||||
/*
|
||||
** Functions for accessing sqlite3_file methods
|
||||
*/
|
||||
//int sqlite3OsClose(sqlite3_file);
|
||||
//int sqlite3OsRead(sqlite3_file*, void*, int amt, i64 offset);
|
||||
//int sqlite3OsWrite(sqlite3_file*, const void*, int amt, i64 offset);
|
||||
//int sqlite3OsTruncate(sqlite3_file*, i64 size);
|
||||
//int sqlite3OsSync(sqlite3_file*, int);
|
||||
//int sqlite3OsFileSize(sqlite3_file*, i64 pSize);
|
||||
//int sqlite3OsLock(sqlite3_file*, int);
|
||||
//int sqlite3OsUnlock(sqlite3_file*, int);
|
||||
//int sqlite3OsCheckReservedLock(sqlite3_file *id, int pResOut);
|
||||
//int sqlite3OsFileControl(sqlite3_file*,int,void);
|
||||
//#define SQLITE_FCNTL_DB_UNCHANGED 0xca093fa0
|
||||
const u32 SQLITE_FCNTL_DB_UNCHANGED = 0xca093fa0;
|
||||
|
||||
//int sqlite3OsSectorSize(sqlite3_file *id);
|
||||
//int sqlite3OsDeviceCharacteristics(sqlite3_file *id);
|
||||
//int sqlite3OsShmMap(sqlite3_file *,int,int,int,object volatile *);
|
||||
//int sqlite3OsShmLock(sqlite3_file *id, int, int, int);
|
||||
//void sqlite3OsShmBarrier(sqlite3_file *id);
|
||||
//int sqlite3OsShmUnmap(sqlite3_file *id, int);
|
||||
|
||||
/*
|
||||
** Functions for accessing sqlite3_vfs methods
|
||||
*/
|
||||
//int sqlite3OsOpen(sqlite3_vfs *, string , sqlite3_file*, int, int );
|
||||
//int sqlite3OsDelete(sqlite3_vfs *, string , int);
|
||||
//int sqlite3OsAccess(sqlite3_vfs *, string , int, int pResOut);
|
||||
//int sqlite3OsFullPathname(sqlite3_vfs *, string , int, char );
|
||||
#if !SQLITE_OMIT_LOAD_EXTENSION
|
||||
//void *sqlite3OsDlOpen(sqlite3_vfs *, string );
|
||||
//void sqlite3OsDlError(sqlite3_vfs *, int, char );
|
||||
//void (*sqlite3OsDlSym(sqlite3_vfs *, object *, string ))(void);
|
||||
//void sqlite3OsDlClose(sqlite3_vfs *, object );
|
||||
#endif
|
||||
//int sqlite3OsRandomness(sqlite3_vfs *, int, char );
|
||||
//int sqlite3OsSleep(sqlite3_vfs *, int);
|
||||
//int sqlite3OsCurrentTimeInt64(sqlite3_vfs *, sqlite3_int64);
|
||||
|
||||
/*
|
||||
** Convenience functions for opening and closing files using
|
||||
** sqlite3Malloc() to obtain space for the file-handle structure.
|
||||
*/
|
||||
//int sqlite3OsOpenMalloc(sqlite3_vfs *, string , sqlite3_file **, int,int);
|
||||
//int sqlite3OsCloseFree(sqlite3_file );
|
||||
#endif // * _SQLITE_OS_H_ */
|
||||
|
||||
}
|
||||
}
|
||||
3625
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/os_win_c.cs
Normal file
3625
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/os_win_c.cs
Normal file
File diff suppressed because it is too large
Load Diff
7837
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/pager_c.cs
Normal file
7837
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/pager_c.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,208 @@
|
||||
using Pgno = 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 header file defines the interface that the sqlite page cache
|
||||
** subsystem. The page cache subsystem reads and writes a file a page
|
||||
** at a time and provides a journal for rollback.
|
||||
*************************************************************************
|
||||
** 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 !_PAGER_H_
|
||||
//#define _PAGER_H_
|
||||
|
||||
/*
|
||||
** Default maximum size for persistent journal files. A negative
|
||||
** value means no limit. This value may be overridden using the
|
||||
** sqlite3PagerJournalSizeLimit() API. See also "PRAGMA journal_size_limit".
|
||||
*/
|
||||
#if !SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT
|
||||
const int SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT = -1;//#define SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT -1
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The type used to represent a page number. The first page in a file
|
||||
** is called page 1. 0 is used to represent "not a page".
|
||||
*/
|
||||
//typedef u32 Pgno;
|
||||
|
||||
/*
|
||||
** Each open file is managed by a separate instance of the "Pager" structure.
|
||||
*/
|
||||
//typedef struct Pager Pager;
|
||||
|
||||
/*
|
||||
** Handle type for pages.
|
||||
*/
|
||||
//typedef struct PgHdr DbPage;
|
||||
|
||||
/*
|
||||
** Page number PAGER_MJ_PGNO is never used in an SQLite database (it is
|
||||
** reserved for working around a windows/posix incompatibility). It is
|
||||
** used in the journal to signify that the remainder of the journal file
|
||||
** is devoted to storing a master journal name - there are no more pages to
|
||||
** roll back. See comments for function writeMasterJournal() in pager.c
|
||||
** for details.
|
||||
*/
|
||||
//#define PAGER_MJ_PGNO(x) ((Pgno)((PENDING_BYTE/((x)->pageSize))+1))
|
||||
static Pgno PAGER_MJ_PGNO( Pager x )
|
||||
{
|
||||
return ( (Pgno)( ( PENDING_BYTE / ( ( x ).pageSize ) ) + 1 ) );
|
||||
}
|
||||
/*
|
||||
** Allowed values for the flags parameter to sqlite3PagerOpen().
|
||||
**
|
||||
** NOTE: These values must match the corresponding BTREE_ values in btree.h.
|
||||
*/
|
||||
//#define PAGER_OMIT_JOURNAL 0x0001 /* Do not use a rollback journal */
|
||||
//#define PAGER_NO_READLOCK 0x0002 /* Omit readlocks on readonly files */
|
||||
//#define PAGER_MEMORY 0x0004 /* In-memory database */
|
||||
const int PAGER_OMIT_JOURNAL = 0x0001;
|
||||
const int PAGER_NO_READLOCK = 0x0002;
|
||||
const int PAGER_MEMORY = 0x0004;
|
||||
|
||||
/*
|
||||
** Valid values for the second argument to sqlite3PagerLockingMode().
|
||||
*/
|
||||
//#define PAGER_LOCKINGMODE_QUERY -1
|
||||
//#define PAGER_LOCKINGMODE_NORMAL 0
|
||||
//#define PAGER_LOCKINGMODE_EXCLUSIVE 1
|
||||
static int PAGER_LOCKINGMODE_QUERY = -1;
|
||||
static int PAGER_LOCKINGMODE_NORMAL = 0;
|
||||
static int PAGER_LOCKINGMODE_EXCLUSIVE = 1;
|
||||
|
||||
/*
|
||||
** Numeric constants that encode the journalmode.
|
||||
*/
|
||||
//#define PAGER_JOURNALMODE_QUERY (-1) /* Query the value of journalmode */
|
||||
//#define PAGER_JOURNALMODE_DELETE 0 /* Commit by deleting journal file */
|
||||
//#define PAGER_JOURNALMODE_PERSIST 1 /* Commit by zeroing journal header */
|
||||
//#define PAGER_JOURNALMODE_OFF 2 /* Journal omitted. */
|
||||
//#define PAGER_JOURNALMODE_TRUNCATE 3 /* Commit by truncating journal */
|
||||
//#define PAGER_JOURNALMODE_MEMORY 4 /* In-memory journal file */
|
||||
//#define PAGER_JOURNALMODE_WAL 5 /* Use write-ahead logging */
|
||||
const int PAGER_JOURNALMODE_QUERY = -1;
|
||||
const int PAGER_JOURNALMODE_DELETE = 0;
|
||||
const int PAGER_JOURNALMODE_PERSIST = 1;
|
||||
const int PAGER_JOURNALMODE_OFF = 2;
|
||||
const int PAGER_JOURNALMODE_TRUNCATE = 3;
|
||||
const int PAGER_JOURNALMODE_MEMORY = 4;
|
||||
const int PAGER_JOURNALMODE_WAL = 5;
|
||||
|
||||
/*
|
||||
** The remainder of this file contains the declarations of the functions
|
||||
** that make up the Pager sub-system API. See source code comments for
|
||||
** a detailed description of each routine.
|
||||
*/
|
||||
/* Open and close a Pager connection. */
|
||||
//int sqlite3PagerOpen(
|
||||
// sqlite3_vfs*,
|
||||
// Pager **ppPager,
|
||||
// const char*,
|
||||
// int,
|
||||
// int,
|
||||
// int,
|
||||
//// void()(DbPage)
|
||||
//);
|
||||
//int sqlite3PagerClose(Pager *pPager);
|
||||
//int sqlite3PagerReadFileheader(Pager*, int, unsigned char);
|
||||
|
||||
/* Functions used to configure a Pager object. */
|
||||
//void sqlite3PagerSetBusyhandler(Pager*, int()(void ), object );
|
||||
//int sqlite3PagerSetPagesize(Pager*, u32*, int);
|
||||
//int sqlite3PagerMaxPageCount(Pager*, int);
|
||||
//void sqlite3PagerSetCachesize(Pager*, int);
|
||||
//void sqlite3PagerSetSafetyLevel(Pager*,int,int,int);
|
||||
//int sqlite3PagerLockingMode(Pager *, int);
|
||||
//int sqlite3PagerSetJournalMode(Pager *, int);
|
||||
//int sqlite3PagerGetJournalMode(Pager);
|
||||
//int sqlite3PagerOkToChangeJournalMode(Pager);
|
||||
//i64 sqlite3PagerJournalSizeLimit(Pager *, i64);
|
||||
//sqlite3_backup **sqlite3PagerBackupPtr(Pager);
|
||||
|
||||
/* Functions used to obtain and release page references. */
|
||||
//int sqlite3PagerAcquire(Pager *pPager, Pgno pgno, DbPage **ppPage, int clrFlag);
|
||||
//#define sqlite3PagerGet(A,B,C) sqlite3PagerAcquire(A,B,C,0)
|
||||
//DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno);
|
||||
//void sqlite3PagerRef(DbPage);
|
||||
//void sqlite3PagerUnref(DbPage);
|
||||
|
||||
/* Operations on page references. */
|
||||
//int sqlite3PagerWrite(DbPage);
|
||||
//void sqlite3PagerDontWrite(DbPage);
|
||||
//int sqlite3PagerMovepage(Pager*,DbPage*,Pgno,int);
|
||||
//int sqlite3PagerPageRefcount(DbPage);
|
||||
//void *sqlite3PagerGetData(DbPage );
|
||||
//void *sqlite3PagerGetExtra(DbPage );
|
||||
|
||||
/* Functions used to manage pager transactions and savepoints. */
|
||||
//void sqlite3PagerPagecount(Pager*, int);
|
||||
//int sqlite3PagerBegin(Pager*, int exFlag, int);
|
||||
//int sqlite3PagerCommitPhaseOne(Pager*,string zMaster, int);
|
||||
//int sqlite3PagerExclusiveLock(Pager);
|
||||
//int sqlite3PagerSync(Pager *pPager);
|
||||
//int sqlite3PagerCommitPhaseTwo(Pager);
|
||||
//int sqlite3PagerRollback(Pager);
|
||||
//int sqlite3PagerOpenSavepoint(Pager *pPager, int n);
|
||||
//int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint);
|
||||
//int sqlite3PagerSharedLock(Pager *pPager);
|
||||
|
||||
//int sqlite3PagerCheckpoint(Pager *pPager, int, int*, int);
|
||||
//int sqlite3PagerWalSupported(Pager* pPager);
|
||||
//int sqlite3PagerWalCallback(Pager* pPager);
|
||||
//int sqlite3PagerOpenWal(Pager* pPager, int* pisOpen);
|
||||
//int sqlite3PagerCloseWal(Pager* pPager);
|
||||
|
||||
/* Functions used to query pager state and configuration. */
|
||||
//u8 sqlite3PagerIsreadonly(Pager);
|
||||
//int sqlite3PagerRefcount(Pager);
|
||||
//int sqlite3PagerMemUsed(Pager);
|
||||
//string sqlite3PagerFilename(Pager);
|
||||
//const sqlite3_vfs *sqlite3PagerVfs(Pager);
|
||||
//sqlite3_file *sqlite3PagerFile(Pager);
|
||||
//string sqlite3PagerJournalname(Pager);
|
||||
//int sqlite3PagerNosync(Pager);
|
||||
//void *sqlite3PagerTempSpace(Pager);
|
||||
//int sqlite3PagerIsMemdb(Pager);
|
||||
|
||||
/* Functions used to truncate the database file. */
|
||||
//void sqlite3PagerTruncateImage(Pager*,Pgno);
|
||||
|
||||
//#if (SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_WAL)
|
||||
//void *sqlite3PagerCodec(DbPage );
|
||||
//#endif
|
||||
|
||||
/* Functions to support testing and debugging. */
|
||||
//#if !NDEBUG || SQLITE_TEST
|
||||
// Pgno sqlite3PagerPagenumber(DbPage);
|
||||
// int sqlite3PagerIswriteable(DbPage);
|
||||
//#endif
|
||||
//#if SQLITE_TEST
|
||||
// int *sqlite3PagerStats(Pager);
|
||||
// void sqlite3PagerRefdump(Pager);
|
||||
// void disable_simulated_io_errors(void);
|
||||
// void enable_simulated_io_errors(void);
|
||||
//#else
|
||||
//# define disable_simulated_io_errors()
|
||||
//# define enable_simulated_io_errors()
|
||||
//#endif
|
||||
}
|
||||
}
|
||||
4360
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/parse_c.cs
Normal file
4360
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/parse_c.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,330 @@
|
||||
/*
|
||||
*************************************************************************
|
||||
** 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
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
public partial class Sqlite3
|
||||
{
|
||||
//#define TK_SEMI 1
|
||||
//#define TK_EXPLAIN 2
|
||||
//#define TK_QUERY 3
|
||||
//#define TK_PLAN 4
|
||||
//#define TK_BEGIN 5
|
||||
//#define TK_TRANSACTION 6
|
||||
//#define TK_DEFERRED 7
|
||||
//#define TK_IMMEDIATE 8
|
||||
//#define TK_EXCLUSIVE 9
|
||||
//#define TK_COMMIT 10
|
||||
//#define TK_END 11
|
||||
//#define TK_ROLLBACK 12
|
||||
//#define TK_SAVEPOINT 13
|
||||
//#define TK_RELEASE 14
|
||||
//#define TK_TO 15
|
||||
//#define TK_TABLE 16
|
||||
//#define TK_CREATE 17
|
||||
//#define TK_IF 18
|
||||
//#define TK_NOT 19
|
||||
//#define TK_EXISTS 20
|
||||
//#define TK_TEMP 21
|
||||
//#define TK_LP 22
|
||||
//#define TK_RP 23
|
||||
//#define TK_AS 24
|
||||
//#define TK_COMMA 25
|
||||
//#define TK_ID 26
|
||||
//#define TK_INDEXED 27
|
||||
//#define TK_ABORT 28
|
||||
//#define TK_ACTION 29
|
||||
//#define TK_AFTER 30
|
||||
//#define TK_ANALYZE 31
|
||||
//#define TK_ASC 32
|
||||
//#define TK_ATTACH 33
|
||||
//#define TK_BEFORE 34
|
||||
//#define TK_BY 35
|
||||
//#define TK_CASCADE 36
|
||||
//#define TK_CAST 37
|
||||
//#define TK_COLUMNKW 38
|
||||
//#define TK_CONFLICT 39
|
||||
//#define TK_DATABASE 40
|
||||
//#define TK_DESC 41
|
||||
//#define TK_DETACH 42
|
||||
//#define TK_EACH 43
|
||||
//#define TK_FAIL 44
|
||||
//#define TK_FOR 45
|
||||
//#define TK_IGNORE 46
|
||||
//#define TK_INITIALLY 47
|
||||
//#define TK_INSTEAD 48
|
||||
//#define TK_LIKE_KW 49
|
||||
//#define TK_MATCH 50
|
||||
//#define TK_NO 51
|
||||
//#define TK_KEY 52
|
||||
//#define TK_OF 53
|
||||
//#define TK_OFFSET 54
|
||||
//#define TK_PRAGMA 55
|
||||
//#define TK_RAISE 56
|
||||
//#define TK_REPLACE 57
|
||||
//#define TK_RESTRICT 58
|
||||
//#define TK_ROW 59
|
||||
//#define TK_TRIGGER 60
|
||||
//#define TK_VACUUM 61
|
||||
//#define TK_VIEW 62
|
||||
//#define TK_VIRTUAL 63
|
||||
//#define TK_REINDEX 64
|
||||
//#define TK_RENAME 65
|
||||
//#define TK_CTIME_KW 66
|
||||
//#define TK_ANY 67
|
||||
//#define TK_OR 68
|
||||
//#define TK_AND 69
|
||||
//#define TK_IS 70
|
||||
//#define TK_BETWEEN 71
|
||||
//#define TK_IN 72
|
||||
//#define TK_ISNULL 73
|
||||
//#define TK_NOTNULL 74
|
||||
//#define TK_NE 75
|
||||
//#define TK_EQ 76
|
||||
//#define TK_GT 77
|
||||
//#define TK_LE 78
|
||||
//#define TK_LT 79
|
||||
//#define TK_GE 80
|
||||
//#define TK_ESCAPE 81
|
||||
//#define TK_BITAND 82
|
||||
//#define TK_BITOR 83
|
||||
//#define TK_LSHIFT 84
|
||||
//#define TK_RSHIFT 85
|
||||
//#define TK_PLUS 86
|
||||
//#define TK_MINUS 87
|
||||
//#define TK_STAR 88
|
||||
//#define TK_SLASH 89
|
||||
//#define TK_REM 90
|
||||
//#define TK_CONCAT 91
|
||||
//#define TK_COLLATE 92
|
||||
//#define TK_BITNOT 93
|
||||
//#define TK_STRING 94
|
||||
//#define TK_JOIN_KW 95
|
||||
//#define TK_CONSTRAINT 96
|
||||
//#define TK_DEFAULT 97
|
||||
//#define TK_NULL 98
|
||||
//#define TK_PRIMARY 99
|
||||
//#define TK_UNIQUE 100
|
||||
//#define TK_CHECK 101
|
||||
//#define TK_REFERENCES 102
|
||||
//#define TK_AUTOINCR 103
|
||||
//#define TK_ON 104
|
||||
//#define TK_INSERT 105
|
||||
//#define TK_DELETE 106
|
||||
//#define TK_UPDATE 107
|
||||
//#define TK_SET 108
|
||||
//#define TK_DEFERRABLE 109
|
||||
//#define TK_FOREIGN 110
|
||||
//#define TK_DROP 111
|
||||
//#define TK_UNION 112
|
||||
//#define TK_ALL 113
|
||||
//#define TK_EXCEPT 114
|
||||
//#define TK_INTERSECT 115
|
||||
//#define TK_SELECT 116
|
||||
//#define TK_DISTINCT 117
|
||||
//#define TK_DOT 118
|
||||
//#define TK_FROM 119
|
||||
//#define TK_JOIN 120
|
||||
//#define TK_USING 121
|
||||
//#define TK_ORDER 122
|
||||
//#define TK_GROUP 123
|
||||
//#define TK_HAVING 124
|
||||
//#define TK_LIMIT 125
|
||||
//#define TK_WHERE 126
|
||||
//#define TK_INTO 127
|
||||
//#define TK_VALUES 128
|
||||
//#define TK_INTEGER 129
|
||||
//#define TK_FLOAT 130
|
||||
//#define TK_BLOB 131
|
||||
//#define TK_REGISTER 132
|
||||
//#define TK_VARIABLE 133
|
||||
//#define TK_CASE 134
|
||||
//#define TK_WHEN 135
|
||||
//#define TK_THEN 136
|
||||
//#define TK_ELSE 137
|
||||
//#define TK_INDEX 138
|
||||
//#define TK_ALTER 139
|
||||
//#define TK_ADD 140
|
||||
//#define TK_TO_TEXT 141
|
||||
//#define TK_TO_BLOB 142
|
||||
//#define TK_TO_NUMERIC 143
|
||||
//#define TK_TO_INT 144
|
||||
//#define TK_TO_REAL 145
|
||||
//#define TK_ISNOT 146
|
||||
//#define TK_END_OF_FILE 147
|
||||
//#define TK_ILLEGAL 148
|
||||
//#define TK_SPACE 149
|
||||
//#define TK_UNCLOSED_STRING 150
|
||||
//#define TK_FUNCTION 151
|
||||
//#define TK_COLUMN 152
|
||||
//#define TK_AGG_FUNCTION 153
|
||||
//#define TK_AGG_COLUMN 154
|
||||
//#define TK_CONST_FUNC 155
|
||||
//#define TK_UMINUS 156
|
||||
//#define TK_UPLUS 157
|
||||
public const int TK_SEMI = 1;
|
||||
public const int TK_EXPLAIN = 2;
|
||||
public const int TK_QUERY = 3;
|
||||
public const int TK_PLAN = 4;
|
||||
public const int TK_BEGIN = 5;
|
||||
public const int TK_TRANSACTION = 6;
|
||||
public const int TK_DEFERRED = 7;
|
||||
public const int TK_IMMEDIATE = 8;
|
||||
public const int TK_EXCLUSIVE = 9;
|
||||
public const int TK_COMMIT = 10;
|
||||
public const int TK_END = 11;
|
||||
public const int TK_ROLLBACK = 12;
|
||||
public const int TK_SAVEPOINT = 13;
|
||||
public const int TK_RELEASE = 14;
|
||||
public const int TK_TO = 15;
|
||||
public const int TK_TABLE = 16;
|
||||
public const int TK_CREATE = 17;
|
||||
public const int TK_IF = 18;
|
||||
public const int TK_NOT = 19;
|
||||
public const int TK_EXISTS = 20;
|
||||
public const int TK_TEMP = 21;
|
||||
public const int TK_LP = 22;
|
||||
public const int TK_RP = 23;
|
||||
public const int TK_AS = 24;
|
||||
public const int TK_COMMA = 25;
|
||||
public const int TK_ID = 26;
|
||||
public const int TK_INDEXED = 27;
|
||||
public const int TK_ABORT = 28;
|
||||
public const int TK_ACTION = 29;
|
||||
public const int TK_AFTER = 30;
|
||||
public const int TK_ANALYZE = 31;
|
||||
public const int TK_ASC = 32;
|
||||
public const int TK_ATTACH = 33;
|
||||
public const int TK_BEFORE = 34;
|
||||
public const int TK_BY = 35;
|
||||
public const int TK_CASCADE = 36;
|
||||
public const int TK_CAST = 37;
|
||||
public const int TK_COLUMNKW = 38;
|
||||
public const int TK_CONFLICT = 39;
|
||||
public const int TK_DATABASE = 40;
|
||||
public const int TK_DESC = 41;
|
||||
public const int TK_DETACH = 42;
|
||||
public const int TK_EACH = 43;
|
||||
public const int TK_FAIL = 44;
|
||||
public const int TK_FOR = 45;
|
||||
public const int TK_IGNORE = 46;
|
||||
public const int TK_INITIALLY = 47;
|
||||
public const int TK_INSTEAD = 48;
|
||||
public const int TK_LIKE_KW = 49;
|
||||
public const int TK_MATCH = 50;
|
||||
public const int TK_NO = 51;
|
||||
public const int TK_KEY = 52;
|
||||
public const int TK_OF = 53;
|
||||
public const int TK_OFFSET = 54;
|
||||
public const int TK_PRAGMA = 55;
|
||||
public const int TK_RAISE = 56;
|
||||
public const int TK_REPLACE = 57;
|
||||
public const int TK_RESTRICT = 58;
|
||||
public const int TK_ROW = 59;
|
||||
public const int TK_TRIGGER = 60;
|
||||
public const int TK_VACUUM = 61;
|
||||
public const int TK_VIEW = 62;
|
||||
public const int TK_VIRTUAL = 63;
|
||||
public const int TK_REINDEX = 64;
|
||||
public const int TK_RENAME = 65;
|
||||
public const int TK_CTIME_KW = 66;
|
||||
public const int TK_ANY = 67;
|
||||
public const int TK_OR = 68;
|
||||
public const int TK_AND = 69;
|
||||
public const int TK_IS = 70;
|
||||
public const int TK_BETWEEN = 71;
|
||||
public const int TK_IN = 72;
|
||||
public const int TK_ISNULL = 73;
|
||||
public const int TK_NOTNULL = 74;
|
||||
public const int TK_NE = 75;
|
||||
public const int TK_EQ = 76;
|
||||
public const int TK_GT = 77;
|
||||
public const int TK_LE = 78;
|
||||
public const int TK_LT = 79;
|
||||
public const int TK_GE = 80;
|
||||
public const int TK_ESCAPE = 81;
|
||||
public const int TK_BITAND = 82;
|
||||
public const int TK_BITOR = 83;
|
||||
public const int TK_LSHIFT = 84;
|
||||
public const int TK_RSHIFT = 85;
|
||||
public const int TK_PLUS = 86;
|
||||
public const int TK_MINUS = 87;
|
||||
public const int TK_STAR = 88;
|
||||
public const int TK_SLASH = 89;
|
||||
public const int TK_REM = 90;
|
||||
public const int TK_CONCAT = 91;
|
||||
public const int TK_COLLATE = 92;
|
||||
public const int TK_BITNOT = 93;
|
||||
public const int TK_STRING = 94;
|
||||
public const int TK_JOIN_KW = 95;
|
||||
public const int TK_CONSTRAINT = 96;
|
||||
public const int TK_DEFAULT = 97;
|
||||
public const int TK_NULL = 98;
|
||||
public const int TK_PRIMARY = 99;
|
||||
public const int TK_UNIQUE = 100;
|
||||
public const int TK_CHECK = 101;
|
||||
public const int TK_REFERENCES = 102;
|
||||
public const int TK_AUTOINCR = 103;
|
||||
public const int TK_ON = 104;
|
||||
public const int TK_INSERT = 105;
|
||||
public const int TK_DELETE = 106;
|
||||
public const int TK_UPDATE = 107;
|
||||
public const int TK_SET = 108;
|
||||
public const int TK_DEFERRABLE = 109;
|
||||
public const int TK_FOREIGN = 110;
|
||||
public const int TK_DROP = 111;
|
||||
public const int TK_UNION = 112;
|
||||
public const int TK_ALL = 113;
|
||||
public const int TK_EXCEPT = 114;
|
||||
public const int TK_INTERSECT = 115;
|
||||
public const int TK_SELECT = 116;
|
||||
public const int TK_DISTINCT = 117;
|
||||
public const int TK_DOT = 118;
|
||||
public const int TK_FROM = 119;
|
||||
public const int TK_JOIN = 120;
|
||||
public const int TK_USING = 121;
|
||||
public const int TK_ORDER = 122;
|
||||
public const int TK_GROUP = 123;
|
||||
public const int TK_HAVING = 124;
|
||||
public const int TK_LIMIT = 125;
|
||||
public const int TK_WHERE = 126;
|
||||
public const int TK_INTO = 127;
|
||||
public const int TK_VALUES = 128;
|
||||
public const int TK_INTEGER = 129;
|
||||
public const int TK_FLOAT = 130;
|
||||
public const int TK_BLOB = 131;
|
||||
public const int TK_REGISTER = 132;
|
||||
public const int TK_VARIABLE = 133;
|
||||
public const int TK_CASE = 134;
|
||||
public const int TK_WHEN = 135;
|
||||
public const int TK_THEN = 136;
|
||||
public const int TK_ELSE = 137;
|
||||
public const int TK_INDEX = 138;
|
||||
public const int TK_ALTER = 139;
|
||||
public const int TK_ADD = 140;
|
||||
public const int TK_TO_TEXT = 141;
|
||||
public const int TK_TO_BLOB = 142;
|
||||
public const int TK_TO_NUMERIC = 143;
|
||||
public const int TK_TO_INT = 144;
|
||||
public const int TK_TO_REAL = 145;
|
||||
public const int TK_ISNOT = 146;
|
||||
public const int TK_END_OF_FILE = 147;
|
||||
public const int TK_ILLEGAL = 148;
|
||||
public const int TK_SPACE = 149;
|
||||
public const int TK_UNCLOSED_STRING = 150;
|
||||
public const int TK_FUNCTION = 151;
|
||||
public const int TK_COLUMN = 152;
|
||||
public const int TK_AGG_FUNCTION = 153;
|
||||
public const int TK_AGG_COLUMN = 154;
|
||||
public const int TK_CONST_FUNC = 155;
|
||||
public const int TK_UMINUS = 156;
|
||||
public const int TK_UPLUS = 157;
|
||||
}
|
||||
}
|
||||
1171
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/pcache1_c.cs
Normal file
1171
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/pcache1_c.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,746 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
using u32 = System.UInt32;
|
||||
using Pgno = System.UInt32;
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
using sqlite3_value = Sqlite3.Mem;
|
||||
using sqlite3_pcache = Sqlite3.PCache1;
|
||||
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/*
|
||||
** 2008 August 05
|
||||
**
|
||||
** 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 that page cache.
|
||||
*************************************************************************
|
||||
** 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"
|
||||
|
||||
/*
|
||||
** A complete page cache is an instance of this structure.
|
||||
*/
|
||||
public class PCache
|
||||
{
|
||||
public PgHdr pDirty, pDirtyTail; /* List of dirty pages in LRU order */
|
||||
public PgHdr pSynced; /* Last synced page in dirty page list */
|
||||
public int _nRef; /* Number of referenced pages */
|
||||
public int nMax; /* Configured cache size */
|
||||
public int szPage; /* Size of every page in this cache */
|
||||
public int szExtra; /* Size of extra space for each page */
|
||||
public bool bPurgeable; /* True if pages are on backing store */
|
||||
public dxStress xStress; //int (*xStress)(void*,PgHdr*); /* Call to try make a page clean */
|
||||
public object pStress; /* Argument to xStress */
|
||||
public sqlite3_pcache pCache; /* Pluggable cache module */
|
||||
public PgHdr pPage1; /* Reference to page 1 */
|
||||
|
||||
public int nRef /* Number of referenced pages */
|
||||
{
|
||||
get
|
||||
{
|
||||
return _nRef;
|
||||
}
|
||||
set
|
||||
{
|
||||
_nRef = value;
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
pDirty = null;
|
||||
pDirtyTail = null;
|
||||
pSynced = null;
|
||||
nRef = 0;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
** Some of the Debug.Assert() macros in this code are too expensive to run
|
||||
** even during normal debugging. Use them only rarely on long-running
|
||||
** tests. Enable the expensive asserts using the
|
||||
** -DSQLITE_ENABLE_EXPENSIVE_ASSERT=1 compile-time option.
|
||||
*/
|
||||
#if SQLITE_ENABLE_EXPENSIVE_ASSERT
|
||||
//# define expensive_assert(X) Debug.Assert(X)
|
||||
static void expensive_assert( bool x ) { Debug.Assert( x ); }
|
||||
#else
|
||||
//# define expensive_assert(X)
|
||||
#endif
|
||||
|
||||
/********************************** Linked List Management ********************/
|
||||
|
||||
#if !NDEBUG && SQLITE_ENABLE_EXPENSIVE_ASSERT
|
||||
/*
|
||||
** Check that the pCache.pSynced variable is set correctly. If it
|
||||
** is not, either fail an Debug.Assert or return zero. Otherwise, return
|
||||
** non-zero. This is only used in debugging builds, as follows:
|
||||
**
|
||||
** expensive_assert( pcacheCheckSynced(pCache) );
|
||||
*/
|
||||
static int pcacheCheckSynced(PCache pCache){
|
||||
PgHdr p ;
|
||||
for(p=pCache.pDirtyTail; p!=pCache.pSynced; p=p.pDirtyPrev){
|
||||
Debug.Assert( p.nRef !=0|| (p.flags&PGHDR_NEED_SYNC) !=0);
|
||||
}
|
||||
return (p==null || p.nRef!=0 || (p.flags&PGHDR_NEED_SYNC)==0)?1:0;
|
||||
}
|
||||
#endif //* !NDEBUG && SQLITE_ENABLE_EXPENSIVE_ASSERT */
|
||||
|
||||
/*
|
||||
** Remove page pPage from the list of dirty pages.
|
||||
*/
|
||||
static void pcacheRemoveFromDirtyList( PgHdr pPage )
|
||||
{
|
||||
PCache p = pPage.pCache;
|
||||
|
||||
Debug.Assert( pPage.pDirtyNext != null || pPage == p.pDirtyTail );
|
||||
Debug.Assert( pPage.pDirtyPrev != null || pPage == p.pDirty );
|
||||
|
||||
/* Update the PCache1.pSynced variable if necessary. */
|
||||
if ( p.pSynced == pPage )
|
||||
{
|
||||
PgHdr pSynced = pPage.pDirtyPrev;
|
||||
while ( pSynced != null && ( pSynced.flags & PGHDR_NEED_SYNC ) != 0 )
|
||||
{
|
||||
pSynced = pSynced.pDirtyPrev;
|
||||
}
|
||||
p.pSynced = pSynced;
|
||||
}
|
||||
|
||||
if ( pPage.pDirtyNext != null )
|
||||
{
|
||||
pPage.pDirtyNext.pDirtyPrev = pPage.pDirtyPrev;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert( pPage == p.pDirtyTail );
|
||||
p.pDirtyTail = pPage.pDirtyPrev;
|
||||
}
|
||||
if ( pPage.pDirtyPrev != null )
|
||||
{
|
||||
pPage.pDirtyPrev.pDirtyNext = pPage.pDirtyNext;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert( pPage == p.pDirty );
|
||||
p.pDirty = pPage.pDirtyNext;
|
||||
}
|
||||
pPage.pDirtyNext = null;
|
||||
pPage.pDirtyPrev = null;
|
||||
|
||||
#if SQLITE_ENABLE_EXPENSIVE_ASSERT
|
||||
expensive_assert( pcacheCheckSynced(p) );
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** Add page pPage to the head of the dirty list (PCache1.pDirty is set to
|
||||
** pPage).
|
||||
*/
|
||||
static void pcacheAddToDirtyList( PgHdr pPage )
|
||||
{
|
||||
PCache p = pPage.pCache;
|
||||
|
||||
Debug.Assert( pPage.pDirtyNext == null && pPage.pDirtyPrev == null && p.pDirty != pPage );
|
||||
|
||||
pPage.pDirtyNext = p.pDirty;
|
||||
if ( pPage.pDirtyNext != null )
|
||||
{
|
||||
Debug.Assert( pPage.pDirtyNext.pDirtyPrev == null );
|
||||
pPage.pDirtyNext.pDirtyPrev = pPage;
|
||||
}
|
||||
p.pDirty = pPage;
|
||||
if ( null == p.pDirtyTail )
|
||||
{
|
||||
p.pDirtyTail = pPage;
|
||||
}
|
||||
if ( null == p.pSynced && 0 == ( pPage.flags & PGHDR_NEED_SYNC ) )
|
||||
{
|
||||
p.pSynced = pPage;
|
||||
}
|
||||
#if SQLITE_ENABLE_EXPENSIVE_ASSERT
|
||||
expensive_assert( pcacheCheckSynced(p) );
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** Wrapper around the pluggable caches xUnpin method. If the cache is
|
||||
** being used for an in-memory database, this function is a no-op.
|
||||
*/
|
||||
static void pcacheUnpin( PgHdr p )
|
||||
{
|
||||
PCache pCache = p.pCache;
|
||||
if ( pCache.bPurgeable )
|
||||
{
|
||||
if ( p.pgno == 1 )
|
||||
{
|
||||
pCache.pPage1 = null;
|
||||
}
|
||||
sqlite3GlobalConfig.pcache.xUnpin( pCache.pCache, p, false );
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************** General Interfaces ******
|
||||
**
|
||||
** Initialize and shutdown the page cache subsystem. Neither of these
|
||||
** functions are threadsafe.
|
||||
*/
|
||||
static int sqlite3PcacheInitialize()
|
||||
{
|
||||
if ( sqlite3GlobalConfig.pcache.xInit == null )
|
||||
{
|
||||
/* IMPLEMENTATION-OF: R-26801-64137 If the xInit() method is NULL, then the
|
||||
** built-in default page cache is used instead of the application defined
|
||||
** page cache. */
|
||||
sqlite3PCacheSetDefault();
|
||||
}
|
||||
return sqlite3GlobalConfig.pcache.xInit( sqlite3GlobalConfig.pcache.pArg );
|
||||
}
|
||||
static void sqlite3PcacheShutdown()
|
||||
{
|
||||
if ( sqlite3GlobalConfig.pcache.xShutdown != null )
|
||||
{
|
||||
/* IMPLEMENTATION-OF: R-26000-56589 The xShutdown() method may be NULL. */
|
||||
sqlite3GlobalConfig.pcache.xShutdown( sqlite3GlobalConfig.pcache.pArg );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the size in bytes of a PCache object.
|
||||
*/
|
||||
static int sqlite3PcacheSize()
|
||||
{
|
||||
return 4;
|
||||
}// sizeof( PCache ); }
|
||||
|
||||
/*
|
||||
** Create a new PCache object. Storage space to hold the object
|
||||
** has already been allocated and is passed in as the p pointer.
|
||||
** The caller discovers how much space needs to be allocated by
|
||||
** calling sqlite3PcacheSize().
|
||||
*/
|
||||
static void sqlite3PcacheOpen(
|
||||
int szPage, /* Size of every page */
|
||||
int szExtra, /* Extra space associated with each page */
|
||||
bool bPurgeable, /* True if pages are on backing store */
|
||||
dxStress xStress,//int (*xStress)(void*,PgHdr*),/* Call to try to make pages clean */
|
||||
object pStress, /* Argument to xStress */
|
||||
PCache p /* Preallocated space for the PCache */
|
||||
)
|
||||
{
|
||||
p.Clear();//memset(p, 0, sizeof(PCache));
|
||||
p.szPage = szPage;
|
||||
p.szExtra = szExtra;
|
||||
p.bPurgeable = bPurgeable;
|
||||
p.xStress = xStress;
|
||||
p.pStress = pStress;
|
||||
p.nMax = 100;
|
||||
}
|
||||
|
||||
/*
|
||||
** Change the page size for PCache object. The caller must ensure that there
|
||||
** are no outstanding page references when this function is called.
|
||||
*/
|
||||
static void sqlite3PcacheSetPageSize( PCache pCache, int szPage )
|
||||
{
|
||||
Debug.Assert( pCache.nRef == 0 && pCache.pDirty == null );
|
||||
if ( pCache.pCache != null )
|
||||
{
|
||||
sqlite3GlobalConfig.pcache.xDestroy( ref pCache.pCache );
|
||||
pCache.pCache = null;
|
||||
}
|
||||
pCache.szPage = szPage;
|
||||
}
|
||||
|
||||
/*
|
||||
** Try to obtain a page from the cache.
|
||||
*/
|
||||
static int sqlite3PcacheFetch(
|
||||
PCache pCache, /* Obtain the page from this cache */
|
||||
u32 pgno, /* Page number to obtain */
|
||||
int createFlag, /* If true, create page if it does not exist already */
|
||||
ref PgHdr ppPage /* Write the page here */
|
||||
)
|
||||
{
|
||||
PgHdr pPage = null;
|
||||
int eCreate;
|
||||
|
||||
Debug.Assert( pCache != null );
|
||||
Debug.Assert( createFlag == 1 || createFlag == 0 );
|
||||
Debug.Assert( pgno > 0 );
|
||||
|
||||
/* If the pluggable cache (sqlite3_pcache*) has not been allocated,
|
||||
** allocate it now.
|
||||
*/
|
||||
if ( null == pCache.pCache && createFlag != 0 )
|
||||
{
|
||||
sqlite3_pcache p;
|
||||
int nByte;
|
||||
nByte = pCache.szPage + pCache.szExtra + 0;// sizeof( PgHdr );
|
||||
p = sqlite3GlobalConfig.pcache.xCreate( nByte, pCache.bPurgeable );
|
||||
//if ( null == p )
|
||||
//{
|
||||
// return SQLITE_NOMEM;
|
||||
//}
|
||||
sqlite3GlobalConfig.pcache.xCachesize( p, pCache.nMax );
|
||||
pCache.pCache = p;
|
||||
}
|
||||
|
||||
eCreate = createFlag * ( 1 + ( ( !pCache.bPurgeable || null == pCache.pDirty ) ? 1 : 0 ) );
|
||||
|
||||
if ( pCache.pCache != null )
|
||||
{
|
||||
pPage = sqlite3GlobalConfig.pcache.xFetch( pCache.pCache, pgno, eCreate );
|
||||
}
|
||||
|
||||
if ( null == pPage && eCreate == 1 )
|
||||
{
|
||||
PgHdr pPg;
|
||||
|
||||
/* Find a dirty page to write-out and recycle. First try to find a
|
||||
** page that does not require a journal-sync (one with PGHDR_NEED_SYNC
|
||||
** cleared), but if that is not possible settle for any other
|
||||
** unreferenced dirty page.
|
||||
*/
|
||||
#if SQLITE_ENABLE_EXPENSIVE_ASSERT
|
||||
expensive_assert( pcacheCheckSynced(pCache) );
|
||||
#endif
|
||||
for ( pPg = pCache.pSynced;
|
||||
pPg != null && ( pPg.nRef != 0 || ( pPg.flags & PGHDR_NEED_SYNC ) != 0 );
|
||||
pPg = pPg.pDirtyPrev
|
||||
)
|
||||
;
|
||||
pCache.pSynced = pPg;
|
||||
if ( null == pPg )
|
||||
{
|
||||
for ( pPg = pCache.pDirtyTail; pPg != null && pPg.nRef != 0; pPg = pPg.pDirtyPrev )
|
||||
;
|
||||
}
|
||||
if ( pPg != null )
|
||||
{
|
||||
int rc;
|
||||
#if SQLITE_LOG_CACHE_SPILL
|
||||
sqlite3_log(SQLITE_FULL,
|
||||
"spill page %d making room for %d - cache used: %d/%d",
|
||||
pPg->pgno, pgno,
|
||||
sqlite3GlobalConfig.pcache.xPagecount(pCache->pCache),
|
||||
pCache->nMax);
|
||||
#endif
|
||||
rc = pCache.xStress( pCache.pStress, pPg );
|
||||
if ( rc != SQLITE_OK && rc != SQLITE_BUSY )
|
||||
{
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
pPage = sqlite3GlobalConfig.pcache.xFetch( pCache.pCache, pgno, 2 );
|
||||
}
|
||||
|
||||
if ( pPage != null )
|
||||
{
|
||||
if ( null == pPage.pData )
|
||||
{
|
||||
// memset(pPage, 0, sizeof(PgHdr));
|
||||
pPage.pData = sqlite3Malloc( pCache.szPage );// pPage->pData = (void*)&pPage[1];
|
||||
//pPage->pExtra = (void*)&((char*)pPage->pData)[pCache->szPage];
|
||||
//memset(pPage->pExtra, 0, pCache->szExtra);
|
||||
pPage.pCache = pCache;
|
||||
pPage.pgno = pgno;
|
||||
}
|
||||
Debug.Assert( pPage.pCache == pCache );
|
||||
Debug.Assert( pPage.pgno == pgno );
|
||||
//assert(pPage->pData == (void*)&pPage[1]);
|
||||
//assert(pPage->pExtra == (void*)&((char*)&pPage[1])[pCache->szPage]);
|
||||
if ( 0 == pPage.nRef )
|
||||
{
|
||||
pCache.nRef++;
|
||||
}
|
||||
pPage.nRef++;
|
||||
if ( pgno == 1 )
|
||||
{
|
||||
pCache.pPage1 = pPage;
|
||||
}
|
||||
}
|
||||
ppPage = pPage;
|
||||
return ( pPage == null && eCreate != 0 ) ? SQLITE_NOMEM : SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Decrement the reference count on a page. If the page is clean and the
|
||||
** reference count drops to 0, then it is made elible for recycling.
|
||||
*/
|
||||
static void sqlite3PcacheRelease( PgHdr p )
|
||||
{
|
||||
Debug.Assert( p.nRef > 0 );
|
||||
p.nRef--;
|
||||
if ( p.nRef == 0 )
|
||||
{
|
||||
PCache pCache = p.pCache;
|
||||
pCache.nRef--;
|
||||
if ( ( p.flags & PGHDR_DIRTY ) == 0 )
|
||||
{
|
||||
pcacheUnpin( p );
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Move the page to the head of the dirty list. */
|
||||
pcacheRemoveFromDirtyList( p );
|
||||
pcacheAddToDirtyList( p );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Increase the reference count of a supplied page by 1.
|
||||
*/
|
||||
static void sqlite3PcacheRef( PgHdr p )
|
||||
{
|
||||
Debug.Assert( p.nRef > 0 );
|
||||
p.nRef++;
|
||||
}
|
||||
|
||||
/*
|
||||
** Drop a page from the cache. There must be exactly one reference to the
|
||||
** page. This function deletes that reference, so after it returns the
|
||||
** page pointed to by p is invalid.
|
||||
*/
|
||||
static void sqlite3PcacheDrop( PgHdr p )
|
||||
{
|
||||
PCache pCache;
|
||||
Debug.Assert( p.nRef == 1 );
|
||||
if ( ( p.flags & PGHDR_DIRTY ) != 0 )
|
||||
{
|
||||
pcacheRemoveFromDirtyList( p );
|
||||
}
|
||||
pCache = p.pCache;
|
||||
pCache.nRef--;
|
||||
if ( p.pgno == 1 )
|
||||
{
|
||||
pCache.pPage1 = null;
|
||||
}
|
||||
sqlite3GlobalConfig.pcache.xUnpin( pCache.pCache, p, true );
|
||||
}
|
||||
|
||||
/*
|
||||
** Make sure the page is marked as dirty. If it isn't dirty already,
|
||||
** make it so.
|
||||
*/
|
||||
static void sqlite3PcacheMakeDirty( PgHdr p )
|
||||
{
|
||||
p.flags &= ~PGHDR_DONT_WRITE;
|
||||
Debug.Assert( p.nRef > 0 );
|
||||
if ( 0 == ( p.flags & PGHDR_DIRTY ) )
|
||||
{
|
||||
p.flags |= PGHDR_DIRTY;
|
||||
pcacheAddToDirtyList( p );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Make sure the page is marked as clean. If it isn't clean already,
|
||||
** make it so.
|
||||
*/
|
||||
static void sqlite3PcacheMakeClean( PgHdr p )
|
||||
{
|
||||
if ( ( p.flags & PGHDR_DIRTY ) != 0 )
|
||||
{
|
||||
pcacheRemoveFromDirtyList( p );
|
||||
p.flags &= ~( PGHDR_DIRTY | PGHDR_NEED_SYNC );
|
||||
if ( p.nRef == 0 )
|
||||
{
|
||||
pcacheUnpin( p );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Make every page in the cache clean.
|
||||
*/
|
||||
static void sqlite3PcacheCleanAll( PCache pCache )
|
||||
{
|
||||
PgHdr p;
|
||||
while ( ( p = pCache.pDirty ) != null )
|
||||
{
|
||||
sqlite3PcacheMakeClean( p );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Clear the PGHDR_NEED_SYNC flag from all dirty pages.
|
||||
*/
|
||||
static void sqlite3PcacheClearSyncFlags( PCache pCache )
|
||||
{
|
||||
PgHdr p;
|
||||
for ( p = pCache.pDirty; p != null; p = p.pDirtyNext )
|
||||
{
|
||||
p.flags &= ~PGHDR_NEED_SYNC;
|
||||
}
|
||||
pCache.pSynced = pCache.pDirtyTail;
|
||||
}
|
||||
|
||||
/*
|
||||
** Change the page number of page p to newPgno.
|
||||
*/
|
||||
static void sqlite3PcacheMove( PgHdr p, Pgno newPgno )
|
||||
{
|
||||
PCache pCache = p.pCache;
|
||||
Debug.Assert( p.nRef > 0 );
|
||||
Debug.Assert( newPgno > 0 );
|
||||
sqlite3GlobalConfig.pcache.xRekey( pCache.pCache, p, p.pgno, newPgno );
|
||||
p.pgno = newPgno;
|
||||
if ( ( p.flags & PGHDR_DIRTY ) != 0 && ( p.flags & PGHDR_NEED_SYNC ) != 0 )
|
||||
{
|
||||
pcacheRemoveFromDirtyList( p );
|
||||
pcacheAddToDirtyList( p );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Drop every cache entry whose page number is greater than "pgno". The
|
||||
** caller must ensure that there are no outstanding references to any pages
|
||||
** other than page 1 with a page number greater than pgno.
|
||||
**
|
||||
** If there is a reference to page 1 and the pgno parameter passed to this
|
||||
** function is 0, then the data area associated with page 1 is zeroed, but
|
||||
** the page object is not dropped.
|
||||
*/
|
||||
static void sqlite3PcacheTruncate( PCache pCache, u32 pgno )
|
||||
{
|
||||
if ( pCache.pCache != null )
|
||||
{
|
||||
PgHdr p;
|
||||
PgHdr pNext;
|
||||
for ( p = pCache.pDirty; p != null; p = pNext )
|
||||
{
|
||||
pNext = p.pDirtyNext;
|
||||
/* This routine never gets call with a positive pgno except right
|
||||
** after sqlite3PcacheCleanAll(). So if there are dirty pages,
|
||||
** it must be that pgno==0.
|
||||
*/
|
||||
Debug.Assert( p.pgno > 0 );
|
||||
if ( ALWAYS( p.pgno > pgno ) )
|
||||
{
|
||||
Debug.Assert( ( p.flags & PGHDR_DIRTY ) != 0 );
|
||||
sqlite3PcacheMakeClean( p );
|
||||
}
|
||||
}
|
||||
if ( pgno == 0 && pCache.pPage1 != null )
|
||||
{
|
||||
// memset( pCache.pPage1.pData, 0, pCache.szPage );
|
||||
pCache.pPage1.pData = sqlite3Malloc( pCache.szPage );
|
||||
pgno = 1;
|
||||
}
|
||||
sqlite3GlobalConfig.pcache.xTruncate( pCache.pCache, pgno + 1 );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Close a cache.
|
||||
*/
|
||||
static void sqlite3PcacheClose( PCache pCache )
|
||||
{
|
||||
if ( pCache.pCache != null )
|
||||
{
|
||||
sqlite3GlobalConfig.pcache.xDestroy( ref pCache.pCache );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Discard the contents of the cache.
|
||||
*/
|
||||
static void sqlite3PcacheClear( PCache pCache )
|
||||
{
|
||||
sqlite3PcacheTruncate( pCache, 0 );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Merge two lists of pages connected by pDirty and in pgno order.
|
||||
** Do not both fixing the pDirtyPrev pointers.
|
||||
*/
|
||||
static PgHdr pcacheMergeDirtyList( PgHdr pA, PgHdr pB )
|
||||
{
|
||||
PgHdr result = new PgHdr();
|
||||
PgHdr pTail = result;
|
||||
while ( pA != null && pB != null )
|
||||
{
|
||||
if ( pA.pgno < pB.pgno )
|
||||
{
|
||||
pTail.pDirty = pA;
|
||||
pTail = pA;
|
||||
pA = pA.pDirty;
|
||||
}
|
||||
else
|
||||
{
|
||||
pTail.pDirty = pB;
|
||||
pTail = pB;
|
||||
pB = pB.pDirty;
|
||||
}
|
||||
}
|
||||
if ( pA != null )
|
||||
{
|
||||
pTail.pDirty = pA;
|
||||
}
|
||||
else if ( pB != null )
|
||||
{
|
||||
pTail.pDirty = pB;
|
||||
}
|
||||
else
|
||||
{
|
||||
pTail.pDirty = null;
|
||||
}
|
||||
return result.pDirty;
|
||||
}
|
||||
|
||||
/*
|
||||
** Sort the list of pages in accending order by pgno. Pages are
|
||||
** connected by pDirty pointers. The pDirtyPrev pointers are
|
||||
** corrupted by this sort.
|
||||
**
|
||||
** Since there cannot be more than 2^31 distinct pages in a database,
|
||||
** there cannot be more than 31 buckets required by the merge sorter.
|
||||
** One extra bucket is added to catch overflow in case something
|
||||
** ever changes to make the previous sentence incorrect.
|
||||
*/
|
||||
//#define N_SORT_BUCKET 32
|
||||
const int N_SORT_BUCKET = 32;
|
||||
|
||||
static PgHdr pcacheSortDirtyList( PgHdr pIn )
|
||||
{
|
||||
PgHdr[] a;
|
||||
PgHdr p;//a[N_SORT_BUCKET], p;
|
||||
int i;
|
||||
a = new PgHdr[N_SORT_BUCKET];//memset(a, 0, sizeof(a));
|
||||
while ( pIn != null )
|
||||
{
|
||||
p = pIn;
|
||||
pIn = p.pDirty;
|
||||
p.pDirty = null;
|
||||
for ( i = 0; ALWAYS( i < N_SORT_BUCKET - 1 ); i++ )
|
||||
{
|
||||
if ( a[i] == null )
|
||||
{
|
||||
a[i] = p;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
p = pcacheMergeDirtyList( a[i], p );
|
||||
a[i] = null;
|
||||
}
|
||||
}
|
||||
if ( NEVER( i == N_SORT_BUCKET - 1 ) )
|
||||
{
|
||||
/* To get here, there need to be 2^(N_SORT_BUCKET) elements in
|
||||
** the input list. But that is impossible.
|
||||
*/
|
||||
a[i] = pcacheMergeDirtyList( a[i], p );
|
||||
}
|
||||
}
|
||||
p = a[0];
|
||||
for ( i = 1; i < N_SORT_BUCKET; i++ )
|
||||
{
|
||||
p = pcacheMergeDirtyList( p, a[i] );
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a list of all dirty pages in the cache, sorted by page number.
|
||||
*/
|
||||
static PgHdr sqlite3PcacheDirtyList( PCache pCache )
|
||||
{
|
||||
PgHdr p;
|
||||
for ( p = pCache.pDirty; p != null; p = p.pDirtyNext )
|
||||
{
|
||||
p.pDirty = p.pDirtyNext;
|
||||
}
|
||||
return pcacheSortDirtyList( pCache.pDirty );
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the total number of referenced pages held by the cache.
|
||||
*/
|
||||
static int sqlite3PcacheRefCount( PCache pCache )
|
||||
{
|
||||
return pCache.nRef;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the number of references to the page supplied as an argument.
|
||||
*/
|
||||
static int sqlite3PcachePageRefcount( PgHdr p )
|
||||
{
|
||||
return p.nRef;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the total number of pages in the cache.
|
||||
*/
|
||||
static int sqlite3PcachePagecount( PCache pCache )
|
||||
{
|
||||
int nPage = 0;
|
||||
if ( pCache.pCache != null )
|
||||
{
|
||||
nPage = sqlite3GlobalConfig.pcache.xPagecount( pCache.pCache );
|
||||
}
|
||||
return nPage;
|
||||
}
|
||||
|
||||
#if SQLITE_TEST
|
||||
/*
|
||||
** Get the suggested cache-size value.
|
||||
*/
|
||||
static int sqlite3PcacheGetCachesize( PCache pCache )
|
||||
{
|
||||
return pCache.nMax;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Set the suggested cache-size value.
|
||||
*/
|
||||
static void sqlite3PcacheSetCachesize( PCache pCache, int mxPage )
|
||||
{
|
||||
pCache.nMax = mxPage;
|
||||
if ( pCache.pCache != null )
|
||||
{
|
||||
sqlite3GlobalConfig.pcache.xCachesize( pCache.pCache, mxPage );
|
||||
}
|
||||
}
|
||||
|
||||
#if SQLITE_CHECK_PAGES || (SQLITE_DEBUG)
|
||||
/*
|
||||
** For all dirty pages currently in the cache, invoke the specified
|
||||
** callback. This is only used if the SQLITE_CHECK_PAGES macro is
|
||||
** defined.
|
||||
*/
|
||||
static void sqlite3PcacheIterateDirty( PCache pCache, dxIter xIter )
|
||||
{
|
||||
PgHdr pDirty;
|
||||
for ( pDirty = pCache.pDirty; pDirty != null; pDirty = pDirty.pDirtyNext )
|
||||
{
|
||||
xIter( pDirty );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
using i16 = System.Int16;
|
||||
using u32 = System.UInt32;
|
||||
using Pgno = System.UInt32;
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/*
|
||||
** 2008 August 05
|
||||
**
|
||||
** 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 page cache
|
||||
** subsystem.
|
||||
*************************************************************************
|
||||
** 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 !_PCACHE_H_
|
||||
|
||||
//typedef struct PgHdr PgHdr;
|
||||
//typedef struct PCache PCache;
|
||||
|
||||
/*
|
||||
** Every page in the cache is controlled by an instance of the following
|
||||
** structure.
|
||||
*/
|
||||
public class PgHdr
|
||||
{
|
||||
public byte[] pData; /* Content of this page */
|
||||
public MemPage pExtra; /* Extra content */
|
||||
public PgHdr pDirty; /* Transient list of dirty pages */
|
||||
public Pgno pgno; /* The page number for this page */
|
||||
public Pager pPager; /* The pager to which this page belongs */
|
||||
#if SQLITE_CHECK_PAGES || (SQLITE_DEBUG)
|
||||
public int pageHash; /* Hash of page content */
|
||||
#endif
|
||||
public int flags; /* PGHDR flags defined below */
|
||||
/**********************************************************************
|
||||
** Elements above are public. All that follows is private to pcache.c
|
||||
** and should not be accessed by other modules.
|
||||
*/
|
||||
public int nRef; /* Number of users of this page */
|
||||
public PCache pCache; /* Cache that owns this page */
|
||||
public bool CacheAllocated; /* True, if allocated from cache */
|
||||
|
||||
public PgHdr pDirtyNext; /* Next element in list of dirty pages */
|
||||
public PgHdr pDirtyPrev; /* Previous element in list of dirty pages */
|
||||
public PgHdr1 pPgHdr1; /* Cache page header this this page */
|
||||
|
||||
public static implicit operator bool( PgHdr b )
|
||||
{
|
||||
return ( b != null );
|
||||
}
|
||||
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
sqlite3_free( ref this.pData );
|
||||
this.pData = null;
|
||||
this.pExtra = null;
|
||||
this.pDirty = null;
|
||||
this.pgno = 0;
|
||||
this.pPager = null;
|
||||
#if SQLITE_CHECK_PAGES
|
||||
this.pageHash=0;
|
||||
#endif
|
||||
this.flags = 0;
|
||||
this.nRef = 0;
|
||||
this.CacheAllocated = false;
|
||||
this.pCache = null;
|
||||
this.pDirtyNext = null;
|
||||
this.pDirtyPrev = null;
|
||||
this.pPgHdr1 = null;
|
||||
}
|
||||
};
|
||||
|
||||
/* Bit values for PgHdr.flags */
|
||||
//#define PGHDR_DIRTY 0x002 /* Page has changed */
|
||||
//#define PGHDR_NEED_SYNC 0x004 /* Fsync the rollback journal before
|
||||
// ** writing this page to the database */
|
||||
//#define PGHDR_NEED_READ 0x008 /* Content is unread */
|
||||
//#define PGHDR_REUSE_UNLIKELY 0x010 /* A hint that reuse is unlikely */
|
||||
//#define PGHDR_DONT_WRITE 0x020 /* Do not write content to disk */
|
||||
|
||||
const int PGHDR_DIRTY = 0x002; /* Page has changed */
|
||||
const int PGHDR_NEED_SYNC = 0x004;/* Fsync the rollback journal before
|
||||
** writing this page to the database */
|
||||
const int PGHDR_NEED_READ = 0x008;/* Content is unread */
|
||||
const int PGHDR_REUSE_UNLIKELY = 0x010;/* A hint that reuse is unlikely */
|
||||
const int PGHDR_DONT_WRITE = 0x020;/* Do not write content to disk */
|
||||
|
||||
/* Initialize and shutdown the page cache subsystem */
|
||||
//int sqlite3PcacheInitialize(void);
|
||||
//void sqlite3PcacheShutdown(void);
|
||||
|
||||
/* Page cache buffer management:
|
||||
** These routines implement SQLITE_CONFIG_PAGECACHE.
|
||||
*/
|
||||
//void sqlite3PCacheBufferSetup(void *, int sz, int n);
|
||||
|
||||
/* Create a new pager cache.
|
||||
** Under memory stress, invoke xStress to try to make pages clean.
|
||||
** Only clean and unpinned pages can be reclaimed.
|
||||
*/
|
||||
//void sqlite3PcacheOpen(
|
||||
// int szPage, /* Size of every page */
|
||||
// int szExtra, /* Extra space associated with each page */
|
||||
// int bPurgeable, /* True if pages are on backing store */
|
||||
// int (*xStress)(void*, PgHdr*), /* Call to try to make pages clean */
|
||||
// void pStress, /* Argument to xStress */
|
||||
// PCache pToInit /* Preallocated space for the PCache */
|
||||
//);
|
||||
|
||||
/* Modify the page-size after the cache has been created. */
|
||||
//void sqlite3PcacheSetPageSize(PCache *, int);
|
||||
|
||||
/* Return the size in bytes of a PCache object. Used to preallocate
|
||||
** storage space.
|
||||
*/
|
||||
//int sqlite3PcacheSize(void);
|
||||
|
||||
/* One release per successful fetch. Page is pinned until released.
|
||||
** Reference counted.
|
||||
*/
|
||||
//int sqlite3PcacheFetch(PCache*, Pgno, int createFlag, PgHdr**);
|
||||
//void sqlite3PcacheRelease(PgHdr*);
|
||||
|
||||
//void sqlite3PcacheDrop(PgHdr*); /* Remove page from cache */
|
||||
//void sqlite3PcacheMakeDirty(PgHdr*); /* Make sure page is marked dirty */
|
||||
//void sqlite3PcacheMakeClean(PgHdr*); /* Mark a single page as clean */
|
||||
//void sqlite3PcacheCleanAll(PCache*); /* Mark all dirty list pages as clean */
|
||||
|
||||
/* Change a page number. Used by incr-vacuum. */
|
||||
//void sqlite3PcacheMove(PgHdr*, Pgno);
|
||||
|
||||
/* Remove all pages with pgno>x. Reset the cache if x==0 */
|
||||
//void sqlite3PcacheTruncate(PCache*, Pgno x);
|
||||
|
||||
/* Get a list of all dirty pages in the cache, sorted by page number */
|
||||
//PgHdr *sqlite3PcacheDirtyList(PCache*);
|
||||
|
||||
/* Reset and close the cache object */
|
||||
//void sqlite3PcacheClose(PCache*);
|
||||
|
||||
/* Clear flags from pages of the page cache */
|
||||
//void sqlite3PcacheClearSyncFlags(PCache *);
|
||||
|
||||
/* Discard the contents of the cache */
|
||||
//void sqlite3PcacheClear(PCache*);
|
||||
|
||||
/* Return the total number of outstanding page references */
|
||||
//int sqlite3PcacheRefCount(PCache*);
|
||||
|
||||
/* Increment the reference count of an existing page */
|
||||
//void sqlite3PcacheRef(PgHdr*);
|
||||
|
||||
//int sqlite3PcachePageRefcount(PgHdr*);
|
||||
|
||||
|
||||
/* Return the total number of pages stored in the cache */
|
||||
//int sqlite3PcachePagecount(PCache*);
|
||||
|
||||
#if SQLITE_CHECK_PAGES
|
||||
/* Iterate through all dirty pages currently stored in the cache. This
|
||||
** interface is only available if SQLITE_CHECK_PAGES is defined when the
|
||||
** library is built.
|
||||
*/
|
||||
|
||||
//void sqlite3PcacheIterateDirty(PCache pCache, void (*xIter)(PgHdr *));
|
||||
#endif
|
||||
|
||||
/* Set and get the suggested cache-size for the specified pager-cache.
|
||||
**
|
||||
** If no global maximum is configured, then the system attempts to limit
|
||||
** the total number of pages cached by purgeable pager-caches to the sum
|
||||
** of the suggested cache-sizes.
|
||||
*/
|
||||
//void sqlite3PcacheSetCachesize(PCache *, int);
|
||||
#if SQLITE_TEST
|
||||
//int sqlite3PcacheGetCachesize(PCache *);
|
||||
#endif
|
||||
|
||||
#if SQLITE_ENABLE_MEMORY_MANAGEMENT
|
||||
/* Try to return memory used by the pcache module to the main memory heap */
|
||||
//int sqlite3PcacheReleaseMemory(int);
|
||||
#endif
|
||||
|
||||
#if SQLITE_TEST
|
||||
//void sqlite3PcacheStats(int*,int*,int*,int*);
|
||||
#endif
|
||||
|
||||
//void sqlite3PCacheSetDefault(void);
|
||||
|
||||
#endif //* _PCACHE_H_ */
|
||||
}
|
||||
}
|
||||
1887
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/pragma_c.cs
Normal file
1887
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/pragma_c.cs
Normal file
File diff suppressed because it is too large
Load Diff
1080
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/prepare_c.cs
Normal file
1080
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/prepare_c.cs
Normal file
File diff suppressed because it is too large
Load Diff
1446
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/printf_c.cs
Normal file
1446
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/printf_c.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,203 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
using i64 = System.Int64;
|
||||
using u8 = System.Byte;
|
||||
using u32 = System.UInt32;
|
||||
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.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file contains code to implement a pseudo-random number
|
||||
** generator (PRNG) for SQLite.
|
||||
**
|
||||
** Random numbers are used by some of the database backends in order
|
||||
** to generate random integer keys for tables or random filenames.
|
||||
*************************************************************************
|
||||
** 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 threads share a single random number generator.
|
||||
** This structure is the current state of the generator.
|
||||
*/
|
||||
public class sqlite3PrngType
|
||||
{
|
||||
public bool isInit; /* True if initialized */
|
||||
public int i;
|
||||
public int j; /* State variables */
|
||||
public u8[] s = new u8[256]; /* State variables */
|
||||
|
||||
public sqlite3PrngType Copy()
|
||||
{
|
||||
sqlite3PrngType cp = (sqlite3PrngType)MemberwiseClone();
|
||||
cp.s = new u8[s.Length];
|
||||
Array.Copy( s, cp.s, s.Length );
|
||||
return cp;
|
||||
}
|
||||
}
|
||||
public static sqlite3PrngType sqlite3Prng = new sqlite3PrngType();
|
||||
/*
|
||||
** Get a single 8-bit random value from the RC4 PRNG. The Mutex
|
||||
** must be held while executing this routine.
|
||||
**
|
||||
** Why not just use a library random generator like lrand48() for this?
|
||||
** Because the OP_NewRowid opcode in the VDBE depends on having a very
|
||||
** good source of random numbers. The lrand48() library function may
|
||||
** well be good enough. But maybe not. Or maybe lrand48() has some
|
||||
** subtle problems on some systems that could cause problems. It is hard
|
||||
** to know. To minimize the risk of problems due to bad lrand48()
|
||||
** implementations, SQLite uses this random number generator based
|
||||
** on RC4, which we know works very well.
|
||||
**
|
||||
** (Later): Actually, OP_NewRowid does not depend on a good source of
|
||||
** randomness any more. But we will leave this code in all the same.
|
||||
*/
|
||||
static u8 randomu8()
|
||||
{
|
||||
u8 t;
|
||||
|
||||
/* The "wsdPrng" macro will resolve to the pseudo-random number generator
|
||||
** 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, wsdPrng can refer directly
|
||||
** to the "sqlite3Prng" state vector declared above.
|
||||
*/
|
||||
#if SQLITE_OMIT_WSD
|
||||
struct sqlite3PrngType *p = &GLOBAL(struct sqlite3PrngType, sqlite3Prng);
|
||||
//# define wsdPrng p[0]
|
||||
#else
|
||||
//# define wsdPrng sqlite3Prng
|
||||
sqlite3PrngType wsdPrng = sqlite3Prng;
|
||||
#endif
|
||||
|
||||
|
||||
/* Initialize the state of the random number generator once,
|
||||
** the first time this routine is called. The seed value does
|
||||
** not need to contain a lot of randomness since we are not
|
||||
** trying to do secure encryption or anything like that...
|
||||
**
|
||||
** Nothing in this file or anywhere else in SQLite does any kind of
|
||||
** encryption. The RC4 algorithm is being used as a PRNG (pseudo-random
|
||||
** number generator) not as an encryption device.
|
||||
*/
|
||||
if ( !wsdPrng.isInit )
|
||||
{
|
||||
int i;
|
||||
u8[] k = new u8[256];
|
||||
wsdPrng.j = 0;
|
||||
wsdPrng.i = 0;
|
||||
sqlite3OsRandomness( sqlite3_vfs_find( "" ), 256, k );
|
||||
for ( i = 0; i < 255; i++ )
|
||||
{
|
||||
wsdPrng.s[i] = (u8)i;
|
||||
}
|
||||
for ( i = 0; i < 255; i++ )
|
||||
{
|
||||
wsdPrng.j = (u8)( wsdPrng.j + wsdPrng.s[i] + k[i] );
|
||||
t = wsdPrng.s[wsdPrng.j];
|
||||
wsdPrng.s[wsdPrng.j] = wsdPrng.s[i];
|
||||
wsdPrng.s[i] = t;
|
||||
}
|
||||
wsdPrng.isInit = true;
|
||||
}
|
||||
|
||||
/* Generate and return single random u8
|
||||
*/
|
||||
wsdPrng.i++;
|
||||
t = wsdPrng.s[(u8)wsdPrng.i];
|
||||
wsdPrng.j = (u8)( wsdPrng.j + t );
|
||||
wsdPrng.s[(u8)wsdPrng.i] = wsdPrng.s[wsdPrng.j];
|
||||
wsdPrng.s[wsdPrng.j] = t;
|
||||
t += wsdPrng.s[(u8)wsdPrng.i];
|
||||
return wsdPrng.s[t];
|
||||
}
|
||||
|
||||
/*
|
||||
** Return N random u8s.
|
||||
*/
|
||||
static void sqlite3_randomness( int N, ref i64 pBuf )
|
||||
{
|
||||
u8[] zBuf = new u8[N];
|
||||
pBuf = 0;
|
||||
#if SQLITE_THREADSAFE
|
||||
sqlite3_mutex mutex = sqlite3MutexAlloc( SQLITE_MUTEX_STATIC_PRNG );
|
||||
#endif
|
||||
sqlite3_mutex_enter( mutex );
|
||||
while ( N-- > 0 )
|
||||
{
|
||||
pBuf = (u32)( ( pBuf << 8 ) + randomu8() );// zBuf[N] = randomu8();
|
||||
}
|
||||
sqlite3_mutex_leave( mutex );
|
||||
}
|
||||
|
||||
static void sqlite3_randomness( byte[] pBuf, int Offset, int N )
|
||||
{
|
||||
i64 iBuf = System.DateTime.Now.Ticks;
|
||||
#if SQLITE_THREADSAFE
|
||||
sqlite3_mutex mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_PRNG);
|
||||
#endif
|
||||
sqlite3_mutex_enter( mutex );
|
||||
while ( N-- > 0 )
|
||||
{
|
||||
iBuf = (u32)( ( iBuf << 8 ) + randomu8() );// zBuf[N] = randomu8();
|
||||
pBuf[Offset++] = (byte)iBuf;
|
||||
}
|
||||
sqlite3_mutex_leave( mutex );
|
||||
}
|
||||
|
||||
#if !SQLITE_OMIT_BUILTIN_TEST
|
||||
/*
|
||||
** For testing purposes, we sometimes want to preserve the state of
|
||||
** PRNG and restore the PRNG to its saved state at a later time, or
|
||||
** to reset the PRNG to its initial state. These routines accomplish
|
||||
** those tasks.
|
||||
**
|
||||
** The sqlite3_test_control() interface calls these routines to
|
||||
** control the PRNG.
|
||||
*/
|
||||
static sqlite3PrngType sqlite3SavedPrng = null;
|
||||
static void sqlite3PrngSaveState()
|
||||
{
|
||||
sqlite3SavedPrng = sqlite3Prng.Copy();
|
||||
// memcpy(
|
||||
// &GLOBAL(struct sqlite3PrngType, sqlite3SavedPrng),
|
||||
// &GLOBAL(struct sqlite3PrngType, sqlite3Prng),
|
||||
// sizeof(sqlite3Prng)
|
||||
//);
|
||||
}
|
||||
static void sqlite3PrngRestoreState()
|
||||
{
|
||||
sqlite3Prng = sqlite3SavedPrng.Copy();
|
||||
//memcpy(
|
||||
// &GLOBAL(struct sqlite3PrngType, sqlite3Prng),
|
||||
// &GLOBAL(struct sqlite3PrngType, sqlite3SavedPrng),
|
||||
// sizeof(sqlite3Prng)
|
||||
//);
|
||||
}
|
||||
static void sqlite3PrngResetState()
|
||||
{
|
||||
sqlite3Prng.isInit = false;// GLOBAL(struct sqlite3PrngType, sqlite3Prng).isInit = 0;
|
||||
}
|
||||
#endif //* SQLITE_OMIT_BUILTIN_TEST */
|
||||
}
|
||||
}
|
||||
1420
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/resolve_c.cs
Normal file
1420
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/resolve_c.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,519 @@
|
||||
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;
|
||||
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/*
|
||||
** 2008 December 3
|
||||
**
|
||||
** 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 module implements an object we call a "RowSet".
|
||||
**
|
||||
** The RowSet object is a collection of rowids. Rowids
|
||||
** are inserted into the RowSet in an arbitrary order. Inserts
|
||||
** can be intermixed with tests to see if a given rowid has been
|
||||
** previously inserted into the RowSet.
|
||||
**
|
||||
** After all inserts are finished, it is possible to extract the
|
||||
** elements of the RowSet in sorted order. Once this extraction
|
||||
** process has started, no new elements may be inserted.
|
||||
**
|
||||
** Hence, the primitive operations for a RowSet are:
|
||||
**
|
||||
** CREATE
|
||||
** INSERT
|
||||
** TEST
|
||||
** SMALLEST
|
||||
** DESTROY
|
||||
**
|
||||
** The CREATE and DESTROY primitives are the constructor and destructor,
|
||||
** obviously. The INSERT primitive adds a new element to the RowSet.
|
||||
** TEST checks to see if an element is already in the RowSet. SMALLEST
|
||||
** extracts the least value from the RowSet.
|
||||
**
|
||||
** The INSERT primitive might allocate additional memory. Memory is
|
||||
** allocated in chunks so most INSERTs do no allocation. There is an
|
||||
** upper bound on the size of allocated memory. No memory is freed
|
||||
** until DESTROY.
|
||||
**
|
||||
** The TEST primitive includes a "batch" number. The TEST primitive
|
||||
** will only see elements that were inserted before the last change
|
||||
** in the batch number. In other words, if an INSERT occurs between
|
||||
** two TESTs where the TESTs have the same batch nubmer, then the
|
||||
** value added by the INSERT will not be visible to the second TEST.
|
||||
** The initial batch number is zero, so if the very first TEST contains
|
||||
** a non-zero batch number, it will see all prior INSERTs.
|
||||
**
|
||||
** No INSERTs may occurs after a SMALLEST. An assertion will fail if
|
||||
** that is attempted.
|
||||
**
|
||||
** The cost of an INSERT is roughly constant. (Sometime new memory
|
||||
** has to be allocated on an INSERT.) The cost of a TEST with a new
|
||||
** batch number is O(NlogN) where N is the number of elements in the RowSet.
|
||||
** The cost of a TEST using the same batch number is O(logN). The cost
|
||||
** of the first SMALLEST is O(NlogN). Second and subsequent SMALLEST
|
||||
** primitives are constant time. The cost of DESTROY is O(N).
|
||||
**
|
||||
** There is an added cost of O(N) when switching between TEST and
|
||||
** SMALLEST primitives.
|
||||
*************************************************************************
|
||||
** 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"
|
||||
|
||||
/*
|
||||
** Target size for allocation chunks.
|
||||
*/
|
||||
//#define ROWSET_ALLOCATION_SIZE 1024
|
||||
const int ROWSET_ALLOCATION_SIZE = 1024;
|
||||
/*
|
||||
** The number of rowset entries per allocation chunk.
|
||||
*/
|
||||
//#define ROWSET_ENTRY_PER_CHUNK \
|
||||
// ((ROWSET_ALLOCATION_SIZE-8)/sizeof(struct RowSetEntry))
|
||||
const int ROWSET_ENTRY_PER_CHUNK = 63;
|
||||
|
||||
/*
|
||||
** Each entry in a RowSet is an instance of the following object.
|
||||
*/
|
||||
public class RowSetEntry
|
||||
{
|
||||
public i64 v; /* ROWID value for this entry */
|
||||
public RowSetEntry pRight; /* Right subtree (larger entries) or list */
|
||||
public RowSetEntry pLeft; /* Left subtree (smaller entries) */
|
||||
};
|
||||
|
||||
/*
|
||||
** Index entries are allocated in large chunks (instances of the
|
||||
** following structure) to reduce memory allocation overhead. The
|
||||
** chunks are kept on a linked list so that they can be deallocated
|
||||
** when the RowSet is destroyed.
|
||||
*/
|
||||
public class RowSetChunk
|
||||
{
|
||||
public RowSetChunk pNextChunk; /* Next chunk on list of them all */
|
||||
public RowSetEntry[] aEntry = new RowSetEntry[ROWSET_ENTRY_PER_CHUNK]; /* Allocated entries */
|
||||
};
|
||||
|
||||
/*
|
||||
** A RowSet in an instance of the following structure.
|
||||
**
|
||||
** A typedef of this structure if found in sqliteInt.h.
|
||||
*/
|
||||
public class RowSet
|
||||
{
|
||||
public RowSetChunk pChunk; /* List of all chunk allocations */
|
||||
public sqlite3 db; /* The database connection */
|
||||
public RowSetEntry pEntry; /* /* List of entries using pRight */
|
||||
public RowSetEntry pLast; /* Last entry on the pEntry list */
|
||||
public RowSetEntry[] pFresh; /* Source of new entry objects */
|
||||
public RowSetEntry pTree; /* Binary tree of entries */
|
||||
public int nFresh; /* Number of objects on pFresh */
|
||||
public bool isSorted; /* True if pEntry is sorted */
|
||||
public u8 iBatch; /* Current insert batch */
|
||||
|
||||
public RowSet( sqlite3 db, int N )
|
||||
{
|
||||
this.pChunk = null;
|
||||
this.db = db;
|
||||
this.pEntry = null;
|
||||
this.pLast = null;
|
||||
this.pFresh = new RowSetEntry[N];
|
||||
this.pTree = null;
|
||||
this.nFresh = N;
|
||||
this.isSorted = true;
|
||||
this.iBatch = 0;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
** Turn bulk memory into a RowSet object. N bytes of memory
|
||||
** are available at pSpace. The db pointer is used as a memory context
|
||||
** for any subsequent allocations that need to occur.
|
||||
** Return a pointer to the new RowSet object.
|
||||
**
|
||||
** It must be the case that N is sufficient to make a Rowset. If not
|
||||
** an assertion fault occurs.
|
||||
**
|
||||
** If N is larger than the minimum, use the surplus as an initial
|
||||
** allocation of entries available to be filled.
|
||||
*/
|
||||
static RowSet sqlite3RowSetInit( sqlite3 db, object pSpace, u32 N )
|
||||
{
|
||||
RowSet p = new RowSet( db, (int)N );
|
||||
//Debug.Assert(N >= ROUND8(sizeof(*p)) );
|
||||
// p = pSpace;
|
||||
// p.pChunk = 0;
|
||||
// p.db = db;
|
||||
// p.pEntry = 0;
|
||||
// p.pLast = 0;
|
||||
// p.pTree = 0;
|
||||
// p.pFresh =(struct RowSetEntry*)(ROUND8(sizeof(*p)) + (char*)p);
|
||||
// p.nFresh = (u16)((N - ROUND8(sizeof(*p)))/sizeof(struct RowSetEntry));
|
||||
// p.isSorted = 1;
|
||||
// p.iBatch = 0;
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Deallocate all chunks from a RowSet. This frees all memory that
|
||||
** the RowSet has allocated over its lifetime. This routine is
|
||||
** the destructor for the RowSet.
|
||||
*/
|
||||
static void sqlite3RowSetClear( RowSet p )
|
||||
{
|
||||
RowSetChunk pChunk, pNextChunk;
|
||||
for ( pChunk = p.pChunk; pChunk != null; pChunk = pNextChunk )
|
||||
{
|
||||
pNextChunk = pChunk.pNextChunk;
|
||||
sqlite3DbFree( p.db, ref pChunk );
|
||||
}
|
||||
p.pChunk = null;
|
||||
p.nFresh = 0;
|
||||
p.pEntry = null;
|
||||
p.pLast = null;
|
||||
p.pTree = null;
|
||||
p.isSorted = true;
|
||||
}
|
||||
|
||||
/*
|
||||
** Insert a new value into a RowSet.
|
||||
**
|
||||
** The mallocFailed flag of the database connection is set if a
|
||||
** memory allocation fails.
|
||||
*/
|
||||
static void sqlite3RowSetInsert( RowSet p, i64 rowid )
|
||||
{
|
||||
RowSetEntry pEntry; /* The new entry */
|
||||
RowSetEntry pLast; /* The last prior entry */
|
||||
Debug.Assert( p != null );
|
||||
if ( p.nFresh == 0 )
|
||||
{
|
||||
RowSetChunk pNew;
|
||||
pNew = new RowSetChunk();//sqlite3DbMallocRaw(p.db, sizeof(*pNew));
|
||||
if ( pNew == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
pNew.pNextChunk = p.pChunk;
|
||||
p.pChunk = pNew;
|
||||
p.pFresh = pNew.aEntry;
|
||||
p.nFresh = ROWSET_ENTRY_PER_CHUNK;
|
||||
}
|
||||
p.pFresh[p.pFresh.Length - p.nFresh] = new RowSetEntry();
|
||||
pEntry = p.pFresh[p.pFresh.Length - p.nFresh];
|
||||
p.nFresh--;
|
||||
pEntry.v = rowid;
|
||||
pEntry.pRight = null;
|
||||
pLast = p.pLast;
|
||||
if ( pLast != null )
|
||||
{
|
||||
if ( p.isSorted && rowid <= pLast.v )
|
||||
{
|
||||
p.isSorted = false;
|
||||
}
|
||||
pLast.pRight = pEntry;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert( p.pEntry == null );/* Fires if INSERT after SMALLEST */
|
||||
p.pEntry = pEntry;
|
||||
}
|
||||
p.pLast = pEntry;
|
||||
}
|
||||
|
||||
/*
|
||||
** Merge two lists of RowSetEntry objects. Remove duplicates.
|
||||
**
|
||||
** The input lists are connected via pRight pointers and are
|
||||
** assumed to each already be in sorted order.
|
||||
*/
|
||||
static RowSetEntry rowSetMerge(
|
||||
RowSetEntry pA, /* First sorted list to be merged */
|
||||
RowSetEntry pB /* Second sorted list to be merged */
|
||||
)
|
||||
{
|
||||
RowSetEntry head = new RowSetEntry();
|
||||
RowSetEntry pTail;
|
||||
|
||||
pTail = head;
|
||||
while ( pA != null && pB != null )
|
||||
{
|
||||
Debug.Assert( pA.pRight == null || pA.v <= pA.pRight.v );
|
||||
Debug.Assert( pB.pRight == null || pB.v <= pB.pRight.v );
|
||||
if ( pA.v < pB.v )
|
||||
{
|
||||
pTail.pRight = pA;
|
||||
pA = pA.pRight;
|
||||
pTail = pTail.pRight;
|
||||
}
|
||||
else if ( pB.v < pA.v )
|
||||
{
|
||||
pTail.pRight = pB;
|
||||
pB = pB.pRight;
|
||||
pTail = pTail.pRight;
|
||||
}
|
||||
else
|
||||
{
|
||||
pA = pA.pRight;
|
||||
}
|
||||
}
|
||||
if ( pA != null )
|
||||
{
|
||||
Debug.Assert( pA.pRight == null || pA.v <= pA.pRight.v );
|
||||
pTail.pRight = pA;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert( pB == null || pB.pRight == null || pB.v <= pB.pRight.v );
|
||||
pTail.pRight = pB;
|
||||
}
|
||||
return head.pRight;
|
||||
}
|
||||
|
||||
/*
|
||||
** Sort all elements on the pEntry list of the RowSet into ascending order.
|
||||
*/
|
||||
static void rowSetSort( RowSet p )
|
||||
{
|
||||
u32 i;
|
||||
RowSetEntry pEntry;
|
||||
RowSetEntry[] aBucket = new RowSetEntry[40];
|
||||
|
||||
Debug.Assert( p.isSorted == false );
|
||||
//memset(aBucket, 0, sizeof(aBucket));
|
||||
while ( p.pEntry != null )
|
||||
{
|
||||
pEntry = p.pEntry;
|
||||
p.pEntry = pEntry.pRight;
|
||||
pEntry.pRight = null;
|
||||
for ( i = 0; aBucket[i] != null; i++ )
|
||||
{
|
||||
pEntry = rowSetMerge( aBucket[i], pEntry );
|
||||
aBucket[i] = null;
|
||||
}
|
||||
aBucket[i] = pEntry;
|
||||
}
|
||||
pEntry = null;
|
||||
for ( i = 0; i < aBucket.Length; i++ )//sizeof(aBucket)/sizeof(aBucket[0])
|
||||
{
|
||||
pEntry = rowSetMerge( pEntry, aBucket[i] );
|
||||
}
|
||||
p.pEntry = pEntry;
|
||||
p.pLast = null;
|
||||
p.isSorted = true;
|
||||
}
|
||||
|
||||
/*
|
||||
** The input, pIn, is a binary tree (or subtree) of RowSetEntry objects.
|
||||
** Convert this tree into a linked list connected by the pRight pointers
|
||||
** and return pointers to the first and last elements of the new list.
|
||||
*/
|
||||
static void rowSetTreeToList(
|
||||
RowSetEntry pIn, /* Root of the input tree */
|
||||
ref RowSetEntry ppFirst, /* Write head of the output list here */
|
||||
ref RowSetEntry ppLast /* Write tail of the output list here */
|
||||
)
|
||||
{
|
||||
Debug.Assert( pIn != null );
|
||||
if ( pIn.pLeft != null )
|
||||
{
|
||||
RowSetEntry p = new RowSetEntry();
|
||||
rowSetTreeToList( pIn.pLeft, ref ppFirst, ref p );
|
||||
p.pRight = pIn;
|
||||
}
|
||||
else
|
||||
{
|
||||
ppFirst = pIn;
|
||||
}
|
||||
if ( pIn.pRight != null )
|
||||
{
|
||||
rowSetTreeToList( pIn.pRight, ref pIn.pRight, ref ppLast );
|
||||
}
|
||||
else
|
||||
{
|
||||
ppLast = pIn;
|
||||
}
|
||||
Debug.Assert( ( ppLast ).pRight == null );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Convert a sorted list of elements (connected by pRight) into a binary
|
||||
** tree with depth of iDepth. A depth of 1 means the tree contains a single
|
||||
** node taken from the head of *ppList. A depth of 2 means a tree with
|
||||
** three nodes. And so forth.
|
||||
**
|
||||
** Use as many entries from the input list as required and update the
|
||||
** *ppList to point to the unused elements of the list. If the input
|
||||
** list contains too few elements, then construct an incomplete tree
|
||||
** and leave *ppList set to NULL.
|
||||
**
|
||||
** Return a pointer to the root of the constructed binary tree.
|
||||
*/
|
||||
static RowSetEntry rowSetNDeepTree(
|
||||
ref RowSetEntry ppList,
|
||||
int iDepth
|
||||
)
|
||||
{
|
||||
RowSetEntry p; /* Root of the new tree */
|
||||
RowSetEntry pLeft; /* Left subtree */
|
||||
if ( ppList == null )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if ( iDepth == 1 )
|
||||
{
|
||||
p = ppList;
|
||||
ppList = p.pRight;
|
||||
p.pLeft = p.pRight = null;
|
||||
return p;
|
||||
}
|
||||
pLeft = rowSetNDeepTree( ref ppList, iDepth - 1 );
|
||||
p = ppList;
|
||||
if ( p == null )
|
||||
{
|
||||
return pLeft;
|
||||
}
|
||||
p.pLeft = pLeft;
|
||||
ppList = p.pRight;
|
||||
p.pRight = rowSetNDeepTree( ref ppList, iDepth - 1 );
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert a sorted list of elements into a binary tree. Make the tree
|
||||
** as deep as it needs to be in order to contain the entire list.
|
||||
*/
|
||||
static RowSetEntry rowSetListToTree( RowSetEntry pList )
|
||||
{
|
||||
int iDepth; /* Depth of the tree so far */
|
||||
RowSetEntry p; /* Current tree root */
|
||||
RowSetEntry pLeft; /* Left subtree */
|
||||
|
||||
Debug.Assert( pList != null );
|
||||
p = pList;
|
||||
pList = p.pRight;
|
||||
p.pLeft = p.pRight = null;
|
||||
for ( iDepth = 1; pList != null; iDepth++ )
|
||||
{
|
||||
pLeft = p;
|
||||
p = pList;
|
||||
pList = p.pRight;
|
||||
p.pLeft = pLeft;
|
||||
p.pRight = rowSetNDeepTree( ref pList, iDepth );
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert the list in p.pEntry into a sorted list if it is not
|
||||
** sorted already. If there is a binary tree on p.pTree, then
|
||||
** convert it into a list too and merge it into the p.pEntry list.
|
||||
*/
|
||||
static void rowSetToList( RowSet p )
|
||||
{
|
||||
if ( !p.isSorted )
|
||||
{
|
||||
rowSetSort( p );
|
||||
}
|
||||
if ( p.pTree != null )
|
||||
{
|
||||
RowSetEntry pHead = new RowSetEntry();
|
||||
RowSetEntry pTail = new RowSetEntry();
|
||||
rowSetTreeToList( p.pTree, ref pHead, ref pTail );
|
||||
p.pTree = null;
|
||||
p.pEntry = rowSetMerge( p.pEntry, pHead );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Extract the smallest element from the RowSet.
|
||||
** Write the element into *pRowid. Return 1 on success. Return
|
||||
** 0 if the RowSet is already empty.
|
||||
**
|
||||
** After this routine has been called, the sqlite3RowSetInsert()
|
||||
** routine may not be called again.
|
||||
*/
|
||||
static int sqlite3RowSetNext( RowSet p, ref i64 pRowid )
|
||||
{
|
||||
rowSetToList( p );
|
||||
if ( p.pEntry != null )
|
||||
{
|
||||
pRowid = p.pEntry.v;
|
||||
p.pEntry = p.pEntry.pRight;
|
||||
if ( p.pEntry == null )
|
||||
{
|
||||
sqlite3RowSetClear( p );
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Check to see if element iRowid was inserted into the the rowset as
|
||||
** part of any insert batch prior to iBatch. Return 1 or 0.
|
||||
*/
|
||||
static int sqlite3RowSetTest( RowSet pRowSet, u8 iBatch, sqlite3_int64 iRowid )
|
||||
{
|
||||
RowSetEntry p;
|
||||
if ( iBatch != pRowSet.iBatch )
|
||||
{
|
||||
if ( pRowSet.pEntry != null )
|
||||
{
|
||||
rowSetToList( pRowSet );
|
||||
pRowSet.pTree = rowSetListToTree( pRowSet.pEntry );
|
||||
pRowSet.pEntry = null;
|
||||
pRowSet.pLast = null;
|
||||
}
|
||||
pRowSet.iBatch = iBatch;
|
||||
}
|
||||
p = pRowSet.pTree;
|
||||
while ( p != null )
|
||||
{
|
||||
if ( p.v < iRowid )
|
||||
{
|
||||
p = p.pRight;
|
||||
}
|
||||
else if ( p.v > iRowid )
|
||||
{
|
||||
p = p.pLeft;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
5174
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/select_c.cs
Normal file
5174
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/select_c.cs
Normal file
File diff suppressed because it is too large
Load Diff
7485
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/sqlite3_h.cs
Normal file
7485
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/sqlite3_h.cs
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,226 @@
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/*
|
||||
** 2007 May 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 defines various limits of what SQLite can process.
|
||||
*************************************************************************
|
||||
** 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
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
/*
|
||||
** The maximum length of a TEXT or BLOB in bytes. This also
|
||||
** limits the size of a row in a table or index.
|
||||
**
|
||||
** The hard limit is the ability of a 32-bit signed integer
|
||||
** to count the size: 2^31-1 or 2147483647.
|
||||
*/
|
||||
#if !SQLITE_MAX_LENGTH
|
||||
const int SQLITE_MAX_LENGTH = 1000000000;
|
||||
#endif
|
||||
|
||||
/*
|
||||
** This is the maximum number of
|
||||
**
|
||||
** * Columns in a table
|
||||
** * Columns in an index
|
||||
** * Columns in a view
|
||||
** * Terms in the SET clause of an UPDATE statement
|
||||
** * Terms in the result set of a SELECT statement
|
||||
** * Terms in the GROUP BY or ORDER BY clauses of a SELECT statement.
|
||||
** * Terms in the VALUES clause of an INSERT statement
|
||||
**
|
||||
** The hard upper limit here is 32676. Most database people will
|
||||
** tell you that in a well-normalized database, you usually should
|
||||
** not have more than a dozen or so columns in any table. And if
|
||||
** that is the case, there is no point in having more than a few
|
||||
** dozen values in any of the other situations described above.
|
||||
*/
|
||||
#if !SQLITE_MAX_COLUMN
|
||||
const int SQLITE_MAX_COLUMN = 2000;
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The maximum length of a single SQL statement in bytes.
|
||||
**
|
||||
** It used to be the case that setting this value to zero would
|
||||
** turn the limit off. That is no longer true. It is not possible
|
||||
** to turn this limit off.
|
||||
*/
|
||||
#if !SQLITE_MAX_SQL_LENGTH
|
||||
const int SQLITE_MAX_SQL_LENGTH = 1000000000;
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The maximum depth of an expression tree. This is limited to
|
||||
** some extent by SQLITE_MAX_SQL_LENGTH. But sometime you might
|
||||
** want to place more severe limits on the complexity of an
|
||||
** expression.
|
||||
**
|
||||
** A value of 0 used to mean that the limit was not enforced.
|
||||
** But that is no longer true. The limit is now strictly enforced
|
||||
** at all times.
|
||||
*/
|
||||
#if !SQLITE_MAX_EXPR_DEPTH
|
||||
const int SQLITE_MAX_EXPR_DEPTH = 1000;
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The maximum number of terms in a compound SELECT statement.
|
||||
** The code generator for compound SELECT statements does one
|
||||
** level of recursion for each term. A stack overflow can result
|
||||
** if the number of terms is too large. In practice, most SQL
|
||||
** never has more than 3 or 4 terms. Use a value of 0 to disable
|
||||
** any limit on the number of terms in a compount SELECT.
|
||||
*/
|
||||
#if !SQLITE_MAX_COMPOUND_SELECT
|
||||
const int SQLITE_MAX_COMPOUND_SELECT = 250;
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The maximum number of opcodes in a VDBE program.
|
||||
** Not currently enforced.
|
||||
*/
|
||||
#if !SQLITE_MAX_VDBE_OP
|
||||
const int SQLITE_MAX_VDBE_OP = 25000;
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The maximum number of arguments to an SQL function.
|
||||
*/
|
||||
#if !SQLITE_MAX_FUNCTION_ARG
|
||||
const int SQLITE_MAX_FUNCTION_ARG = 127;//# define SQLITE_MAX_FUNCTION_ARG 127
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The maximum number of in-memory pages to use for the main database
|
||||
** table and for temporary tables. The SQLITE_DEFAULT_CACHE_SIZE
|
||||
*/
|
||||
#if !SQLITE_DEFAULT_CACHE_SIZE
|
||||
const int SQLITE_DEFAULT_CACHE_SIZE = 2000;
|
||||
#endif
|
||||
#if !SQLITE_DEFAULT_TEMP_CACHE_SIZE
|
||||
const int SQLITE_DEFAULT_TEMP_CACHE_SIZE = 500;
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The default number of frames to accumulate in the log file before
|
||||
** checkpointing the database in WAL mode.
|
||||
*/
|
||||
#if !SQLITE_DEFAULT_WAL_AUTOCHECKPOINT
|
||||
const int SQLITE_DEFAULT_WAL_AUTOCHECKPOINT = 1000;
|
||||
//# define SQLITE_DEFAULT_WAL_AUTOCHECKPOINT 1000
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** The maximum number of attached databases. This must be between 0
|
||||
** and 62. The upper bound on 62 is because a 64-bit integer bitmap
|
||||
** is used internally to track attached databases.
|
||||
*/
|
||||
#if !SQLITE_MAX_ATTACHED
|
||||
const int SQLITE_MAX_ATTACHED = 10;
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** The maximum value of a ?nnn wildcard that the parser will accept.
|
||||
*/
|
||||
#if !SQLITE_MAX_VARIABLE_NUMBER
|
||||
const int SQLITE_MAX_VARIABLE_NUMBER = 999;
|
||||
#endif
|
||||
|
||||
/* Maximum page size. The upper bound on this value is 65536. This a limit
|
||||
** imposed by the use of 16-bit offsets within each page.
|
||||
**
|
||||
**
|
||||
** Earlier versions of SQLite allowed the user to change this value at
|
||||
** compile time. This is no longer permitted, on the grounds that it creates
|
||||
** a library that is technically incompatible with an SQLite library
|
||||
** compiled with a different limit. If a process operating on a database
|
||||
** with a page-size of 65536 bytes crashes, then an instance of SQLite
|
||||
** compiled with the default page-size limit will not be able to rollback
|
||||
** the aborted transaction. This could lead to database corruption.
|
||||
*/
|
||||
//#if SQLITE_MAX_PAGE_SIZE
|
||||
//# undef SQLITE_MAX_PAGE_SIZE
|
||||
//#endif
|
||||
//#define SQLITE_MAX_PAGE_SIZE 65536
|
||||
const int SQLITE_MAX_PAGE_SIZE = 65535;
|
||||
|
||||
|
||||
/*
|
||||
** The default size of a database page.
|
||||
*/
|
||||
#if !SQLITE_DEFAULT_PAGE_SIZE
|
||||
const int SQLITE_DEFAULT_PAGE_SIZE = 1024;
|
||||
#endif
|
||||
#if SQLITE_DEFAULT_PAGE_SIZE //SQLITE_DEFAULT_PAGE_SIZE>SQLITE_MAX_PAGE_SIZE
|
||||
//# undef SQLITE_DEFAULT_PAGE_SIZE
|
||||
const int SQLITE_DEFAULT_PAGE_SIZE SQLITE_MAX_PAGE_SIZE
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Ordinarily, if no value is explicitly provided, SQLite creates databases
|
||||
** with page size SQLITE_DEFAULT_PAGE_SIZE. However, based on certain
|
||||
** device characteristics (sector-size and atomic write() support),
|
||||
** SQLite may choose a larger value. This constant is the maximum value
|
||||
** SQLite will choose on its own.
|
||||
*/
|
||||
#if !SQLITE_MAX_DEFAULT_PAGE_SIZE
|
||||
const int SQLITE_MAX_DEFAULT_PAGE_SIZE = 8192;
|
||||
#endif
|
||||
#if SQLITE_MAX_DEFAULT_PAGE_SIZE //SQLITE_MAX_DEFAULT_PAGE_SIZE>SQLITE_MAX_PAGE_SIZE
|
||||
//# undef SQLITE_MAX_DEFAULT_PAGE_SIZE
|
||||
const int SQLITE_MAX_DEFAULT_PAGE_SIZE SQLITE_MAX_PAGE_SIZE
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Maximum number of pages in one database file.
|
||||
**
|
||||
** This is really just the default value for the max_page_count pragma.
|
||||
** This value can be lowered (or raised) at run-time using that the
|
||||
** max_page_count macro.
|
||||
*/
|
||||
#if !SQLITE_MAX_PAGE_COUNT
|
||||
const int SQLITE_MAX_PAGE_COUNT = 1073741823;
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Maximum length (in bytes) of the pattern in a LIKE or GLOB
|
||||
** operator.
|
||||
*/
|
||||
//#if !SQLITE_MAX_LIKE_PATTERN_LENGTH
|
||||
const int SQLITE_MAX_LIKE_PATTERN_LENGTH = 50000;
|
||||
//#endif
|
||||
|
||||
/*
|
||||
** Maximum depth of recursion for triggers.
|
||||
**
|
||||
** A value of 1 means that a trigger program will not be able to itself
|
||||
** fire any triggers. A value of 0 means that no trigger programs at all
|
||||
** may be executed.
|
||||
*/
|
||||
#if !SQLITE_MAX_TRIGGER_DEPTH
|
||||
const int SQLITE_MAX_TRIGGER_DEPTH = 101; // # define SQLITE_MAX_TRIGGER_DEPTH 1000
|
||||
#else
|
||||
const int SQLITE_MAX_TRIGGER_DEPTH=1;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
using sqlite3_value = Sqlite3.Mem;
|
||||
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/*
|
||||
** 2008 June 18
|
||||
**
|
||||
** 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 module implements the sqlite3_status() interface and related
|
||||
** functionality.
|
||||
*************************************************************************
|
||||
** 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 "vdbeInt.h"
|
||||
|
||||
/*
|
||||
** Variables in which to record status information.
|
||||
*/
|
||||
//typedef struct sqlite3StatType sqlite3StatType;
|
||||
public class sqlite3StatType
|
||||
{
|
||||
public int[] nowValue = new int[10]; /* Current value */
|
||||
public int[] mxValue = new int[10]; /* Maximum value */
|
||||
}
|
||||
public static sqlite3StatType sqlite3Stat = new sqlite3StatType();
|
||||
|
||||
/* The "wsdStat" macro will resolve to the status information
|
||||
** 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 "sqlite3Stat" state vector declared above.
|
||||
*/
|
||||
#if SQLITE_OMIT_WSD
|
||||
//# define wsdStatInit sqlite3StatType *x = &GLOBAL(sqlite3StatType,sqlite3Stat)
|
||||
//# define wsdStat x[0]
|
||||
#else
|
||||
//# define wsdStatInit
|
||||
static void wsdStatInit()
|
||||
{
|
||||
}
|
||||
//# define wsdStat sqlite3Stat
|
||||
static sqlite3StatType wsdStat = sqlite3Stat;
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Return the current value of a status parameter.
|
||||
*/
|
||||
static int sqlite3StatusValue( int op )
|
||||
{
|
||||
wsdStatInit();
|
||||
Debug.Assert( op >= 0 && op < ArraySize( wsdStat.nowValue ) );
|
||||
return wsdStat.nowValue[op];
|
||||
}
|
||||
|
||||
/*
|
||||
** Add N to the value of a status record. It is assumed that the
|
||||
** caller holds appropriate locks.
|
||||
*/
|
||||
static void sqlite3StatusAdd( int op, int N )
|
||||
{
|
||||
wsdStatInit();
|
||||
Debug.Assert( op >= 0 && op < ArraySize( wsdStat.nowValue ) );
|
||||
wsdStat.nowValue[op] += N;
|
||||
if ( wsdStat.nowValue[op] > wsdStat.mxValue[op] )
|
||||
{
|
||||
wsdStat.mxValue[op] = wsdStat.nowValue[op];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Set the value of a status to X.
|
||||
*/
|
||||
static void sqlite3StatusSet( int op, int X )
|
||||
{
|
||||
wsdStatInit();
|
||||
Debug.Assert( op >= 0 && op < ArraySize( wsdStat.nowValue ) );
|
||||
wsdStat.nowValue[op] = X;
|
||||
if ( wsdStat.nowValue[op] > wsdStat.mxValue[op] )
|
||||
{
|
||||
wsdStat.mxValue[op] = wsdStat.nowValue[op];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Query status information.
|
||||
**
|
||||
** This implementation assumes that reading or writing an aligned
|
||||
** 32-bit integer is an atomic operation. If that assumption is not true,
|
||||
** then this routine is not threadsafe.
|
||||
*/
|
||||
static int sqlite3_status( int op, ref int pCurrent, ref int pHighwater, int resetFlag )
|
||||
{
|
||||
wsdStatInit();
|
||||
if ( op < 0 || op >= ArraySize( wsdStat.nowValue ) )
|
||||
{
|
||||
return SQLITE_MISUSE_BKPT();
|
||||
}
|
||||
pCurrent = wsdStat.nowValue[op];
|
||||
pHighwater = wsdStat.mxValue[op];
|
||||
if ( resetFlag != 0 )
|
||||
{
|
||||
wsdStat.mxValue[op] = wsdStat.nowValue[op];
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
/*
|
||||
** Query status information for a single database connection
|
||||
*/
|
||||
static int sqlite3_db_status(
|
||||
sqlite3 db, /* The database connection whose status is desired */
|
||||
int op, /* Status verb */
|
||||
ref int pCurrent, /* Write current value here */
|
||||
ref int pHighwater, /* Write high-water mark here */
|
||||
int resetFlag /* Reset high-water mark if true */
|
||||
)
|
||||
{
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
sqlite3_mutex_enter( db.mutex );
|
||||
switch ( op )
|
||||
{
|
||||
case SQLITE_DBSTATUS_LOOKASIDE_USED:
|
||||
{
|
||||
pCurrent = db.lookaside.nOut;
|
||||
pHighwater = db.lookaside.mxOut;
|
||||
if ( resetFlag != 0 )
|
||||
{
|
||||
db.lookaside.mxOut = db.lookaside.nOut;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SQLITE_DBSTATUS_LOOKASIDE_HIT:
|
||||
case SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE:
|
||||
case SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL:
|
||||
{
|
||||
testcase( op == SQLITE_DBSTATUS_LOOKASIDE_HIT );
|
||||
testcase( op == SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE );
|
||||
testcase( op == SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL );
|
||||
Debug.Assert( ( op - SQLITE_DBSTATUS_LOOKASIDE_HIT ) >= 0 );
|
||||
Debug.Assert( ( op - SQLITE_DBSTATUS_LOOKASIDE_HIT ) < 3 );
|
||||
pCurrent = 0;
|
||||
pHighwater = db.lookaside.anStat[op - SQLITE_DBSTATUS_LOOKASIDE_HIT];
|
||||
if ( resetFlag != 0 )
|
||||
{
|
||||
db.lookaside.anStat[op - SQLITE_DBSTATUS_LOOKASIDE_HIT] = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return an approximation for the amount of memory currently used
|
||||
** by all pagers associated with the given database connection. The
|
||||
** highwater mark is meaningless and is returned as zero.
|
||||
*/
|
||||
case SQLITE_DBSTATUS_CACHE_USED:
|
||||
{
|
||||
int totalUsed = 0;
|
||||
int i;
|
||||
sqlite3BtreeEnterAll( db );
|
||||
for ( i = 0; i < db.nDb; i++ )
|
||||
{
|
||||
Btree pBt = db.aDb[i].pBt;
|
||||
if ( pBt != null )
|
||||
{
|
||||
Pager pPager = sqlite3BtreePager( pBt );
|
||||
totalUsed += sqlite3PagerMemUsed( pPager );
|
||||
}
|
||||
}
|
||||
sqlite3BtreeLeaveAll( db );
|
||||
pCurrent = totalUsed;
|
||||
pHighwater = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
** *pCurrent gets an accurate estimate of the amount of memory used
|
||||
** to store the schema for all databases (main, temp, and any ATTACHed
|
||||
** databases. *pHighwater is set to zero.
|
||||
*/
|
||||
case SQLITE_DBSTATUS_SCHEMA_USED:
|
||||
{
|
||||
int i; /* Used to iterate through schemas */
|
||||
int nByte = 0; /* Used to accumulate return value */
|
||||
|
||||
sqlite3BtreeEnterAll( db );
|
||||
//db.pnBytesFreed = nByte;
|
||||
for ( i = 0; i < db.nDb; i++ )
|
||||
{
|
||||
Schema pSchema = db.aDb[i].pSchema;
|
||||
if ( ALWAYS( pSchema != null ) )
|
||||
{
|
||||
HashElem p;
|
||||
|
||||
//nByte += (int)(sqlite3GlobalConfig.m.xRoundup(sizeof(HashElem)) * (
|
||||
// pSchema.tblHash.count
|
||||
// + pSchema.trigHash.count
|
||||
// + pSchema.idxHash.count
|
||||
// + pSchema.fkeyHash.count
|
||||
//));
|
||||
//nByte += (int)sqlite3MallocSize( pSchema.tblHash.ht );
|
||||
//nByte += (int)sqlite3MallocSize( pSchema.trigHash.ht );
|
||||
//nByte += (int)sqlite3MallocSize( pSchema.idxHash.ht );
|
||||
//nByte += (int)sqlite3MallocSize( pSchema.fkeyHash.ht );
|
||||
|
||||
for ( p = sqliteHashFirst( pSchema.trigHash ); p != null; p = sqliteHashNext( p ) )
|
||||
{
|
||||
Trigger t = (Trigger)sqliteHashData( p );
|
||||
sqlite3DeleteTrigger( db, ref t );
|
||||
}
|
||||
for ( p = sqliteHashFirst( pSchema.tblHash ); p != null; p = sqliteHashNext( p ) )
|
||||
{
|
||||
Table t = (Table)sqliteHashData( p );
|
||||
sqlite3DeleteTable( db, ref t );
|
||||
}
|
||||
}
|
||||
}
|
||||
db.pnBytesFreed = 0;
|
||||
sqlite3BtreeLeaveAll( db );
|
||||
|
||||
pHighwater = 0;
|
||||
pCurrent = nByte;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
** *pCurrent gets an accurate estimate of the amount of memory used
|
||||
** to store all prepared statements.
|
||||
** *pHighwater is set to zero.
|
||||
*/
|
||||
case SQLITE_DBSTATUS_STMT_USED:
|
||||
{
|
||||
Vdbe pVdbe; /* Used to iterate through VMs */
|
||||
int nByte = 0; /* Used to accumulate return value */
|
||||
|
||||
//db.pnBytesFreed = nByte;
|
||||
for ( pVdbe = db.pVdbe; pVdbe != null; pVdbe = pVdbe.pNext )
|
||||
{
|
||||
sqlite3VdbeDeleteObject( db, ref pVdbe );
|
||||
}
|
||||
db.pnBytesFreed = 0;
|
||||
|
||||
pHighwater = 0;
|
||||
pCurrent = nByte;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
rc = SQLITE_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
sqlite3_mutex_leave( db.mutex );
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
using i64 = System.Int64;
|
||||
|
||||
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 the sqlite3_get_table() and //sqlite3_free_table()
|
||||
** interface routines. These are just wrappers around the main
|
||||
** interface routine of sqlite3_exec().
|
||||
**
|
||||
** These routines are in a separate files so that they will not be linked
|
||||
** if they are not used.
|
||||
*************************************************************************
|
||||
** 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 <stdlib.h>
|
||||
//#include <string.h>
|
||||
|
||||
#if NO_SQLITE_OMIT_GET_TABLE //#if !SQLITE_OMIT_GET_TABLE
|
||||
|
||||
/*
|
||||
** This structure is used to pass data from sqlite3_get_table() through
|
||||
** to the callback function is uses to build the result.
|
||||
*/
|
||||
class TabResult {
|
||||
public string[] azResult;
|
||||
public string zErrMsg;
|
||||
public int nResult;
|
||||
public int nAlloc;
|
||||
public int nRow;
|
||||
public int nColumn;
|
||||
public int nData;
|
||||
public int rc;
|
||||
};
|
||||
|
||||
/*
|
||||
** This routine is called once for each row in the result table. Its job
|
||||
** is to fill in the TabResult structure appropriately, allocating new
|
||||
** memory as necessary.
|
||||
*/
|
||||
static public int sqlite3_get_table_cb( object pArg, i64 nCol, object Oargv, object Ocolv )
|
||||
{
|
||||
string[] argv = (string[])Oargv;
|
||||
string[]colv = (string[])Ocolv;
|
||||
TabResult p = (TabResult)pArg;
|
||||
int need;
|
||||
int i;
|
||||
string z;
|
||||
|
||||
/* Make sure there is enough space in p.azResult to hold everything
|
||||
** we need to remember from this invocation of the callback.
|
||||
*/
|
||||
if( p.nRow==0 && argv!=null ){
|
||||
need = (int)nCol*2;
|
||||
}else{
|
||||
need = (int)nCol;
|
||||
}
|
||||
if( p.nData + need >= p.nAlloc ){
|
||||
string[] azNew;
|
||||
p.nAlloc = p.nAlloc*2 + need + 1;
|
||||
azNew = new string[p.nAlloc];//sqlite3_realloc( p.azResult, sizeof(char*)*p.nAlloc );
|
||||
if( azNew==null ) goto malloc_failed;
|
||||
p.azResult = azNew;
|
||||
}
|
||||
|
||||
/* If this is the first row, then generate an extra row containing
|
||||
** the names of all columns.
|
||||
*/
|
||||
if( p.nRow==0 ){
|
||||
p.nColumn = (int)nCol;
|
||||
for(i=0; i<nCol; i++){
|
||||
z = sqlite3_mprintf("%s", colv[i]);
|
||||
if( z==null ) goto malloc_failed;
|
||||
p.azResult[p.nData++ -1] = z;
|
||||
}
|
||||
}else if( p.nColumn!=nCol ){
|
||||
//sqlite3_free(ref p.zErrMsg);
|
||||
p.zErrMsg = sqlite3_mprintf(
|
||||
"sqlite3_get_table() called with two or more incompatible queries"
|
||||
);
|
||||
p.rc = SQLITE_ERROR;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Copy over the row data
|
||||
*/
|
||||
if( argv!=null ){
|
||||
for(i=0; i<nCol; i++){
|
||||
if( argv[i]==null ){
|
||||
z = null;
|
||||
}else{
|
||||
int n = sqlite3Strlen30(argv[i])+1;
|
||||
//z = sqlite3_malloc( n );
|
||||
//if( z==0 ) goto malloc_failed;
|
||||
z= argv[i];//memcpy(z, argv[i], n);
|
||||
}
|
||||
p.azResult[p.nData++ -1] = z;
|
||||
}
|
||||
p.nRow++;
|
||||
}
|
||||
return 0;
|
||||
|
||||
malloc_failed:
|
||||
p.rc = SQLITE_NOMEM;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Query the database. But instead of invoking a callback for each row,
|
||||
** malloc() for space to hold the result and return the entire results
|
||||
** at the conclusion of the call.
|
||||
**
|
||||
** The result that is written to ***pazResult is held in memory obtained
|
||||
** from malloc(). But the caller cannot free this memory directly.
|
||||
** Instead, the entire table should be passed to //sqlite3_free_table() when
|
||||
** the calling procedure is finished using it.
|
||||
*/
|
||||
static public int sqlite3_get_table(
|
||||
sqlite3 db, /* The database on which the SQL executes */
|
||||
string zSql, /* The SQL to be executed */
|
||||
ref string[] pazResult, /* Write the result table here */
|
||||
ref int pnRow, /* Write the number of rows in the result here */
|
||||
ref int pnColumn, /* Write the number of columns of result here */
|
||||
ref string pzErrMsg /* Write error messages here */
|
||||
){
|
||||
int rc;
|
||||
TabResult res = new TabResult();
|
||||
|
||||
pazResult = null;
|
||||
pnColumn = 0;
|
||||
pnRow = 0;
|
||||
pzErrMsg = "";
|
||||
res.zErrMsg = "";
|
||||
res.nResult = 0;
|
||||
res.nRow = 0;
|
||||
res.nColumn = 0;
|
||||
res.nData = 1;
|
||||
res.nAlloc = 20;
|
||||
res.rc = SQLITE_OK;
|
||||
res.azResult = new string[res.nAlloc];// sqlite3_malloc( sizeof( char* ) * res.nAlloc );
|
||||
if( res.azResult==null ){
|
||||
db.errCode = SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
res.azResult[0] = null;
|
||||
rc = sqlite3_exec(db, zSql, (dxCallback) sqlite3_get_table_cb, res, ref pzErrMsg);
|
||||
//Debug.Assert( sizeof(res.azResult[0])>= sizeof(res.nData) );
|
||||
//res.azResult = SQLITE_INT_TO_PTR( res.nData );
|
||||
if( (rc&0xff)==SQLITE_ABORT ){
|
||||
//sqlite3_free_table(ref res.azResult[1] );
|
||||
if( res.zErrMsg !=""){
|
||||
if( pzErrMsg !=null ){
|
||||
//sqlite3_free(ref pzErrMsg);
|
||||
pzErrMsg = sqlite3_mprintf("%s",res.zErrMsg);
|
||||
}
|
||||
//sqlite3_free(ref res.zErrMsg);
|
||||
}
|
||||
db.errCode = res.rc; /* Assume 32-bit assignment is atomic */
|
||||
return res.rc;
|
||||
}
|
||||
//sqlite3_free(ref res.zErrMsg);
|
||||
if( rc!=SQLITE_OK ){
|
||||
//sqlite3_free_table(ref res.azResult[1]);
|
||||
return rc;
|
||||
}
|
||||
if( res.nAlloc>res.nData ){
|
||||
string[] azNew;
|
||||
Array.Resize(ref res.azResult, res.nData-1);//sqlite3_realloc( res.azResult, sizeof(char*)*(res.nData+1) );
|
||||
//if( azNew==null ){
|
||||
// //sqlite3_free_table(ref res.azResult[1]);
|
||||
// db.errCode = SQLITE_NOMEM;
|
||||
// return SQLITE_NOMEM;
|
||||
//}
|
||||
res.nAlloc = res.nData+1;
|
||||
//res.azResult = azNew;
|
||||
}
|
||||
pazResult = res.azResult;
|
||||
pnColumn = res.nColumn;
|
||||
pnRow = res.nRow;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine frees the space the sqlite3_get_table() malloced.
|
||||
*/
|
||||
static void //sqlite3_free_table(
|
||||
ref string azResult /* Result returned from from sqlite3_get_table() */
|
||||
){
|
||||
if( azResult !=null){
|
||||
int i, n;
|
||||
//azResult--;
|
||||
//Debug.Assert( azResult!=0 );
|
||||
//n = SQLITE_PTR_TO_INT(azResult[0]);
|
||||
//for(i=1; i<n; i++){ if( azResult[i] ) //sqlite3_free(azResult[i]); }
|
||||
//sqlite3_free(ref azResult);
|
||||
}
|
||||
}
|
||||
|
||||
#endif //* SQLITE_OMIT_GET_TABLE */
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,714 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
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.
|
||||
**
|
||||
*************************************************************************
|
||||
** An tokenizer for SQL
|
||||
**
|
||||
** This file contains C code that splits an SQL input string up into
|
||||
** individual tokens and sends those tokens one-by-one over to the
|
||||
** parser for analysis.
|
||||
*************************************************************************
|
||||
** 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"
|
||||
//#include <stdlib.h>
|
||||
|
||||
/*
|
||||
** The charMap() macro maps alphabetic characters into their
|
||||
** lower-case ASCII equivalent. On ASCII machines, this is just
|
||||
** an upper-to-lower case map. On EBCDIC machines we also need
|
||||
** to adjust the encoding. Only alphabetic characters and underscores
|
||||
** need to be translated.
|
||||
*/
|
||||
#if SQLITE_ASCII
|
||||
//# define charMap(X) sqlite3UpperToLower[(unsigned char)X]
|
||||
#endif
|
||||
//#if SQLITE_EBCDIC
|
||||
//# define charMap(X) ebcdicToAscii[(unsigned char)X]
|
||||
//const unsigned char ebcdicToAscii[] = {
|
||||
///* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
|
||||
// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x */
|
||||
// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1x */
|
||||
// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2x */
|
||||
// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 3x */
|
||||
// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 4x */
|
||||
// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 5x */
|
||||
// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 0, 0, /* 6x */
|
||||
// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 7x */
|
||||
// 0, 97, 98, 99,100,101,102,103,104,105, 0, 0, 0, 0, 0, 0, /* 8x */
|
||||
// 0,106,107,108,109,110,111,112,113,114, 0, 0, 0, 0, 0, 0, /* 9x */
|
||||
// 0, 0,115,116,117,118,119,120,121,122, 0, 0, 0, 0, 0, 0, /* Ax */
|
||||
// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* Bx */
|
||||
// 0, 97, 98, 99,100,101,102,103,104,105, 0, 0, 0, 0, 0, 0, /* Cx */
|
||||
// 0,106,107,108,109,110,111,112,113,114, 0, 0, 0, 0, 0, 0, /* Dx */
|
||||
// 0, 0,115,116,117,118,119,120,121,122, 0, 0, 0, 0, 0, 0, /* Ex */
|
||||
// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* Fx */
|
||||
//};
|
||||
//#endif
|
||||
|
||||
/*
|
||||
** The sqlite3KeywordCode function looks up an identifier to determine if
|
||||
** it is a keyword. If it is a keyword, the token code of that keyword is
|
||||
** returned. If the input is not a keyword, TK_ID is returned.
|
||||
**
|
||||
** The implementation of this routine was generated by a program,
|
||||
** mkkeywordhash.h, located in the tool subdirectory of the distribution.
|
||||
** The output of the mkkeywordhash.c program is written into a file
|
||||
** named keywordhash.h and then included into this source file by
|
||||
** the #include below.
|
||||
*/
|
||||
//#include "keywordhash.h"
|
||||
|
||||
|
||||
/*
|
||||
** If X is a character that can be used in an identifier then
|
||||
** IdChar(X) will be true. Otherwise it is false.
|
||||
**
|
||||
** For ASCII, any character with the high-order bit set is
|
||||
** allowed in an identifier. For 7-bit characters,
|
||||
** sqlite3IsIdChar[X] must be 1.
|
||||
**
|
||||
** For EBCDIC, the rules are more complex but have the same
|
||||
** end result.
|
||||
**
|
||||
** Ticket #1066. the SQL standard does not allow '$' in the
|
||||
** middle of identfiers. But many SQL implementations do.
|
||||
** SQLite will allow '$' in identifiers for compatibility.
|
||||
** But the feature is undocumented.
|
||||
*/
|
||||
#if SQLITE_ASCII
|
||||
//#define IdChar(C) ((sqlite3CtypeMap[(unsigned char)C]&0x46)!=0)
|
||||
#endif
|
||||
//#if SQLITE_EBCDIC
|
||||
//const char sqlite3IsEbcdicIdChar[] = {
|
||||
///* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */
|
||||
// 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 4x */
|
||||
// 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, /* 5x */
|
||||
// 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, /* 6x */
|
||||
// 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, /* 7x */
|
||||
// 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, /* 8x */
|
||||
// 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, /* 9x */
|
||||
// 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, /* Ax */
|
||||
// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* Bx */
|
||||
// 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* Cx */
|
||||
// 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* Dx */
|
||||
// 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* Ex */
|
||||
// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, /* Fx */
|
||||
//};
|
||||
//#define IdChar(C) (((c=C)>=0x42 && sqlite3IsEbcdicIdChar[c-0x40]))
|
||||
//#endif
|
||||
|
||||
|
||||
/*
|
||||
** Return the length of the token that begins at z[iOffset + 0].
|
||||
** Store the token type in *tokenType before returning.
|
||||
*/
|
||||
static int sqlite3GetToken( string z, int iOffset, ref int tokenType )
|
||||
{
|
||||
int i;
|
||||
byte c = 0;
|
||||
switch ( z[iOffset + 0] )
|
||||
{
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\n':
|
||||
case '\f':
|
||||
case '\r':
|
||||
{
|
||||
testcase( z[iOffset + 0] == ' ' );
|
||||
testcase( z[iOffset + 0] == '\t' );
|
||||
testcase( z[iOffset + 0] == '\n' );
|
||||
testcase( z[iOffset + 0] == '\f' );
|
||||
testcase( z[iOffset + 0] == '\r' );
|
||||
for ( i = 1; z.Length > iOffset + i && sqlite3Isspace( z[iOffset + i] ); i++ )
|
||||
{
|
||||
}
|
||||
tokenType = TK_SPACE;
|
||||
return i;
|
||||
}
|
||||
case '-':
|
||||
{
|
||||
if ( z.Length > iOffset + 1 && z[iOffset + 1] == '-' )
|
||||
{
|
||||
/* IMP: R-15891-05542 -- syntax diagram for comments */
|
||||
for ( i = 2; z.Length > iOffset + i && ( c = (byte)z[iOffset + i] ) != 0 && c != '\n'; i++ )
|
||||
{
|
||||
}
|
||||
tokenType = TK_SPACE; /* IMP: R-22934-25134 */
|
||||
return i;
|
||||
}
|
||||
tokenType = TK_MINUS;
|
||||
return 1;
|
||||
}
|
||||
case '(':
|
||||
{
|
||||
tokenType = TK_LP;
|
||||
return 1;
|
||||
}
|
||||
case ')':
|
||||
{
|
||||
tokenType = TK_RP;
|
||||
return 1;
|
||||
}
|
||||
case ';':
|
||||
{
|
||||
tokenType = TK_SEMI;
|
||||
return 1;
|
||||
}
|
||||
case '+':
|
||||
{
|
||||
tokenType = TK_PLUS;
|
||||
return 1;
|
||||
}
|
||||
case '*':
|
||||
{
|
||||
tokenType = TK_STAR;
|
||||
return 1;
|
||||
}
|
||||
case '/':
|
||||
{
|
||||
if ( iOffset + 2 >= z.Length || z[iOffset + 1] != '*' )
|
||||
{
|
||||
tokenType = TK_SLASH;
|
||||
return 1;
|
||||
}
|
||||
/* IMP: R-15891-05542 -- syntax diagram for comments */
|
||||
for ( i = 3, c = (byte)z[iOffset + 2]; iOffset + i < z.Length && ( c != '*' || ( z[iOffset + i] != '/' ) && ( c != 0 ) ); i++ )
|
||||
{
|
||||
c = (byte)z[iOffset + i];
|
||||
}
|
||||
if ( iOffset + i == z.Length )
|
||||
c = 0;
|
||||
if ( c != 0 )
|
||||
i++;
|
||||
tokenType = TK_SPACE; /* IMP: R-22934-25134 */
|
||||
return i;
|
||||
}
|
||||
case '%':
|
||||
{
|
||||
tokenType = TK_REM;
|
||||
return 1;
|
||||
}
|
||||
case '=':
|
||||
{
|
||||
tokenType = TK_EQ;
|
||||
return 1 + ( z[iOffset + 1] == '=' ? 1 : 0 );
|
||||
}
|
||||
case '<':
|
||||
{
|
||||
if ( ( c = (byte)z[iOffset + 1] ) == '=' )
|
||||
{
|
||||
tokenType = TK_LE;
|
||||
return 2;
|
||||
}
|
||||
else if ( c == '>' )
|
||||
{
|
||||
tokenType = TK_NE;
|
||||
return 2;
|
||||
}
|
||||
else if ( c == '<' )
|
||||
{
|
||||
tokenType = TK_LSHIFT;
|
||||
return 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
tokenType = TK_LT;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
case '>':
|
||||
{
|
||||
if ( z.Length > iOffset + 1 && ( c = (byte)z[iOffset + 1] ) == '=' )
|
||||
{
|
||||
tokenType = TK_GE;
|
||||
return 2;
|
||||
}
|
||||
else if ( c == '>' )
|
||||
{
|
||||
tokenType = TK_RSHIFT;
|
||||
return 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
tokenType = TK_GT;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
case '!':
|
||||
{
|
||||
if ( z[iOffset + 1] != '=' )
|
||||
{
|
||||
tokenType = TK_ILLEGAL;
|
||||
return 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
tokenType = TK_NE;
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
case '|':
|
||||
{
|
||||
if ( z[iOffset + 1] != '|' )
|
||||
{
|
||||
tokenType = TK_BITOR;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
tokenType = TK_CONCAT;
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
case ',':
|
||||
{
|
||||
tokenType = TK_COMMA;
|
||||
return 1;
|
||||
}
|
||||
case '&':
|
||||
{
|
||||
tokenType = TK_BITAND;
|
||||
return 1;
|
||||
}
|
||||
case '~':
|
||||
{
|
||||
tokenType = TK_BITNOT;
|
||||
return 1;
|
||||
}
|
||||
case '`':
|
||||
case '\'':
|
||||
case '"':
|
||||
{
|
||||
int delim = z[iOffset + 0];
|
||||
testcase( delim == '`' );
|
||||
testcase( delim == '\'' );
|
||||
testcase( delim == '"' );
|
||||
for ( i = 1; ( iOffset + i ) < z.Length && ( c = (byte)z[iOffset + i] ) != 0; i++ )
|
||||
{
|
||||
if ( c == delim )
|
||||
{
|
||||
if ( z.Length > iOffset + i + 1 && z[iOffset + i + 1] == delim )
|
||||
{
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( ( iOffset + i == z.Length && c != delim ) || z[iOffset + i] != delim )
|
||||
{
|
||||
tokenType = TK_ILLEGAL;
|
||||
return i + 1;
|
||||
}
|
||||
if ( c == '\'' )
|
||||
{
|
||||
tokenType = TK_STRING;
|
||||
return i + 1;
|
||||
}
|
||||
else if ( c != 0 )
|
||||
{
|
||||
tokenType = TK_ID;
|
||||
return i + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
tokenType = TK_ILLEGAL;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
case '.':
|
||||
{
|
||||
#if !SQLITE_OMIT_FLOATING_POINT
|
||||
if ( !sqlite3Isdigit( z[iOffset + 1] ) )
|
||||
#endif
|
||||
{
|
||||
tokenType = TK_DOT;
|
||||
return 1;
|
||||
}
|
||||
/* If the next character is a digit, this is a floating point
|
||||
** number that begins with ".". Fall thru into the next case */
|
||||
goto case '0';
|
||||
}
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
{
|
||||
testcase( z[iOffset] == '0' );
|
||||
testcase( z[iOffset] == '1' );
|
||||
testcase( z[iOffset] == '2' );
|
||||
testcase( z[iOffset] == '3' );
|
||||
testcase( z[iOffset] == '4' );
|
||||
testcase( z[iOffset] == '5' );
|
||||
testcase( z[iOffset] == '6' );
|
||||
testcase( z[iOffset] == '7' );
|
||||
testcase( z[iOffset] == '8' );
|
||||
testcase( z[iOffset] == '9' );
|
||||
tokenType = TK_INTEGER;
|
||||
for ( i = 0; z.Length > iOffset + i && sqlite3Isdigit( z[iOffset + i] ); i++ )
|
||||
{
|
||||
}
|
||||
#if !SQLITE_OMIT_FLOATING_POINT
|
||||
if ( z.Length > iOffset + i && z[iOffset + i] == '.' )
|
||||
{
|
||||
i++;
|
||||
while ( z.Length > iOffset + i && sqlite3Isdigit( z[iOffset + i] ) )
|
||||
{
|
||||
i++;
|
||||
}
|
||||
tokenType = TK_FLOAT;
|
||||
}
|
||||
if ( z.Length > iOffset + i + 1 && ( z[iOffset + i] == 'e' || z[iOffset + i] == 'E' ) &&
|
||||
( sqlite3Isdigit( z[iOffset + i + 1] )
|
||||
|| z.Length > iOffset + i + 2 && ( ( z[iOffset + i + 1] == '+' || z[iOffset + i + 1] == '-' ) && sqlite3Isdigit( z[iOffset + i + 2] ) )
|
||||
)
|
||||
)
|
||||
{
|
||||
i += 2;
|
||||
while ( z.Length > iOffset + i && sqlite3Isdigit( z[iOffset + i] ) )
|
||||
{
|
||||
i++;
|
||||
}
|
||||
tokenType = TK_FLOAT;
|
||||
}
|
||||
#endif
|
||||
while ( iOffset + i < z.Length && IdChar( (byte)z[iOffset + i] ) )
|
||||
{
|
||||
tokenType = TK_ILLEGAL;
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
case '[':
|
||||
{
|
||||
for ( i = 1, c = (byte)z[iOffset + 0]; c != ']' && ( iOffset + i ) < z.Length && ( c = (byte)z[iOffset + i] ) != 0; i++ )
|
||||
{
|
||||
}
|
||||
tokenType = c == ']' ? TK_ID : TK_ILLEGAL;
|
||||
return i;
|
||||
}
|
||||
case '?':
|
||||
{
|
||||
tokenType = TK_VARIABLE;
|
||||
for ( i = 1; z.Length > iOffset + i && sqlite3Isdigit( z[iOffset + i] ); i++ )
|
||||
{
|
||||
}
|
||||
return i;
|
||||
}
|
||||
case '#':
|
||||
{
|
||||
for ( i = 1; z.Length > iOffset + i && sqlite3Isdigit( z[iOffset + i] ); i++ )
|
||||
{
|
||||
}
|
||||
if ( i > 1 )
|
||||
{
|
||||
/* Parameters of the form #NNN (where NNN is a number) are used
|
||||
** internally by sqlite3NestedParse. */
|
||||
tokenType = TK_REGISTER;
|
||||
return i;
|
||||
}
|
||||
/* Fall through into the next case if the '#' is not followed by
|
||||
** a digit. Try to match #AAAA where AAAA is a parameter name. */
|
||||
goto case ':';
|
||||
}
|
||||
#if !SQLITE_OMIT_TCL_VARIABLE
|
||||
case '$':
|
||||
#endif
|
||||
case '@': /* For compatibility with MS SQL Server */
|
||||
case ':':
|
||||
{
|
||||
int n = 0;
|
||||
testcase( z[iOffset + 0] == '$' );
|
||||
testcase( z[iOffset + 0] == '@' );
|
||||
testcase( z[iOffset + 0] == ':' );
|
||||
tokenType = TK_VARIABLE;
|
||||
for ( i = 1; z.Length > iOffset + i && ( c = (byte)z[iOffset + i] ) != 0; i++ )
|
||||
{
|
||||
if ( IdChar( c ) )
|
||||
{
|
||||
n++;
|
||||
#if !SQLITE_OMIT_TCL_VARIABLE
|
||||
}
|
||||
else if ( c == '(' && n > 0 )
|
||||
{
|
||||
do
|
||||
{
|
||||
i++;
|
||||
} while ( ( iOffset + i ) < z.Length && ( c = (byte)z[iOffset + i] ) != 0 && !sqlite3Isspace( c ) && c != ')' );
|
||||
if ( c == ')' )
|
||||
{
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
tokenType = TK_ILLEGAL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if ( c == ':' && z[iOffset + i + 1] == ':' )
|
||||
{
|
||||
i++;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( n == 0 )
|
||||
tokenType = TK_ILLEGAL;
|
||||
return i;
|
||||
}
|
||||
#if !SQLITE_OMIT_BLOB_LITERAL
|
||||
case 'x':
|
||||
case 'X':
|
||||
{
|
||||
testcase( z[iOffset + 0] == 'x' );
|
||||
testcase( z[iOffset + 0] == 'X' );
|
||||
if ( z.Length > iOffset + 1 && z[iOffset + 1] == '\'' )
|
||||
{
|
||||
tokenType = TK_BLOB;
|
||||
for ( i = 2; z.Length > iOffset + i && sqlite3Isxdigit( z[iOffset + i] ); i++ )
|
||||
{
|
||||
}
|
||||
if ( iOffset + i == z.Length || z[iOffset + i] != '\'' || i % 2 != 0 )
|
||||
{
|
||||
tokenType = TK_ILLEGAL;
|
||||
while ( z.Length > iOffset + i && z[iOffset + i] != '\'' )
|
||||
{
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if ( z.Length > iOffset + i )
|
||||
i++;
|
||||
return i;
|
||||
}
|
||||
goto default;
|
||||
/* Otherwise fall through to the next case */
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
{
|
||||
if ( !IdChar( (byte)z[iOffset] ) )
|
||||
{
|
||||
break;
|
||||
}
|
||||
for ( i = 1; i < z.Length - iOffset && IdChar( (byte)z[iOffset + i] ); i++ )
|
||||
{
|
||||
}
|
||||
tokenType = keywordCode( z, iOffset, i );
|
||||
return i;
|
||||
}
|
||||
}
|
||||
tokenType = TK_ILLEGAL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Run the parser on the given SQL string. The parser structure is
|
||||
** passed in. An SQLITE_ status code is returned. If an error occurs
|
||||
** then an and attempt is made to write an error message into
|
||||
** memory obtained from sqlite3_malloc() and to make pzErrMsg point to that
|
||||
** error message.
|
||||
*/
|
||||
static int sqlite3RunParser( Parse pParse, string zSql, ref string pzErrMsg )
|
||||
{
|
||||
int nErr = 0; /* Number of errors encountered */
|
||||
int i; /* Loop counter */
|
||||
yyParser pEngine; /* The LEMON-generated LALR(1) parser */
|
||||
int tokenType = 0; /* type of the next token */
|
||||
int lastTokenParsed = -1; /* type of the previous token */
|
||||
byte enableLookaside; /* Saved value of db->lookaside.bEnabled */
|
||||
sqlite3 db = pParse.db; /* The database connection */
|
||||
int mxSqlLen; /* Max length of an SQL string */
|
||||
|
||||
|
||||
mxSqlLen = db.aLimit[SQLITE_LIMIT_SQL_LENGTH];
|
||||
if ( db.activeVdbeCnt == 0 )
|
||||
{
|
||||
db.u1.isInterrupted = false;
|
||||
}
|
||||
pParse.rc = SQLITE_OK;
|
||||
pParse.zTail = new StringBuilder( zSql );
|
||||
i = 0;
|
||||
Debug.Assert( pzErrMsg != null );
|
||||
pEngine = sqlite3ParserAlloc();//sqlite3ParserAlloc((void*(*)(size_t))sqlite3Malloc);
|
||||
//if ( pEngine == null )
|
||||
//{
|
||||
// db.mallocFailed = 1;
|
||||
// return SQLITE_NOMEM;
|
||||
//}
|
||||
Debug.Assert( pParse.pNewTable == null );
|
||||
Debug.Assert( pParse.pNewTrigger == null );
|
||||
Debug.Assert( pParse.nVar == 0 );
|
||||
Debug.Assert( pParse.nzVar == 0 );
|
||||
Debug.Assert( pParse.azVar == null );
|
||||
enableLookaside = db.lookaside.bEnabled;
|
||||
if ( db.lookaside.pStart != 0 )
|
||||
db.lookaside.bEnabled = 1;
|
||||
while ( /* 0 == db.mallocFailed && */ i < zSql.Length )
|
||||
{
|
||||
Debug.Assert( i >= 0 );
|
||||
//pParse->sLastToken.z = &zSql[i];
|
||||
pParse.sLastToken.n = sqlite3GetToken( zSql, i, ref tokenType );
|
||||
pParse.sLastToken.z = zSql.Substring( i );
|
||||
i += pParse.sLastToken.n;
|
||||
if ( i > mxSqlLen )
|
||||
{
|
||||
pParse.rc = SQLITE_TOOBIG;
|
||||
break;
|
||||
}
|
||||
switch ( tokenType )
|
||||
{
|
||||
case TK_SPACE:
|
||||
{
|
||||
if ( db.u1.isInterrupted )
|
||||
{
|
||||
sqlite3ErrorMsg( pParse, "interrupt" );
|
||||
pParse.rc = SQLITE_INTERRUPT;
|
||||
goto abort_parse;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TK_ILLEGAL:
|
||||
{
|
||||
sqlite3DbFree( db, ref pzErrMsg );
|
||||
pzErrMsg = sqlite3MPrintf( db, "unrecognized token: \"%T\"",
|
||||
(object)pParse.sLastToken );
|
||||
nErr++;
|
||||
goto abort_parse;
|
||||
}
|
||||
case TK_SEMI:
|
||||
{
|
||||
//pParse.zTail = new StringBuilder(zSql.Substring( i,zSql.Length-i ));
|
||||
/* Fall thru into the default case */
|
||||
goto default;
|
||||
}
|
||||
default:
|
||||
{
|
||||
sqlite3Parser( pEngine, tokenType, pParse.sLastToken, pParse );
|
||||
lastTokenParsed = tokenType;
|
||||
if ( pParse.rc != SQLITE_OK )
|
||||
{
|
||||
goto abort_parse;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
abort_parse:
|
||||
pParse.zTail = new StringBuilder( zSql.Length <= i ? "" : zSql.Substring( i, zSql.Length - i ) );
|
||||
if ( zSql.Length >= i && nErr == 0 && pParse.rc == SQLITE_OK )
|
||||
{
|
||||
if ( lastTokenParsed != TK_SEMI )
|
||||
{
|
||||
sqlite3Parser( pEngine, TK_SEMI, pParse.sLastToken, pParse );
|
||||
}
|
||||
sqlite3Parser( pEngine, 0, pParse.sLastToken, pParse );
|
||||
}
|
||||
#if YYTRACKMAXSTACKDEPTH
|
||||
sqlite3StatusSet(SQLITE_STATUS_PARSER_STACK,
|
||||
sqlite3ParserStackPeak(pEngine)
|
||||
);
|
||||
#endif //* YYDEBUG */
|
||||
sqlite3ParserFree( pEngine, null );//sqlite3_free );
|
||||
db.lookaside.bEnabled = enableLookaside;
|
||||
//if ( db.mallocFailed != 0 )
|
||||
//{
|
||||
// pParse.rc = SQLITE_NOMEM;
|
||||
//}
|
||||
if ( pParse.rc != SQLITE_OK && pParse.rc != SQLITE_DONE && pParse.zErrMsg == "" )
|
||||
{
|
||||
sqlite3SetString( ref pParse.zErrMsg, db, sqlite3ErrStr( pParse.rc ) );
|
||||
}
|
||||
//assert( pzErrMsg!=0 );
|
||||
if ( pParse.zErrMsg != null )
|
||||
{
|
||||
pzErrMsg = pParse.zErrMsg;
|
||||
sqlite3_log( pParse.rc, "%s", pzErrMsg );
|
||||
pParse.zErrMsg = "";
|
||||
nErr++;
|
||||
}
|
||||
if ( pParse.pVdbe != null && pParse.nErr > 0 && pParse.nested == 0 )
|
||||
{
|
||||
sqlite3VdbeDelete( ref pParse.pVdbe );
|
||||
pParse.pVdbe = null;
|
||||
}
|
||||
#if NO_SQLITE_OMIT_SHARED_CACHE
|
||||
//#if !SQLITE_OMIT_SHARED_CACHE
|
||||
if ( pParse.nested == 0 )
|
||||
{
|
||||
sqlite3DbFree( db, ref pParse.aTableLock );
|
||||
pParse.aTableLock = null;
|
||||
pParse.nTableLock = 0;
|
||||
}
|
||||
#endif
|
||||
#if !SQLITE_OMIT_VIRTUALTABLE
|
||||
pParse.apVtabLock = null;//sqlite3_free( pParse.apVtabLock );
|
||||
#endif
|
||||
if ( !IN_DECLARE_VTAB(pParse) )
|
||||
{
|
||||
/* If the pParse.declareVtab flag is set, do not delete any table
|
||||
** structure built up in pParse.pNewTable. The calling code (see vtab.c)
|
||||
** will take responsibility for freeing the Table structure.
|
||||
*/
|
||||
sqlite3DeleteTable( db, ref pParse.pNewTable );
|
||||
}
|
||||
|
||||
#if !SQLITE_OMIT_TRIGGER
|
||||
sqlite3DeleteTrigger( db, ref pParse.pNewTrigger );
|
||||
#endif
|
||||
//for ( i = pParse.nzVar - 1; i >= 0; i-- )
|
||||
// sqlite3DbFree( db, pParse.azVar[i] );
|
||||
sqlite3DbFree( db, ref pParse.azVar );
|
||||
sqlite3DbFree( db, ref pParse.aAlias );
|
||||
while ( pParse.pAinc != null )
|
||||
{
|
||||
AutoincInfo p = pParse.pAinc;
|
||||
pParse.pAinc = p.pNext;
|
||||
sqlite3DbFree( db, ref p );
|
||||
}
|
||||
while ( pParse.pZombieTab != null )
|
||||
{
|
||||
Table p = pParse.pZombieTab;
|
||||
pParse.pZombieTab = p.pNextZombie;
|
||||
sqlite3DeleteTable( db, ref p );
|
||||
}
|
||||
if ( nErr > 0 && pParse.rc == SQLITE_OK )
|
||||
{
|
||||
pParse.rc = SQLITE_ERROR;
|
||||
}
|
||||
return nErr;
|
||||
}
|
||||
}
|
||||
}
|
||||
1310
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/trigger_c.cs
Normal file
1310
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/trigger_c.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,781 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
using u8 = System.Byte;
|
||||
using u32 = System.UInt32;
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
using sqlite3_value = Sqlite3.Mem;
|
||||
|
||||
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
|
||||
** to handle UPDATE 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"
|
||||
|
||||
#if !SQLITE_OMIT_VIRTUALTABLE
|
||||
/* Forward declaration */
|
||||
//static void updateVirtualTable(
|
||||
//Parse pParse, /* The parsing context */
|
||||
//SrcList pSrc, /* The virtual table to be modified */
|
||||
//Table pTab, /* The virtual table */
|
||||
//ExprList pChanges, /* The columns to change in the UPDATE statement */
|
||||
//Expr pRowidExpr, /* Expression used to recompute the rowid */
|
||||
//int aXRef, /* Mapping from columns of pTab to entries in pChanges */
|
||||
//Expr *pWhere, /* WHERE clause of the UPDATE statement */
|
||||
//int onError /* ON CONFLICT strategy */
|
||||
//);
|
||||
#endif // * SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
||||
/*
|
||||
** The most recently coded instruction was an OP_Column to retrieve the
|
||||
** i-th column of table pTab. This routine sets the P4 parameter of the
|
||||
** OP_Column to the default value, if any.
|
||||
**
|
||||
** The default value of a column is specified by a DEFAULT clause in the
|
||||
** column definition. This was either supplied by the user when the table
|
||||
** was created, or added later to the table definition by an ALTER TABLE
|
||||
** command. If the latter, then the row-records in the table btree on disk
|
||||
** may not contain a value for the column and the default value, taken
|
||||
** from the P4 parameter of the OP_Column instruction, is returned instead.
|
||||
** If the former, then all row-records are guaranteed to include a value
|
||||
** for the column and the P4 value is not required.
|
||||
**
|
||||
** Column definitions created by an ALTER TABLE command may only have
|
||||
** literal default values specified: a number, null or a string. (If a more
|
||||
** complicated default expression value was provided, it is evaluated
|
||||
** when the ALTER TABLE is executed and one of the literal values written
|
||||
** into the sqlite_master table.)
|
||||
**
|
||||
** Therefore, the P4 parameter is only required if the default value for
|
||||
** the column is a literal number, string or null. The sqlite3ValueFromExpr()
|
||||
** function is capable of transforming these types of expressions into
|
||||
** sqlite3_value objects.
|
||||
**
|
||||
** If parameter iReg is not negative, code an OP_RealAffinity instruction
|
||||
** on register iReg. This is used when an equivalent integer value is
|
||||
** stored in place of an 8-byte floating point value in order to save
|
||||
** space.
|
||||
*/
|
||||
static void sqlite3ColumnDefault( Vdbe v, Table pTab, int i, int iReg )
|
||||
{
|
||||
Debug.Assert( pTab != null );
|
||||
if ( null == pTab.pSelect )
|
||||
{
|
||||
sqlite3_value pValue = new sqlite3_value();
|
||||
int enc = ENC( sqlite3VdbeDb( v ) );
|
||||
Column pCol = pTab.aCol[i];
|
||||
#if SQLITE_DEBUG
|
||||
VdbeComment( v, "%s.%s", pTab.zName, pCol.zName );
|
||||
#endif
|
||||
Debug.Assert( i < pTab.nCol );
|
||||
sqlite3ValueFromExpr( sqlite3VdbeDb( v ), pCol.pDflt, enc,
|
||||
pCol.affinity, ref pValue );
|
||||
if ( pValue != null )
|
||||
{
|
||||
sqlite3VdbeChangeP4( v, -1, pValue, P4_MEM );
|
||||
}
|
||||
#if !SQLITE_OMIT_FLOATING_POINT
|
||||
if ( iReg >= 0 && pTab.aCol[i].affinity == SQLITE_AFF_REAL )
|
||||
{
|
||||
sqlite3VdbeAddOp1( v, OP_RealAffinity, iReg );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Process an UPDATE statement.
|
||||
**
|
||||
** UPDATE OR IGNORE table_wxyz SET a=b, c=d WHERE e<5 AND f NOT NULL;
|
||||
** \_______/ \________/ \______/ \________________/
|
||||
* onError pTabList pChanges pWhere
|
||||
*/
|
||||
static void sqlite3Update(
|
||||
Parse pParse, /* The parser context */
|
||||
SrcList pTabList, /* The table in which we should change things */
|
||||
ExprList pChanges, /* Things to be changed */
|
||||
Expr pWhere, /* The WHERE clause. May be null */
|
||||
int onError /* How to handle constraint errors */
|
||||
)
|
||||
{
|
||||
int i, j; /* Loop counters */
|
||||
Table pTab; /* The table to be updated */
|
||||
int addr = 0; /* VDBE instruction address of the start of the loop */
|
||||
WhereInfo pWInfo; /* Information about the WHERE clause */
|
||||
Vdbe v; /* The virtual database engine */
|
||||
Index pIdx; /* For looping over indices */
|
||||
int nIdx; /* Number of indices that need updating */
|
||||
int iCur; /* VDBE Cursor number of pTab */
|
||||
sqlite3 db; /* The database structure */
|
||||
int[] aRegIdx = null; /* One register assigned to each index to be updated */
|
||||
int[] aXRef = null; /* aXRef[i] is the index in pChanges.a[] of the
|
||||
** an expression for the i-th column of the table.
|
||||
** aXRef[i]==-1 if the i-th column is not changed. */
|
||||
bool chngRowid; /* True if the record number is being changed */
|
||||
Expr pRowidExpr = null; /* Expression defining the new record number */
|
||||
bool openAll = false; /* True if all indices need to be opened */
|
||||
AuthContext sContext; /* The authorization context */
|
||||
NameContext sNC; /* The name-context to resolve expressions in */
|
||||
int iDb; /* Database containing the table being updated */
|
||||
bool okOnePass; /* True for one-pass algorithm without the FIFO */
|
||||
bool hasFK; /* True if foreign key processing is required */
|
||||
|
||||
#if !SQLITE_OMIT_TRIGGER
|
||||
bool isView; /* True when updating a view (INSTEAD OF trigger) */
|
||||
Trigger pTrigger; /* List of triggers on pTab, if required */
|
||||
int tmask = 0; /* Mask of TRIGGER_BEFORE|TRIGGER_AFTER */
|
||||
#endif
|
||||
int newmask; /* Mask of NEW.* columns accessed by BEFORE triggers */
|
||||
|
||||
/* Register Allocations */
|
||||
int regRowCount = 0; /* A count of rows changed */
|
||||
int regOldRowid; /* The old rowid */
|
||||
int regNewRowid; /* The new rowid */
|
||||
int regNew;
|
||||
int regOld = 0;
|
||||
int regRowSet = 0; /* Rowset of rows to be updated */
|
||||
|
||||
sContext = new AuthContext(); //memset( &sContext, 0, sizeof( sContext ) );
|
||||
db = pParse.db;
|
||||
if ( pParse.nErr != 0 /*|| db.mallocFailed != 0 */ )
|
||||
{
|
||||
goto update_cleanup;
|
||||
}
|
||||
Debug.Assert( pTabList.nSrc == 1 );
|
||||
|
||||
/* Locate the table which we want to update.
|
||||
*/
|
||||
pTab = sqlite3SrcListLookup( pParse, pTabList );
|
||||
if ( pTab == null )
|
||||
goto update_cleanup;
|
||||
iDb = sqlite3SchemaToIndex( pParse.db, pTab.pSchema );
|
||||
|
||||
/* Figure out if we have any triggers and if the table being
|
||||
** updated is a view.
|
||||
*/
|
||||
#if !SQLITE_OMIT_TRIGGER
|
||||
pTrigger = sqlite3TriggersExist( pParse, pTab, TK_UPDATE, pChanges, out tmask );
|
||||
isView = pTab.pSelect != null;
|
||||
Debug.Assert( pTrigger != null || tmask == 0 );
|
||||
#else
|
||||
const Trigger pTrigger = null;//# define pTrigger 0
|
||||
const int tmask = 0; //# define tmask 0
|
||||
#endif
|
||||
#if SQLITE_OMIT_TRIGGER || SQLITE_OMIT_VIEW
|
||||
// # undef isView
|
||||
const bool isView = false; //# define isView 0
|
||||
#endif
|
||||
|
||||
if ( sqlite3ViewGetColumnNames( pParse, pTab ) != 0 )
|
||||
{
|
||||
goto update_cleanup;
|
||||
}
|
||||
if ( sqlite3IsReadOnly( pParse, pTab, tmask ) )
|
||||
{
|
||||
goto update_cleanup;
|
||||
}
|
||||
aXRef = new int[pTab.nCol];// sqlite3DbMallocRaw(db, sizeof(int) * pTab.nCol);
|
||||
//if ( aXRef == null ) goto update_cleanup;
|
||||
for ( i = 0; i < pTab.nCol; i++ )
|
||||
aXRef[i] = -1;
|
||||
|
||||
/* Allocate a cursors for the main database table and for all indices.
|
||||
** The index cursors might not be used, but if they are used they
|
||||
** need to occur right after the database cursor. So go ahead and
|
||||
** allocate enough space, just in case.
|
||||
*/
|
||||
pTabList.a[0].iCursor = iCur = pParse.nTab++;
|
||||
for ( pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext )
|
||||
{
|
||||
pParse.nTab++;
|
||||
}
|
||||
|
||||
/* Initialize the name-context */
|
||||
sNC = new NameContext();// memset(&sNC, 0, sNC).Length;
|
||||
sNC.pParse = pParse;
|
||||
sNC.pSrcList = pTabList;
|
||||
|
||||
/* Resolve the column names in all the expressions of the
|
||||
** of the UPDATE statement. Also find the column index
|
||||
** for each column to be updated in the pChanges array. For each
|
||||
** column to be updated, make sure we have authorization to change
|
||||
** that column.
|
||||
*/
|
||||
chngRowid = false;
|
||||
for ( i = 0; i < pChanges.nExpr; i++ )
|
||||
{
|
||||
if ( sqlite3ResolveExprNames( sNC, ref pChanges.a[i].pExpr ) != 0 )
|
||||
{
|
||||
goto update_cleanup;
|
||||
}
|
||||
for ( j = 0; j < pTab.nCol; j++ )
|
||||
{
|
||||
if ( pTab.aCol[j].zName.Equals( pChanges.a[i].zName, StringComparison.InvariantCultureIgnoreCase ) )
|
||||
{
|
||||
if ( j == pTab.iPKey )
|
||||
{
|
||||
chngRowid = true;
|
||||
pRowidExpr = pChanges.a[i].pExpr;
|
||||
}
|
||||
aXRef[j] = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( j >= pTab.nCol )
|
||||
{
|
||||
if ( sqlite3IsRowid( pChanges.a[i].zName ) )
|
||||
{
|
||||
chngRowid = true;
|
||||
pRowidExpr = pChanges.a[i].pExpr;
|
||||
}
|
||||
else
|
||||
{
|
||||
sqlite3ErrorMsg( pParse, "no such column: %s", pChanges.a[i].zName );
|
||||
pParse.checkSchema = 1;
|
||||
goto update_cleanup;
|
||||
}
|
||||
}
|
||||
#if NO_SQLITE_OMIT_AUTHORIZATION //#if !SQLITE_OMIT_AUTHORIZATION
|
||||
{
|
||||
int rc;
|
||||
rc = sqlite3AuthCheck(pParse, SQLITE_UPDATE, pTab.zName,
|
||||
pTab.aCol[j].zName, db.aDb[iDb].zName);
|
||||
if( rc==SQLITE_DENY ){
|
||||
goto update_cleanup;
|
||||
}else if( rc==SQLITE_IGNORE ){
|
||||
aXRef[j] = -1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
hasFK = sqlite3FkRequired( pParse, pTab, aXRef, chngRowid ? 1 : 0 ) != 0;
|
||||
|
||||
/* Allocate memory for the array aRegIdx[]. There is one entry in the
|
||||
** array for each index associated with table being updated. Fill in
|
||||
** the value with a register number for indices that are to be used
|
||||
** and with zero for unused indices.
|
||||
*/
|
||||
for ( nIdx = 0, pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext, nIdx++ )
|
||||
{
|
||||
}
|
||||
if ( nIdx > 0 )
|
||||
{
|
||||
aRegIdx = new int[nIdx]; // sqlite3DbMallocRaw(db, Index*.Length * nIdx);
|
||||
if ( aRegIdx == null )
|
||||
goto update_cleanup;
|
||||
}
|
||||
for ( j = 0, pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext, j++ )
|
||||
{
|
||||
int reg;
|
||||
if ( hasFK || chngRowid )
|
||||
{
|
||||
reg = ++pParse.nMem;
|
||||
}
|
||||
else
|
||||
{
|
||||
reg = 0;
|
||||
for ( i = 0; i < pIdx.nColumn; i++ )
|
||||
{
|
||||
if ( aXRef[pIdx.aiColumn[i]] >= 0 )
|
||||
{
|
||||
reg = ++pParse.nMem;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
aRegIdx[j] = reg;
|
||||
}
|
||||
|
||||
/* Begin generating code. */
|
||||
v = sqlite3GetVdbe( pParse );
|
||||
if ( v == null )
|
||||
goto update_cleanup;
|
||||
if ( pParse.nested == 0 )
|
||||
sqlite3VdbeCountChanges( v );
|
||||
sqlite3BeginWriteOperation( pParse, 1, iDb );
|
||||
|
||||
#if !SQLITE_OMIT_VIRTUALTABLE
|
||||
/* Virtual tables must be handled separately */
|
||||
if ( IsVirtual( pTab ) )
|
||||
{
|
||||
updateVirtualTable( pParse, pTabList, pTab, pChanges, pRowidExpr, aXRef,
|
||||
pWhere, onError );
|
||||
pWhere = null;
|
||||
pTabList = null;
|
||||
goto update_cleanup;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Allocate required registers. */
|
||||
regOldRowid = regNewRowid = ++pParse.nMem;
|
||||
if ( pTrigger != null || hasFK )
|
||||
{
|
||||
regOld = pParse.nMem + 1;
|
||||
pParse.nMem += pTab.nCol;
|
||||
}
|
||||
if ( chngRowid || pTrigger != null || hasFK )
|
||||
{
|
||||
regNewRowid = ++pParse.nMem;
|
||||
}
|
||||
regNew = pParse.nMem + 1;
|
||||
pParse.nMem += pTab.nCol;
|
||||
|
||||
/* Start the view context. */
|
||||
if ( isView )
|
||||
{
|
||||
sqlite3AuthContextPush( pParse, sContext, pTab.zName );
|
||||
}
|
||||
|
||||
/* If we are trying to update 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 all the expressions in the
|
||||
** WHERE clause.
|
||||
*/
|
||||
if ( sqlite3ResolveExprNames( sNC, ref pWhere ) != 0 )
|
||||
{
|
||||
goto update_cleanup;
|
||||
}
|
||||
|
||||
/* Begin the database scan
|
||||
*/
|
||||
sqlite3VdbeAddOp2( v, OP_Null, 0, regOldRowid );
|
||||
ExprList NullOrderby = null;
|
||||
pWInfo = sqlite3WhereBegin( pParse, pTabList, pWhere, ref NullOrderby, WHERE_ONEPASS_DESIRED );
|
||||
if ( pWInfo == null )
|
||||
goto update_cleanup;
|
||||
okOnePass = pWInfo.okOnePass != 0;
|
||||
|
||||
/* Remember the rowid of every item to be updated.
|
||||
*/
|
||||
sqlite3VdbeAddOp2( v, OP_Rowid, iCur, regOldRowid );
|
||||
if ( !okOnePass )
|
||||
{
|
||||
regRowSet = ++pParse.nMem;
|
||||
sqlite3VdbeAddOp2( v, OP_RowSetAdd, regRowSet, regOldRowid );
|
||||
}
|
||||
|
||||
/* End the database scan loop.
|
||||
*/
|
||||
sqlite3WhereEnd( pWInfo );
|
||||
|
||||
/* Initialize the count of updated rows
|
||||
*/
|
||||
if ( ( db.flags & SQLITE_CountRows ) != 0 && null == pParse.pTriggerTab )
|
||||
{
|
||||
regRowCount = ++pParse.nMem;
|
||||
sqlite3VdbeAddOp2( v, OP_Integer, 0, regRowCount );
|
||||
}
|
||||
|
||||
if ( !isView )
|
||||
{
|
||||
/*
|
||||
** Open every index that needs updating. Note that if any
|
||||
** index could potentially invoke a REPLACE conflict resolution
|
||||
** action, then we need to open all indices because we might need
|
||||
** to be deleting some records.
|
||||
*/
|
||||
if ( !okOnePass )
|
||||
sqlite3OpenTable( pParse, iCur, iDb, pTab, OP_OpenWrite );
|
||||
if ( onError == OE_Replace )
|
||||
{
|
||||
openAll = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
openAll = false;
|
||||
for ( pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext )
|
||||
{
|
||||
if ( pIdx.onError == OE_Replace )
|
||||
{
|
||||
openAll = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for ( i = 0, pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext, i++ )
|
||||
{
|
||||
if ( openAll || aRegIdx[i] > 0 )
|
||||
{
|
||||
KeyInfo pKey = sqlite3IndexKeyinfo( pParse, pIdx );
|
||||
sqlite3VdbeAddOp4( v, OP_OpenWrite, iCur + i + 1, pIdx.tnum, iDb,
|
||||
pKey, P4_KEYINFO_HANDOFF );
|
||||
Debug.Assert( pParse.nTab > iCur + i + 1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Top of the update loop */
|
||||
if ( okOnePass )
|
||||
{
|
||||
int a1 = sqlite3VdbeAddOp1( v, OP_NotNull, regOldRowid );
|
||||
addr = sqlite3VdbeAddOp0( v, OP_Goto );
|
||||
sqlite3VdbeJumpHere( v, a1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
addr = sqlite3VdbeAddOp3( v, OP_RowSetRead, regRowSet, 0, regOldRowid );
|
||||
}
|
||||
|
||||
/* Make cursor iCur point to the record that is being updated. If
|
||||
** this record does not exist for some reason (deleted by a trigger,
|
||||
** for example, then jump to the next iteration of the RowSet loop. */
|
||||
sqlite3VdbeAddOp3( v, OP_NotExists, iCur, addr, regOldRowid );
|
||||
|
||||
/* If the record number will change, set register regNewRowid to
|
||||
** contain the new value. If the record number is not being modified,
|
||||
** then regNewRowid is the same register as regOldRowid, which is
|
||||
** already populated. */
|
||||
Debug.Assert( chngRowid || pTrigger != null || hasFK || regOldRowid == regNewRowid );
|
||||
if ( chngRowid )
|
||||
{
|
||||
sqlite3ExprCode( pParse, pRowidExpr, regNewRowid );
|
||||
sqlite3VdbeAddOp1( v, OP_MustBeInt, regNewRowid );
|
||||
}
|
||||
|
||||
/* If there are triggers on this table, populate an array of registers
|
||||
** with the required old.* column data. */
|
||||
if ( hasFK || pTrigger != null )
|
||||
{
|
||||
u32 oldmask = ( hasFK ? sqlite3FkOldmask( pParse, pTab ) : 0 );
|
||||
oldmask |= sqlite3TriggerColmask( pParse,
|
||||
pTrigger, pChanges, 0, TRIGGER_BEFORE | TRIGGER_AFTER, pTab, onError
|
||||
);
|
||||
for ( i = 0; i < pTab.nCol; i++ )
|
||||
{
|
||||
if ( aXRef[i] < 0 || oldmask == 0xffffffff || ( i < 32 && 0 != ( oldmask & ( 1 << i ) ) ) )
|
||||
{
|
||||
sqlite3ExprCodeGetColumnOfTable( v, pTab, iCur, i, regOld + i );
|
||||
}
|
||||
else
|
||||
{
|
||||
sqlite3VdbeAddOp2( v, OP_Null, 0, regOld + i );
|
||||
}
|
||||
}
|
||||
if ( chngRowid == false )
|
||||
{
|
||||
sqlite3VdbeAddOp2( v, OP_Copy, regOldRowid, regNewRowid );
|
||||
}
|
||||
}
|
||||
|
||||
/* Populate the array of registers beginning at regNew with the new
|
||||
** row data. This array is used to check constaints, create the new
|
||||
** table and index records, and as the values for any new.* references
|
||||
** made by triggers.
|
||||
**
|
||||
** If there are one or more BEFORE triggers, then do not populate the
|
||||
** registers associated with columns that are (a) not modified by
|
||||
** this UPDATE statement and (b) not accessed by new.* references. The
|
||||
** values for registers not modified by the UPDATE must be reloaded from
|
||||
** the database after the BEFORE triggers are fired anyway (as the trigger
|
||||
** may have modified them). So not loading those that are not going to
|
||||
** be used eliminates some redundant opcodes.
|
||||
*/
|
||||
newmask = (int)sqlite3TriggerColmask(
|
||||
pParse, pTrigger, pChanges, 1, TRIGGER_BEFORE, pTab, onError
|
||||
);
|
||||
for ( i = 0; i < pTab.nCol; i++ )
|
||||
{
|
||||
if ( i == pTab.iPKey )
|
||||
{
|
||||
sqlite3VdbeAddOp2( v, OP_Null, 0, regNew + i );
|
||||
}
|
||||
else
|
||||
{
|
||||
j = aXRef[i];
|
||||
if ( j >= 0 )
|
||||
{
|
||||
sqlite3ExprCode( pParse, pChanges.a[j].pExpr, regNew + i );
|
||||
}
|
||||
else if ( 0 == ( tmask & TRIGGER_BEFORE ) || i > 31 || ( newmask & ( 1 << i ) ) != 0 )
|
||||
{
|
||||
/* This branch loads the value of a column that will not be changed
|
||||
** into a register. This is done if there are no BEFORE triggers, or
|
||||
** if there are one or more BEFORE triggers that use this value via
|
||||
** a new.* reference in a trigger program.
|
||||
*/
|
||||
testcase( i == 31 );
|
||||
testcase( i == 32 );
|
||||
sqlite3VdbeAddOp3( v, OP_Column, iCur, i, regNew + i );
|
||||
sqlite3ColumnDefault( v, pTab, i, regNew + i );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Fire any BEFORE UPDATE triggers. This happens before constraints are
|
||||
** verified. One could argue that this is wrong.
|
||||
*/
|
||||
if ( ( tmask & TRIGGER_BEFORE ) != 0 )
|
||||
{
|
||||
sqlite3VdbeAddOp2( v, OP_Affinity, regNew, pTab.nCol );
|
||||
sqlite3TableAffinityStr( v, pTab );
|
||||
sqlite3CodeRowTrigger( pParse, pTrigger, TK_UPDATE, pChanges,
|
||||
TRIGGER_BEFORE, pTab, regOldRowid, onError, addr );
|
||||
|
||||
/* The row-trigger may have deleted the row being updated. In this
|
||||
** case, jump to the next row. No updates or AFTER triggers are
|
||||
** required. This behaviour - what happens when the row being updated
|
||||
** is deleted or renamed by a BEFORE trigger - is left undefined in the
|
||||
** documentation.
|
||||
*/
|
||||
sqlite3VdbeAddOp3( v, OP_NotExists, iCur, addr, regOldRowid );
|
||||
|
||||
/* If it did not delete it, the row-trigger may still have modified
|
||||
** some of the columns of the row being updated. Load the values for
|
||||
** all columns not modified by the update statement into their
|
||||
** registers in case this has happened.
|
||||
*/
|
||||
for ( i = 0; i < pTab.nCol; i++ )
|
||||
{
|
||||
if ( aXRef[i] < 0 && i != pTab.iPKey )
|
||||
{
|
||||
sqlite3VdbeAddOp3( v, OP_Column, iCur, i, regNew + i );
|
||||
sqlite3ColumnDefault( v, pTab, i, regNew + i );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !isView )
|
||||
{
|
||||
int j1; /* Address of jump instruction */
|
||||
|
||||
/* Do constraint checks. */
|
||||
int iDummy;
|
||||
sqlite3GenerateConstraintChecks( pParse, pTab, iCur, regNewRowid,
|
||||
aRegIdx, ( chngRowid ? regOldRowid : 0 ), true, onError, addr, out iDummy );
|
||||
|
||||
/* Do FK constraint checks. */
|
||||
if ( hasFK )
|
||||
{
|
||||
sqlite3FkCheck( pParse, pTab, regOldRowid, 0 );
|
||||
}
|
||||
|
||||
/* Delete the index entries associated with the current record. */
|
||||
j1 = sqlite3VdbeAddOp3( v, OP_NotExists, iCur, 0, regOldRowid );
|
||||
sqlite3GenerateRowIndexDelete( pParse, pTab, iCur, aRegIdx );
|
||||
|
||||
/* If changing the record number, delete the old record. */
|
||||
if ( hasFK || chngRowid )
|
||||
{
|
||||
sqlite3VdbeAddOp2( v, OP_Delete, iCur, 0 );
|
||||
}
|
||||
sqlite3VdbeJumpHere( v, j1 );
|
||||
|
||||
if ( hasFK )
|
||||
{
|
||||
sqlite3FkCheck( pParse, pTab, 0, regNewRowid );
|
||||
}
|
||||
|
||||
/* Insert the new index entries and the new record. */
|
||||
sqlite3CompleteInsertion( pParse, pTab, iCur, regNewRowid, aRegIdx, true, false, false );
|
||||
|
||||
/* 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 updated. */
|
||||
if ( hasFK )
|
||||
{
|
||||
sqlite3FkActions( pParse, pTab, pChanges, regOldRowid );
|
||||
}
|
||||
}
|
||||
|
||||
/* Increment the row counter
|
||||
*/
|
||||
if ( ( db.flags & SQLITE_CountRows ) != 0 && null == pParse.pTriggerTab )
|
||||
{
|
||||
sqlite3VdbeAddOp2( v, OP_AddImm, regRowCount, 1 );
|
||||
}
|
||||
|
||||
sqlite3CodeRowTrigger( pParse, pTrigger, TK_UPDATE, pChanges,
|
||||
TRIGGER_AFTER, pTab, regOldRowid, onError, addr );
|
||||
|
||||
/* Repeat the above with the next record to be updated, until
|
||||
** all record selected by the WHERE clause have been updated.
|
||||
*/
|
||||
sqlite3VdbeAddOp2( v, OP_Goto, 0, addr );
|
||||
sqlite3VdbeJumpHere( v, addr );
|
||||
|
||||
/* Close all tables */
|
||||
for ( i = 0, pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext, i++ )
|
||||
{
|
||||
if ( openAll || aRegIdx[i] > 0 )
|
||||
{
|
||||
sqlite3VdbeAddOp2( v, OP_Close, iCur + i + 1, 0 );
|
||||
}
|
||||
}
|
||||
sqlite3VdbeAddOp2( v, OP_Close, iCur, 0 );
|
||||
|
||||
/* 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 changed. If this routine is
|
||||
** generating code because of a call to sqlite3NestedParse(), do not
|
||||
** invoke the callback function.
|
||||
*/
|
||||
if ( ( db.flags & SQLITE_CountRows ) != 0 && null == pParse.pTriggerTab && 0 == pParse.nested )
|
||||
{
|
||||
sqlite3VdbeAddOp2( v, OP_ResultRow, regRowCount, 1 );
|
||||
sqlite3VdbeSetNumCols( v, 1 );
|
||||
sqlite3VdbeSetColName( v, 0, COLNAME_NAME, "rows updated", SQLITE_STATIC );
|
||||
}
|
||||
|
||||
update_cleanup:
|
||||
#if NO_SQLITE_OMIT_AUTHORIZATION //#if !SQLITE_OMIT_AUTHORIZATION
|
||||
sqlite3AuthContextPop(sContext);
|
||||
#endif
|
||||
sqlite3DbFree( db, ref aRegIdx );
|
||||
sqlite3DbFree( db, ref aXRef );
|
||||
sqlite3SrcListDelete( db, ref pTabList );
|
||||
sqlite3ExprListDelete( db, ref pChanges );
|
||||
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
|
||||
|
||||
#if !SQLITE_OMIT_VIRTUALTABLE
|
||||
/*
|
||||
** Generate code for an UPDATE of a virtual table.
|
||||
**
|
||||
** The strategy is that we create an ephemerial table that contains
|
||||
** for each row to be changed:
|
||||
**
|
||||
** (A) The original rowid of that row.
|
||||
** (B) The revised rowid for the row. (note1)
|
||||
** (C) The content of every column in the row.
|
||||
**
|
||||
** Then we loop over this ephemeral table and for each row in
|
||||
** the ephermeral table call VUpdate.
|
||||
**
|
||||
** When finished, drop the ephemeral table.
|
||||
**
|
||||
** (note1) Actually, if we know in advance that (A) is always the same
|
||||
** as (B) we only store (A), then duplicate (A) when pulling
|
||||
** it out of the ephemeral table before calling VUpdate.
|
||||
*/
|
||||
static void updateVirtualTable(
|
||||
Parse pParse, /* The parsing context */
|
||||
SrcList pSrc, /* The virtual table to be modified */
|
||||
Table pTab, /* The virtual table */
|
||||
ExprList pChanges, /* The columns to change in the UPDATE statement */
|
||||
Expr pRowid, /* Expression used to recompute the rowid */
|
||||
int[] aXRef, /* Mapping from columns of pTab to entries in pChanges */
|
||||
Expr pWhere, /* WHERE clause of the UPDATE statement */
|
||||
int onError /* ON CONFLICT strategy */
|
||||
)
|
||||
{
|
||||
Vdbe v = pParse.pVdbe; /* Virtual machine under construction */
|
||||
ExprList pEList = null; /* The result set of the SELECT statement */
|
||||
Select pSelect = null; /* The SELECT statement */
|
||||
Expr pExpr; /* Temporary expression */
|
||||
int ephemTab; /* Table holding the result of the SELECT */
|
||||
int i; /* Loop counter */
|
||||
int addr; /* Address of top of loop */
|
||||
int iReg; /* First register in set passed to OP_VUpdate */
|
||||
sqlite3 db = pParse.db; /* Database connection */
|
||||
VTable pVTab = sqlite3GetVTable( db, pTab );
|
||||
SelectDest dest = new SelectDest();
|
||||
|
||||
/* Construct the SELECT statement that will find the new values for
|
||||
** all updated rows.
|
||||
*/
|
||||
pEList = sqlite3ExprListAppend( pParse, 0, sqlite3Expr( db, TK_ID, "_rowid_" ) );
|
||||
if ( pRowid != null)
|
||||
{
|
||||
pEList = sqlite3ExprListAppend( pParse, pEList,
|
||||
sqlite3ExprDup( db, pRowid, 0 ) );
|
||||
}
|
||||
Debug.Assert( pTab.iPKey < 0 );
|
||||
for ( i = 0; i < pTab.nCol; i++ )
|
||||
{
|
||||
if ( aXRef[i] >= 0 )
|
||||
{
|
||||
pExpr = sqlite3ExprDup( db, pChanges.a[aXRef[i]].pExpr, 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
pExpr = sqlite3Expr( db, TK_ID, pTab.aCol[i].zName );
|
||||
}
|
||||
pEList = sqlite3ExprListAppend( pParse, pEList, pExpr );
|
||||
}
|
||||
pSelect = sqlite3SelectNew( pParse, pEList, pSrc, pWhere, null, null, null, 0, null, null );
|
||||
|
||||
/* Create the ephemeral table into which the update results will
|
||||
** be stored.
|
||||
*/
|
||||
Debug.Assert( v != null);
|
||||
ephemTab = pParse.nTab++;
|
||||
sqlite3VdbeAddOp2( v, OP_OpenEphemeral, ephemTab, pTab.nCol + 1 + ( ( pRowid != null ) ? 1 : 0 ) );
|
||||
sqlite3VdbeChangeP5( v, BTREE_UNORDERED );
|
||||
|
||||
/* fill the ephemeral table
|
||||
*/
|
||||
sqlite3SelectDestInit( dest, SRT_Table, ephemTab );
|
||||
sqlite3Select( pParse, pSelect, ref dest );
|
||||
|
||||
/* Generate code to scan the ephemeral table and call VUpdate. */
|
||||
iReg = ++pParse.nMem;
|
||||
pParse.nMem += pTab.nCol + 1;
|
||||
addr = sqlite3VdbeAddOp2( v, OP_Rewind, ephemTab, 0 );
|
||||
sqlite3VdbeAddOp3( v, OP_Column, ephemTab, 0, iReg );
|
||||
sqlite3VdbeAddOp3( v, OP_Column, ephemTab, ( pRowid != null ? 1 : 0 ), iReg + 1 );
|
||||
for ( i = 0; i < pTab.nCol; i++ )
|
||||
{
|
||||
sqlite3VdbeAddOp3( v, OP_Column, ephemTab, i + 1 + ( ( pRowid != null ) ? 1 : 0 ), iReg + 2 + i );
|
||||
}
|
||||
sqlite3VtabMakeWritable( pParse, pTab );
|
||||
sqlite3VdbeAddOp4( v, OP_VUpdate, 0, pTab.nCol + 2, iReg, pVTab, P4_VTAB );
|
||||
sqlite3VdbeChangeP5( v, (byte)( onError == OE_Default ? OE_Abort : onError ) );
|
||||
sqlite3MayAbort( pParse );
|
||||
sqlite3VdbeAddOp2( v, OP_Next, ephemTab, addr + 1 );
|
||||
sqlite3VdbeJumpHere( v, addr );
|
||||
sqlite3VdbeAddOp2( v, OP_Close, ephemTab, 0 );
|
||||
|
||||
/* Cleanup */
|
||||
sqlite3SelectDelete( db, ref pSelect );
|
||||
}
|
||||
#endif // * SQLITE_OMIT_VIRTUALTABLE */
|
||||
}
|
||||
}
|
||||
633
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/utf_c.cs
Normal file
633
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/utf_c.cs
Normal file
@@ -0,0 +1,633 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using u32 = System.UInt32;
|
||||
|
||||
namespace Community.CsharpSqlite
|
||||
{
|
||||
public partial class Sqlite3
|
||||
{
|
||||
/*
|
||||
** 2004 April 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 routines used to translate between UTF-8,
|
||||
** UTF-16, UTF-16BE, and UTF-16LE.
|
||||
**
|
||||
** Notes on UTF-8:
|
||||
**
|
||||
** Byte-0 Byte-1 Byte-2 Byte-3 Value
|
||||
** 0xxxxxxx 00000000 00000000 0xxxxxxx
|
||||
** 110yyyyy 10xxxxxx 00000000 00000yyy yyxxxxxx
|
||||
** 1110zzzz 10yyyyyy 10xxxxxx 00000000 zzzzyyyy yyxxxxxx
|
||||
** 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx 000uuuuu zzzzyyyy yyxxxxxx
|
||||
**
|
||||
**
|
||||
** Notes on UTF-16: (with wwww+1==uuuuu)
|
||||
**
|
||||
** Word-0 Word-1 Value
|
||||
** 110110ww wwzzzzyy 110111yy yyxxxxxx 000uuuuu zzzzyyyy yyxxxxxx
|
||||
** zzzzyyyy yyxxxxxx 00000000 zzzzyyyy yyxxxxxx
|
||||
**
|
||||
**
|
||||
** BOM or Byte Order Mark:
|
||||
** 0xff 0xfe little-endian utf-16 follows
|
||||
** 0xfe 0xff big-endian utf-16 follows
|
||||
**
|
||||
*************************************************************************
|
||||
** 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"
|
||||
//#include <assert.h>
|
||||
//#include "vdbeInt.h"
|
||||
|
||||
#if !SQLITE_AMALGAMATION
|
||||
/*
|
||||
** The following constant value is used by the SQLITE_BIGENDIAN and
|
||||
** SQLITE_LITTLEENDIAN macros.
|
||||
*/
|
||||
//const int sqlite3one = 1;
|
||||
#endif //* SQLITE_AMALGAMATION */
|
||||
|
||||
/*
|
||||
** This lookup table is used to help decode the first byte of
|
||||
** a multi-byte UTF8 character.
|
||||
*/
|
||||
static byte[] sqlite3Utf8Trans1 = new byte[] {
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00,
|
||||
};
|
||||
|
||||
|
||||
//#define WRITE_UTF8(zOut, c) { \
|
||||
// if( c<0x00080 ){ \
|
||||
// *zOut++ = (u8)(c&0xFF); \
|
||||
// } \
|
||||
// else if( c<0x00800 ){ \
|
||||
// *zOut++ = 0xC0 + (u8)((c>>6)&0x1F); \
|
||||
// *zOut++ = 0x80 + (u8)(c & 0x3F); \
|
||||
// } \
|
||||
// else if( c<0x10000 ){ \
|
||||
// *zOut++ = 0xE0 + (u8)((c>>12)&0x0F); \
|
||||
// *zOut++ = 0x80 + (u8)((c>>6) & 0x3F); \
|
||||
// *zOut++ = 0x80 + (u8)(c & 0x3F); \
|
||||
// }else{ \
|
||||
// *zOut++ = 0xF0 + (u8)((c>>18) & 0x07); \
|
||||
// *zOut++ = 0x80 + (u8)((c>>12) & 0x3F); \
|
||||
// *zOut++ = 0x80 + (u8)((c>>6) & 0x3F); \
|
||||
// *zOut++ = 0x80 + (u8)(c & 0x3F); \
|
||||
// } \
|
||||
//}
|
||||
|
||||
//#define WRITE_UTF16LE(zOut, c) { \
|
||||
// if( c<=0xFFFF ){ \
|
||||
// *zOut++ = (u8)(c&0x00FF); \
|
||||
// *zOut++ = (u8)((c>>8)&0x00FF); \
|
||||
// }else{ \
|
||||
// *zOut++ = (u8)(((c>>10)&0x003F) + (((c-0x10000)>>10)&0x00C0)); \
|
||||
// *zOut++ = (u8)(0x00D8 + (((c-0x10000)>>18)&0x03)); \
|
||||
// *zOut++ = (u8)(c&0x00FF); \
|
||||
// *zOut++ = (u8)(0x00DC + ((c>>8)&0x03)); \
|
||||
// } \
|
||||
//}
|
||||
|
||||
//#define WRITE_UTF16BE(zOut, c) { \
|
||||
// if( c<=0xFFFF ){ \
|
||||
// *zOut++ = (u8)((c>>8)&0x00FF); \
|
||||
// *zOut++ = (u8)(c&0x00FF); \
|
||||
// }else{ \
|
||||
// *zOut++ = (u8)(0x00D8 + (((c-0x10000)>>18)&0x03)); \
|
||||
// *zOut++ = (u8)(((c>>10)&0x003F) + (((c-0x10000)>>10)&0x00C0)); \
|
||||
// *zOut++ = (u8)(0x00DC + ((c>>8)&0x03)); \
|
||||
// *zOut++ = (u8)(c&0x00FF); \
|
||||
// } \
|
||||
//}
|
||||
|
||||
//#define READ_UTF16LE(zIn, TERM, c){ \
|
||||
// c = (*zIn++); \
|
||||
// c += ((*zIn++)<<8); \
|
||||
// if( c>=0xD800 && c<0xE000 && TERM ){ \
|
||||
// int c2 = (*zIn++); \
|
||||
// c2 += ((*zIn++)<<8); \
|
||||
// c = (c2&0x03FF) + ((c&0x003F)<<10) + (((c&0x03C0)+0x0040)<<10); \
|
||||
// } \
|
||||
//}
|
||||
|
||||
//#define READ_UTF16BE(zIn, TERM, c){ \
|
||||
// c = ((*zIn++)<<8); \
|
||||
// c += (*zIn++); \
|
||||
// if( c>=0xD800 && c<0xE000 && TERM ){ \
|
||||
// int c2 = ((*zIn++)<<8); \
|
||||
// c2 += (*zIn++); \
|
||||
// c = (c2&0x03FF) + ((c&0x003F)<<10) + (((c&0x03C0)+0x0040)<<10); \
|
||||
// } \
|
||||
//}
|
||||
|
||||
/*
|
||||
** Translate a single UTF-8 character. Return the unicode value.
|
||||
**
|
||||
** During translation, assume that the byte that zTerm points
|
||||
** is a 0x00.
|
||||
**
|
||||
** Write a pointer to the next unread byte back into pzNext.
|
||||
**
|
||||
** Notes On Invalid UTF-8:
|
||||
**
|
||||
** * This routine never allows a 7-bit character (0x00 through 0x7f) to
|
||||
** be encoded as a multi-byte character. Any multi-byte character that
|
||||
** attempts to encode a value between 0x00 and 0x7f is rendered as 0xfffd.
|
||||
**
|
||||
** * This routine never allows a UTF16 surrogate value to be encoded.
|
||||
** If a multi-byte character attempts to encode a value between
|
||||
** 0xd800 and 0xe000 then it is rendered as 0xfffd.
|
||||
**
|
||||
** * Bytes in the range of 0x80 through 0xbf which occur as the first
|
||||
** byte of a character are interpreted as single-byte characters
|
||||
** and rendered as themselves even though they are technically
|
||||
** invalid characters.
|
||||
**
|
||||
** * This routine accepts an infinite number of different UTF8 encodings
|
||||
** for unicode values 0x80 and greater. It do not change over-length
|
||||
** encodings to 0xfffd as some systems recommend.
|
||||
*/
|
||||
//#define READ_UTF8(zIn, zTerm, c) \
|
||||
// c = *(zIn++); \
|
||||
// if( c>=0xc0 ){ \
|
||||
// c = sqlite3Utf8Trans1[c-0xc0]; \
|
||||
// while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \
|
||||
// c = (c<<6) + (0x3f & *(zIn++)); \
|
||||
// } \
|
||||
// if( c<0x80 \
|
||||
// || (c&0xFFFFF800)==0xD800 \
|
||||
// || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \
|
||||
// }
|
||||
static u32 sqlite3Utf8Read(
|
||||
string zIn, /* First byte of UTF-8 character */
|
||||
ref string pzNext /* Write first byte past UTF-8 char here */
|
||||
)
|
||||
{
|
||||
//unsigned int c;
|
||||
/* Same as READ_UTF8() above but without the zTerm parameter.
|
||||
** For this routine, we assume the UTF8 string is always zero-terminated.
|
||||
*/
|
||||
if ( String.IsNullOrEmpty( zIn ) )
|
||||
return 0;
|
||||
//c = *( zIn++ );
|
||||
//if ( c >= 0xc0 )
|
||||
//{
|
||||
// c = sqlite3Utf8Trans1[c - 0xc0];
|
||||
// while ( ( *zIn & 0xc0 ) == 0x80 )
|
||||
// {
|
||||
// c = ( c << 6 ) + ( 0x3f & *( zIn++ ) );
|
||||
// }
|
||||
// if ( c < 0x80
|
||||
// || ( c & 0xFFFFF800 ) == 0xD800
|
||||
// || ( c & 0xFFFFFFFE ) == 0xFFFE ) { c = 0xFFFD; }
|
||||
//}
|
||||
//*pzNext = zIn;
|
||||
int zIndex = 0;
|
||||
u32 c = zIn[zIndex++];
|
||||
if ( c >= 0xc0 )
|
||||
{
|
||||
//if ( c > 0xff ) c = 0;
|
||||
//else
|
||||
{
|
||||
//c = sqlite3Utf8Trans1[c - 0xc0];
|
||||
while ( zIndex != zIn.Length && ( zIn[zIndex] & 0xc0 ) == 0x80 )
|
||||
{
|
||||
c = (u32)( ( c << 6 ) + ( 0x3f & zIn[zIndex++] ) );
|
||||
}
|
||||
if ( c < 0x80
|
||||
|| ( c & 0xFFFFF800 ) == 0xD800
|
||||
|| ( c & 0xFFFFFFFE ) == 0xFFFE )
|
||||
{
|
||||
c = 0xFFFD;
|
||||
}
|
||||
}
|
||||
}
|
||||
pzNext = zIn.Substring( zIndex );
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** If the TRANSLATE_TRACE macro is defined, the value of each Mem is
|
||||
** printed on stderr on the way into and out of sqlite3VdbeMemTranslate().
|
||||
*/
|
||||
/* #define TRANSLATE_TRACE 1 */
|
||||
|
||||
#if NO_SQLITE_OMIT_UTF16 //#if !SQLITE_OMIT_UTF16
|
||||
|
||||
/*
|
||||
** This routine transforms the internal text encoding used by pMem to
|
||||
** desiredEnc. It is an error if the string is already of the desired
|
||||
** encoding, or if pMem does not contain a string value.
|
||||
*/
|
||||
static int sqlite3VdbeMemTranslate(Mem pMem, int desiredEnc){
|
||||
int len; /* Maximum length of output string in bytes */
|
||||
Debugger.Break (); // TODO -
|
||||
//unsigned char *zOut; /* Output buffer */
|
||||
//unsigned char *zIn; /* Input iterator */
|
||||
//unsigned char *zTerm; /* End of input */
|
||||
//unsigned char *z; /* Output iterator */
|
||||
//unsigned int c;
|
||||
|
||||
Debug.Assert( pMem.db==null || sqlite3_mutex_held(pMem.db.mutex) );
|
||||
Debug.Assert( (pMem.flags&MEM_Str )!=0);
|
||||
Debug.Assert( pMem.enc!=desiredEnc );
|
||||
Debug.Assert( pMem.enc!=0 );
|
||||
Debug.Assert( pMem.n>=0 );
|
||||
|
||||
#if TRANSLATE_TRACE && SQLITE_DEBUG
|
||||
{
|
||||
char zBuf[100];
|
||||
sqlite3VdbeMemPrettyPrint(pMem, zBuf);
|
||||
fprintf(stderr, "INPUT: %s\n", zBuf);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If the translation is between UTF-16 little and big endian, then
|
||||
** all that is required is to swap the byte order. This case is handled
|
||||
** differently from the others.
|
||||
*/
|
||||
Debugger.Break (); // TODO -
|
||||
//if( pMem->enc!=SQLITE_UTF8 && desiredEnc!=SQLITE_UTF8 ){
|
||||
// u8 temp;
|
||||
// int rc;
|
||||
// rc = sqlite3VdbeMemMakeWriteable(pMem);
|
||||
// if( rc!=SQLITE_OK ){
|
||||
// Debug.Assert( rc==SQLITE_NOMEM );
|
||||
// return SQLITE_NOMEM;
|
||||
// }
|
||||
// zIn = (u8*)pMem.z;
|
||||
// zTerm = &zIn[pMem->n&~1];
|
||||
// while( zIn<zTerm ){
|
||||
// temp = *zIn;
|
||||
// *zIn = *(zIn+1);
|
||||
// zIn++;
|
||||
// *zIn++ = temp;
|
||||
// }
|
||||
// pMem->enc = desiredEnc;
|
||||
// goto translate_out;
|
||||
//}
|
||||
|
||||
/* Set len to the maximum number of bytes required in the output buffer. */
|
||||
if( desiredEnc==SQLITE_UTF8 ){
|
||||
/* When converting from UTF-16, the maximum growth results from
|
||||
** translating a 2-byte character to a 4-byte UTF-8 character.
|
||||
** A single byte is required for the output string
|
||||
** nul-terminator.
|
||||
*/
|
||||
pMem->n &= ~1;
|
||||
len = pMem.n * 2 + 1;
|
||||
}else{
|
||||
/* When converting from UTF-8 to UTF-16 the maximum growth is caused
|
||||
** when a 1-byte UTF-8 character is translated into a 2-byte UTF-16
|
||||
** character. Two bytes are required in the output buffer for the
|
||||
** nul-terminator.
|
||||
*/
|
||||
len = pMem.n * 2 + 2;
|
||||
}
|
||||
|
||||
/* Set zIn to point at the start of the input buffer and zTerm to point 1
|
||||
** byte past the end.
|
||||
**
|
||||
** Variable zOut is set to point at the output buffer, space obtained
|
||||
** from sqlite3Malloc().
|
||||
*/
|
||||
Debugger.Break (); // TODO -
|
||||
//zIn = (u8*)pMem.z;
|
||||
//zTerm = &zIn[pMem->n];
|
||||
//zOut = sqlite3DbMallocRaw(pMem->db, len);
|
||||
//if( !zOut ){
|
||||
// return SQLITE_NOMEM;
|
||||
//}
|
||||
//z = zOut;
|
||||
|
||||
//if( pMem->enc==SQLITE_UTF8 ){
|
||||
// if( desiredEnc==SQLITE_UTF16LE ){
|
||||
// /* UTF-8 -> UTF-16 Little-endian */
|
||||
// while( zIn<zTerm ){
|
||||
///* c = sqlite3Utf8Read(zIn, zTerm, (const u8**)&zIn); */
|
||||
//READ_UTF8(zIn, zTerm, c);
|
||||
// WRITE_UTF16LE(z, c);
|
||||
// }
|
||||
// }else{
|
||||
// Debug.Assert( desiredEnc==SQLITE_UTF16BE );
|
||||
// /* UTF-8 -> UTF-16 Big-endian */
|
||||
// while( zIn<zTerm ){
|
||||
///* c = sqlite3Utf8Read(zIn, zTerm, (const u8**)&zIn); */
|
||||
//READ_UTF8(zIn, zTerm, c);
|
||||
// WRITE_UTF16BE(z, c);
|
||||
// }
|
||||
// }
|
||||
// pMem->n = (int)(z - zOut);
|
||||
// *z++ = 0;
|
||||
//}else{
|
||||
// Debug.Assert( desiredEnc==SQLITE_UTF8 );
|
||||
// if( pMem->enc==SQLITE_UTF16LE ){
|
||||
// /* UTF-16 Little-endian -> UTF-8 */
|
||||
// while( zIn<zTerm ){
|
||||
// READ_UTF16LE(zIn, zIn<zTerm, c);
|
||||
// WRITE_UTF8(z, c);
|
||||
// }
|
||||
// }else{
|
||||
// /* UTF-16 Big-endian -> UTF-8 */
|
||||
// while( zIn<zTerm ){
|
||||
// READ_UTF16BE(zIn, zIn<zTerm, c);
|
||||
// WRITE_UTF8(z, c);
|
||||
// }
|
||||
// }
|
||||
// pMem->n = (int)(z - zOut);
|
||||
//}
|
||||
//*z = 0;
|
||||
//Debug.Assert( (pMem->n+(desiredEnc==SQLITE_UTF8?1:2))<=len );
|
||||
|
||||
//sqlite3VdbeMemRelease(pMem);
|
||||
//pMem->flags &= ~(MEM_Static|MEM_Dyn|MEM_Ephem);
|
||||
//pMem->enc = desiredEnc;
|
||||
//pMem->flags |= (MEM_Term|MEM_Dyn);
|
||||
//pMem.z = (char*)zOut;
|
||||
//pMem.zMalloc = pMem.z;
|
||||
|
||||
translate_out:
|
||||
#if TRANSLATE_TRACE && SQLITE_DEBUG
|
||||
{
|
||||
char zBuf[100];
|
||||
sqlite3VdbeMemPrettyPrint(pMem, zBuf);
|
||||
fprintf(stderr, "OUTPUT: %s\n", zBuf);
|
||||
}
|
||||
#endif
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine checks for a byte-order mark at the beginning of the
|
||||
** UTF-16 string stored in pMem. If one is present, it is removed and
|
||||
** the encoding of the Mem adjusted. This routine does not do any
|
||||
** byte-swapping, it just sets Mem.enc appropriately.
|
||||
**
|
||||
** The allocation (static, dynamic etc.) and encoding of the Mem may be
|
||||
** changed by this function.
|
||||
*/
|
||||
static int sqlite3VdbeMemHandleBom(Mem pMem){
|
||||
int rc = SQLITE_OK;
|
||||
int bom = 0;
|
||||
byte[] b01 = new byte[2];
|
||||
Encoding.Unicode.GetBytes( pMem.z, 0, 1,b01,0 );
|
||||
assert( pMem->n>=0 );
|
||||
if( pMem->n>1 ){
|
||||
// u8 b1 = *(u8 *)pMem.z;
|
||||
// u8 b2 = *(((u8 *)pMem.z) + 1);
|
||||
if( b01[0]==0xFE && b01[1]==0xFF ){// if( b1==0xFE && b2==0xFF ){
|
||||
bom = SQLITE_UTF16BE;
|
||||
}
|
||||
if( b01[0]==0xFF && b01[1]==0xFE ){ // if( b1==0xFF && b2==0xFE ){
|
||||
bom = SQLITE_UTF16LE;
|
||||
}
|
||||
}
|
||||
|
||||
if( bom!=0 ){
|
||||
rc = sqlite3VdbeMemMakeWriteable(pMem);
|
||||
if( rc==SQLITE_OK ){
|
||||
pMem.n -= 2;
|
||||
Debugger.Break (); // TODO -
|
||||
//memmove(pMem.z, pMem.z[2], pMem.n);
|
||||
//pMem.z[pMem.n] = '\0';
|
||||
//pMem.z[pMem.n+1] = '\0';
|
||||
pMem.flags |= MEM_Term;
|
||||
pMem.enc = bom;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
#endif // * SQLITE_OMIT_UTF16 */
|
||||
|
||||
/*
|
||||
** pZ is a UTF-8 encoded unicode string. If nByte is less than zero,
|
||||
** return the number of unicode characters in pZ up to (but not including)
|
||||
** the first 0x00 byte. If nByte is not less than zero, return the
|
||||
** number of unicode characters in the first nByte of pZ (or up to
|
||||
** the first 0x00, whichever comes first).
|
||||
*/
|
||||
static int sqlite3Utf8CharLen( string zIn, int nByte )
|
||||
{
|
||||
//int r = 0;
|
||||
//string z = zIn;
|
||||
if ( zIn.Length == 0 )
|
||||
return 0;
|
||||
int zInLength = zIn.Length;
|
||||
int zTerm = ( nByte >= 0 && nByte <= zInLength ) ? nByte : zInLength;
|
||||
//Debug.Assert( z<=zTerm );
|
||||
//for ( int i = 0 ; i < zTerm ; i++ ) //while( *z!=0 && z<zTerm ){
|
||||
//{
|
||||
// SQLITE_SKIP_UTF8( ref z);// SQLITE_SKIP_UTF8(z);
|
||||
// r++;
|
||||
//}
|
||||
//return r;
|
||||
if ( zTerm == zInLength )
|
||||
return zInLength - ( zIn[zTerm - 1] == 0 ? 1 : 0 );
|
||||
else
|
||||
return nByte;
|
||||
}
|
||||
|
||||
/* This test function is not currently used by the automated test-suite.
|
||||
** Hence it is only available in debug builds.
|
||||
*/
|
||||
#if SQLITE_TEST && SQLITE_DEBUG
|
||||
/*
|
||||
** Translate UTF-8 to UTF-8.
|
||||
**
|
||||
** This has the effect of making sure that the string is well-formed
|
||||
** UTF-8. Miscoded characters are removed.
|
||||
**
|
||||
** The translation is done in-place and aborted if the output
|
||||
** overruns the input.
|
||||
*/
|
||||
static int sqlite3Utf8To8(byte[] zIn){
|
||||
//byte[] zOut = zIn;
|
||||
//byte[] zStart = zIn;
|
||||
//u32 c;
|
||||
|
||||
// while( zIn[0] && zOut<=zIn ){
|
||||
// c = sqlite3Utf8Read(zIn, (const u8**)&zIn);
|
||||
// if( c!=0xfffd ){
|
||||
// WRITE_UTF8(zOut, c);
|
||||
// }
|
||||
//}
|
||||
//zOut = 0;
|
||||
//return (int)(zOut - zStart);
|
||||
try
|
||||
{
|
||||
string z1 = Encoding.UTF8.GetString( zIn, 0, zIn.Length );
|
||||
byte[] zOut = Encoding.UTF8.GetBytes( z1 );
|
||||
//if ( zOut.Length != zIn.Length )
|
||||
// return 0;
|
||||
//else
|
||||
{
|
||||
Array.Copy( zOut, 0, zIn, 0,zIn.Length );
|
||||
return zIn.Length;}
|
||||
}
|
||||
catch ( EncoderFallbackException e )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if NO_SQLITE_OMIT_UTF16 //#if !SQLITE_OMIT_UTF16
|
||||
/*
|
||||
** Convert a UTF-16 string in the native encoding into a UTF-8 string.
|
||||
** Memory to hold the UTF-8 string is obtained from sqlite3Malloc and must
|
||||
** be freed by the calling function.
|
||||
**
|
||||
** NULL is returned if there is an allocation error.
|
||||
*/
|
||||
static string sqlite3Utf16to8(sqlite3 db, string z, int nByte, u8 enc){
|
||||
Debugger.Break (); // TODO -
|
||||
Mem m = Pool.Allocate_Mem();
|
||||
// memset(&m, 0, sizeof(m));
|
||||
// m.db = db;
|
||||
// sqlite3VdbeMemSetStr(&m, z, nByte, enc, SQLITE_STATIC);
|
||||
// sqlite3VdbeChangeEncoding(&m, SQLITE_UTF8);
|
||||
// if( db.mallocFailed !=0{
|
||||
// sqlite3VdbeMemRelease(&m);
|
||||
// m.z = 0;
|
||||
// }
|
||||
// Debug.Assert( (m.flags & MEM_Term)!=0 || db.mallocFailed !=0);
|
||||
// Debug.Assert( (m.flags & MEM_Str)!=0 || db.mallocFailed !=0);
|
||||
assert( (m.flags & MEM_Dyn)!=0 || db->mallocFailed );
|
||||
assert( m.z || db->mallocFailed );
|
||||
return m.z;
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert a UTF-8 string to the UTF-16 encoding specified by parameter
|
||||
** enc. A pointer to the new string is returned, and the value of *pnOut
|
||||
** is set to the length of the returned string in bytes. The call should
|
||||
** arrange to call sqlite3DbFree() on the returned pointer when it is
|
||||
** no longer required.
|
||||
**
|
||||
** If a malloc failure occurs, NULL is returned and the db.mallocFailed
|
||||
** flag set.
|
||||
*/
|
||||
#if SQLITE_ENABLE_STAT2
|
||||
char *sqlite3Utf8to16(sqlite3 db, u8 enc, char *z, int n, int *pnOut){
|
||||
Mem m;
|
||||
memset(&m, 0, sizeof(m));
|
||||
m.db = db;
|
||||
sqlite3VdbeMemSetStr(&m, z, n, SQLITE_UTF8, SQLITE_STATIC);
|
||||
if( sqlite3VdbeMemTranslate(&m, enc) ){
|
||||
assert( db->mallocFailed );
|
||||
return 0;
|
||||
}
|
||||
assert( m.z==m.zMalloc );
|
||||
*pnOut = m.n;
|
||||
return m.z;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** zIn is a UTF-16 encoded unicode string at least nChar characters long.
|
||||
** Return the number of bytes in the first nChar unicode characters
|
||||
** in pZ. nChar must be non-negative.
|
||||
*/
|
||||
int sqlite3Utf16ByteLen(const void *zIn, int nChar){
|
||||
int c;
|
||||
unsigned char const *z = zIn;
|
||||
int n = 0;
|
||||
|
||||
if( SQLITE_UTF16NATIVE==SQLITE_UTF16BE ){
|
||||
while( n<nChar ){
|
||||
READ_UTF16BE(z, 1, c);
|
||||
n++;
|
||||
}
|
||||
}else{
|
||||
while( n<nChar ){
|
||||
READ_UTF16LE(z, 1, c);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
return (int)(z-(unsigned char const *)zIn);
|
||||
}
|
||||
|
||||
#if SQLITE_TEST
|
||||
/*
|
||||
** This routine is called from the TCL test function "translate_selftest".
|
||||
** It checks that the primitives for serializing and deserializing
|
||||
** characters in each encoding are inverses of each other.
|
||||
*/
|
||||
/*
|
||||
** This routine is called from the TCL test function "translate_selftest".
|
||||
** It checks that the primitives for serializing and deserializing
|
||||
** characters in each encoding are inverses of each other.
|
||||
*/
|
||||
void sqlite3UtfSelfTest(void){
|
||||
unsigned int i, t;
|
||||
unsigned char zBuf[20];
|
||||
unsigned char *z;
|
||||
int n;
|
||||
unsigned int c;
|
||||
|
||||
for(i=0; i<0x00110000; i++){
|
||||
z = zBuf;
|
||||
WRITE_UTF8(z, i);
|
||||
n = (int)(z-zBuf);
|
||||
assert( n>0 && n<=4 );
|
||||
z[0] = 0;
|
||||
z = zBuf;
|
||||
c = sqlite3Utf8Read(z, (const u8**)&z);
|
||||
t = i;
|
||||
if( i>=0xD800 && i<=0xDFFF ) t = 0xFFFD;
|
||||
if( (i&0xFFFFFFFE)==0xFFFE ) t = 0xFFFD;
|
||||
assert( c==t );
|
||||
assert( (z-zBuf)==n );
|
||||
}
|
||||
for(i=0; i<0x00110000; i++){
|
||||
if( i>=0xD800 && i<0xE000 ) continue;
|
||||
z = zBuf;
|
||||
WRITE_UTF16LE(z, i);
|
||||
n = (int)(z-zBuf);
|
||||
assert( n>0 && n<=4 );
|
||||
z[0] = 0;
|
||||
z = zBuf;
|
||||
READ_UTF16LE(z, 1, c);
|
||||
assert( c==i );
|
||||
assert( (z-zBuf)==n );
|
||||
}
|
||||
for(i=0; i<0x00110000; i++){
|
||||
if( i>=0xD800 && i<0xE000 ) continue;
|
||||
z = zBuf;
|
||||
WRITE_UTF16BE(z, i);
|
||||
n = (int)(z-zBuf);
|
||||
assert( n>0 && n<=4 );
|
||||
z[0] = 0;
|
||||
z = zBuf;
|
||||
READ_UTF16BE(z, 1, c);
|
||||
assert( c==i );
|
||||
assert( (z-zBuf)==n );
|
||||
}
|
||||
}
|
||||
#endif // * SQLITE_TEST */
|
||||
#endif // * SQLITE_OMIT_UTF16 */
|
||||
}
|
||||
}
|
||||
1677
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/util_c.cs
Normal file
1677
plugins/cordova-sqlite-storage/src/wp/csharp-sqlite-src/util_c.cs
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user