Easy SQLite interface for XUL
2
Dead simple interface for structured SQLite access within XUL, using Mozilla's SQLite Storage API.
//Opens or creates a database file for SQLite access.
(SQLiteObject) SQLite(databaseFile);
//Creates a named SQL query.
(function) SQLite#queryFunction(functionName, SQL)
(mixed) SQLite#queries[functionName](...)
//Example:
var S = SQLite('myDB.sqlite');
S.queryFunction('getByID','SELECT data FROM myTable WHERE id=%n1 LIMIT 1');
var X = S.queries.getByID(5)[0].data; //returns array of rows in object form
//Create an object with a set of queryFunctions.
(object) SQLite#queryFunction((object)functionDefs)
//Example:
var S = new SQLite('database.SQLite');
var test = S.queryFunction({
create: 'CREATE TABLE IF NOT EXISTS test (a INTEGER, b STRING)',
addRow: 'INSERT INTO test (a,b) VALUES(%n1, %s2)',
getAll: 'SELECT * FROM test',
getRowByA: 'SELECT b FROM test WHERE a = %n1 LIMIT 1',
getRowByB: 'SELECT a FROM test WHERE b like %s1 LIMIT 1',
clear: 'DELETE FROM test'
});
test.addRow(5,'testing');
test.addRow(6,'also testing');
var X = test.getRowByA(6)[0].a;
//The Code:
var undefined; //for fast tests for undefined values
var RE_SET_ORDER=1, RE_MATCH_ORDER=0; //constants for matchAll
String.prototype.matchAll = function (re,set) {
//Required for SQLite parameter matching
if (set==undefined) set=RE_MATCH_ORDER;
//copy source string, initialize output array
var ret=[],ts = this;
while (re.test(ts)) {
//match
ret.push(ts.match(re));
//remove match
ts=ts.replace(re,'');
}
//If we have to swap orders, do so.
if (set==RE_SET_ORDER) {
var i,j,nRet = [];
for (i=0; i<ret.length; i++) {
for (j=0; j<ret[i].length; j++) {
if (i==0) nRet[j]=[];
nRet[j].push(ret[i][j]);
}
}
ret=nRet;
}
return ret;
}
function SQLite(db) {
//simple call should return object, not just run function
if (!(this instanceof SQLite))
return new SQLite(db);
//Create file. AChrom is install directory for XUL app
var
file = Components.classes["@mozilla.org/file/directory_service;1"]
.getService(Components.interfaces.nsIProperties)
.get("AChrom", Components.interfaces.nsIFile);
//Create storage service instance
var
storageService = Components.classes["@mozilla.org/storage/service;1"]
.getService(Components.interfaces.mozIStorageService);
//expand the file to the requested database
file.append(db);
//open the database connection
this.conn = storageService.openDatabase(file);
//Return SQLite object
return this;
}
//Cache for SQLite queries
SQLite.prototype.queries={};
//Create new Query function(s)
SQLite.prototype.queryFunction=function (name,query) {
//If name is an object
if (typeof name == 'object') {
//initialize our return value
var queryGroup={};
//for each function NVP
for (var i in name)
//create a matching queryFunction
queryGroup[i]=arguments.callee.apply(this,[i,name[i]]);
//return the query namespace
return queryGroup;
}
var
//allow the internal function to access this SQLite instance
_this=this,
//use _conn as a shortcut if necessary
_conn=this.conn,
//Match all % parameters (for type matching)
params=query.matchAll(/%(n|s|f)(\d+)/i),
//And replace with their real counterparts
realQuery=query.replace(/%(n|s|f)(\d+)/ig,'?$2'),
//create the statement
statement=_conn.createStatement(realQuery),
//Create the query function, loaded with the variables available above.
exec = function () {
var i,idx;
//walk through the parameters
for (i=0; i<params.length; i++) {
//get the param index for the parameter
idx=statement.getParameterIndex('?'+params[i][2]);
switch (params[i][1].toLowerCase()) {
case 'n':
//it's a number. Use int64's
statement.bindInt64Parameter(idx,arguments[idx]);
break;
case 'f':
//it's a float. Use doubles.
statement.bindDoubleParameter(idx,arguments[idx]);
break;
default:
//Assume it's a UTF8 string; ASCII and blobs will handle this fine on the input side
statement.bindUTF8StringParameter(idx,arguments[idx]);
}
}
//Some variables for the return
var
cols = statement.columnCount,
col = 0,
colTypes=[],
colNames=[],
ret=[],
name,
rowData;
//If we've requested columns
if (cols>0) {
while (statement.executeStep()) {
//execute like we're expecting a return value.
rowData={};
for (col=0; col<cols; col++) {
if (colNames[col]==undefined) {
//populate the colNames and colTypes arrays, as needed
colNames[col]=statement.getColumnName(col);
colTypes[col]=statement.getTypeOfIndex(col);
}
name=colNames[col];
switch (colTypes[col]) {
//Depending on the column type (this really matters on output)
//fill the rowData object
case 0:
rowData[name]=null; break;
case 1:
rowData[name]=statement.getInt64(col); break;
case 2:
rowData[name]=statement.getDouble(col); break;
case 3:
rowData[name]=statement.getUTF8String(col); break;
case 4:
rowData[name]=statement.getBlob(col); break;
}
}
//And, once this row is done, append it to the array.
ret.push(rowData);
}
} else {
//If there's no columns to grab, just run the query
statement.execute();
}
//Either way, reset to stave off hobgoblins
statement.reset();
if (ret.length>0) //We've got return values? Good! Send 'em up.
return ret;
//We don't? Well, an insert will return the last insert ID
//and a no-value select will return 0.
return _conn.lastInsertRowID;
};
//Add exec to our set of precached queries, and return it.
return this.queries[name]=exec;
};






There are currently no comments for this snippet.