diff --git a/src/database.cc b/src/database.cc index deebab58c..5ccbca74a 100644 --- a/src/database.cc +++ b/src/database.cc @@ -371,6 +371,22 @@ Napi::Value Database::Configure(const Napi::CallbackInfo& info) { baton->status = info[1].As().Int32Value(); db->Schedule(SetBusyTimeout, baton); } + else if (info[0].StrictEquals( Napi::String::New(env, "limit"))) { + REQUIRE_ARGUMENTS(3); + if (!info[1].IsNumber()) { + Napi::TypeError::New(env, "limit id must be an integer").ThrowAsJavaScriptException(); + return env.Null(); + } + if (!info[2].IsNumber()) { + Napi::TypeError::New(env, "limit value must be an integer").ThrowAsJavaScriptException(); + return env.Null(); + } + Napi::Function handle; + int id = info[1].As().Int32Value(); + int value = info[2].As().Int32Value(); + Baton* baton = new LimitBaton(db, handle, id, value); + db->Schedule(SetLimit, baton); + } else { Napi::TypeError::New(env, (StringConcat( #if V8_MAJOR_VERSION > 6 @@ -415,6 +431,15 @@ void Database::SetBusyTimeout(Baton* b) { sqlite3_busy_timeout(baton->db->_handle, baton->status); } +void Database::SetLimit(Baton* b) { + std::unique_ptr baton(static_cast(b)); + + assert(baton->db->open); + assert(baton->db->_handle); + + sqlite3_limit(baton->db->_handle, baton->id, baton->value); +} + void Database::RegisterTraceCallback(Baton* b) { std::unique_ptr baton(b); assert(baton->db->open); diff --git a/src/database.h b/src/database.h index ba8785fff..420b5dd1c 100644 --- a/src/database.h +++ b/src/database.h @@ -80,6 +80,13 @@ class Database : public Napi::ObjectWrap { Baton(db_, cb_), filename(filename_) {} }; + struct LimitBaton : Baton { + int id; + int value; + LimitBaton(Database* db_, Napi::Function cb_, int id_, int value_) : + Baton(db_, cb_), id(id_), value(value_) {} + }; + typedef void (*Work_Callback)(Baton* baton); struct Call { @@ -169,6 +176,7 @@ class Database : public Napi::ObjectWrap { Napi::Value Interrupt(const Napi::CallbackInfo& info); static void SetBusyTimeout(Baton* baton); + static void SetLimit(Baton* baton); static void RegisterTraceCallback(Baton* baton); static void TraceCallback(void* db, const char* sql); diff --git a/src/node_sqlite3.cc b/src/node_sqlite3.cc index b101b451f..6f47a68a8 100644 --- a/src/node_sqlite3.cc +++ b/src/node_sqlite3.cc @@ -61,6 +61,19 @@ Napi::Object RegisterModule(Napi::Env env, Napi::Object exports) { DEFINE_CONSTANT_INTEGER(exports, SQLITE_FORMAT, FORMAT) DEFINE_CONSTANT_INTEGER(exports, SQLITE_RANGE, RANGE) DEFINE_CONSTANT_INTEGER(exports, SQLITE_NOTADB, NOTADB) + + DEFINE_CONSTANT_INTEGER(exports, SQLITE_LIMIT_LENGTH, LIMIT_LENGTH) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_LIMIT_SQL_LENGTH, LIMIT_SQL_LENGTH) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_LIMIT_COLUMN, LIMIT_COLUMN) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_LIMIT_EXPR_DEPTH, LIMIT_EXPR_DEPTH) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_LIMIT_COMPOUND_SELECT, LIMIT_COMPOUND_SELECT) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_LIMIT_VDBE_OP, LIMIT_VDBE_OP) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_LIMIT_FUNCTION_ARG, LIMIT_FUNCTION_ARG) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_LIMIT_ATTACHED, LIMIT_ATTACHED) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_LIMIT_LIKE_PATTERN_LENGTH, LIMIT_LIKE_PATTERN_LENGTH) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_LIMIT_VARIABLE_NUMBER, LIMIT_VARIABLE_NUMBER) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_LIMIT_TRIGGER_DEPTH, LIMIT_TRIGGER_DEPTH) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_LIMIT_WORKER_THREADS, LIMIT_WORKER_THREADS) }); return exports; diff --git a/test/limit.test.js b/test/limit.test.js new file mode 100644 index 000000000..d79f2c8df --- /dev/null +++ b/test/limit.test.js @@ -0,0 +1,28 @@ +var sqlite3 = require('..'); + +describe('limit', function() { + var db; + + before(function(done) { + db = new sqlite3.Database(':memory:', done); + }); + + it('should support applying limits via configure', function(done) { + db.configure('limit', sqlite3.LIMIT_ATTACHED, 0); + db.exec("ATTACH 'test/support/prepare.db' AS zing", function(err) { + if (!err) { + throw new Error('ATTACH should not succeed'); + } + if (err.errno === sqlite3.ERROR && + err.message === 'SQLITE_ERROR: too many attached databases - max 0') { + db.configure('limit', sqlite3.LIMIT_ATTACHED, 1); + db.exec("ATTACH 'test/support/prepare.db' AS zing", function(err) { + if (err) throw err; + db.close(done); + }); + } else { + throw err; + } + }); + }); +});