Commit aefb3cc4 by Orlando Vazquez

Use a linked list for `statement.step`

parent d8e7c186
...@@ -479,7 +479,7 @@ int Statement::EIO_AfterStep(eio_req *req) { ...@@ -479,7 +479,7 @@ int Statement::EIO_AfterStep(eio_req *req) {
HandleScope scope; HandleScope scope;
Statement *sto = (class Statement *)(req->data); Statement *sto = (class Statement *) req->data;
sqlite3* db = sqlite3_db_handle(sto->stmt_); sqlite3* db = sqlite3_db_handle(sto->stmt_);
Local<Value> argv[2]; Local<Value> argv[2];
...@@ -498,39 +498,42 @@ int Statement::EIO_AfterStep(eio_req *req) { ...@@ -498,39 +498,42 @@ int Statement::EIO_AfterStep(eio_req *req) {
else { else {
Local<Object> row = Object::New(); Local<Object> row = Object::New();
for (int i = 0; i < sto->column_count_; i++) { struct cell_node *cell = sto->cells
assert(sto->column_data_); , *next = NULL;
if (((int*)sto->column_types_)[i] != SQLITE_NULL)
assert(((void**)sto->column_data_)[i]);
assert(sto->column_names_[i]);
assert(sto->column_types_[i]);
switch (sto->column_types_[i]) { for (int i = 0; cell; i++) {
switch (cell->type) {
case SQLITE_INTEGER: case SQLITE_INTEGER:
row->Set(String::NewSymbol((char*) sto->column_names_[i]), row->Set(String::NewSymbol((char*) sto->column_names_[i]),
Int32::New(*(int*) (sto->column_data_[i]))); Int32::New(*(int*) cell->value));
free(cell->value);
break; break;
case SQLITE_FLOAT: case SQLITE_FLOAT:
row->Set(String::NewSymbol(sto->column_names_[i]), row->Set(String::NewSymbol(sto->column_names_[i]),
Number::New(*(double*) (sto->column_data_[i]))); Number::New(*(double*) cell->value));
free(cell->value);
break; break;
case SQLITE_TEXT: 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]), row->Set(String::NewSymbol(sto->column_names_[i]),
String::New((char *) (sto->column_data_[i]))); String::New(str->data, str->bytes-1));
// don't free this pointer, it's owned by sqlite3 free(str);
}
break; break;
case SQLITE_NULL: case SQLITE_NULL:
row->Set(String::New(sto->column_names_[i]), row->Set(String::New(sto->column_names_[i]),
Local<Value>::New(Null())); Local<Value>::New(Null()));
break; break;
// no default
} }
next = cell->next;
free(cell);
cell=next;
} }
argv[1] = row; argv[1] = row;
} }
...@@ -563,63 +566,18 @@ int Statement::EIO_AfterStep(eio_req *req) { ...@@ -563,63 +566,18 @@ int Statement::EIO_AfterStep(eio_req *req) {
void Statement::FreeColumnData(void) { void Statement::FreeColumnData(void) {
if (!column_count_) return; if (!column_count_) return;
for (int i = 0; i < column_count_; i++) {
switch (column_types_[i]) {
case SQLITE_INTEGER:
free((int *)column_data_[i]);
break;
case SQLITE_FLOAT:
free((double *) column_data_[i]);
break;
}
column_data_[i] = NULL;
}
free(column_names_); free(column_names_);
free(column_types_);
free(column_data_);
column_count_ = 0; column_count_ = 0;
column_types_ = NULL;
column_names_ = NULL; column_names_ = NULL;
column_data_ = NULL;
} }
void Statement::InitializeColumns(void) { void Statement::InitializeColumns(void) {
this->column_count_ = sqlite3_column_count(this->stmt_); this->column_count_ = sqlite3_column_count(this->stmt_);
assert(this->column_count_); this->column_names_ = (char **) calloc(this->column_count_, sizeof(char *));
this->column_types_ = (int *) calloc(this->column_count_, sizeof(int));
this->column_names_ = (char **) calloc(this->column_count_,
sizeof(char *));
if (this->column_count_) {
this->column_data_ = (void **) calloc(this->column_count_,
sizeof(void *));
}
for (int i = 0; i < this->column_count_; i++) { for (int i = 0; i < this->column_count_; i++) {
this->column_types_[i] = sqlite3_column_type(this->stmt_, i);
// Don't free this! // Don't free this!
this->column_names_[i] = (char *) sqlite3_column_name(this->stmt_, i); this->column_names_[i] = (char *) sqlite3_column_name(this->stmt_, i);
switch(this->column_types_[i]) {
case SQLITE_INTEGER:
this->column_data_[i] = (int *) malloc(sizeof(int));
break;
case SQLITE_FLOAT:
this->column_data_[i] = (double *) malloc(sizeof(double));
break;
case SQLITE_NULL:
this->column_data_[i] = NULL;
break;
// no need to allocate memory for strings
default: {
// unsupported type
}
}
} }
} }
...@@ -645,55 +603,65 @@ int Statement::EIO_Step(eio_req *req) { ...@@ -645,55 +603,65 @@ int Statement::EIO_Step(eio_req *req) {
sto->error_ = false; sto->error_ = false;
if (rc == SQLITE_ROW) { if (rc == SQLITE_ROW) {
// If these pointers are NULL, look up and store the number of columns // If this pointer is NULL, look up and store the columns names.
// their names and types. if (!sto->column_names_) {
// Otherwise that means we have already looked up the column types and
// names so we can simply re-use that info.
if (!sto->column_types_ && !sto->column_names_) {
sto->InitializeColumns(); sto->InitializeColumns();
} }
assert(sto->column_types_ && sto->column_data_ && sto->column_names_); struct cell_node *cell_head = NULL
, *cell_prev = NULL
, *cell = NULL;
for (int i = 0; i < sto->column_count_; i++) { for (int i = 0; i < sto->column_count_; i++) {
int type = sto->column_types_[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(type) { switch (cell->type) {
case SQLITE_INTEGER: case SQLITE_INTEGER:
*(int*)(sto->column_data_[i]) = sqlite3_column_int(stmt, i); cell->value = (int *) malloc(sizeof(int));
assert(sto->column_data_[i]); * (int *) cell->value = sqlite3_column_int(stmt, i);
break; break;
case SQLITE_FLOAT: case SQLITE_FLOAT:
*(double*)(sto->column_data_[i]) = sqlite3_column_double(stmt, i); cell->value = (double *) malloc(sizeof(double));
* (double *) cell->value = sqlite3_column_double(stmt, i);
break; break;
case SQLITE_TEXT: { case SQLITE_TEXT: {
// It shouldn't be necessary to copy or free() this value, char *text = (char *) sqlite3_column_text(stmt, i);
// according to http://www.sqlite.org/c3ref/column_blob.html int size = 1+sqlite3_column_bytes(stmt, i);
// it will be reclaimed on the next step, reset, or finalize. struct string_t *str = (struct string_t *)
// I'm going to assume it's okay to keep this pointer around malloc(sizeof(size_t) + size);
// until it is used in `EIO_AfterStep` str->bytes = size;
sto->column_data_[i] = (char *) sqlite3_column_text(stmt, i); memcpy(str->data, text, size);
cell->value = str;
} }
break; break;
case SQLITE_NULL: case SQLITE_NULL:
sto->column_data_[i] = NULL; cell->value = NULL;
break; break;
default: { default: {
assert(0 && "unsupported type"); assert(0 && "unsupported type");
} }
if (cell->type != SQLITE_NULL) {
assert(cell->value);
} }
if (sto->column_types_[i] != SQLITE_NULL)
assert(sto->column_data_[i]);
assert(sto->column_names_[i]);
assert(sto->column_types_[i]);
} }
assert(sto->column_data_);
cell_prev = cell;
}
sto->cells = cell_head;
assert(sto->column_names_); assert(sto->column_names_);
assert(sto->column_types_);
} }
else if (rc == SQLITE_DONE) { else if (rc == SQLITE_DONE) {
// nothing to do in this case // nothing to do in this case
...@@ -735,6 +703,7 @@ int Statement::EIO_AfterFetchAll(eio_req *req) { ...@@ -735,6 +703,7 @@ int Statement::EIO_AfterFetchAll(eio_req *req) {
Statement *sto = fetchall_req->sto; Statement *sto = fetchall_req->sto;
struct row_node *cur = fetchall_req->rows; struct row_node *cur = fetchall_req->rows;
struct cell_node *cell = NULL;
Local<Value> argv[2]; Local<Value> argv[2];
...@@ -750,7 +719,7 @@ int Statement::EIO_AfterFetchAll(eio_req *req) { ...@@ -750,7 +719,7 @@ int Statement::EIO_AfterFetchAll(eio_req *req) {
for (int row_count = 0; cur; row_count++, cur=cur->next) { for (int row_count = 0; cur; row_count++, cur=cur->next) {
Local<Object> row = Object::New(); Local<Object> row = Object::New();
struct cell_node *cell = cur->cells; cell = cur->cells;
// walk down the list // walk down the list
for (int i = 0; cell; i++, cell=cell->next) { for (int i = 0; cell; i++, cell=cell->next) {
...@@ -881,54 +850,49 @@ int Statement::EIO_FetchAll(eio_req *req) { ...@@ -881,54 +850,49 @@ int Statement::EIO_FetchAll(eio_req *req) {
, &ret); , &ret);
cur->next = NULL; cur->next = NULL;
if (!head) { // If this is the first row, set head to cur and hold it there since it
head = cur; // was the first result. Otherwise set the `next` field on the `prev`
} // pointer to attach the newly allocated element.
else { (!head ? head : prev->next) = cur;
prev->next = cur;
}
struct cell_node *cell_head = NULL struct cell_node *cell_head = NULL
, *cell_prev = NULL , *cell_prev = NULL
, *cell = NULL; , *cell = NULL;
for (int i = 0; i < sto->column_count_; i++) { for (int i = 0; i < sto->column_count_; i++) {
cell = (struct cell_node *) mpool_alloc(fetchall_req->pool cell = (struct cell_node *)
, sizeof(struct cell_node) mpool_alloc(fetchall_req->pool, sizeof(struct cell_node), &ret);
, &ret);
if (!cell_head) { // If this is the first cell, set cell_head to cell and hold it there
cell_head = cell; // since it was the first result. Otherwise set the `next` field on the
} // `prev` pointer to attach the newly allocated element.
else { (!cell_head ? cell_head : cell_prev->next) = cell;
cell_prev->next = cell;
}
cell->type = sqlite3_column_type(sto->stmt_, i); cell->type = sqlite3_column_type(sto->stmt_, i);
cell->next = NULL; cell->next = NULL;
switch (cell->type) { switch (cell->type) {
case SQLITE_INTEGER: case SQLITE_INTEGER:
cell->value = (int *) mpool_alloc(fetchall_req->pool cell->value = (int *)
, sizeof(int) mpool_alloc(fetchall_req->pool , sizeof(int) , &ret);
, &ret);
* (int *) cell->value = sqlite3_column_int(stmt, i); * (int *) cell->value = sqlite3_column_int(stmt, i);
break; break;
case SQLITE_FLOAT: case SQLITE_FLOAT:
cell->value = (int *) mpool_alloc(fetchall_req->pool cell->value = (double *)
, sizeof(int) mpool_alloc(fetchall_req->pool , sizeof(double) , &ret);
, &ret);
* (double *) cell->value = sqlite3_column_double(stmt, i); * (double *) cell->value = sqlite3_column_double(stmt, i);
break; break;
case SQLITE_TEXT: { case SQLITE_TEXT: {
char *text = (char *) sqlite3_column_text(stmt, i); char *text = (char *) sqlite3_column_text(stmt, i);
int size = 1+sqlite3_column_bytes(stmt, i); int size = 1+sqlite3_column_bytes(stmt, i);
struct string_t *str = (struct string_t *) struct string_t *str = (struct string_t *)
mpool_alloc(fetchall_req->pool mpool_alloc(fetchall_req->pool, sizeof(size_t) + size, &ret);
, sizeof(size_t) + size-1
, &ret);
str->bytes = size; str->bytes = size;
memcpy(str->data, text, size); memcpy(str->data, text, size);
cell->value = str; cell->value = str;
......
...@@ -30,6 +30,24 @@ extern "C" { ...@@ -30,6 +30,24 @@ extern "C" {
using namespace v8; using namespace v8;
using namespace node; using namespace node;
struct cell_node {
void *value;
int type;
struct cell_node *next;
};
struct row_node {
struct cell_node *cells;
struct row_node *next;
};
// represent strings with this struct
struct string_t {
size_t bytes;
char data[];
};
class Statement : public EventEmitter { class Statement : public EventEmitter {
public: public:
...@@ -43,16 +61,12 @@ class Statement : public EventEmitter { ...@@ -43,16 +61,12 @@ class Statement : public EventEmitter {
Statement(sqlite3_stmt* stmt, int first_rc = -1, int mode = 0) Statement(sqlite3_stmt* stmt, int first_rc = -1, int mode = 0)
: EventEmitter(), first_rc_(first_rc), mode_(mode), stmt_(stmt) { : EventEmitter(), first_rc_(first_rc), mode_(mode), stmt_(stmt) {
column_count_ = -1; column_count_ = -1;
column_types_ = NULL;
column_names_ = NULL; column_names_ = NULL;
column_data_ = NULL;
} }
~Statement() { ~Statement() {
if (stmt_) sqlite3_finalize(stmt_); if (stmt_) sqlite3_finalize(stmt_);
if (column_types_) free(column_types_); if (column_names_) FreeColumnData();
if (column_names_) free(column_names_);
if (column_data_) FreeColumnData();
} }
static Handle<Value> Bind(const Arguments &args); static Handle<Value> Bind(const Arguments &args);
...@@ -87,14 +101,15 @@ class Statement : public EventEmitter { ...@@ -87,14 +101,15 @@ class Statement : public EventEmitter {
private: private:
int column_count_; int column_count_;
int *column_types_;
char **column_names_; char **column_names_;
void **column_data_;
bool error_; bool error_;
int first_rc_; int first_rc_;
int mode_; int mode_;
sqlite3_stmt* stmt_; sqlite3_stmt* stmt_;
// for statment.step
cell_node *cells;
}; };
// indicates the key type (integer index or name string) // indicates the key type (integer index or name string)
...@@ -128,20 +143,6 @@ struct bind_pair { ...@@ -128,20 +143,6 @@ struct bind_pair {
size_t value_size; size_t value_size;
}; };
// Results will stored in a multi-dimensional linked list.
// That is, a linked list (rows) of linked lists (row values)
// Results are composed of rows. Rows are composed of cells.
struct cell_node {
void *value;
int type;
struct cell_node *next;
};
struct row_node {
struct cell_node *cells;
struct row_node *next;
};
struct fetchall_request { struct fetchall_request {
Persistent<Function> cb; Persistent<Function> cb;
Statement *sto; Statement *sto;
...@@ -150,10 +151,4 @@ struct fetchall_request { ...@@ -150,10 +151,4 @@ struct fetchall_request {
struct row_node *rows; struct row_node *rows;
}; };
// represent strings with this struct
struct string_t {
size_t bytes;
char data[];
};
#endif #endif
var fs = require("fs"), var fs = require("fs"),
sys = require("sys"), sys = require("sys"),
sqlite = require("../sqlite"); sqlite = require("sqlite");
var puts = sys.puts; var puts = sys.puts;
var inspect = sys.inspect; var inspect = sys.inspect;
......
...@@ -25,14 +25,14 @@ function createTestTable(db, callback) { ...@@ -25,14 +25,14 @@ function createTestTable(db, callback) {
var testRows = [ [ 1, "foo", 9 ] var testRows = [ [ 1, "foo", 9 ]
, [ 2, "bar", 8 ] , [ 2, "bar", 8 ]
, [ 3, "baz", 7 ] , [ 3, null, 7 ]
, [ 4, "quux", 6 ] , [ 4, "quux", 6 ]
, [ 5, "juju", 5 ] , [ 5, "juju", null ]
]; ];
var testRowsExpected = [ { id: 5, name: 'juju', age: 5 } var testRowsExpected = [ { id: 5, name: 'juju', age: null }
, { id: 4, name: 'quux', age: 6 } , { id: 4, name: 'quux', age: 6 }
, { id: 3, name: 'baz', age: 7 } , { id: 3, name: null, age: 7 }
, { id: 2, name: 'bar', age: 8 } , { id: 2, name: 'bar', age: 8 }
, { id: 1, name: 'foo', age: 9 } , { id: 1, name: 'foo', age: 9 }
]; ];
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment