Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial commit of contents of svn repo. node_sqlite is sqlite3 bindin…
…gs for node.js
- Loading branch information
0 parents
commit 4093c78
Showing
9 changed files
with
480 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,8 @@ | |||
syntax:glob | |||
|
|||
build | |||
README.html | |||
.svn | |||
test.db | |||
.lock-wscript | |||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,61 @@ | |||
Node.js bindings for sqlite3 | |||
============================ | |||
|
|||
Functions | |||
------------------- | |||
|
|||
### `new sqlite3.Db(filename)` | |||
|
|||
Returns an object representing the sqlite3 database with given filename. | |||
|
|||
### `sqlite3.Db.query(sql [,bindings] [,callback])` | |||
|
|||
Executes the query `sql`, with variables bound from `bindings`. The | |||
variables can take the form `?` or `?NNN` where `NNN` is a number, in which | |||
case `bindings` should be an array of values, or the form `$VVV` where | |||
`VVV` is an identifier, in which canse `bindings` should be an object | |||
with keys matching the variable names. | |||
|
|||
If provided the `callback` is called with an argument for each | |||
statement in the query. Each argument is an array of objects mapping | |||
column names to values. | |||
|
|||
Each callback argument `rows` also has these properties | |||
|
|||
- **`rows.count`** is the number of rows affected by the query. | |||
- **`rows.rowid`** is the `ROWID` of the last `INSERT` command | |||
|
|||
Within the callback, `this` is an array of all such arrays, with a | |||
`count` property giving the total number of rows affected. That same | |||
`this` object is returned by `query`. | |||
|
|||
### `sqlite3.Db.close()` | |||
|
|||
Closes the database. | |||
|
|||
|
|||
Example | |||
-------- | |||
|
|||
var sqlite3 = require("./sqlite3"); | |||
var db = new sqlite3.Db("test.db"); | |||
db.query("INSERT INTO test (column) VALUES ($value)", {$value: 10}); | |||
db.query("SELECT column FROM test WHERE rowid<?", [5], function (rows) { | |||
process.assert(rows[0].column == 10); | |||
}); | |||
db.query("UPDATE test SET column=20; SELECT column FROM test;", | |||
function (update, select) { | |||
assert(update.count == 1); | |||
assert(select[0].column == 20); | |||
}); | |||
db.close(); | |||
|
|||
Build | |||
----- | |||
|
|||
`$` **`node-waf build`** | |||
|
|||
Test | |||
---- | |||
|
|||
`$` **`node test.js`** |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,26 @@ | |||
var sys = require("sys"); | |||
|
|||
function handler(curr, prev) { | |||
sys.puts("Handling"); | |||
sys.puts("the current mtime is: " + curr.mtime); | |||
sys.puts("the previous mtime was: " + prev.mtime); | |||
sys.exec("clear;rm -f test.db; node-waf build && node test.js"); | |||
} | |||
|
|||
sys.puts(JSON.stringify(process.ARGV)); | |||
for (f in process.ARGV) { | |||
f = process.ARGV[f] | |||
sys.puts("Watching " + f); | |||
process.watchFile(f, handler); | |||
} | |||
|
|||
//var tcp = require("tcp"); | |||
//var server = tcp.createServer(); | |||
//server.listen(7000, "localhost"); | |||
|
|||
for (;;) { | |||
sys.exec("sleep 1").wait(); | |||
} | |||
|
|||
//var p = new process.Promise(); | |||
//p.wait(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,29 @@ | |||
#!/usr/bin/python | |||
# Wait for changes for files specified on the command line, or here in the file | |||
# Then compile and run test. And do this forever. | |||
|
|||
import os, sys, time | |||
|
|||
#filenames = sys.argv[1:] | |||
filenames = ["sqlite3_bindings.cc", "wscript", "sqlite3.js", "test.js"] | |||
mdname = "README" | |||
|
|||
def handler(): | |||
os.system("clear; rm -f test.db") | |||
os.system("node-waf build && node test.js && sqlite3 test.db .dump"); | |||
|
|||
mtime = [] | |||
mdtime = None | |||
while True: | |||
m = [os.stat(filename).st_mtime for filename in filenames] | |||
if mtime != m: | |||
handler() | |||
mtime = m | |||
|
|||
m = os.stat(mdname).st_mtime | |||
if mdtime != m: | |||
os.system("Markdown.pl < %s > %s.html" % (mdname, mdname)) | |||
mdtime = m | |||
|
|||
time.sleep(1) | |||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,19 @@ | |||
var sys = require("sys"); | |||
var bindings = require("./sqlite3_bindings"); | |||
|
|||
var Db = bindings.Db; | |||
|
|||
Db.prototype.query = function (sql, bindings, callback) { | |||
if (typeof(bindings) == "function") { | |||
var tmp = bindings; | |||
bindings = callback; | |||
callback = tmp; | |||
} | |||
var result = this.performQuery(sql, bindings); | |||
if (typeof(callback) == "function") { | |||
callback.apply(result, result); | |||
} | |||
return result; | |||
} | |||
|
|||
exports.Db = Db; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,240 @@ | |||
#include <sqlite3.h> | |||
#include <v8.h> | |||
#include <node.h> | |||
#include <node_events.h> | |||
#include <deque> | |||
|
|||
using namespace v8; | |||
using namespace node; | |||
|
|||
class Sqlite3Db : public EventEmitter | |||
{ | |||
public: | |||
static void Init(v8::Handle<Object> target) | |||
{ | |||
HandleScope scope; | |||
|
|||
Local<FunctionTemplate> t = FunctionTemplate::New(New); | |||
|
|||
t->Inherit(EventEmitter::constructor_template); | |||
t->InstanceTemplate()->SetInternalFieldCount(1); | |||
|
|||
NODE_SET_PROTOTYPE_METHOD(t, "performQuery", PerformQuery); | |||
NODE_SET_PROTOTYPE_METHOD(t, "close", Close); | |||
|
|||
target->Set(v8::String::NewSymbol("Db"), t->GetFunction()); | |||
} | |||
|
|||
protected: | |||
Sqlite3Db(sqlite3* db) : db_(db) { | |||
} | |||
|
|||
~Sqlite3Db() { | |||
sqlite3_close(db_); | |||
} | |||
|
|||
sqlite3* db_; | |||
|
|||
operator sqlite3* () { return db_; } | |||
|
|||
protected: | |||
static Handle<Value> New(const Arguments& args) | |||
{ | |||
HandleScope scope; | |||
|
|||
if (args.Length() == 0 || !args[0]->IsString()) { | |||
return ThrowException(Exception::TypeError( | |||
String::New("First argument must be a string"))); | |||
} | |||
|
|||
String::Utf8Value filename(args[0]->ToString()); | |||
sqlite3* db; | |||
int rc = sqlite3_open(*filename, &db); | |||
if (rc) { | |||
Local<String> err = v8::String::New(sqlite3_errmsg(db)); | |||
sqlite3_close(db); | |||
return ThrowException(Exception::Error(err)); | |||
} | |||
(new Sqlite3Db(db))->Wrap(args.This()); | |||
return args.This(); | |||
} | |||
|
|||
|
|||
static Handle<Value> PerformQuery(const Arguments& args) | |||
{ | |||
HandleScope scope; | |||
Sqlite3Db* db = ObjectWrap::Unwrap<Sqlite3Db>(args.This()); | |||
|
|||
if (args.Length() == 0 || !args[0]->IsString()) { | |||
return ThrowException(Exception::TypeError( | |||
String::New("First argument must be a string"))); | |||
} | |||
|
|||
String::Utf8Value usql(args[0]->ToString()); | |||
const char* sql(*usql); | |||
|
|||
int changes = 0; | |||
int param = 0; | |||
|
|||
std::deque< Handle<Array> > resulting; | |||
|
|||
for(;;) { | |||
|
|||
sqlite3_stmt* stmt; | |||
int rc = sqlite3_prepare_v2(*db, sql, -1, &stmt, &sql); | |||
if (!stmt) break; | |||
Statement statement(stmt); | |||
|
|||
if (args.Length() > 1) { | |||
if (args[1]->IsArray()) { | |||
Local<Array> a(Array::Cast(*args[1])); | |||
int start = param; | |||
int stop = start + sqlite3_bind_parameter_count(statement); | |||
for (; param < a->Length() && param < stop; ++param) { | |||
Local<Value> v = a->Get(Integer::New(param)); | |||
statement.Bind(param+1-start, v); | |||
} | |||
} else if (args[1]->IsObject()) { | |||
Local<Array> keys(args[1]->ToObject()->GetPropertyNames()); | |||
for (int k = 0; k < keys->Length(); ++k) { | |||
Local<Value> key(keys->Get(Integer::New(k))); | |||
statement.Bind(key, args[1]->ToObject()->Get(key)); | |||
} | |||
} else if (args[1]->IsUndefined() || args[1]->IsNull()) { | |||
// That's okay | |||
} else { | |||
return ThrowException(Exception::TypeError( | |||
String::New("Second argument invalid"))); | |||
} | |||
} | |||
|
|||
std::deque< Handle<Object> > rows; | |||
|
|||
for (int r = 0; ; ++r) { | |||
int rc = sqlite3_step(statement); | |||
if (rc == SQLITE_ROW) { | |||
Local<Object> row = Object::New(); | |||
for (int c = 0; c < sqlite3_column_count(statement); ++c) { | |||
Handle<Value> value; | |||
switch (sqlite3_column_type(statement, c)) { | |||
case SQLITE_INTEGER: | |||
value = Integer::New(sqlite3_column_int(statement, c)); | |||
break; | |||
case SQLITE_FLOAT: | |||
value = Number::New(sqlite3_column_double(statement, c)); | |||
break; | |||
case SQLITE_TEXT: | |||
value = String::New((const char*) sqlite3_column_text(statement, c)); | |||
break; | |||
case SQLITE_NULL: | |||
default: // We don't handle any other types just now | |||
value = Undefined(); | |||
break; | |||
} | |||
row->Set(String::NewSymbol(sqlite3_column_name(statement, c)), | |||
value); | |||
} | |||
rows.push_back(row); | |||
} else if (rc == SQLITE_DONE) { | |||
break; | |||
} else { | |||
return ThrowException(Exception::Error( | |||
v8::String::New(sqlite3_errmsg(*db)))); | |||
} | |||
} | |||
|
|||
changes += sqlite3_changes(*db); | |||
|
|||
Local<Array> rosult(Array::New(rows.size())); | |||
std::deque< Handle<Object> >::const_iterator ri(rows.begin()); | |||
for (int r = 0; r < rows.size(); ++r, ++ri) | |||
rosult->Set(Integer::New(r), *ri); | |||
rosult->Set(String::New("changes"), Integer::New(sqlite3_changes(*db))); | |||
rosult->Set(String::New("rowid"), | |||
Integer::New(sqlite3_last_insert_rowid(*db))); | |||
resulting.push_back(rosult); | |||
} | |||
|
|||
Local<Array> result(Array::New(0)); | |||
result->Set(String::New("changes"), Integer::New(changes)); | |||
result->Set(String::New("rowid"), | |||
Integer::New(sqlite3_last_insert_rowid(*db))); | |||
std::deque< Handle<Array> >::iterator ri(resulting.begin()); | |||
for (int r = 0; r < resulting.size(); ++r, ++ri) { | |||
result->Set(Integer::New(r), *ri); | |||
} | |||
return result; | |||
} | |||
|
|||
|
|||
static Handle<Value> Close (const Arguments& args) | |||
{ | |||
Sqlite3Db* db = ObjectWrap::Unwrap<Sqlite3Db>(args.This()); | |||
HandleScope scope; | |||
db->Close(); | |||
return Undefined(); | |||
} | |||
|
|||
void Close() { | |||
sqlite3_close(db_); | |||
db_ = NULL; | |||
Detach(); | |||
} | |||
|
|||
class Statement : public EventEmitter | |||
{ | |||
public: | |||
Statement(sqlite3_stmt* stmt) : stmt_(stmt) {} | |||
|
|||
~Statement() { sqlite3_finalize(stmt_); } | |||
|
|||
operator sqlite3_stmt* () { return stmt_; } | |||
|
|||
bool Bind(int index, Handle<Value> value) | |||
{ | |||
HandleScope scope; | |||
if (value->IsInt32()) { | |||
sqlite3_bind_int(stmt_, index, value->Int32Value()); | |||
} else if (value->IsNumber()) { | |||
sqlite3_bind_double(stmt_, index, value->NumberValue()); | |||
} else if (value->IsString()) { | |||
String::Utf8Value text(value); | |||
sqlite3_bind_text(stmt_, index, *text, text.length(), SQLITE_TRANSIENT); | |||
} else { | |||
return false; | |||
} | |||
return true; | |||
} | |||
|
|||
bool Bind(Handle<Value> key, Handle<Value> value) { | |||
HandleScope scope; | |||
String::Utf8Value skey(key); | |||
//string x = ":" + key | |||
int index = sqlite3_bind_parameter_index(stmt_, *skey); | |||
Bind(index, value); | |||
} | |||
|
|||
Handle<Object> Cast() | |||
{ | |||
HandleScope scope; | |||
Local<ObjectTemplate> t(ObjectTemplate::New()); | |||
t->SetInternalFieldCount(1); | |||
Local<Object> thus = t->NewInstance(); | |||
thus->SetInternalField(0, External::New(this)); | |||
//Wrap(thus); | |||
return thus; | |||
} | |||
|
|||
protected: | |||
sqlite3_stmt* stmt_; | |||
}; | |||
|
|||
|
|||
}; | |||
|
|||
|
|||
extern "C" void init (v8::Handle<Object> target) | |||
{ | |||
Sqlite3Db::Init(target); | |||
} | |||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1 @@ | |||
build/default/sqlite3_bindings.node |
Oops, something went wrong.