Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
N
node-sqlite3
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
俞永鹏
node-sqlite3
Commits
eaf72f23
Commit
eaf72f23
authored
Feb 17, 2011
by
Konstantin Käfer
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
saving work
parent
c09549d0
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
334 additions
and
1927 deletions
+334
-1927
sqlite3.js
lib/sqlite3.js
+10
-262
database.cc
src/database.cc
+131
-484
database.h
src/database.h
+46
-62
macros.h
src/macros.h
+8
-19
statement.cc
src/statement.cc
+116
-988
statement.h
src/statement.h
+23
-112
No files found.
lib/sqlite3.js
View file @
eaf72f23
...
@@ -16,273 +16,22 @@
...
@@ -16,273 +16,22 @@
var
sqlite3
=
module
.
exports
=
exports
=
require
(
'./sqlite3_bindings'
);
var
sqlite3
=
module
.
exports
=
exports
=
require
(
'./sqlite3_bindings'
);
var
sys
=
require
(
"sys"
);
var
sys
=
require
(
"sys"
);
function
noop
(
err
)
{
// function noop(err) {
if
(
err
)
throw
err
;
// if (err) throw err;
};
var
Database
=
sqlite3
.
Database
;
// var realClose = Database.prototype.close;
// sqlite3.Database.prototype.close = function(callback) {
// if (this.status === sqlite3.STATUS_IDLE) {
// return realClose.call(this, callback);
// } else {
// this.once('idle', function() {
// realClose.call(this, callback);
// });
// }
// };
// var realClose = sqlite3.Database.prototype.close;
// sqlite3.Database.prototype.close = function() {
// var db = this;
// process.nextTick(function() {
// if (!db.idle) {
// db.on('idle', function fn() {
// realClose.call(db);
// db.removeListener('idle', fn);
// });
// } else {
// realClose.call(db);
// }
// });
// return this;
// };
// };
var
Database
=
sqlite3
.
Database
;
var
Statement
=
sqlite3
.
Statement
;
/*
sqlite3.Database.prototype.query = function(sql, bindings, rowCallback) {
var self = this;
if (typeof(bindings) == "function") {
rowCallback = bindings;
bindings = undefined;
}
this.prepare(sql, function(error, statement) {
function next() {
_doStep(self, statement, rowCallback);
}
if (error) {
return rowCallback (error);
}
if (statement) {
if (Array.isArray(bindings)) {
statement.bindArray(bindings, next);
}
else if (typeof(bindings) === 'object') {
statement.bindObject(bindings, next);
}
else {
next();
}
}
else {
rowCallback();
}
});
}
function _doStep(db, statement, rowCallback) {
statement.step(function (error, row) {
if (error) {
return rowCallback(error);
}
if (!row) {
rowCallback();
statement.finalize(function(){});
return;
}
rowCallback(undefined, row);
_doStep(db, statement, rowCallback);
});
}
// Execute a single SQL query with the given optional parameters. Calls
// `callback` with all rows or an error on query completion.
sqlite3.Database.prototype.execute = function (sql /* , bindings, callback ) {
var self = this;
var bindings, callback;
var n = arguments.length;
switch (n) {
case 3:
callback = arguments[2];
bindings = arguments[1];
break;
case 2:
callback = arguments[1];
break;
default: throw new Error("Invalid number of arguments ("+n+")");
}
self.prepare(sql, function (error, statement) {
function next (error) {
if (error) return callback(new Error("Error binding: " + error.toString()));
fetchAll(statement);
}
if (error) {
return callback(error);
}
if (bindings) {
if (Array.isArray(bindings)) {
statement.bindArray(bindings, next);
}
else if (typeof(bindings) === 'object') {
statement.bindObject(bindings, next);
}
}
else {
next();
}
function fetchAll(statement) {
statement.fetchAll(function (error, rows) {
if (error) {
return callback(error);
}
statement.finalize(function () {
callback(undefined, rows);
});
});
}
});
}
// Execute SQL statements separated by semi-colons.
// SQL must contain no placeholders. Results are discarded.
sqlite3.Database.prototype.executeScript = function (script, callback) {
var self = this;
(function stepOverSQL(sql) {
self.prepare(sql, function(error, statement) {
if (error) {
return callback(error);
}
statement.step(function (error, row) {
var tail;
if (error) {
callback(error);
return;
}
if (!row) {
statement.finalize(function(){});
tail = statement.tail;
if (typeof tail == "string") {
tail = tail.trim();
}
if (tail) {
stepOverSQL(tail);
}
else {
callback();
}
}
});
});
})(script);
}
sqlite3.Database.prototype.insertMany = function (table, columns, rows, callback) {
var columnsFragment = columns.join(",");
var placeholdersFragment = [];
var i = columns.length;
if (!rows.length) {
callback();
return;
}
while (i--) {
placeholdersFragment.push('?');
}
placeholdersFragment = placeholdersFragment.join(", ");
var sql = [ 'INSERT INTO'
, table
, '('
, columnsFragment
, ')'
, 'VALUES'
, '('
, placeholdersFragment
, ')'
]
.join(" ");
var i = rows.length;
var statement;
// Database#prepare(sql, [bind1, bind2, ...], [callback])
Database
.
prototype
.
prepare
=
function
(
sql
)
{
var
callback
,
params
=
Array
.
prototype
.
slice
.
call
(
arguments
,
1
);
function doStep(i) {
if
(
params
.
length
&&
typeof
params
[
params
.
length
-
1
]
===
'function'
)
{
statement.bindArray(rows[i], function () {
callback
=
params
.
pop
();
statement.step(function (error, row) {
if (error) return callback(error);
statement.reset();
if (i) {
doStep(--i);
}
else {
statement.finalize(function () {
callback();
});
}
}
});
});
}
this.prepare(sql, function (error, stmt) {
if (error) return callback(error);
statement = stmt;
doStep(--i);
});
}
return
new
Statement
(
this
,
sql
,
params
,
callback
);
sqlite3.fromErrorCode = function(code) {
switch (code) {
case 0: return "SQLITE_OK";
case 1: return "SQLITE_ERROR";
case 2: return "SQLITE_INTERNAL";
case 3: return "SQLITE_PERM";
case 4: return "SQLITE_ABORT";
case 5: return "SQLITE_BUSY";
case 6: return "SQLITE_LOCKED";
case 7: return "SQLITE_NOMEM";
case 8: return "SQLITE_READONLY";
case 9: return "SQLITE_INTERRUPT";
case 10: return "SQLITE_IOERR";
case 11: return "SQLITE_CORRUPT";
case 12: return "SQLITE_NOTFOUND";
case 13: return "SQLITE_FULL";
case 14: return "SQLITE_CANTOPEN";
case 15: return "SQLITE_PROTOCOL";
case 16: return "SQLITE_EMPTY";
case 17: return "SQLITE_SCHEMA";
case 18: return "SQLITE_TOOBIG";
case 19: return "SQLITE_CONSTRAINT";
case 20: return "SQLITE_MISMATCH";
case 21: return "SQLITE_MISUSE";
case 22: return "SQLITE_NOLFS";
case 23: return "SQLITE_AUTH";
case 24: return "SQLITE_FORMAT";
case 25: return "SQLITE_RANGE";
case 26: return "SQLITE_NOTADB";
}
};
};
sqlite3.sanitizeError = function(err, data) {
err.message = exports.fromErrorCode(err.errno) + ', ' + err.message +
' in query "' + err.query +
'" with values ' + JSON.stringify(data, false, 4);
return err;
};
*/
\ No newline at end of file
src/database.cc
View file @
eaf72f23
...
@@ -38,259 +38,196 @@ void Database::Init(v8::Handle<Object> target) {
...
@@ -38,259 +38,196 @@ void Database::Init(v8::Handle<Object> target) {
constructor_template
->
InstanceTemplate
()
->
SetInternalFieldCount
(
1
);
constructor_template
->
InstanceTemplate
()
->
SetInternalFieldCount
(
1
);
constructor_template
->
SetClassName
(
String
::
NewSymbol
(
"Database"
));
constructor_template
->
SetClassName
(
String
::
NewSymbol
(
"Database"
));
NODE_SET_PROTOTYPE_METHOD
(
constructor_template
,
"open"
,
Open
);
NODE_SET_PROTOTYPE_METHOD
(
constructor_template
,
"openSync"
,
OpenSync
);
NODE_SET_PROTOTYPE_METHOD
(
constructor_template
,
"close"
,
Close
);
NODE_SET_PROTOTYPE_METHOD
(
constructor_template
,
"close"
,
Close
);
NODE_SET_PROTOTYPE_METHOD
(
constructor_template
,
"closeSync"
,
CloseSync
);
NODE_SET_PROTOTYPE_METHOD
(
constructor_template
,
"prepare"
,
Prepare
);
target
->
Set
(
v8
::
String
::
NewSymbol
(
"Database"
),
target
->
Set
(
v8
::
String
::
NewSymbol
(
"Database"
),
constructor_template
->
GetFunction
());
constructor_template
->
GetFunction
());
// insert/update execution result mask
NODE_DEFINE_CONSTANT
(
target
,
EXEC_EMPTY
);
NODE_DEFINE_CONSTANT
(
target
,
EXEC_LAST_INSERT_ID
);
NODE_DEFINE_CONSTANT
(
target
,
EXEC_AFFECTED_ROWS
);
}
}
Handle
<
Value
>
Database
::
New
(
const
Arguments
&
args
)
{
void
Database
::
Process
(
Database
*
db
)
{
HandleScope
scope
;
if
(
!
db
->
open
&&
db
->
locked
&&
!
db
->
queue
.
empty
())
{
EXCEPTION
(
String
::
New
(
"Database is closed"
),
SQLITE_MISUSE
,
exception
);
REQUIRE_ARGUMENT_STRING
(
0
,
filename
);
OPTIONAL_ARGUMENT_INTEGER
(
1
,
mode
,
SQLITE_OPEN_READWRITE
|
SQLITE_OPEN_CREATE
);
Database
*
db
=
new
Database
();
db
->
filename
=
std
::
string
(
*
filename
);
db
->
open_mode
=
mode
;
args
.
This
()
->
Set
(
String
::
NewSymbol
(
"filename"
),
args
[
0
]
->
ToString
(),
ReadOnly
);
args
.
This
()
->
Set
(
String
::
NewSymbol
(
"mode"
),
Integer
::
New
(
mode
),
ReadOnly
);
db
->
Wrap
(
args
.
This
());
return
args
.
This
();
}
void
Database
::
ProcessQueue
(
Database
*
db
)
{
while
(
!
db
->
queue
.
empty
())
{
Call
*
call
=
db
->
queue
.
front
();
if
(
!
(
call
->
Data
()
&
db
->
status
))
{
// The next task in the queue requires a different status than the
// one we're currently in. Wait before invoking it.
if
(
db
->
pending
==
0
)
{
EXCEPTION
(
"Invalid function call sequence"
,
SQLITE_MISUSE
,
exception
);
Local
<
Value
>
argv
[]
=
{
String
::
NewSymbol
(
"error"
),
exception
};
Local
<
Value
>
argv
[]
=
{
String
::
NewSymbol
(
"error"
),
exception
};
Local
<
Function
>
fn
=
Local
<
Function
>::
Cast
(
db
->
handle_
->
Get
(
String
::
NewSymbol
(
"emit"
)));
EMIT_EVENT
(
db
->
handle_
,
2
,
argv
);
return
;
TryCatch
try_catch
;
fn
->
Call
(
db
->
handle_
,
2
,
argv
);
if
(
try_catch
.
HasCaught
())
{
FatalException
(
try_catch
);
}
ev_unref
(
EV_DEFAULT_UC
);
db
->
Unref
();
db
->
queue
.
pop
();
delete
call
;
}
break
;
}
}
if
(
call
->
Mode
()
==
Deferred
::
Exclusive
&&
db
->
pending
>
0
)
{
while
(
db
->
open
&&
!
db
->
locked
&&
!
db
->
queue
.
empty
()
)
{
// We have to wait for the pending tasks to complete before we
Call
*
call
=
db
->
queue
.
front
();
// execute the exclusive task.
// break;
if
(
call
->
exclusive
&&
db
->
pending
>
0
)
{
break
;
break
;
}
}
ev_unref
(
EV_DEFAULT_UC
);
call
->
callback
(
call
->
baton
);
db
->
Unref
();
TryCatch
try_catch
;
call
->
Invoke
();
if
(
try_catch
.
HasCaught
())
{
FatalException
(
try_catch
);
}
db
->
queue
.
pop
();
db
->
queue
.
pop
();
delete
call
;
delete
call
;
}
}
}
}
Handle
<
Value
>
Database
::
Open
(
const
Arguments
&
args
)
{
inline
void
Database
::
Schedule
(
Database
*
db
,
EIO_Callback
callback
,
Baton
*
baton
,
HandleScope
scope
;
bool
exclusive
=
false
)
{
Database
*
db
=
ObjectWrap
::
Unwrap
<
Database
>
(
args
.
This
());
if
(
!
db
->
open
&&
db
->
locked
)
{
EXCEPTION
(
String
::
New
(
"Database is closed"
),
SQLITE_MISUSE
,
exception
);
REQUIRE_ARGUMENT_FUNCTION
(
0
,
callback
);
if
(
!
(
baton
)
->
callback
.
IsEmpty
())
{
Local
<
Value
>
argv
[]
=
{
exception
};
// Make sure that node doesn't exit before all elements in the queue have
TRY_CATCH_CALL
(
db
->
handle_
,
(
baton
)
->
callback
,
1
,
argv
);
// been dealt with.
db
->
Ref
();
ev_ref
(
EV_DEFAULT_UC
);
if
(
db
->
status
!=
IsClosed
)
{
db
->
queue
.
push
(
new
Call
(
args
,
IsClosed
,
Deferred
::
Exclusive
));
}
}
else
{
else
{
db
->
status
=
IsOpening
;
Local
<
Value
>
argv
[]
=
{
String
::
NewSymbol
(
"error"
),
exception
}
;
db
->
pending
++
;
EMIT_EVENT
(
db
->
handle_
,
2
,
argv
)
;
Baton
*
baton
=
new
Baton
(
db
,
Persistent
<
Function
>::
New
(
callback
));
}
eio_custom
(
EIO_Open
,
EIO_PRI_DEFAULT
,
EIO_AfterOpen
,
baton
)
;
return
;
}
}
return
args
.
This
();
if
(
!
db
->
open
||
db
->
locked
||
(
exclusive
&&
db
->
pending
>
0
))
{
}
db
->
queue
.
push
(
new
Call
(
callback
,
baton
,
exclusive
));
bool
Database
::
Open
(
Database
*
db
)
{
db
->
error_status
=
sqlite3_open_v2
(
db
->
filename
.
c_str
(),
&
db
->
handle
,
SQLITE_OPEN_FULLMUTEX
|
db
->
open_mode
,
NULL
);
if
(
db
->
error_status
!=
SQLITE_OK
)
{
db
->
error_message
=
std
::
string
(
sqlite3_errmsg
(
db
->
handle
));
return
false
;
}
}
else
{
else
{
return
true
;
callback
(
baton
)
;
}
}
}
}
Handle
<
Value
>
Database
::
OpenSync
(
const
Arguments
&
args
)
{
Handle
<
Value
>
Database
::
New
(
const
Arguments
&
args
)
{
HandleScope
scope
;
HandleScope
scope
;
Database
*
db
=
ObjectWrap
::
Unwrap
<
Database
>
(
args
.
This
());
REQUIRE_ARGUMENT_FUNCTION
(
0
,
callback
);
if
(
!
args
.
IsConstructCall
())
{
return
ThrowException
(
Exception
::
TypeError
(
if
(
db
->
status
==
IsClosed
)
{
String
::
New
(
"Use the new keyword to create new Database objects"
))
Local
<
Value
>
argv
[
1
];
);
if
(
Open
(
db
))
{
db
->
status
=
IsOpen
;
argv
[
0
]
=
Local
<
Value
>::
New
(
Null
());
}
else
{
EXCEPTION
(
db
->
error_message
.
c_str
(),
db
->
error_status
,
exception
);
argv
[
0
]
=
exception
;
}
}
TRY_CATCH_CALL
(
args
.
This
(),
callback
,
1
,
argv
);
REQUIRE_ARGUMENT_STRING
(
0
,
filename
);
ProcessQueue
(
db
);
int
pos
=
1
;
int
mode
=
SQLITE_OPEN_READWRITE
|
SQLITE_OPEN_CREATE
;
if
(
args
.
Length
()
>=
pos
&&
args
[
pos
]
->
IsInt32
())
{
mode
=
args
[
pos
++
]
->
Int32Value
();
}
}
else
{
return
ThrowException
(
Exception
::
Error
(
Local
<
Function
>
callback
;
String
::
New
(
"Database is already open"
)));
if
(
args
.
Length
()
>=
pos
&&
args
[
pos
]
->
IsFunction
())
{
callback
=
Local
<
Function
>::
Cast
(
args
[
pos
++
]);
}
}
Database
*
db
=
new
Database
();
db
->
Wrap
(
args
.
This
());
args
.
This
()
->
Set
(
String
::
NewSymbol
(
"filename"
),
args
[
0
]
->
ToString
(),
ReadOnly
);
args
.
This
()
->
Set
(
String
::
NewSymbol
(
"mode"
),
Integer
::
New
(
mode
),
ReadOnly
);
// Start opening the database.
OpenBaton
*
baton
=
new
OpenBaton
();
baton
->
db
=
db
;
baton
->
callback
=
Persistent
<
Function
>::
New
(
callback
);
baton
->
filename
=
*
filename
;
baton
->
mode
=
SQLITE_OPEN_FULLMUTEX
|
mode
;
EIO_BeginOpen
(
baton
);
return
args
.
This
();
return
args
.
This
();
}
}
void
Database
::
EIO_BeginOpen
(
Baton
*
baton
)
{
baton
->
db
->
Ref
();
ev_ref
(
EV_DEFAULT_UC
);
fprintf
(
stderr
,
"Open started
\n
"
);
eio_custom
(
EIO_Open
,
EIO_PRI_DEFAULT
,
EIO_AfterOpen
,
baton
);
}
int
Database
::
EIO_Open
(
eio_req
*
req
)
{
int
Database
::
EIO_Open
(
eio_req
*
req
)
{
Baton
*
baton
=
static_cast
<
Baton
*>
(
req
->
data
);
OpenBaton
*
baton
=
static_cast
<
OpenBaton
*>
(
req
->
data
);
Open
(
baton
->
db
);
Database
*
db
=
baton
->
db
;
fprintf
(
stderr
,
"Open performed
\n
"
);
baton
->
status
=
sqlite3_open_v2
(
baton
->
filename
.
c_str
(),
&
db
->
handle
,
baton
->
mode
,
NULL
);
if
(
baton
->
status
!=
SQLITE_OK
)
{
baton
->
message
=
std
::
string
(
sqlite3_errmsg
(
db
->
handle
));
}
return
0
;
return
0
;
}
}
int
Database
::
EIO_AfterOpen
(
eio_req
*
req
)
{
int
Database
::
EIO_AfterOpen
(
eio_req
*
req
)
{
HandleScope
scope
;
HandleScope
scope
;
Baton
*
baton
=
static_cast
<
Baton
*>
(
req
->
data
);
OpenBaton
*
baton
=
static_cast
<
Open
Baton
*>
(
req
->
data
);
Database
*
db
=
baton
->
db
;
Database
*
db
=
baton
->
db
;
ev_unref
(
EV_DEFAULT_UC
);
db
->
Unref
();
db
->
Unref
();
db
->
pending
--
;
ev_unref
(
EV_DEFAULT_UC
)
;
Local
<
Value
>
argv
[
1
];
Local
<
Value
>
argv
[
1
];
if
(
db
->
error_status
=
=
SQLITE_OK
)
{
if
(
baton
->
status
!
=
SQLITE_OK
)
{
db
->
status
=
IsOpen
;
EXCEPTION
(
String
::
New
(
baton
->
message
.
c_str
()),
baton
->
status
,
exception
)
;
argv
[
0
]
=
Local
<
Value
>::
New
(
Null
())
;
argv
[
0
]
=
exception
;
}
}
else
{
else
{
db
->
status
=
IsClosed
;
db
->
open
=
true
;
EXCEPTION
(
db
->
error_message
.
c_str
(),
db
->
error_status
,
exception
);
argv
[
0
]
=
Local
<
Value
>::
New
(
Null
());
argv
[
0
]
=
exception
;
}
}
if
(
!
baton
->
callback
.
IsEmpty
())
{
TRY_CATCH_CALL
(
db
->
handle_
,
baton
->
callback
,
1
,
argv
);
TRY_CATCH_CALL
(
db
->
handle_
,
baton
->
callback
,
1
,
argv
);
ProcessQueue
(
db
);
}
else
if
(
!
db
->
open
)
{
Local
<
Value
>
args
[]
=
{
String
::
NewSymbol
(
"error"
),
argv
[
0
]
};
EMIT_EVENT
(
db
->
handle_
,
2
,
args
);
}
baton
->
callback
.
Dispose
();
if
(
db
->
open
)
{
delete
baton
;
Local
<
Value
>
args
[]
=
{
String
::
NewSymbol
(
"open"
)
};
EMIT_EVENT
(
db
->
handle_
,
1
,
args
);
fprintf
(
stderr
,
"Open completed
\n
"
);
Process
(
db
);
}
delete
baton
;
return
0
;
return
0
;
}
}
Handle
<
Value
>
Database
::
Close
(
const
Arguments
&
args
)
{
Handle
<
Value
>
Database
::
Close
(
const
Arguments
&
args
)
{
HandleScope
scope
;
HandleScope
scope
;
Database
*
db
=
ObjectWrap
::
Unwrap
<
Database
>
(
args
.
This
());
Database
*
db
=
ObjectWrap
::
Unwrap
<
Database
>
(
args
.
This
());
OPTIONAL_ARGUMENT_FUNCTION
(
0
,
callback
);
REQUIRE_ARGUMENT_FUNCTION
(
0
,
callback
);
// Make sure that node doesn't exit before all elements in the queue have
// been dealt with.
db
->
Ref
();
db
->
Ref
();
ev_ref
(
EV_DEFAULT_UC
);
ev_ref
(
EV_DEFAULT_UC
);
if
(
db
->
status
!=
IsOpen
||
db
->
pending
>
0
)
{
Baton
*
baton
=
new
Baton
();
db
->
queue
.
push
(
new
Call
(
args
,
IsOpen
,
Deferred
::
Exclusive
));
baton
->
db
=
db
;
}
baton
->
callback
=
Persistent
<
Function
>::
New
(
callback
);
else
{
Schedule
(
db
,
EIO_BeginClose
,
baton
,
true
);
db
->
status
=
IsClosing
;
db
->
pending
++
;
Baton
*
baton
=
new
Baton
(
db
,
Persistent
<
Function
>::
New
(
callback
));
eio_custom
(
EIO_Close
,
EIO_PRI_DEFAULT
,
EIO_AfterClose
,
baton
);
}
return
args
.
This
();
return
args
.
This
();
}
}
bool
Database
::
Close
(
Database
*
db
)
{
void
Database
::
EIO_BeginClose
(
Baton
*
baton
)
{
assert
(
db
->
handle
);
assert
(
baton
->
db
->
open
);
db
->
error_status
=
sqlite3_close
(
db
->
handle
);
assert
(
!
baton
->
db
->
locked
);
assert
(
baton
->
db
->
pending
==
0
);
if
(
db
->
error_status
!=
SQLITE_OK
)
{
fprintf
(
stderr
,
"Close started
\n
"
);
db
->
error_message
=
std
::
string
(
sqlite3_errmsg
(
db
->
handle
));
baton
->
db
->
locked
=
true
;
return
false
;
eio_custom
(
EIO_Close
,
EIO_PRI_DEFAULT
,
EIO_AfterClose
,
baton
);
}
else
{
db
->
handle
=
NULL
;
return
true
;
}
}
}
int
Database
::
EIO_Close
(
eio_req
*
req
)
{
Baton
*
baton
=
static_cast
<
Baton
*>
(
req
->
data
);
Database
*
db
=
baton
->
db
;
Handle
<
Value
>
Database
::
CloseSync
(
const
Arguments
&
args
)
{
fprintf
(
stderr
,
"Close performed
\n
"
);
HandleScope
scope
;
baton
->
status
=
sqlite3_close
(
db
->
handle
);
Database
*
db
=
ObjectWrap
::
Unwrap
<
Database
>
(
args
.
This
());
REQUIRE_ARGUMENT_FUNCTION
(
0
,
callback
);
if
(
db
->
status
==
IsOpen
&&
db
->
pending
==
0
)
{
Local
<
Value
>
argv
[
1
];
if
(
Close
(
db
))
{
db
->
status
=
IsClosed
;
argv
[
0
]
=
Local
<
Value
>::
New
(
Null
());
}
else
{
EXCEPTION
(
db
->
error_message
.
c_str
(),
db
->
error_status
,
exception
);
argv
[
0
]
=
exception
;
}
TRY_CATCH_CALL
(
args
.
This
(),
callback
,
1
,
argv
);
if
(
baton
->
status
!=
SQLITE_OK
)
{
ProcessQueue
(
db
);
baton
->
message
=
std
::
string
(
sqlite3_errmsg
(
db
->
handle
)
);
}
}
else
{
else
{
db
->
queue
.
push
(
new
Call
(
args
,
IsOpen
,
Deferred
::
Exclusive
))
;
db
->
handle
=
NULL
;
}
}
return
args
.
This
();
}
int
Database
::
EIO_Close
(
eio_req
*
req
)
{
Baton
*
baton
=
static_cast
<
Baton
*>
(
req
->
data
);
Close
(
baton
->
db
);
return
0
;
return
0
;
}
}
...
@@ -301,331 +238,41 @@ int Database::EIO_AfterClose(eio_req *req) {
...
@@ -301,331 +238,41 @@ int Database::EIO_AfterClose(eio_req *req) {
ev_unref
(
EV_DEFAULT_UC
);
ev_unref
(
EV_DEFAULT_UC
);
db
->
Unref
();
db
->
Unref
();
db
->
pending
--
;
Local
<
Value
>
argv
[
1
];
Local
<
Value
>
argv
[
1
];
if
(
db
->
error_status
!=
SQLITE_OK
)
{
if
(
baton
->
status
!=
SQLITE_OK
)
{
db
->
status
=
IsOpen
;
EXCEPTION
(
String
::
New
(
baton
->
message
.
c_str
()),
baton
->
status
,
exception
);
EXCEPTION
(
db
->
error_message
.
c_str
(),
db
->
error_status
,
exception
);
argv
[
0
]
=
exception
;
argv
[
0
]
=
exception
;
}
}
else
{
else
{
db
->
status
=
IsClosed
;
db
->
open
=
false
;
// Leave db->locked to indicate that this db object has reached
// the end of its life.
argv
[
0
]
=
Local
<
Value
>::
New
(
Null
());
argv
[
0
]
=
Local
<
Value
>::
New
(
Null
());
}
}
// Fire callbacks.
if
(
!
baton
->
callback
.
IsEmpty
())
{
TRY_CATCH_CALL
(
db
->
handle_
,
baton
->
callback
,
1
,
argv
);
TRY_CATCH_CALL
(
db
->
handle_
,
baton
->
callback
,
1
,
argv
);
ProcessQueue
(
db
);
baton
->
callback
.
Dispose
();
delete
baton
;
return
0
;
}
// // TODO: libeio'fy
// Hooks
// static int CommitHook(void* v_this) {
// HandleScope scope;
// Database* db = static_cast<Database*>(v_this);
// db->Emit(String::New("commit"), 0, NULL);
// // TODO: allow change in return value to convert to rollback...somehow
// return 0;
// }
//
// static void RollbackHook(void* v_this) {
// HandleScope scope;
// Database* db = static_cast<Database*>(v_this);
// db->Emit(String::New("rollback"), 0, NULL);
// }
//
// static void UpdateHook(void* v_this, int operation, const char* database,
// const char* table, sqlite_int64 rowid) {
// HandleScope scope;
// Database* db = static_cast<Database*>(v_this);
// Local<Value> args[] = { Int32::New(operation), String::New(database),
// String::New(table), Number::New(rowid) };
// db->Emit(String::New("update"), 4, args);
// }
int
Database
::
EIO_AfterPrepareAndStep
(
eio_req
*
req
)
{
ev_unref
(
EV_DEFAULT_UC
);
struct
prepare_request
*
prep_req
=
(
struct
prepare_request
*
)(
req
->
data
);
HandleScope
scope
;
Local
<
Value
>
argv
[
2
];
int
argc
=
0
;
// if the prepare failed
if
(
req
->
result
!=
SQLITE_OK
)
{
argv
[
0
]
=
Exception
::
Error
(
String
::
New
(
sqlite3_errmsg
(
prep_req
->
db
->
handle
)));
argc
=
1
;
}
else
{
if
(
req
->
int1
==
SQLITE_DONE
)
{
if
(
prep_req
->
mode
!=
EXEC_EMPTY
)
{
argv
[
0
]
=
Local
<
Value
>::
New
(
Undefined
());
// no error
Local
<
Object
>
info
=
Object
::
New
();
if
(
prep_req
->
mode
&
EXEC_LAST_INSERT_ID
)
{
info
->
Set
(
String
::
NewSymbol
(
"last_inserted_id"
),
Integer
::
NewFromUnsigned
(
prep_req
->
lastInsertId
));
}
}
if
(
prep_req
->
mode
&
EXEC_AFFECTED_ROWS
)
{
else
if
(
db
->
open
)
{
info
->
Set
(
String
::
NewSymbol
(
"affected_rows"
),
Local
<
Value
>
args
[]
=
{
String
::
NewSymbol
(
"error"
),
argv
[
0
]
};
Integer
::
New
(
prep_req
->
affectedRows
));
EMIT_EVENT
(
db
->
handle_
,
2
,
args
);
}
argv
[
1
]
=
info
;
argc
=
2
;
}
else
{
argc
=
0
;
}
}
else
{
argv
[
0
]
=
External
::
New
(
prep_req
->
stmt
);
argv
[
1
]
=
Integer
::
New
(
req
->
int1
);
Persistent
<
Object
>
statement
(
Statement
::
constructor_template
->
GetFunction
()
->
NewInstance
(
2
,
argv
));
if
(
prep_req
->
tail
)
{
statement
->
Set
(
String
::
New
(
"tail"
),
String
::
New
(
prep_req
->
tail
));
}
}
argv
[
0
]
=
Local
<
Value
>::
New
(
Undefined
());
if
(
!
db
->
open
)
{
argv
[
1
]
=
Local
<
Value
>::
New
(
statement
);
Local
<
Value
>
args
[]
=
{
String
::
NewSymbol
(
"close"
),
argv
[
0
]
};
argc
=
2
;
EMIT_EVENT
(
db
->
handle_
,
1
,
args
);
}
Process
(
db
);
}
TryCatch
try_catch
;
prep_req
->
db
->
Unref
();
prep_req
->
cb
->
Call
(
Context
::
GetCurrent
()
->
Global
(),
argc
,
argv
);
if
(
try_catch
.
HasCaught
())
{
FatalException
(
try_catch
);
}
prep_req
->
cb
.
Dispose
();
free
(
prep_req
);
return
0
;
}
int
Database
::
EIO_PrepareAndStep
(
eio_req
*
req
)
{
struct
prepare_request
*
prep_req
=
(
struct
prepare_request
*
)(
req
->
data
);
prep_req
->
stmt
=
NULL
;
prep_req
->
tail
=
NULL
;
sqlite3
*
db
=
prep_req
->
db
->
handle
;
int
rc
=
sqlite3_prepare_v2
(
db
,
prep_req
->
sql
,
-
1
,
&
(
prep_req
->
stmt
),
&
(
prep_req
->
tail
));
req
->
result
=
rc
;
req
->
int1
=
-
1
;
// This might be a INSERT statement. Let's try to get the first row.
// This is to optimize out further calls to the thread pool. This is only
// possible in the case where there are no variable placeholders/bindings
// in the SQL.
if
(
rc
==
SQLITE_OK
&&
!
sqlite3_bind_parameter_count
(
prep_req
->
stmt
))
{
rc
=
sqlite3_step
(
prep_req
->
stmt
);
req
->
int1
=
rc
;
// no more rows to return, clean up statement
if
(
rc
==
SQLITE_DONE
)
{
rc
=
sqlite3_finalize
(
prep_req
->
stmt
);
prep_req
->
stmt
=
NULL
;
assert
(
rc
==
SQLITE_OK
);
}
}
prep_req
->
lastInsertId
=
0
;
prep_req
->
affectedRows
=
0
;
// load custom properties
if
(
prep_req
->
mode
&
EXEC_LAST_INSERT_ID
)
prep_req
->
lastInsertId
=
sqlite3_last_insert_rowid
(
db
);
if
(
prep_req
->
mode
&
EXEC_AFFECTED_ROWS
)
prep_req
->
affectedRows
=
sqlite3_changes
(
db
);
return
0
;
}
Handle
<
Value
>
Database
::
PrepareAndStep
(
const
Arguments
&
args
)
{
HandleScope
scope
;
REQUIRE_ARGUMENT_STRING
(
0
,
sql
);
REQUIRE_ARGUMENT_FUNCTION
(
1
,
cb
);
OPTIONAL_ARGUMENT_INTEGER
(
2
,
mode
,
EXEC_EMPTY
);
Database
*
db
=
ObjectWrap
::
Unwrap
<
Database
>
(
args
.
This
());
struct
prepare_request
*
prep_req
=
(
struct
prepare_request
*
)
calloc
(
1
,
sizeof
(
struct
prepare_request
)
+
sql
.
length
());
if
(
!
prep_req
)
{
V8
::
LowMemoryNotification
();
return
ThrowException
(
Exception
::
Error
(
String
::
New
(
"Could not allocate enough memory"
)));
}
}
strcpy
(
prep_req
->
sql
,
*
sql
);
delete
baton
;
prep_req
->
cb
=
Persistent
<
Function
>::
New
(
cb
);
prep_req
->
db
=
db
;
prep_req
->
mode
=
mode
;
eio_custom
(
EIO_PrepareAndStep
,
EIO_PRI_DEFAULT
,
EIO_AfterPrepareAndStep
,
prep_req
);
ev_ref
(
EV_DEFAULT_UC
);
db
->
Ref
();
return
Undefined
();
}
int
Database
::
EIO_AfterPrepare
(
eio_req
*
req
)
{
ev_unref
(
EV_DEFAULT_UC
);
struct
prepare_request
*
prep_req
=
(
struct
prepare_request
*
)(
req
->
data
);
HandleScope
scope
;
Local
<
Value
>
argv
[
3
];
int
argc
=
0
;
// if the prepare failed
if
(
req
->
result
!=
SQLITE_OK
)
{
argv
[
0
]
=
Exception
::
Error
(
String
::
New
(
sqlite3_errmsg
(
prep_req
->
db
->
handle
)));
argc
=
1
;
}
else
{
argv
[
0
]
=
External
::
New
(
prep_req
->
stmt
);
argv
[
1
]
=
Integer
::
New
(
-
1
);
argv
[
2
]
=
Integer
::
New
(
prep_req
->
mode
);
Persistent
<
Object
>
statement
(
Statement
::
constructor_template
->
GetFunction
()
->
NewInstance
(
3
,
argv
));
if
(
prep_req
->
tail
)
{
statement
->
Set
(
String
::
New
(
"tail"
),
String
::
New
(
prep_req
->
tail
));
}
argc
=
2
;
argv
[
0
]
=
Local
<
Value
>::
New
(
Undefined
());
argv
[
1
]
=
Local
<
Value
>::
New
(
statement
);
}
TryCatch
try_catch
;
prep_req
->
db
->
Unref
();
prep_req
->
cb
->
Call
(
Context
::
GetCurrent
()
->
Global
(),
argc
,
argv
);
if
(
try_catch
.
HasCaught
())
{
FatalException
(
try_catch
);
}
prep_req
->
cb
.
Dispose
();
free
(
prep_req
);
return
0
;
}
int
Database
::
EIO_Prepare
(
eio_req
*
req
)
{
struct
prepare_request
*
prep_req
=
(
struct
prepare_request
*
)(
req
->
data
);
prep_req
->
stmt
=
NULL
;
prep_req
->
tail
=
NULL
;
sqlite3
*
db
=
prep_req
->
db
->
handle
;
int
rc
=
sqlite3_prepare_v2
(
db
,
prep_req
->
sql
,
-
1
,
&
(
prep_req
->
stmt
),
&
(
prep_req
->
tail
));
req
->
result
=
rc
;
prep_req
->
lastInsertId
=
0
;
prep_req
->
affectedRows
=
0
;
// load custom properties
fprintf
(
stderr
,
"Close completed
\n
"
);
if
(
prep_req
->
mode
&
EXEC_LAST_INSERT_ID
)
prep_req
->
lastInsertId
=
sqlite3_last_insert_rowid
(
db
);
if
(
prep_req
->
mode
&
EXEC_AFFECTED_ROWS
)
prep_req
->
affectedRows
=
sqlite3_changes
(
db
);
return
0
;
return
0
;
}
}
// Statement#prepare(sql, [ options ,] callback);
Handle
<
Value
>
Database
::
Prepare
(
const
Arguments
&
args
)
{
HandleScope
scope
;
Local
<
Object
>
options
;
Local
<
Function
>
cb
;
int
mode
;
REQUIRE_ARGUMENT_STRING
(
0
,
sql
);
// middle argument could be options or
switch
(
args
.
Length
())
{
case
2
:
if
(
!
args
[
1
]
->
IsFunction
())
{
return
ThrowException
(
Exception
::
TypeError
(
String
::
New
(
"Argument 1 must be a function"
)));
}
cb
=
Local
<
Function
>::
Cast
(
args
[
1
]);
options
=
Object
::
New
();
break
;
case
3
:
if
(
!
args
[
1
]
->
IsObject
())
{
return
ThrowException
(
Exception
::
TypeError
(
String
::
New
(
"Argument 1 must be an object"
)));
}
options
=
Local
<
Function
>::
Cast
(
args
[
1
]);
if
(
!
args
[
2
]
->
IsFunction
())
{
return
ThrowException
(
Exception
::
TypeError
(
String
::
New
(
"Argument 2 must be a function"
)));
}
cb
=
Local
<
Function
>::
Cast
(
args
[
2
]);
break
;
}
mode
=
EXEC_EMPTY
;
if
(
options
->
Get
(
String
::
New
(
"lastInsertRowID"
))
->
IsTrue
())
{
mode
|=
EXEC_LAST_INSERT_ID
;
}
if
(
options
->
Get
(
String
::
New
(
"affectedRows"
))
->
IsTrue
())
{
mode
|=
EXEC_AFFECTED_ROWS
;
}
Database
*
db
=
ObjectWrap
::
Unwrap
<
Database
>
(
args
.
This
());
struct
prepare_request
*
prep_req
=
(
struct
prepare_request
*
)
calloc
(
1
,
sizeof
(
struct
prepare_request
)
+
sql
.
length
());
if
(
!
prep_req
)
{
V8
::
LowMemoryNotification
();
return
ThrowException
(
Exception
::
Error
(
String
::
New
(
"Could not allocate enough memory"
)));
}
strcpy
(
prep_req
->
sql
,
*
sql
);
prep_req
->
cb
=
Persistent
<
Function
>::
New
(
cb
);
prep_req
->
db
=
db
;
prep_req
->
mode
=
mode
;
eio_custom
(
EIO_Prepare
,
EIO_PRI_DEFAULT
,
EIO_AfterPrepare
,
prep_req
);
ev_ref
(
EV_DEFAULT_UC
);
db
->
Ref
();
return
Undefined
();
}
/**
/**
* Override this so that we can properly close the database when this object
* Override this so that we can properly close the database when this object
* gets garbage collected.
* gets garbage collected.
...
...
src/database.h
View file @
eaf72f23
...
@@ -19,8 +19,6 @@
...
@@ -19,8 +19,6 @@
#include <node.h>
#include <node.h>
#include <node_events.h>
#include <node_events.h>
#include "deferred_call.h"
#include <string>
#include <string>
#include <queue>
#include <queue>
...
@@ -29,34 +27,52 @@
...
@@ -29,34 +27,52 @@
using
namespace
v8
;
using
namespace
v8
;
using
namespace
node
;
using
namespace
node
;
class
Database
;
static
struct
Baton
{
Database
*
db
;
Persistent
<
Function
>
callback
;
int
status
;
std
::
string
message
;
~
Baton
()
{
callback
.
Dispose
();
}
};
static
struct
OpenBaton
:
Baton
{
std
::
string
filename
;
int
mode
;
};
class
Database
:
public
EventEmitter
{
class
Database
:
public
EventEmitter
{
public
:
public
:
static
Persistent
<
FunctionTemplate
>
constructor_template
;
static
Persistent
<
FunctionTemplate
>
constructor_template
;
static
void
Init
(
v8
::
Handle
<
Object
>
target
);
static
void
Init
(
v8
::
Handle
<
Object
>
target
);
static
enum
Status
{
static
inline
bool
HasInstance
(
Handle
<
Value
>
val
)
{
IsClosed
=
1
<<
0
,
if
(
!
val
->
IsObject
())
return
false
;
IsOpening
=
1
<<
1
,
Local
<
Object
>
obj
=
val
->
ToObject
();
IsOpen
=
1
<<
2
,
return
constructor_template
->
HasInstance
(
obj
);
IsClosing
=
1
<<
3
,
}
DoesntMatter
=
IsClosed
|
IsOpening
|
IsOpen
|
IsClosing
};
typedef
Deferred
::
Call
<
Status
>
Call
;
typedef
void
(
*
EIO_Callback
)(
Baton
*
baton
)
;
struct
Baton
{
struct
Call
{
Baton
(
Database
*
db_
,
Persistent
<
Function
>
callback_
)
:
Call
(
EIO_Callback
callback_
,
Baton
*
baton_
,
bool
exclusive_
=
false
)
:
db
(
db_
),
callback
(
callback_
)
{};
callback
(
callback_
),
exclusive
(
exclusive_
),
baton
(
baton_
)
{};
Database
*
db
;
EIO_Callback
callback
;
Persistent
<
Function
>
callback
;
bool
exclusive
;
Baton
*
baton
;
};
};
protected
:
friend
class
Statement
;
protected
:
Database
()
:
EventEmitter
(),
Database
()
:
EventEmitter
(),
handle
(
NULL
),
handle
(
NULL
),
pending
(
0
),
open
(
false
),
status
(
IsClosed
)
{
locked
(
false
),
pending
(
0
)
{
}
}
...
@@ -65,64 +81,32 @@ class Database : public EventEmitter {
...
@@ -65,64 +81,32 @@ class Database : public EventEmitter {
}
}
static
Handle
<
Value
>
New
(
const
Arguments
&
args
);
static
Handle
<
Value
>
New
(
const
Arguments
&
args
);
static
void
EIO_BeginOpen
(
Baton
*
baton
);
static
void
ProcessQueue
(
Database
*
db
);
static
Handle
<
Value
>
OpenSync
(
const
Arguments
&
args
);
static
Handle
<
Value
>
Open
(
const
Arguments
&
args
);
static
bool
Open
(
Database
*
db
);
static
int
EIO_Open
(
eio_req
*
req
);
static
int
EIO_Open
(
eio_req
*
req
);
static
int
EIO_AfterOpen
(
eio_req
*
req
);
static
int
EIO_AfterOpen
(
eio_req
*
req
);
static
Handle
<
Value
>
CloseSync
(
const
Arguments
&
args
);
static
void
Schedule
(
Database
*
db
,
EIO_Callback
callback
,
Baton
*
baton
,
bool
exclusive
);
static
void
Process
(
Database
*
db
);
static
Handle
<
Value
>
Close
(
const
Arguments
&
args
);
static
Handle
<
Value
>
Close
(
const
Arguments
&
args
);
static
bool
Close
(
Database
*
db
);
static
void
EIO_BeginClose
(
Baton
*
baton
);
static
int
EIO_Close
(
eio_req
*
req
);
static
int
EIO_Close
(
eio_req
*
req
);
static
int
EIO_AfterClose
(
eio_req
*
req
);
static
int
EIO_AfterClose
(
eio_req
*
req
);
static
int
EIO_AfterPrepareAndStep
(
eio_req
*
req
);
static
int
EIO_PrepareAndStep
(
eio_req
*
req
);
static
Handle
<
Value
>
PrepareAndStep
(
const
Arguments
&
args
);
static
int
EIO_AfterPrepare
(
eio_req
*
req
);
static
int
EIO_Prepare
(
eio_req
*
req
);
static
Handle
<
Value
>
Prepare
(
const
Arguments
&
args
);
void
Wrap
(
Handle
<
Object
>
handle
);
void
Wrap
(
Handle
<
Object
>
handle
);
static
void
Destruct
(
Persistent
<
Value
>
value
,
void
*
data
);
static
void
Destruct
(
Persistent
<
Value
>
value
,
void
*
data
);
static
int
EIO_Destruct
(
eio_req
*
req
);
static
int
EIO_Destruct
(
eio_req
*
req
);
static
int
EIO_AfterDestruct
(
eio_req
*
req
);
static
int
EIO_AfterDestruct
(
eio_req
*
req
);
protected
:
protected
:
sqlite3
*
handle
;
sqlite3
*
handle
;
std
::
string
filename
;
int
open_mode
;
std
::
string
error_message
;
bool
open
;
int
error_status
;
bool
locked
;
unsigned
int
pending
;
int
pending
;
Status
status
;
std
::
queue
<
Call
*>
queue
;
std
::
queue
<
Call
*>
queue
;
private
:
};
enum
ExecMode
{
EXEC_EMPTY
=
0
,
EXEC_LAST_INSERT_ID
=
1
,
EXEC_AFFECTED_ROWS
=
2
};
struct
prepare_request
{
Persistent
<
Function
>
cb
;
Database
*
db
;
sqlite3_stmt
*
stmt
;
int
mode
;
sqlite3_int64
lastInsertId
;
int
affectedRows
;
const
char
*
tail
;
char
sql
[
1
];
};
};
#endif
#endif
src/macros.h
View file @
eaf72f23
...
@@ -121,7 +121,7 @@ const char* sqlite_code_string(int code);
...
@@ -121,7 +121,7 @@ const char* sqlite_code_string(int code);
String::NewSymbol(sqlite_code_string(errno)), \
String::NewSymbol(sqlite_code_string(errno)), \
String::NewSymbol(": ") \
String::NewSymbol(": ") \
), \
), \
String::New(msg)
\
(msg)
\
) \
) \
); \
); \
Local<Object> name ##_obj = name->ToObject(); \
Local<Object> name ##_obj = name->ToObject(); \
...
@@ -129,30 +129,19 @@ const char* sqlite_code_string(int code);
...
@@ -129,30 +129,19 @@ const char* sqlite_code_string(int code);
name ##_obj->Set(NODE_PSYMBOL("code"), \
name ##_obj->Set(NODE_PSYMBOL("code"), \
String::NewSymbol(sqlite_code_string(errno)));
String::NewSymbol(sqlite_code_string(errno)));
#define EVENT_ONCE(event, callback) \
Local<Value> argv[2] = { \
String::NewSymbol(event), \
args.This()->Get(String::NewSymbol(callback)) \
}; \
v8::Local<v8::Value> fn_val = args.This()->Get(String::NewSymbol("once")); \
Local<Function> fn = Local<Function>::Cast(fn_val); \
fn->Call(args.This(), 2, argv);
#define
SET_STRING(sym, str)
\
#define
EMIT_EVENT(obj, argc, argv)
\
Set(String::NewSymbol(#sym), String::New(str), ReadOnly)
TRY_CATCH_CALL((obj), \
Local<Function>::Cast((obj)->Get(String::NewSymbol("emit"))), \
#define SET_INTEGER(sym, i)
\
argc, argv
\
Set(String::NewSymbol(#sym), Integer::New(i), ReadOnly)
);
#define TRY_CATCH_CALL(context, callback, argc, argv) \
#define TRY_CATCH_CALL(context, callback, argc, argv) \
{ \
{ TryCatch try_catch; \
TryCatch try_catch; \
(callback)->Call((context), (argc), (argv)); \
(callback)->Call((context), (argc), (argv)); \
if (try_catch.HasCaught()) { \
if (try_catch.HasCaught()) { \
FatalException(try_catch); \
FatalException(try_catch); \
} \
} }
}
#endif
#endif
src/statement.cc
View file @
eaf72f23
...
@@ -13,16 +13,15 @@
...
@@ -13,16 +13,15 @@
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <string.h>
#include <string.h>
#include <v8.h>
#include <node.h>
#include <node_events.h>
extern
"C"
{
#include "macros.h"
#include <mpool.h>
};
#include "database.h"
#include "database.h"
#include "statement.h"
#include "statement.h"
#include "
macros
.h"
#include "
deferred_call
.h"
static
Persistent
<
String
>
callback_sym
;
Persistent
<
FunctionTemplate
>
Statement
::
constructor_template
;
Persistent
<
FunctionTemplate
>
Statement
::
constructor_template
;
void
Statement
::
Init
(
v8
::
Handle
<
Object
>
target
)
{
void
Statement
::
Init
(
v8
::
Handle
<
Object
>
target
)
{
...
@@ -35,1016 +34,145 @@ void Statement::Init(v8::Handle<Object> target) {
...
@@ -35,1016 +34,145 @@ void Statement::Init(v8::Handle<Object> target) {
constructor_template
->
InstanceTemplate
()
->
SetInternalFieldCount
(
1
);
constructor_template
->
InstanceTemplate
()
->
SetInternalFieldCount
(
1
);
constructor_template
->
SetClassName
(
String
::
NewSymbol
(
"Statement"
));
constructor_template
->
SetClassName
(
String
::
NewSymbol
(
"Statement"
));
NODE_SET_PROTOTYPE_METHOD
(
t
,
"bind"
,
Bind
);
target
->
Set
(
v8
::
String
::
NewSymbol
(
"Statement"
),
NODE_SET_PROTOTYPE_METHOD
(
t
,
"bindObject"
,
BindObject
);
constructor_template
->
GetFunction
());
NODE_SET_PROTOTYPE_METHOD
(
t
,
"bindArray"
,
BindArray
);
NODE_SET_PROTOTYPE_METHOD
(
t
,
"finalize"
,
Finalize
);
NODE_SET_PROTOTYPE_METHOD
(
t
,
"reset"
,
Reset
);
NODE_SET_PROTOTYPE_METHOD
(
t
,
"clearBindings"
,
ClearBindings
);
NODE_SET_PROTOTYPE_METHOD
(
t
,
"step"
,
Step
);
NODE_SET_PROTOTYPE_METHOD
(
t
,
"fetchAll"
,
FetchAll
);
callback_sym
=
Persistent
<
String
>::
New
(
String
::
New
(
"callback"
));
}
}
// { Database db, String sql, Array params, Function callback }
Handle
<
Value
>
Statement
::
New
(
const
Arguments
&
args
)
{
Handle
<
Value
>
Statement
::
New
(
const
Arguments
&
args
)
{
HandleScope
scope
;
HandleScope
scope
;
REQUIRE_ARGUMENT_EXTERNAL
(
0
,
stmt
);
int
first_rc
=
args
[
1
]
->
IntegerValue
();
int
mode
=
args
[
2
]
->
IntegerValue
();
Statement
*
sto
=
new
Statement
((
sqlite3_stmt
*
)
stmt
->
Value
(),
first_rc
,
mode
);
sto
->
Wrap
(
args
.
This
());
sto
->
Ref
();
return
args
.
This
();
}
int
Statement
::
EIO_AfterBindArray
(
eio_req
*
req
)
{
ev_unref
(
EV_DEFAULT_UC
);
HandleScope
scope
;
struct
bind_request
*
bind_req
=
(
struct
bind_request
*
)(
req
->
data
);
Local
<
Value
>
argv
[
1
];
bool
err
=
false
;
if
(
req
->
result
)
{
err
=
true
;
argv
[
0
]
=
Exception
::
Error
(
String
::
New
(
"Error binding parameter"
));
}
TryCatch
try_catch
;
bind_req
->
cb
->
Call
(
Context
::
GetCurrent
()
->
Global
(),
err
?
1
:
0
,
argv
);
if
(
try_catch
.
HasCaught
())
{
FatalException
(
try_catch
);
}
bind_req
->
cb
.
Dispose
();
struct
bind_pair
*
pair
=
bind_req
->
pairs
;
for
(
size_t
i
=
0
;
i
<
bind_req
->
len
;
i
++
,
pair
++
)
{
free
((
char
*
)(
pair
->
key
));
switch
(
pair
->
key_type
)
{
case
KEY_INT
:
free
((
int
*
)(
pair
->
value
));
break
;
case
KEY_STRING
:
free
((
char
*
)(
pair
->
value
));
break
;
}
}
free
(
bind_req
->
pairs
);
free
(
bind_req
);
return
0
;
}
int
Statement
::
EIO_BindArray
(
eio_req
*
req
)
{
struct
bind_request
*
bind_req
=
(
struct
bind_request
*
)(
req
->
data
);
Statement
*
sto
=
bind_req
->
sto
;
int
rc
(
0
);
struct
bind_pair
*
pair
=
bind_req
->
pairs
;
int
index
=
0
;
for
(
size_t
i
=
0
;
i
<
bind_req
->
len
;
i
++
,
pair
++
)
{
switch
(
pair
->
key_type
)
{
case
KEY_INT
:
index
=
*
(
int
*
)(
pair
->
key
);
break
;
case
KEY_STRING
:
index
=
sqlite3_bind_parameter_index
(
sto
->
stmt_
,
(
char
*
)(
pair
->
key
));
break
;
default
:
{
// this SHOULD be unreachable
}
}
if
(
!
index
)
{
req
->
result
=
SQLITE_MISMATCH
;
return
0
;
}
int
rc
=
0
;
switch
(
pair
->
value_type
)
{
case
VALUE_INT
:
rc
=
sqlite3_bind_int
(
sto
->
stmt_
,
index
,
*
(
int
*
)(
pair
->
value
));
break
;
case
VALUE_DOUBLE
:
rc
=
sqlite3_bind_double
(
sto
->
stmt_
,
index
,
*
(
double
*
)(
pair
->
value
));
break
;
case
VALUE_STRING
:
rc
=
sqlite3_bind_text
(
sto
->
stmt_
,
index
,
(
char
*
)(
pair
->
value
),
pair
->
value_size
,
SQLITE_TRANSIENT
);
break
;
case
VALUE_BLOB
:
rc
=
sqlite3_bind_blob
(
sto
->
stmt_
,
index
,
(
char
*
)(
pair
->
value
),
pair
->
value_size
,
SQLITE_TRANSIENT
);
break
;
case
VALUE_NULL
:
rc
=
sqlite3_bind_null
(
sto
->
stmt_
,
index
);
break
;
// should be unreachable
}
}
if
(
rc
)
{
// if (args.Length() < 1 || !args[0]->IsString()) {
req
->
result
=
rc
;
// return ThrowException(Exception::TypeError(
return
0
;
// String::New("First argument must be a SQL query"))
}
// );
// }
return
0
;
//
}
// Local<Function> callback;
// int last = args.Length() - 1;
Handle
<
Value
>
Statement
::
BindObject
(
const
Arguments
&
args
)
{
// if (args.Length() >= 2 && args[last]->IsFunction()) {
HandleScope
scope
;
// callback = Local<Function>::Cast(args[last--]);
Statement
*
sto
=
ObjectWrap
::
Unwrap
<
Statement
>
(
args
.
This
());
// }
//
REQUIRE_ARGUMENTS
(
2
);
// // Process any optional arguments
REQUIRE_ARGUMENT_FUNCTION
(
1
,
cb
);
// for (int i = 1; i <= last; i++) {
//
if
(
!
args
[
0
]
->
IsObject
())
// }
int
length
=
args
.
Length
();
if
(
length
<=
0
||
!
Database
::
HasInstance
(
args
[
0
]))
{
return
ThrowException
(
Exception
::
TypeError
(
return
ThrowException
(
Exception
::
TypeError
(
String
::
New
(
"First argument must be an Array."
)));
String
::
New
(
"First argument must be a Database object"
)));
Local
<
Object
>
obj
=
args
[
0
]
->
ToObject
();
Local
<
Array
>
properties
=
obj
->
GetPropertyNames
();
struct
bind_request
*
bind_req
=
(
struct
bind_request
*
)
calloc
(
1
,
sizeof
(
struct
bind_request
));
int
len
=
bind_req
->
len
=
properties
->
Length
();
bind_req
->
pairs
=
(
struct
bind_pair
*
)
calloc
(
len
,
sizeof
(
struct
bind_pair
));
struct
bind_pair
*
pairs
=
bind_req
->
pairs
;
for
(
uint32_t
i
=
0
;
i
<
properties
->
Length
();
i
++
,
pairs
++
)
{
Local
<
Value
>
name
=
properties
->
Get
(
Integer
::
New
(
i
));
Local
<
Value
>
val
=
obj
->
Get
(
name
->
ToString
());
String
::
Utf8Value
keyValue
(
name
);
// setting key type
pairs
->
key_type
=
KEY_STRING
;
char
*
key
=
(
char
*
)
calloc
(
1
,
keyValue
.
length
()
+
1
);
memcpy
(
key
,
*
keyValue
,
keyValue
.
length
()
+
1
);
pairs
->
key
=
key
;
// setup value
if
(
val
->
IsInt32
())
{
pairs
->
value_type
=
VALUE_INT
;
int
*
value
=
(
int
*
)
malloc
(
sizeof
(
int
));
*
value
=
val
->
Int32Value
();
pairs
->
value
=
value
;
}
}
else
if
(
val
->
IsNumber
())
{
else
if
(
length
<=
1
||
!
args
[
1
]
->
IsString
())
{
pairs
->
value_type
=
VALUE_DOUBLE
;
double
*
value
=
(
double
*
)
malloc
(
sizeof
(
double
));
*
value
=
val
->
NumberValue
();
pairs
->
value
=
value
;
}
else
if
(
val
->
IsString
())
{
pairs
->
value_type
=
VALUE_STRING
;
String
::
Utf8Value
text
(
val
);
char
*
value
=
(
char
*
)
calloc
(
text
.
length
(),
sizeof
(
char
));
memcpy
(
value
,
*
text
,
text
.
length
());
pairs
->
value
=
value
;
pairs
->
value_size
=
text
.
length
();
}
else
if
(
Buffer
::
HasInstance
(
val
))
{
pairs
->
value_type
=
VALUE_BLOB
;
Buffer
*
buffer
=
Buffer
::
Unwrap
<
Buffer
>
(
val
->
ToObject
());
char
*
value
=
(
char
*
)
malloc
(
buffer
->
length
());
memcpy
(
value
,
buffer
->
data
(),
buffer
->
length
());
pairs
->
value
=
value
;
pairs
->
value_size
=
buffer
->
length
();
}
else
if
(
val
->
IsNull
()
||
val
->
IsUndefined
())
{
pairs
->
value_type
=
VALUE_NULL
;
pairs
->
value
=
NULL
;
}
else
{
free
(
pairs
->
key
);
return
ThrowException
(
Exception
::
TypeError
(
return
ThrowException
(
Exception
::
TypeError
(
String
::
New
(
"Unable to bind value of this type"
)));
String
::
New
(
"Second argument must be a SQL query"
)));
}
}
}
else
if
(
length
<=
2
||
!
args
[
2
]
->
IsObject
())
{
bind_req
->
cb
=
Persistent
<
Function
>::
New
(
cb
);
bind_req
->
sto
=
sto
;
eio_custom
(
EIO_BindArray
,
EIO_PRI_DEFAULT
,
EIO_AfterBindArray
,
bind_req
);
ev_ref
(
EV_DEFAULT_UC
);
return
Undefined
();
};
Handle
<
Value
>
Statement
::
BindArray
(
const
Arguments
&
args
)
{
HandleScope
scope
;
Statement
*
sto
=
ObjectWrap
::
Unwrap
<
Statement
>
(
args
.
This
());
REQUIRE_ARGUMENTS
(
2
);
REQUIRE_ARGUMENT_FUNCTION
(
1
,
cb
);
if
(
!
args
[
0
]
->
IsArray
())
return
ThrowException
(
Exception
::
TypeError
(
return
ThrowException
(
Exception
::
TypeError
(
String
::
New
(
"First argument must be an Array."
)));
String
::
New
(
"Third argument must be an array or object of parameters"
)));
struct
bind_request
*
bind_req
=
(
struct
bind_request
*
)
calloc
(
1
,
sizeof
(
struct
bind_request
));
Local
<
Array
>
array
=
Local
<
Array
>::
Cast
(
args
[
0
]);
int
len
=
bind_req
->
len
=
array
->
Length
();
bind_req
->
pairs
=
(
struct
bind_pair
*
)
calloc
(
len
,
sizeof
(
struct
bind_pair
));
struct
bind_pair
*
pairs
=
bind_req
->
pairs
;
// pack the binds into the struct
for
(
int
i
=
0
;
i
<
len
;
i
++
,
pairs
++
)
{
Local
<
Value
>
val
=
array
->
Get
(
i
);
// setting key type
pairs
->
key_type
=
KEY_INT
;
int
*
index
=
(
int
*
)
malloc
(
sizeof
(
int
));
*
index
=
i
+
1
;
// don't forget to `free` this
pairs
->
key
=
index
;
// setup value
if
(
val
->
IsInt32
())
{
pairs
->
value_type
=
VALUE_INT
;
int
*
value
=
(
int
*
)
malloc
(
sizeof
(
int
));
*
value
=
val
->
Int32Value
();
pairs
->
value
=
value
;
}
}
else
if
(
val
->
IsNumber
())
{
else
if
(
length
>
3
&&
!
args
[
3
]
->
IsUndefined
()
&&
!
args
[
3
]
->
IsFunction
())
{
pairs
->
value_type
=
VALUE_DOUBLE
;
double
*
value
=
(
double
*
)
malloc
(
sizeof
(
double
));
*
value
=
val
->
NumberValue
();
pairs
->
value
=
value
;
}
else
if
(
val
->
IsString
())
{
pairs
->
value_type
=
VALUE_STRING
;
String
::
Utf8Value
text
(
val
);
char
*
value
=
(
char
*
)
calloc
(
text
.
length
(),
sizeof
(
char
));
memcpy
(
value
,
*
text
,
text
.
length
());
pairs
->
value
=
value
;
pairs
->
value_size
=
text
.
length
();
}
else
if
(
Buffer
::
HasInstance
(
val
))
{
pairs
->
value_type
=
VALUE_BLOB
;
Buffer
*
buffer
=
Buffer
::
Unwrap
<
Buffer
>
(
val
->
ToObject
());
char
*
value
=
(
char
*
)
malloc
(
buffer
->
length
());
memcpy
(
value
,
buffer
->
data
(),
buffer
->
length
());
pairs
->
value
=
value
;
pairs
->
value_size
=
buffer
->
length
();
}
else
if
(
val
->
IsNull
()
||
val
->
IsUndefined
())
{
pairs
->
value_type
=
VALUE_NULL
;
pairs
->
value
=
NULL
;
}
else
{
free
(
pairs
->
key
);
return
ThrowException
(
Exception
::
TypeError
(
return
ThrowException
(
Exception
::
TypeError
(
String
::
New
(
"Unable to bind value of this type"
)));
String
::
New
(
"Fourth argument must be a function"
)));
}
}
}
bind_req
->
cb
=
Persistent
<
Function
>::
New
(
cb
);
Database
*
db
=
ObjectWrap
::
Unwrap
<
Database
>
(
args
[
0
]
->
ToObject
()
);
bind_req
->
sto
=
sto
;
Local
<
String
>
sql
=
Local
<
String
>::
Cast
(
args
[
1
])
;
eio_custom
(
EIO_BindArray
,
EIO_PRI_DEFAULT
,
EIO_AfterBindArray
,
bind_req
);
args
.
This
()
->
Set
(
String
::
NewSymbol
(
"sql"
),
Persistent
<
String
>::
New
(
sql
),
ReadOnly
);
ev_ref
(
EV_DEFAULT_UC
);
Statement
*
stmt
=
new
Statement
(
db
);
stmt
->
Wrap
(
args
.
This
());
return
Undefined
();
}
// db.prepare "SELECT $x, ?" -> statement
//
// Bind multiple placeholders by array
// statement.bind([ value0, value1 ], callback);
//
// Bind multiple placeholdders by name
// statement.bind({ $x: value }, callback);
//
// Bind single placeholder by name:
// statement.bind('$x', value, callback);
//
// Bind placeholder by position:
// statement.bind(1, value, callback);
Handle
<
Value
>
Statement
::
Bind
(
const
Arguments
&
args
)
{
HandleScope
scope
;
Statement
*
sto
=
ObjectWrap
::
Unwrap
<
Statement
>
(
args
.
This
());
REQUIRE_ARGUMENTS
(
2
);
REQUIRE_ARGUMENT_FUNCTION
(
2
,
cb
);
if
(
!
(
args
[
0
]
->
IsString
()
||
args
[
0
]
->
IsInt32
()
||
args
[
0
]
->
IsArray
()
||
args
[
0
]
->
IsObject
()))
return
ThrowException
(
Exception
::
TypeError
(
String
::
New
(
"First argument must be a string, number, array or object."
)));
struct
bind_request
*
bind_req
=
(
struct
bind_request
*
)
calloc
(
1
,
sizeof
(
struct
bind_request
));
bind_req
->
len
=
1
;
struct
bind_pair
*
pair
=
bind_req
->
pairs
=
(
struct
bind_pair
*
)
calloc
(
1
,
sizeof
(
struct
bind_pair
));
// setup key
if
(
args
[
0
]
->
IsString
())
{
String
::
Utf8Value
keyValue
(
args
[
0
]);
pair
->
key_type
=
KEY_STRING
;
char
*
key
=
(
char
*
)
calloc
(
1
,
keyValue
.
length
()
+
1
);
memcpy
(
key
,
*
keyValue
,
keyValue
.
length
()
+
1
);
pair
->
key
=
key
;
}
else
if
(
args
[
0
]
->
IsInt32
())
{
pair
->
key_type
=
KEY_INT
;
int
*
index
=
(
int
*
)
malloc
(
sizeof
(
int
));
*
index
=
args
[
0
]
->
Int32Value
();
if
(
*
index
<
1
)
return
ThrowException
(
Exception
::
TypeError
(
String
::
New
(
"Parameter position index start with 1."
)));
// don't forget to `free` this
pair
->
key
=
index
;
}
// setup value
if
(
args
[
1
]
->
IsInt32
())
{
pair
->
value_type
=
VALUE_INT
;
int
*
value
=
(
int
*
)
malloc
(
sizeof
(
int
));
*
value
=
args
[
1
]
->
Int32Value
();
pair
->
value
=
value
;
}
else
if
(
args
[
1
]
->
IsNumber
())
{
pair
->
value_type
=
VALUE_DOUBLE
;
double
*
value
=
(
double
*
)
malloc
(
sizeof
(
double
));
*
value
=
args
[
1
]
->
NumberValue
();
pair
->
value
=
value
;
}
else
if
(
args
[
1
]
->
IsString
())
{
pair
->
value_type
=
VALUE_STRING
;
String
::
Utf8Value
text
(
args
[
1
]);
char
*
value
=
(
char
*
)
calloc
(
text
.
length
(),
sizeof
(
char
));
memcpy
(
value
,
*
text
,
text
.
length
());
pair
->
value
=
value
;
pair
->
value_size
=
text
.
length
();
}
else
if
(
Buffer
::
HasInstance
(
args
[
1
]))
{
pair
->
value_type
=
VALUE_BLOB
;
Buffer
*
buffer
=
Buffer
::
Unwrap
<
Buffer
>
(
args
[
1
]
->
ToObject
());
char
*
value
=
(
char
*
)
malloc
(
buffer
->
length
());
memcpy
(
value
,
buffer
->
data
(),
buffer
->
length
());
pair
->
value
=
value
;
pair
->
value_size
=
buffer
->
length
();
}
else
if
(
args
[
1
]
->
IsNull
()
||
args
[
1
]
->
IsUndefined
())
{
pair
->
value_type
=
VALUE_NULL
;
pair
->
value
=
NULL
;
}
else
{
free
(
pair
->
key
);
return
ThrowException
(
Exception
::
TypeError
(
String
::
New
(
"Unable to bind value of this type"
)));
}
bind_req
->
cb
=
Persistent
<
Function
>::
New
(
cb
);
bind_req
->
sto
=
sto
;
eio_custom
(
EIO_BindArray
,
EIO_PRI_DEFAULT
,
EIO_AfterBindArray
,
bind_req
);
ev_ref
(
EV_DEFAULT_UC
);
return
Undefined
();
}
int
Statement
::
EIO_AfterFinalize
(
eio_req
*
req
)
{
ev_unref
(
EV_DEFAULT_UC
);
Statement
*
sto
=
(
class
Statement
*
)(
req
->
data
);
HandleScope
scope
;
Local
<
Function
>
cb
=
sto
->
GetCallback
();
TryCatch
try_catch
;
cb
->
Call
(
sto
->
handle_
,
0
,
NULL
);
if
(
try_catch
.
HasCaught
())
{
PrepareBaton
*
baton
=
new
PrepareBaton
();
FatalException
(
try_catch
);
baton
->
db
=
db
;
}
baton
->
stmt
=
stmt
;
baton
->
callback
=
Persistent
<
Function
>::
New
(
Local
<
Function
>::
Cast
(
args
[
3
]));
sto
->
Unref
();
baton
->
sql
=
std
::
string
(
*
String
::
Utf8Value
(
sql
));
Database
::
Schedule
(
db
,
EIO_BeginPrepare
,
baton
,
false
);
return
0
;
}
int
Statement
::
EIO_Finalize
(
eio_req
*
req
)
{
Statement
*
sto
=
(
class
Statement
*
)(
req
->
data
);
assert
(
sto
->
stmt_
);
return
args
.
This
();
req
->
result
=
sqlite3_finalize
(
sto
->
stmt_
);
sto
->
stmt_
=
NULL
;
return
0
;
}
}
Handle
<
Value
>
Statement
::
Finalize
(
const
Arguments
&
args
)
{
HandleScope
scope
;
Statement
*
sto
=
ObjectWrap
::
Unwrap
<
Statement
>
(
args
.
This
());
if
(
sto
->
HasCallback
())
{
return
ThrowException
(
Exception
::
Error
(
String
::
New
(
"Already stepping"
)));
}
REQUIRE_ARGUMENT_FUNCTION
(
0
,
cb
);
sto
->
SetCallback
(
cb
);
eio_custom
(
EIO_Finalize
,
EIO_PRI_DEFAULT
,
EIO_AfterFinalize
,
sto
);
void
Statement
::
EIO_BeginPrepare
(
Baton
*
baton
)
{
assert
(
baton
->
db
->
open
);
assert
(
!
baton
->
db
->
locked
);
baton
->
db
->
pending
++
;
static_cast
<
PrepareBaton
*>
(
baton
)
->
stmt
->
Ref
();
ev_ref
(
EV_DEFAULT_UC
);
ev_ref
(
EV_DEFAULT_UC
);
fprintf
(
stderr
,
"Prepare started
\n
"
);
return
Undefined
();
eio_custom
(
EIO_Prepare
,
EIO_PRI_DEFAULT
,
EIO_AfterPrepare
,
baton
);
}
}
Handle
<
Value
>
Statement
::
ClearBindings
(
const
Arguments
&
args
)
{
int
Statement
::
EIO_Prepare
(
eio_req
*
req
)
{
HandleScope
scope
;
PrepareBaton
*
baton
=
static_cast
<
PrepareBaton
*>
(
req
->
data
);
Statement
*
sto
=
ObjectWrap
::
Unwrap
<
Statement
>
(
args
.
This
());
Database
*
db
=
baton
->
db
;
SCHECK
(
sqlite3_clear_bindings
(
sto
->
stmt_
));
return
Undefined
();
fprintf
(
stderr
,
"Prepare performed
\n
"
);
}
// baton->status = sqlite3_prepare16_v2(
// db->handle,
Handle
<
Value
>
Statement
::
Reset
(
const
Arguments
&
args
)
{
// const void *zSql, /* SQL statement, UTF-16 encoded */
HandleScope
scope
;
// int nByte, /* Maximum length of zSql in bytes. */
Statement
*
sto
=
ObjectWrap
::
Unwrap
<
Statement
>
(
args
.
This
());
// sqlite3_stmt **ppStmt, /* OUT: Statement handle */
sto
->
FreeColumnData
();
// const void **pzTail /* OUT: Pointer to unused portion of zSql */
SCHECK
(
sqlite3_reset
(
sto
->
stmt_
));
// );
return
Undefined
();
//
}
// baton->status = sqlite3_close(db->handle);
//
int
Statement
::
EIO_AfterStep
(
eio_req
*
req
)
{
// if (baton->status != SQLITE_OK) {
ev_unref
(
EV_DEFAULT_UC
);
// baton->message = std::string(sqlite3_errmsg(db->handle));
// }
HandleScope
scope
;
// else {
// db->handle = NULL;
Statement
*
sto
=
(
class
Statement
*
)
req
->
data
;
// }
sqlite3
*
db
=
sqlite3_db_handle
(
sto
->
stmt_
);
Local
<
Value
>
argv
[
2
];
if
(
sto
->
error_
)
{
Local
<
Value
>
e
=
Exception
::
Error
(
sto
->
error_msg_
);
Local
<
Object
>
obj
=
e
->
ToObject
();
obj
->
Set
(
NODE_PSYMBOL
(
"errno"
),
Integer
::
New
(
sto
->
error_
));
obj
->
Set
(
NODE_PSYMBOL
(
"query"
),
String
::
NewSymbol
(
sqlite3_sql
(
sto
->
stmt_
)));
argv
[
0
]
=
e
;
}
else
{
argv
[
0
]
=
Local
<
Value
>::
New
(
Undefined
());
}
if
(
req
->
result
==
SQLITE_DONE
)
{
argv
[
1
]
=
Local
<
Value
>::
New
(
Null
());
}
else
{
Local
<
Object
>
row
=
Object
::
New
();
struct
cell_node
*
cell
=
sto
->
cells
,
*
next
=
NULL
;
for
(
int
i
=
0
;
cell
;
i
++
)
{
switch
(
cell
->
type
)
{
case
SQLITE_INTEGER
:
row
->
Set
(
String
::
NewSymbol
((
char
*
)
sto
->
column_names_
[
i
]),
Int32
::
New
(
*
(
int
*
)
cell
->
value
));
free
(
cell
->
value
);
break
;
case
SQLITE_FLOAT
:
row
->
Set
(
String
::
NewSymbol
(
sto
->
column_names_
[
i
]),
Number
::
New
(
*
(
double
*
)
cell
->
value
));
free
(
cell
->
value
);
break
;
case
SQLITE_TEXT
:
{
struct
string_t
*
str
=
(
struct
string_t
*
)
cell
->
value
;
// str->bytes-1 to compensate for the NULL terminator
row
->
Set
(
String
::
NewSymbol
(
sto
->
column_names_
[
i
]),
String
::
New
(
str
->
data
,
str
->
bytes
-
1
));
free
(
str
);
}
break
;
case
SQLITE_BLOB
:
{
node
::
Buffer
*
buf
=
Buffer
::
New
(
cell
->
size
);
memcpy
(
buf
->
data
(),
cell
->
value
,
cell
->
size
);
row
->
Set
(
String
::
New
(
sto
->
column_names_
[
i
]),
buf
->
handle_
);
free
(
cell
->
value
);
}
break
;
case
SQLITE_NULL
:
row
->
Set
(
String
::
New
(
sto
->
column_names_
[
i
]),
Local
<
Value
>::
New
(
Null
()));
break
;
}
next
=
cell
->
next
;
free
(
cell
);
cell
=
next
;
}
argv
[
1
]
=
row
;
}
if
(
sto
->
mode_
&
EXEC_LAST_INSERT_ID
)
{
sto
->
handle_
->
Set
(
String
::
New
(
"lastInsertRowID"
),
Integer
::
New
(
sqlite3_last_insert_rowid
(
db
)));
}
if
(
sto
->
mode_
&
EXEC_AFFECTED_ROWS
)
{
sto
->
handle_
->
Set
(
String
::
New
(
"affectedRows"
),
Integer
::
New
(
sqlite3_changes
(
db
)));
}
TryCatch
try_catch
;
Local
<
Function
>
cb
=
sto
->
GetCallback
();
cb
->
Call
(
sto
->
handle_
,
2
,
argv
);
if
(
try_catch
.
HasCaught
())
{
FatalException
(
try_catch
);
}
if
(
req
->
result
==
SQLITE_DONE
&&
sto
->
column_count_
)
{
sto
->
FreeColumnData
();
}
return
0
;
return
0
;
}
}
void
Statement
::
FreeColumnData
(
void
)
{
int
Statement
::
EIO_AfterPrepare
(
eio_req
*
req
)
{
if
(
!
column_count_
)
return
;
free
(
column_names_
);
column_count_
=
0
;
column_names_
=
NULL
;
}
void
Statement
::
InitializeColumns
(
void
)
{
this
->
column_count_
=
sqlite3_column_count
(
this
->
stmt_
);
this
->
column_names_
=
(
char
**
)
calloc
(
this
->
column_count_
,
sizeof
(
char
*
));
for
(
int
i
=
0
;
i
<
this
->
column_count_
;
i
++
)
{
// Don't free this!
this
->
column_names_
[
i
]
=
(
char
*
)
sqlite3_column_name
(
this
->
stmt_
,
i
);
}
}
int
Statement
::
EIO_Step
(
eio_req
*
req
)
{
Statement
*
sto
=
(
class
Statement
*
)(
req
->
data
);
sqlite3_stmt
*
stmt
=
sto
->
stmt_
;
assert
(
stmt
);
int
rc
;
const
char
*
msg
;
// check if we have already taken a step immediately after prepare
if
(
sto
->
first_rc_
!=
-
1
)
{
// This is the first one! Let's just use the rc from when we called
// it in EIO_Prepare
rc
=
req
->
result
=
sto
->
first_rc_
;
// Now we set first_rc_ to -1 so that on the next step, it won't
// think this is the first.
sto
->
first_rc_
=
-
1
;
}
else
{
sqlite3
*
db
=
sqlite3_db_handle
(
stmt
);
sqlite3_mutex
*
mtx
=
sqlite3_db_mutex
(
db
);
sqlite3_mutex_enter
(
mtx
);
rc
=
sqlite3_step
(
stmt
);
if
(
rc
!=
SQLITE_ROW
&&
rc
!=
SQLITE_DONE
)
msg
=
sqlite3_errmsg
(
db
);
sqlite3_mutex_leave
(
mtx
);
req
->
result
=
rc
;
}
sto
->
error_
=
SQLITE_OK
;
if
(
rc
==
SQLITE_ROW
)
{
// If this pointer is NULL, look up and store the columns names.
if
(
!
sto
->
column_names_
)
{
sto
->
InitializeColumns
();
}
struct
cell_node
*
cell_head
=
NULL
,
*
cell_prev
=
NULL
,
*
cell
=
NULL
;
for
(
int
i
=
0
;
i
<
sto
->
column_count_
;
i
++
)
{
cell
=
(
struct
cell_node
*
)
malloc
(
sizeof
(
struct
cell_node
));
// If this is the first cell, set `cell_head` to it, otherwise attach
// the new cell to the end of the list `cell_prev->next`.
(
!
cell_head
?
cell_head
:
cell_prev
->
next
)
=
cell
;
cell
->
type
=
sqlite3_column_type
(
sto
->
stmt_
,
i
);
cell
->
next
=
NULL
;
switch
(
cell
->
type
)
{
case
SQLITE_INTEGER
:
cell
->
value
=
(
int
*
)
malloc
(
sizeof
(
int
));
*
(
int
*
)
cell
->
value
=
sqlite3_column_int
(
stmt
,
i
);
break
;
case
SQLITE_FLOAT
:
cell
->
value
=
(
double
*
)
malloc
(
sizeof
(
double
));
*
(
double
*
)
cell
->
value
=
sqlite3_column_double
(
stmt
,
i
);
break
;
case
SQLITE_TEXT
:
{
char
*
text
=
(
char
*
)
sqlite3_column_text
(
stmt
,
i
);
int
size
=
1
+
sqlite3_column_bytes
(
stmt
,
i
);
struct
string_t
*
str
=
(
struct
string_t
*
)
malloc
(
sizeof
(
size_t
)
+
size
);
str
->
bytes
=
size
;
memcpy
(
str
->
data
,
text
,
size
);
cell
->
value
=
str
;
}
break
;
case
SQLITE_BLOB
:
{
const
void
*
blob
=
sqlite3_column_blob
(
stmt
,
i
);
int
size
=
sqlite3_column_bytes
(
stmt
,
i
);
cell
->
size
=
size
;
unsigned
char
*
pzBlob
=
(
unsigned
char
*
)
malloc
(
size
);
memcpy
(
pzBlob
,
blob
,
size
);
cell
->
value
=
pzBlob
;
}
break
;
case
SQLITE_NULL
:
cell
->
value
=
NULL
;
break
;
default
:
{
assert
(
0
&&
"unsupported type"
);
}
if
(
cell
->
type
!=
SQLITE_NULL
)
{
assert
(
cell
->
value
);
}
}
cell_prev
=
cell
;
}
sto
->
cells
=
cell_head
;
assert
(
sto
->
column_names_
);
}
else
if
(
rc
==
SQLITE_DONE
)
{
// nothing to do in this case
}
else
{
HandleScope
scope
;
HandleScope
scope
;
sto
->
error_
=
rc
;
PrepareBaton
*
baton
=
static_cast
<
PrepareBaton
*>
(
req
->
data
);
sto
->
error_msg_
=
scope
.
Close
(
String
::
NewSymbol
(
msg
));
Database
*
db
=
baton
->
db
;
sto
->
cells
=
NULL
;
Statement
*
stmt
=
baton
->
stmt
;
}
return
0
;
}
Handle
<
Value
>
Statement
::
Step
(
const
Arguments
&
args
)
{
HandleScope
scope
;
Statement
*
sto
=
ObjectWrap
::
Unwrap
<
Statement
>
(
args
.
This
());
if
(
sto
->
HasCallback
())
{
return
ThrowException
(
Exception
::
Error
(
String
::
New
(
"Already stepping"
)));
}
REQUIRE_ARGUMENT_FUNCTION
(
0
,
cb
);
sto
->
SetCallback
(
cb
);
eio_custom
(
EIO_Step
,
EIO_PRI_DEFAULT
,
EIO_AfterStep
,
sto
);
ev_ref
(
EV_DEFAULT_UC
);
stmt
->
Unref
();
return
Undefined
();
}
int
Statement
::
EIO_AfterFetchAll
(
eio_req
*
req
)
{
HandleScope
scope
;
ev_unref
(
EV_DEFAULT_UC
);
ev_unref
(
EV_DEFAULT_UC
);
db
->
pending
--
;
// Local<Value> argv[1];
// if (baton->status != SQLITE_OK) {
// EXCEPTION(String::New(baton->message), baton->status, exception);
// argv[0] = exception;
// }
// else {
// db->open = false;
// // Leave db->locked to indicate that this db object has reached
// // the end of its life.
// argv[0] = Local<Value>::New(Null());
// }
//
// // Fire callbacks.
// if (!baton->callback.IsEmpty()) {
// TRY_CATCH_CALL(db->handle_, baton->callback, 1, argv);
// }
// else if (db->open) {
// Local<Value> args[] = { String::NewSymbol("error"), argv[0] };
// EMIT_EVENT(db->handle_, 2, args);
// }
//
// if (!db->open) {
// Local<Value> args[] = { String::NewSymbol("close"), argv[0] };
// EMIT_EVENT(db->handle_, 1, args);
// Process(db);
// }
fprintf
(
stderr
,
"Prepare completed
\n
"
);
Database
::
Process
(
db
);
delete
baton
;
struct
fetchall_request
*
fetchall_req
=
(
struct
fetchall_request
*
)(
req
->
data
);
Statement
*
sto
=
fetchall_req
->
sto
;
struct
row_node
*
cur
=
fetchall_req
->
rows
;
struct
cell_node
*
cell
=
NULL
;
Local
<
Value
>
argv
[
2
];
if
(
fetchall_req
->
error
!=
NULL
)
{
argv
[
0
]
=
Exception
::
Error
(
String
::
New
(
fetchall_req
->
error
));
argv
[
1
]
=
Local
<
Value
>::
New
(
Undefined
());
}
else
{
argv
[
0
]
=
Local
<
Value
>::
New
(
Undefined
());
Persistent
<
Array
>
results
=
Persistent
<
Array
>::
New
(
Array
::
New
());
for
(
int
row_count
=
0
;
cur
;
row_count
++
,
cur
=
cur
->
next
)
{
Local
<
Object
>
row
=
Object
::
New
();
cell
=
cur
->
cells
;
// walk down the list
for
(
int
i
=
0
;
cell
;
i
++
,
cell
=
cell
->
next
)
{
assert
(
cell
);
if
(
cell
->
type
!=
SQLITE_NULL
)
{
assert
((
void
*
)
cell
->
value
);
}
assert
(
sto
->
column_names_
[
i
]);
switch
(
cell
->
type
)
{
case
SQLITE_INTEGER
:
row
->
Set
(
String
::
NewSymbol
((
char
*
)
sto
->
column_names_
[
i
]),
Int32
::
New
(
*
(
int
*
)
(
cell
->
value
)));
break
;
case
SQLITE_FLOAT
:
row
->
Set
(
String
::
NewSymbol
(
sto
->
column_names_
[
i
]),
Number
::
New
(
*
(
double
*
)
(
cell
->
value
)));
break
;
case
SQLITE_TEXT
:
{
struct
string_t
*
str
=
(
struct
string_t
*
)
(
cell
->
value
);
// str->bytes-1 to compensate for the NULL terminator
row
->
Set
(
String
::
NewSymbol
(
sto
->
column_names_
[
i
]),
String
::
New
(
str
->
data
,
str
->
bytes
-
1
));
}
break
;
case
SQLITE_NULL
:
row
->
Set
(
String
::
New
(
sto
->
column_names_
[
i
]),
Local
<
Value
>::
New
(
Null
()));
break
;
}
}
results
->
Set
(
Integer
::
New
(
row_count
),
row
);
}
argv
[
1
]
=
Local
<
Value
>::
New
(
results
);
}
// unsigned int page_size_p;
// unsigned long num_alloced_p;
// unsigned long user_alloced_p;
// unsigned long max_alloced_p;
// unsigned long tot_alloced_p;
// mpool_stats(fetchall_req->pool,
// &page_size_p,
// &num_alloced_p,
// &user_alloced_p,
// &max_alloced_p,
// &tot_alloced_p);
// printf(
// "%u page size\n"
// "%lu num allocated\n"
// "%lu user allocated\n"
// "%lu max allocated\n"
// "%lu total allocated\n",
// page_size_p,
// num_alloced_p,
// user_alloced_p,
// max_alloced_p,
// tot_alloced_p
// );
if
(
fetchall_req
->
pool
)
{
int
ret
=
mpool_close
(
fetchall_req
->
pool
);
if
(
ret
!=
MPOOL_ERROR_NONE
)
{
req
->
result
=
-
1
;
argv
[
0
]
=
Exception
::
Error
(
String
::
New
(
mpool_strerror
(
ret
)));
argv
[
1
]
=
Local
<
Value
>::
New
(
Undefined
());
}
}
TryCatch
try_catch
;
fetchall_req
->
cb
->
Call
(
Context
::
GetCurrent
()
->
Global
(),
2
,
argv
);
if
(
try_catch
.
HasCaught
())
{
FatalException
(
try_catch
);
}
fetchall_req
->
cb
.
Dispose
();
sto
->
FreeColumnData
();
free
(
fetchall_req
);
return
0
;
}
int
Statement
::
EIO_FetchAll
(
eio_req
*
req
)
{
struct
fetchall_request
*
fetchall_req
=
(
struct
fetchall_request
*
)(
req
->
data
);
Statement
*
sto
=
fetchall_req
->
sto
;
sqlite3_stmt
*
stmt
=
sto
->
stmt_
;
assert
(
stmt
);
int
ret
;
int
rc
=
sqlite3_step
(
stmt
);
if
(
rc
!=
SQLITE_DONE
)
{
/* open the pool */
fetchall_req
->
pool
=
mpool_open
(
MPOOL_FLAG_USE_MAP_ANON
,
0
,
NULL
,
&
ret
);
if
(
fetchall_req
->
pool
==
NULL
)
{
req
->
result
=
-
1
;
fetchall_req
->
rows
=
NULL
;
fetchall_req
->
error
=
(
char
*
)
mpool_strerror
(
ret
);
return
0
;
}
}
else
{
fetchall_req
->
pool
=
NULL
;
fetchall_req
->
rows
=
NULL
;
}
// We're going to be traversing a linked list in two dimensions.
struct
row_node
*
cur
=
NULL
,
*
prev
=
NULL
,
*
head
=
NULL
;
for
(;;
rc
=
sqlite3_step
(
stmt
))
{
if
(
rc
!=
SQLITE_ROW
)
break
;
if
(
!
sto
->
column_names_
)
{
sto
->
InitializeColumns
();
}
cur
=
(
struct
row_node
*
)
mpool_alloc
(
fetchall_req
->
pool
,
sizeof
(
struct
row_node
)
,
&
ret
);
cur
->
next
=
NULL
;
// If this is the first row, assign `cur` to `head` and `hold` head there
// since it was the first result. Otherwise set the `next` field on
// the`prev` pointer to attach the newly allocated element.
(
!
head
?
head
:
prev
->
next
)
=
cur
;
struct
cell_node
*
cell_head
=
NULL
,
*
cell_prev
=
NULL
,
*
cell
=
NULL
;
for
(
int
i
=
0
;
i
<
sto
->
column_count_
;
i
++
)
{
cell
=
(
struct
cell_node
*
)
mpool_alloc
(
fetchall_req
->
pool
,
sizeof
(
struct
cell_node
),
&
ret
);
// Same as above with the row linked list.
(
!
cell_head
?
cell_head
:
cell_prev
->
next
)
=
cell
;
cell
->
type
=
sqlite3_column_type
(
sto
->
stmt_
,
i
);
cell
->
next
=
NULL
;
// TODO: Cache column data in the fetchall req struct.
switch
(
cell
->
type
)
{
case
SQLITE_INTEGER
:
cell
->
value
=
(
int
*
)
mpool_alloc
(
fetchall_req
->
pool
,
sizeof
(
int
)
,
&
ret
);
*
(
int
*
)
cell
->
value
=
sqlite3_column_int
(
stmt
,
i
);
break
;
case
SQLITE_FLOAT
:
cell
->
value
=
(
double
*
)
mpool_alloc
(
fetchall_req
->
pool
,
sizeof
(
double
)
,
&
ret
);
*
(
double
*
)
cell
->
value
=
sqlite3_column_double
(
stmt
,
i
);
break
;
case
SQLITE_TEXT
:
{
char
*
text
=
(
char
*
)
sqlite3_column_text
(
stmt
,
i
);
int
size
=
1
+
sqlite3_column_bytes
(
stmt
,
i
);
struct
string_t
*
str
=
(
struct
string_t
*
)
mpool_alloc
(
fetchall_req
->
pool
,
sizeof
(
size_t
)
+
size
,
&
ret
);
str
->
bytes
=
size
;
memcpy
(
str
->
data
,
text
,
size
);
cell
->
value
=
str
;
}
break
;
case
SQLITE_NULL
:
cell
->
value
=
NULL
;
break
;
default
:
{
assert
(
0
&&
"unsupported type"
);
}
}
assert
(
sto
->
column_names_
[
i
]);
cell_prev
=
cell
;
}
cur
->
cells
=
cell_head
;
prev
=
cur
;
}
// Test for errors
if
(
rc
!=
SQLITE_DONE
)
{
const
char
*
error
=
sqlite3_errmsg
(
sqlite3_db_handle
(
sto
->
stmt_
));
if
(
error
==
NULL
)
{
error
=
"Unknown Error"
;
}
size_t
errorSize
=
sizeof
(
char
)
*
(
strlen
(
error
)
+
1
);
fetchall_req
->
error
=
(
char
*
)
mpool_alloc
(
fetchall_req
->
pool
,
errorSize
,
&
ret
);
if
(
fetchall_req
->
error
!=
NULL
)
{
memcpy
(
fetchall_req
->
error
,
error
,
errorSize
);
}
else
{
fetchall_req
->
error
=
(
char
*
)
mpool_strerror
(
ret
);
}
req
->
result
=
-
1
;
return
0
;
return
0
;
}
req
->
result
=
0
;
fetchall_req
->
rows
=
head
;
return
0
;
}
Handle
<
Value
>
Statement
::
FetchAll
(
const
Arguments
&
args
)
{
HandleScope
scope
;
REQUIRE_ARGUMENT_FUNCTION
(
0
,
cb
);
struct
fetchall_request
*
fetchall_req
=
(
struct
fetchall_request
*
)
calloc
(
1
,
sizeof
(
struct
fetchall_request
));
if
(
!
fetchall_req
)
{
V8
::
LowMemoryNotification
();
return
ThrowException
(
Exception
::
Error
(
String
::
New
(
"Could not allocate enough memory"
)));
}
fetchall_req
->
sto
=
ObjectWrap
::
Unwrap
<
Statement
>
(
args
.
This
());
fetchall_req
->
cb
=
Persistent
<
Function
>::
New
(
cb
);
eio_custom
(
EIO_FetchAll
,
EIO_PRI_DEFAULT
,
EIO_AfterFetchAll
,
fetchall_req
);
ev_ref
(
EV_DEFAULT_UC
);
return
Undefined
();
}
// The following three methods must be called inside a HandleScope
bool
Statement
::
HasCallback
()
{
return
!
handle_
->
GetHiddenValue
(
callback_sym
).
IsEmpty
();
}
void
Statement
::
SetCallback
(
Local
<
Function
>
cb
)
{
handle_
->
SetHiddenValue
(
callback_sym
,
cb
);
}
Local
<
Function
>
Statement
::
GetCallback
()
{
Local
<
Value
>
cb_v
=
handle_
->
GetHiddenValue
(
callback_sym
);
assert
(
cb_v
->
IsFunction
());
Local
<
Function
>
cb
=
Local
<
Function
>::
Cast
(
cb_v
);
handle_
->
DeleteHiddenValue
(
callback_sym
);
return
cb
;
}
}
src/statement.h
View file @
eaf72f23
...
@@ -18,139 +18,50 @@
...
@@ -18,139 +18,50 @@
#include <v8.h>
#include <v8.h>
#include <node.h>
#include <node.h>
#include <node_events.h>
#include <node_events.h>
#include <sqlite3.h>
#include <stdlib.h>
#include <node_buffer.h>
extern
"C"
{
#include "database.h"
#include <mpool.h>
};
#include <string>
#include <queue>
#include <sqlite3.h>
using
namespace
v8
;
using
namespace
v8
;
using
namespace
node
;
using
namespace
node
;
struct
cell_node
{
class
Statement
;
void
*
value
;
int
type
;
struct
cell_node
*
next
;
int
size
;
};
st
ruct
row_node
{
st
atic
struct
PrepareBaton
:
Baton
{
struct
cell_node
*
cells
;
Statement
*
stmt
;
struct
row_node
*
next
;
std
::
string
sql
;
};
};
// represent strings with this struct
struct
string_t
{
size_t
bytes
;
char
data
[];
};
class
Statement
:
public
EventEmitter
{
class
Statement
:
public
EventEmitter
{
public
:
public
:
static
Persistent
<
FunctionTemplate
>
constructor_template
;
static
Persistent
<
FunctionTemplate
>
constructor_template
;
static
void
Init
(
v8
::
Handle
<
Object
>
target
);
static
void
Init
(
Handle
<
Object
>
target
);
static
Handle
<
Value
>
New
(
const
Arguments
&
args
);
static
Handle
<
Value
>
New
(
const
Arguments
&
args
);
protected
:
Statement
(
Database
*
db_
)
:
EventEmitter
()
{
db
=
db_
;
Statement
(
sqlite3_stmt
*
stmt
,
int
first_rc
=
-
1
,
int
mode
=
0
)
db
->
Ref
();
:
EventEmitter
(),
first_rc_
(
first_rc
),
mode_
(
mode
),
stmt_
(
stmt
)
{
column_count_
=
-
1
;
column_names_
=
NULL
;
}
}
~
Statement
()
{
~
Statement
()
{
if
(
stmt_
)
sqlite3_finalize
(
stmt_
);
db
->
Unref
();
if
(
column_names_
)
FreeColumnData
();
}
}
static
Handle
<
Value
>
Bind
(
const
Arguments
&
args
);
protected
:
static
Handle
<
Value
>
BindObject
(
const
Arguments
&
args
);
static
void
EIO_BeginPrepare
(
Baton
*
baton
);
static
Handle
<
Value
>
BindArray
(
const
Arguments
&
args
);
static
int
EIO_Prepare
(
eio_req
*
req
);
static
int
EIO_BindArray
(
eio_req
*
req
);
static
int
EIO_AfterPrepare
(
eio_req
*
req
);
static
int
EIO_AfterBindArray
(
eio_req
*
req
);
static
int
EIO_AfterFinalize
(
eio_req
*
req
);
static
int
EIO_Finalize
(
eio_req
*
req
);
static
Handle
<
Value
>
Finalize
(
const
Arguments
&
args
);
static
Handle
<
Value
>
Reset
(
const
Arguments
&
args
);
static
Handle
<
Value
>
ClearBindings
(
const
Arguments
&
args
);
static
int
EIO_AfterStep
(
eio_req
*
req
);
static
int
EIO_Step
(
eio_req
*
req
);
static
Handle
<
Value
>
Step
(
const
Arguments
&
args
);
static
int
EIO_AfterFetchAll
(
eio_req
*
req
);
static
int
EIO_FetchAll
(
eio_req
*
req
);
static
Handle
<
Value
>
FetchAll
(
const
Arguments
&
args
);
void
InitializeColumns
(
void
);
void
FreeColumnData
(
void
);
bool
HasCallback
();
void
SetCallback
(
Local
<
Function
>
cb
);
Local
<
Function
>
GetCallback
();
private
:
Database
*
db
;
private
:
int
column_count_
;
char
**
column_names_
;
int
error_
;
Local
<
String
>
error_msg_
;
int
first_rc_
;
int
mode_
;
sqlite3_stmt
*
stmt_
;
// for statment.step
cell_node
*
cells
;
};
// indicates the key type (integer index or name string)
enum
BindKeyType
{
KEY_INT
,
KEY_STRING
};
// indicate the parameter type
enum
BindValueType
{
VALUE_INT
,
VALUE_DOUBLE
,
VALUE_BLOB
,
VALUE_STRING
,
VALUE_NULL
};
struct
bind_request
{
Persistent
<
Function
>
cb
;
Statement
*
sto
;
struct
bind_pair
*
pairs
;
size_t
len
;
};
struct
bind_pair
{
enum
BindKeyType
key_type
;
enum
BindValueType
value_type
;
void
*
key
;
// char * | int *
void
*
value
;
// char * | int * | double * | 0
size_t
value_size
;
};
struct
fetchall_request
{
sqlite3_stmt
*
handle
;
Persistent
<
Function
>
cb
;
Statement
*
sto
;
mpool_t
*
pool
;
char
*
error
;
struct
row_node
*
rows
;
};
};
#endif
#endif
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment