Skip to content

Commit 1120956

Browse files
author
Joshua Wise
committed
- Rewrote Data and List modules
- Incorporated new modules into read-only query methods - Rewrote pragma method - Added limits to sqlite3 connections to prevent segfaults and data corruption
1 parent 5ec6ecb commit 1120956

File tree

12 files changed

+144
-273
lines changed

12 files changed

+144
-273
lines changed

binding.gyp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
},
1919
"sources": [
2020
"src/objects/int64/int64.cc",
21+
"src/util/data.cc",
2122
"src/objects/database/database.cc",
2223
"src/objects/statement/statement.cc",
2324
"src/objects/transaction/transaction.cc",

src/objects/database/pragma.cc

Lines changed: 26 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,28 @@
11
// .pragma(string sql, [boolean simpleResult]) -> array
22

3-
Data::Row* PragmaMakeRowOfStrings(char** strings, int len) {
4-
Data::Row* row = new Data::Row();
5-
row->column_count = len;
6-
row->values = new Data::Value* [len];
7-
for (int i=0; i<len; ++i) {
8-
row->values[i] = new Data::Text(Nan::New(strings[i]).ToLocalChecked());
9-
}
10-
return row;
11-
}
3+
typedef struct PragmaInfo {
4+
v8::Local<v8::Value> rows;
5+
bool simple;
6+
bool after_first;
7+
} PragmaInfo;
128

139
int PragmaCallback(void* x, int column_count, char** results, char** column_names) {
14-
List<Data::Row>* table = static_cast<List<Data::Row>*>(x);
15-
table[0].Add(PragmaMakeRowOfStrings(column_names, column_count));
16-
table[1].Add(PragmaMakeRowOfStrings(results, column_count));
10+
PragmaInfo* pragma_info = static_cast<PragmaInfo*>(x);
11+
12+
if (pragma_info->simple) {
13+
if (!pragma_info->after_first) {
14+
pragma_info->after_first = true;
15+
pragma_info->rows = Nan::New(results[0]).ToLocalChecked();
16+
}
17+
} else {
18+
v8::Local<v8::Object> row = Nan::New<v8::Object>();
19+
for (int i=0; i<column_count; ++i) {
20+
Nan::Set(row, Nan::New(column_names[i]).ToLocalChecked(), Nan::New(results[i]).ToLocalChecked());
21+
}
22+
v8::Local<v8::Array> rows = v8::Local<v8::Array>::Cast(pragma_info->rows);
23+
Nan::Set(rows, rows->Length(), row);
24+
}
25+
1726
return 0;
1827
}
1928

@@ -34,35 +43,18 @@ NAN_METHOD(Database::Pragma) {
3443
char* err;
3544

3645
// Executes the SQL on the database handle.
37-
List<Data::Row> table[2] {List<Data::Row>{}, List<Data::Row>{}};
38-
sqlite3_exec(db->db_handle, *utf8, PragmaCallback, table, &err);
46+
PragmaInfo pragma_info = {Nan::New<v8::Array>(), simple_result, false};
47+
sqlite3_exec(db->db_handle, *utf8, PragmaCallback, &pragma_info, &err);
3948
if (err != NULL) {
4049
CONCAT2(message, "SQLite: ", err);
4150
sqlite3_free(err);
4251
return Nan::ThrowError(message);
4352
}
4453
sqlite3_free(err);
4554

46-
if (simple_result) {
47-
Data::Row* values = table[1].Shift();
48-
if (values == NULL) {
49-
info.GetReturnValue().Set(Nan::Undefined());
50-
} else {
51-
info.GetReturnValue().Set(values->values[0]->ToJS());
52-
}
53-
delete values;
55+
if (simple_result && !pragma_info.after_first) {
56+
info.GetReturnValue().Set(Nan::Undefined());
5457
} else {
55-
unsigned int i = 0;
56-
v8::Local<v8::Array> arr = Nan::New<v8::Array>();
57-
table[0].Flush([&arr, &i, &table] (Data::Row* keys) {
58-
Data::Row* values = table[1].Shift();
59-
v8::Local<v8::Object> object = Nan::New<v8::Object>();
60-
for (int j=0; j<keys->column_count; ++j) {
61-
Nan::Set(object, v8::Local<v8::String>::Cast(keys->values[j]->ToJS()), values->values[j]->ToJS());
62-
}
63-
Nan::Set(arr, i++, object);
64-
delete values;
65-
});
66-
info.GetReturnValue().Set(arr);
58+
info.GetReturnValue().Set(pragma_info.rows);
6759
}
6860
}

src/objects/statement/all.cc

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,35 +8,32 @@ NAN_METHOD(Statement::All) {
88
QUERY_START(stmt, statement, STATEMENT_BIND, info, info.Length());
99

1010
unsigned int row_count = 0;
11-
List<Data::Row> rows;
11+
List<v8::Local<v8::Value>> rows;
1212

13-
while (sqlite3_step(stmt->st_handle) == SQLITE_ROW) {
14-
++row_count;
15-
rows.Add(new Data::Row(stmt->st_handle, stmt->column_count));
13+
// Get result rows or plucked columns.
14+
if (stmt->state & PLUCK_COLUMN) {
15+
while (sqlite3_step(stmt->st_handle) == SQLITE_ROW) {
16+
++row_count;
17+
rows.Add(Data::GetValueJS(stmt->st_handle, 0));
18+
}
19+
} else {
20+
while (sqlite3_step(stmt->st_handle) == SQLITE_ROW) {
21+
++row_count;
22+
rows.Add(Data::GetRowJS(stmt->st_handle, stmt->column_count));
23+
}
1624
}
25+
26+
// Transfer the result list into a JavaScript array.
1727
if (sqlite3_reset(stmt->st_handle) == SQLITE_OK) {
18-
v8::Local<v8::Array> returned_array = Nan::New<v8::Array>(row_count);
28+
v8::Local<v8::Array> returnedArray = Nan::New<v8::Array>(row_count);
1929

20-
// Get array of result rows or plucked columns.
2130
if (row_count > 0) {
2231
unsigned int i = 0;
23-
if (stmt->state & PLUCK_COLUMN) {
24-
// Fill array with plucked columns.
25-
rows.Flush([&returned_array, &i] (Data::Row* row) {
26-
Nan::Set(returned_array, i++, row->values[0]->ToJS());
27-
});
28-
} else {
29-
// Fill array with row objects.
30-
rows.Flush([&stmt, &returned_array, &i] (Data::Row* row) {
31-
v8::Local<v8::Object> object = Nan::New<v8::Object>();
32-
for (int j=0; j<row->column_count; ++j) {
33-
Nan::Set(object, NEW_INTERNAL_STRING16(sqlite3_column_name16(stmt->st_handle, j)), row->values[j]->ToJS());
34-
}
35-
Nan::Set(returned_array, i++, object);
36-
});
37-
}
32+
rows.Flush([&returnedArray, &i] (v8::Local<v8::Value> value) {
33+
Nan::Set(returnedArray, i++, value);
34+
});
3835
}
39-
QUERY_RETURN(stmt, STATEMENT_CLEAR_BINDINGS, returned_array);
36+
QUERY_RETURN(stmt, STATEMENT_CLEAR_BINDINGS, returnedArray);
4037
}
4138

4239
QUERY_THROW(stmt, STATEMENT_CLEAR_BINDINGS, sqlite3_errmsg(stmt->db->db_handle));

src/objects/statement/each.cc

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,16 @@ NAN_METHOD(Statement::Each) {
99
QUERY_START(stmt, statement, STATEMENT_BIND_T_BUFFERS, info, func_index);
1010
stmt->db->in_each = true;
1111

12-
1312
// Retrieve and feed rows.
1413
while (sqlite3_step(stmt->st_handle) == SQLITE_ROW) {
15-
Data::Row row(stmt->st_handle, stmt->column_count);
1614
v8::MaybeLocal<v8::Value> callback_return_value;
1715

18-
// Flush row to callback.
19-
if (stmt->state & PLUCK_COLUMN) {
20-
v8::Local<v8::Value> args[1] = {row.values[0]->ToJS()};
21-
callback_return_value = callback->Call(Nan::Null(), 1, args);
22-
} else {
23-
v8::Local<v8::Object> object = Nan::New<v8::Object>();
24-
for (int i=0; i<row.column_count; ++i) {
25-
Nan::Set(object, NEW_INTERNAL_STRING16(sqlite3_column_name16(stmt->st_handle, i)), row.values[i]->ToJS());
26-
}
27-
v8::Local<v8::Value> args[1] = {object};
28-
callback_return_value = callback->Call(Nan::Null(), 1, args);
29-
}
16+
// The pluck setting must be within the loop, because it could change in a callback.
17+
v8::Local<v8::Value> callbackValue = stmt->state & PLUCK_COLUMN
18+
? Data::GetValueJS(stmt->st_handle, 0)
19+
: Data::GetRowJS(stmt->st_handle, stmt->column_count);
20+
v8::Local<v8::Value> args[1] = {callbackValue};
21+
callback_return_value = callback->Call(Nan::Null(), 1, args);
3022

3123
// If an exception was thrown in the callback, clean up and stop.
3224
if (callback_return_value.IsEmpty()) {

src/objects/statement/get.cc

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,11 @@ NAN_METHOD(Statement::Get) {
99

1010
int status = sqlite3_step(stmt->st_handle);
1111
if (status == SQLITE_ROW) {
12-
Data::Row row(stmt->st_handle, stmt->column_count);
13-
v8::Local<v8::Value> returned_value;
14-
15-
// Get result row, or plucked column.
16-
if (stmt->state & PLUCK_COLUMN) {
17-
returned_value = row.values[0]->ToJS();
18-
} else {
19-
returned_value = Nan::New<v8::Object>();
20-
v8::Local<v8::Object> returned_object = v8::Local<v8::Object>::Cast(returned_value);
21-
for (int i=0; i<row.column_count; ++i) {
22-
Nan::Set(returned_object, NEW_INTERNAL_STRING16(sqlite3_column_name16(stmt->st_handle, i)), row.values[i]->ToJS());
23-
}
24-
}
25-
12+
v8::Local<v8::Value> returnedValue = stmt->state & PLUCK_COLUMN
13+
? Data::GetValueJS(stmt->st_handle, 0)
14+
: Data::GetRowJS(stmt->st_handle, stmt->column_count);
2615
sqlite3_reset(stmt->st_handle);
27-
QUERY_RETURN(stmt, STATEMENT_CLEAR_BINDINGS, returned_value);
16+
QUERY_RETURN(stmt, STATEMENT_CLEAR_BINDINGS, returnedValue);
2817
} else if (status == SQLITE_DONE) {
2918
sqlite3_reset(stmt->st_handle);
3019
QUERY_RETURN(stmt, STATEMENT_CLEAR_BINDINGS, Nan::Undefined());

src/objects/statement/pluck.cc

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ NAN_METHOD(Statement::Pluck) {
55
if (stmt->column_count == 0) {
66
return Nan::ThrowTypeError("The pluck() method can only be used by read-only statements.");
77
}
8-
if (stmt->state & CONFIG_LOCKED) {
9-
return Nan::ThrowTypeError("A statement's pluck setting cannot be altered after execution.");
10-
}
118

12-
stmt->column_count = 1;
13-
stmt->state |= PLUCK_COLUMN;
9+
if (info.Length() == 0 || info[0]->BooleanValue() == true) {
10+
stmt->state |= PLUCK_COLUMN;
11+
} else {
12+
stmt->state &= ~PLUCK_COLUMN;
13+
}
1414

1515
info.GetReturnValue().Set(info.This());
1616
}

src/objects/statement/run.cc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ NAN_METHOD(Statement::Run) {
1414
if (sqlite3_reset(stmt->st_handle) == SQLITE_OK) {
1515
int changes = sqlite3_total_changes(db_handle) == total_changes_before ? 0 : sqlite3_changes(db_handle);
1616
sqlite3_int64 id = sqlite3_last_insert_rowid(db_handle);
17-
v8::Local<v8::Object> returned_object = Nan::New<v8::Object>();
18-
Nan::Set(returned_object, NEW_INTERNAL_STRING_FAST("changes"), Nan::New<v8::Number>(static_cast<double>(changes)));
19-
Nan::Set(returned_object, NEW_INTERNAL_STRING_FAST("lastInsertROWID"), Int64::NewProperInteger(id));
20-
QUERY_RETURN(stmt, STATEMENT_CLEAR_BINDINGS, returned_object);
17+
v8::Local<v8::Object> returnedObject = Nan::New<v8::Object>();
18+
Nan::Set(returnedObject, NEW_INTERNAL_STRING_FAST("changes"), Nan::New<v8::Number>(static_cast<double>(changes)));
19+
Nan::Set(returnedObject, NEW_INTERNAL_STRING_FAST("lastInsertROWID"), Int64::NewProperInteger(id));
20+
QUERY_RETURN(stmt, STATEMENT_CLEAR_BINDINGS, returnedObject);
2121
}
2222

2323
QUERY_THROW(stmt, STATEMENT_CLEAR_BINDINGS, sqlite3_errmsg(stmt->db->db_handle));

src/objects/transaction/run.cc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ NAN_METHOD(Transaction::Run) {
4343

4444
// Return info object.
4545
sqlite3_int64 id = sqlite3_last_insert_rowid(db_handle);
46-
v8::Local<v8::Object> returned_object = Nan::New<v8::Object>();
47-
Nan::Set(returned_object, NEW_INTERNAL_STRING_FAST("changes"), Nan::New<v8::Number>(static_cast<double>(changes)));
48-
Nan::Set(returned_object, NEW_INTERNAL_STRING_FAST("lastInsertROWID"), Int64::NewProperInteger(id));
49-
QUERY_RETURN(trans, TRANSACTION_CLEAR_BINDINGS, returned_object);
46+
v8::Local<v8::Object> returnedObject = Nan::New<v8::Object>();
47+
Nan::Set(returnedObject, NEW_INTERNAL_STRING_FAST("changes"), Nan::New<v8::Number>(static_cast<double>(changes)));
48+
Nan::Set(returnedObject, NEW_INTERNAL_STRING_FAST("lastInsertROWID"), Int64::NewProperInteger(id));
49+
QUERY_RETURN(trans, TRANSACTION_CLEAR_BINDINGS, returnedObject);
5050
}

src/util/data.cc

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#include <stdint.h>
2+
#include <sqlite3.h>
3+
#include <nan.h>
4+
#include "../objects/int64/int64.h"
5+
#include "../util/macros.h"
6+
7+
namespace Data {
8+
9+
v8::Local<v8::Value> GetIntegerJS(sqlite3_stmt* handle, int column) {
10+
return Int64::NewProperInteger(sqlite3_column_int64(handle, column));
11+
}
12+
v8::Local<v8::Value> GetFloatJS(sqlite3_stmt* handle, int column) {
13+
return Nan::New<v8::Number>(sqlite3_column_double(handle, column));
14+
}
15+
v8::Local<v8::Value> GetTextJS(sqlite3_stmt* handle, int column) {
16+
const void* value = sqlite3_column_text16(handle, column);
17+
int byte_count = sqlite3_column_bytes16(handle, column);
18+
return v8::String::NewFromTwoByte(
19+
v8::Isolate::GetCurrent(),
20+
static_cast<const uint16_t*>(value),
21+
v8::NewStringType::kNormal,
22+
byte_count / sizeof (uint16_t)
23+
).ToLocalChecked();
24+
}
25+
v8::Local<v8::Value> GetBlobJS(sqlite3_stmt* handle, int column) {
26+
const void* value = sqlite3_column_blob(handle, column);
27+
int byte_count = sqlite3_column_bytes(handle, column);
28+
return Nan::CopyBuffer(
29+
static_cast<const char*>(value),
30+
byte_count
31+
).ToLocalChecked();
32+
}
33+
34+
v8::Local<v8::Value> GetValueJS(sqlite3_stmt* handle, int column) {
35+
int type = sqlite3_column_type(handle, column);
36+
switch (type) {
37+
case SQLITE_INTEGER:
38+
return Data::GetIntegerJS(handle, column);
39+
case SQLITE_FLOAT:
40+
return Data::GetFloatJS(handle, column);
41+
case SQLITE_TEXT:
42+
return Data::GetTextJS(handle, column);
43+
case SQLITE_BLOB:
44+
return Data::GetBlobJS(handle, column);
45+
default: // SQLITE_NULL
46+
return Nan::Null();
47+
}
48+
}
49+
50+
v8::Local<v8::Value> GetRowJS(sqlite3_stmt* handle, int column_count) {
51+
v8::Local<v8::Object> row = Nan::New<v8::Object>();
52+
for (int i=0; i<column_count; ++i) {
53+
Nan::Set(row, NEW_INTERNAL_STRING16(sqlite3_column_name16(handle, i)), Data::GetValueJS(handle, i));
54+
}
55+
return row;
56+
}
57+
58+
}

0 commit comments

Comments
 (0)