From 9d04876569bb51fdeeea9967efbd7e0201b56144 Mon Sep 17 00:00:00 2001 From: Arimah Date: Tue, 25 Apr 2023 08:26:25 +0200 Subject: [PATCH 1/3] Fix out-of-bounds read in statement tail parser --- src/better_sqlite3.cpp | 51 +++++++++++++++++++++------------------ src/better_sqlite3.hpp | 42 ++++++++++++++++---------------- src/objects/statement.lzz | 7 ++++-- 3 files changed, 53 insertions(+), 47 deletions(-) diff --git a/src/better_sqlite3.cpp b/src/better_sqlite3.cpp index b560b9d66..ae0c1bcca 100644 --- a/src/better_sqlite3.cpp +++ b/src/better_sqlite3.cpp @@ -859,8 +859,11 @@ void Statement::JS_new (v8::FunctionCallbackInfo const & info) if (handle == NULL) { return ThrowRangeError("The supplied SQL string contains no statements"); } - for (char c; (c = *tail); ++tail) { - if (IS_SKIPPED(c)) continue; + for (char c; (c = *tail); ) { + if (IS_SKIPPED(c)) { + ++tail; + continue; + } if (c == '/' && tail[1] == '*') { tail += 2; for (char c; (c = *tail); ++tail) { @@ -891,9 +894,9 @@ void Statement::JS_new (v8::FunctionCallbackInfo const & info) info.GetReturnValue().Set(info.This()); } -#line 149 "./src/objects/statement.lzz" +#line 152 "./src/objects/statement.lzz" void Statement::JS_run (v8::FunctionCallbackInfo const & info) -#line 149 "./src/objects/statement.lzz" +#line 152 "./src/objects/statement.lzz" { Statement * stmt = node :: ObjectWrap :: Unwrap < Statement > ( info . This ( ) ) ; ( ( void ) 0 ) ; sqlite3_stmt * handle = stmt -> handle ; Database * db = stmt -> db ; if ( ! db -> GetState ( ) -> open ) return ThrowTypeError ( "The database connection is not open" ) ; if ( db -> GetState ( ) -> busy ) return ThrowTypeError ( "This database connection is busy executing a query" ) ; if ( stmt -> locked ) return ThrowTypeError ( "This statement is busy executing a query" ) ; if ( ! db -> GetState ( ) -> unsafe_mode ) { if ( db -> GetState ( ) -> iterators ) return ThrowTypeError ( "This database connection is busy executing a query" ) ; } ( ( void ) 0 ) ; const bool bound = stmt -> bound ; if ( ! bound ) { Binder binder ( handle ) ; if ( ! binder . Bind ( info , info . Length ( ) , stmt ) ) { sqlite3_clear_bindings ( handle ) ; return ; } ( ( void ) 0 ) ; } else if ( info . Length ( ) > 0 ) { return ThrowTypeError ( "This statement already has bound parameters" ) ; } ( ( void ) 0 ) ; db -> GetState ( ) -> busy = true ; v8 :: Isolate * isolate = info . GetIsolate ( ) ; if ( db -> Log ( isolate , handle ) ) { db -> GetState ( ) -> busy = false ; db -> ThrowDatabaseError ( ) ; if ( ! bound ) { sqlite3_clear_bindings ( handle ) ; } return ; } ( ( void ) 0 ) ; sqlite3* db_handle = db->GetHandle(); @@ -916,9 +919,9 @@ void Statement::JS_run (v8::FunctionCallbackInfo const & info) } db -> GetState ( ) -> busy = false ; db -> ThrowDatabaseError ( ) ; if ( ! bound ) { sqlite3_clear_bindings ( handle ) ; } return ; } -#line 172 "./src/objects/statement.lzz" +#line 175 "./src/objects/statement.lzz" void Statement::JS_get (v8::FunctionCallbackInfo const & info) -#line 172 "./src/objects/statement.lzz" +#line 175 "./src/objects/statement.lzz" { Statement * stmt = node :: ObjectWrap :: Unwrap < Statement > ( info . This ( ) ) ; if ( ! stmt -> returns_data ) return ThrowTypeError ( "This statement does not return data. Use run() instead" ) ; sqlite3_stmt * handle = stmt -> handle ; Database * db = stmt -> db ; if ( ! db -> GetState ( ) -> open ) return ThrowTypeError ( "The database connection is not open" ) ; if ( db -> GetState ( ) -> busy ) return ThrowTypeError ( "This database connection is busy executing a query" ) ; if ( stmt -> locked ) return ThrowTypeError ( "This statement is busy executing a query" ) ; const bool bound = stmt -> bound ; if ( ! bound ) { Binder binder ( handle ) ; if ( ! binder . Bind ( info , info . Length ( ) , stmt ) ) { sqlite3_clear_bindings ( handle ) ; return ; } ( ( void ) 0 ) ; } else if ( info . Length ( ) > 0 ) { return ThrowTypeError ( "This statement already has bound parameters" ) ; } ( ( void ) 0 ) ; db -> GetState ( ) -> busy = true ; v8 :: Isolate * isolate = info . GetIsolate ( ) ; if ( db -> Log ( isolate , handle ) ) { db -> GetState ( ) -> busy = false ; db -> ThrowDatabaseError ( ) ; if ( ! bound ) { sqlite3_clear_bindings ( handle ) ; } return ; } ( ( void ) 0 ) ; int status = sqlite3_step(handle); @@ -933,9 +936,9 @@ void Statement::JS_get (v8::FunctionCallbackInfo const & info) sqlite3_reset(handle); db -> GetState ( ) -> busy = false ; db -> ThrowDatabaseError ( ) ; if ( ! bound ) { sqlite3_clear_bindings ( handle ) ; } return ; } -#line 187 "./src/objects/statement.lzz" +#line 190 "./src/objects/statement.lzz" void Statement::JS_all (v8::FunctionCallbackInfo const & info) -#line 187 "./src/objects/statement.lzz" +#line 190 "./src/objects/statement.lzz" { Statement * stmt = node :: ObjectWrap :: Unwrap < Statement > ( info . This ( ) ) ; if ( ! stmt -> returns_data ) return ThrowTypeError ( "This statement does not return data. Use run() instead" ) ; sqlite3_stmt * handle = stmt -> handle ; Database * db = stmt -> db ; if ( ! db -> GetState ( ) -> open ) return ThrowTypeError ( "The database connection is not open" ) ; if ( db -> GetState ( ) -> busy ) return ThrowTypeError ( "This database connection is busy executing a query" ) ; if ( stmt -> locked ) return ThrowTypeError ( "This statement is busy executing a query" ) ; const bool bound = stmt -> bound ; if ( ! bound ) { Binder binder ( handle ) ; if ( ! binder . Bind ( info , info . Length ( ) , stmt ) ) { sqlite3_clear_bindings ( handle ) ; return ; } ( ( void ) 0 ) ; } else if ( info . Length ( ) > 0 ) { return ThrowTypeError ( "This statement already has bound parameters" ) ; } ( ( void ) 0 ) ; db -> GetState ( ) -> busy = true ; v8 :: Isolate * isolate = info . GetIsolate ( ) ; if ( db -> Log ( isolate , handle ) ) { db -> GetState ( ) -> busy = false ; db -> ThrowDatabaseError ( ) ; if ( ! bound ) { sqlite3_clear_bindings ( handle ) ; } return ; } ( ( void ) 0 ) ; v8 :: Local < v8 :: Context > ctx = isolate -> GetCurrentContext ( ) ; @@ -956,9 +959,9 @@ void Statement::JS_all (v8::FunctionCallbackInfo const & info) if (js_error) db->GetState()->was_js_error = true; db -> GetState ( ) -> busy = false ; db -> ThrowDatabaseError ( ) ; if ( ! bound ) { sqlite3_clear_bindings ( handle ) ; } return ; } -#line 208 "./src/objects/statement.lzz" +#line 211 "./src/objects/statement.lzz" void Statement::JS_iterate (v8::FunctionCallbackInfo const & info) -#line 208 "./src/objects/statement.lzz" +#line 211 "./src/objects/statement.lzz" { Addon * addon = static_cast < Addon * > ( info . Data ( ) . As < v8 :: External > ( ) -> Value ( ) ) ; v8 :: Isolate * isolate = info . GetIsolate ( ) ; @@ -968,9 +971,9 @@ void Statement::JS_iterate (v8::FunctionCallbackInfo const & info) addon->privileged_info = NULL; if (!maybeIterator.IsEmpty()) info.GetReturnValue().Set(maybeIterator.ToLocalChecked()); } -#line 218 "./src/objects/statement.lzz" +#line 221 "./src/objects/statement.lzz" void Statement::JS_bind (v8::FunctionCallbackInfo const & info) -#line 218 "./src/objects/statement.lzz" +#line 221 "./src/objects/statement.lzz" { Statement* stmt = node :: ObjectWrap :: Unwrap (info.This()); if (stmt->bound) return ThrowTypeError("The bind() method can only be invoked once per statement object"); @@ -981,9 +984,9 @@ void Statement::JS_bind (v8::FunctionCallbackInfo const & info) stmt->bound = true; info.GetReturnValue().Set(info.This()); } -#line 229 "./src/objects/statement.lzz" +#line 232 "./src/objects/statement.lzz" void Statement::JS_pluck (v8::FunctionCallbackInfo const & info) -#line 229 "./src/objects/statement.lzz" +#line 232 "./src/objects/statement.lzz" { Statement* stmt = node :: ObjectWrap :: Unwrap (info.This()); if (!stmt->returns_data) return ThrowTypeError("The pluck() method is only for statements that return data"); @@ -994,9 +997,9 @@ void Statement::JS_pluck (v8::FunctionCallbackInfo const & info) stmt->mode = use ? Data::PLUCK : stmt->mode == Data::PLUCK ? Data::FLAT : stmt->mode; info.GetReturnValue().Set(info.This()); } -#line 240 "./src/objects/statement.lzz" +#line 243 "./src/objects/statement.lzz" void Statement::JS_expand (v8::FunctionCallbackInfo const & info) -#line 240 "./src/objects/statement.lzz" +#line 243 "./src/objects/statement.lzz" { Statement* stmt = node :: ObjectWrap :: Unwrap (info.This()); if (!stmt->returns_data) return ThrowTypeError("The expand() method is only for statements that return data"); @@ -1007,9 +1010,9 @@ void Statement::JS_expand (v8::FunctionCallbackInfo const & info) stmt->mode = use ? Data::EXPAND : stmt->mode == Data::EXPAND ? Data::FLAT : stmt->mode; info.GetReturnValue().Set(info.This()); } -#line 251 "./src/objects/statement.lzz" +#line 254 "./src/objects/statement.lzz" void Statement::JS_raw (v8::FunctionCallbackInfo const & info) -#line 251 "./src/objects/statement.lzz" +#line 254 "./src/objects/statement.lzz" { Statement* stmt = node :: ObjectWrap :: Unwrap (info.This()); if (!stmt->returns_data) return ThrowTypeError("The raw() method is only for statements that return data"); @@ -1020,9 +1023,9 @@ void Statement::JS_raw (v8::FunctionCallbackInfo const & info) stmt->mode = use ? Data::RAW : stmt->mode == Data::RAW ? Data::FLAT : stmt->mode; info.GetReturnValue().Set(info.This()); } -#line 262 "./src/objects/statement.lzz" +#line 265 "./src/objects/statement.lzz" void Statement::JS_safeIntegers (v8::FunctionCallbackInfo const & info) -#line 262 "./src/objects/statement.lzz" +#line 265 "./src/objects/statement.lzz" { Statement* stmt = node :: ObjectWrap :: Unwrap (info.This()); if ( stmt -> db -> GetState ( ) -> busy ) return ThrowTypeError ( "This database connection is busy executing a query" ) ; @@ -1031,9 +1034,9 @@ void Statement::JS_safeIntegers (v8::FunctionCallbackInfo const & else { if ( info . Length ( ) <= ( 0 ) || ! info [ 0 ] -> IsBoolean ( ) ) return ThrowTypeError ( "Expected " "first" " argument to be " "a boolean" ) ; stmt -> safe_ints = ( info [ 0 ] . As < v8 :: Boolean > ( ) ) -> Value ( ) ; } info.GetReturnValue().Set(info.This()); } -#line 271 "./src/objects/statement.lzz" +#line 274 "./src/objects/statement.lzz" void Statement::JS_columns (v8::FunctionCallbackInfo const & info) -#line 271 "./src/objects/statement.lzz" +#line 274 "./src/objects/statement.lzz" { Statement* stmt = node :: ObjectWrap :: Unwrap (info.This()); if (!stmt->returns_data) return ThrowTypeError("The columns() method is only for statements that return data"); @@ -1076,9 +1079,9 @@ void Statement::JS_columns (v8::FunctionCallbackInfo const & info) info.GetReturnValue().Set(columns); } -#line 314 "./src/objects/statement.lzz" +#line 317 "./src/objects/statement.lzz" void Statement::JS_busy (v8::Local _, v8::PropertyCallbackInfo const & info) -#line 314 "./src/objects/statement.lzz" +#line 317 "./src/objects/statement.lzz" { Statement* stmt = node :: ObjectWrap :: Unwrap (info.This()); info.GetReturnValue().Set(stmt->alive && stmt->locked); diff --git a/src/better_sqlite3.hpp b/src/better_sqlite3.hpp index a3dcdc960..4dcd663b4 100644 --- a/src/better_sqlite3.hpp +++ b/src/better_sqlite3.hpp @@ -340,47 +340,47 @@ class Statement : public node::ObjectWrap explicit Statement (Database * db, sqlite3_stmt * handle, sqlite3_uint64 id, bool returns_data); #line 85 "./src/objects/statement.lzz" static void JS_new (v8::FunctionCallbackInfo const & info); -#line 149 "./src/objects/statement.lzz" +#line 152 "./src/objects/statement.lzz" static void JS_run (v8::FunctionCallbackInfo const & info); -#line 172 "./src/objects/statement.lzz" +#line 175 "./src/objects/statement.lzz" static void JS_get (v8::FunctionCallbackInfo const & info); -#line 187 "./src/objects/statement.lzz" +#line 190 "./src/objects/statement.lzz" static void JS_all (v8::FunctionCallbackInfo const & info); -#line 208 "./src/objects/statement.lzz" +#line 211 "./src/objects/statement.lzz" static void JS_iterate (v8::FunctionCallbackInfo const & info); -#line 218 "./src/objects/statement.lzz" +#line 221 "./src/objects/statement.lzz" static void JS_bind (v8::FunctionCallbackInfo const & info); -#line 229 "./src/objects/statement.lzz" +#line 232 "./src/objects/statement.lzz" static void JS_pluck (v8::FunctionCallbackInfo const & info); -#line 240 "./src/objects/statement.lzz" +#line 243 "./src/objects/statement.lzz" static void JS_expand (v8::FunctionCallbackInfo const & info); -#line 251 "./src/objects/statement.lzz" +#line 254 "./src/objects/statement.lzz" static void JS_raw (v8::FunctionCallbackInfo const & info); -#line 262 "./src/objects/statement.lzz" +#line 265 "./src/objects/statement.lzz" static void JS_safeIntegers (v8::FunctionCallbackInfo const & info); -#line 271 "./src/objects/statement.lzz" +#line 274 "./src/objects/statement.lzz" static void JS_columns (v8::FunctionCallbackInfo const & info); -#line 314 "./src/objects/statement.lzz" +#line 317 "./src/objects/statement.lzz" static void JS_busy (v8::Local _, v8::PropertyCallbackInfo const & info); -#line 319 "./src/objects/statement.lzz" +#line 322 "./src/objects/statement.lzz" Database * const db; -#line 320 "./src/objects/statement.lzz" +#line 323 "./src/objects/statement.lzz" sqlite3_stmt * const handle; -#line 321 "./src/objects/statement.lzz" +#line 324 "./src/objects/statement.lzz" Extras * const extras; -#line 322 "./src/objects/statement.lzz" +#line 325 "./src/objects/statement.lzz" bool alive; -#line 323 "./src/objects/statement.lzz" +#line 326 "./src/objects/statement.lzz" bool locked; -#line 324 "./src/objects/statement.lzz" +#line 327 "./src/objects/statement.lzz" bool bound; -#line 325 "./src/objects/statement.lzz" +#line 328 "./src/objects/statement.lzz" bool has_bind_map; -#line 326 "./src/objects/statement.lzz" +#line 329 "./src/objects/statement.lzz" bool safe_ints; -#line 327 "./src/objects/statement.lzz" +#line 330 "./src/objects/statement.lzz" char mode; -#line 328 "./src/objects/statement.lzz" +#line 331 "./src/objects/statement.lzz" bool const returns_data; }; #line 1 "./src/objects/statement-iterator.lzz" diff --git a/src/objects/statement.lzz b/src/objects/statement.lzz index 740060edb..aaad13591 100644 --- a/src/objects/statement.lzz +++ b/src/objects/statement.lzz @@ -113,8 +113,11 @@ private: if (handle == NULL) { return ThrowRangeError("The supplied SQL string contains no statements"); } - for (char c; (c = *tail); ++tail) { - if (IS_SKIPPED(c)) continue; + for (char c; (c = *tail); ) { + if (IS_SKIPPED(c)) { + ++tail; + continue; + } if (c == '/' && tail[1] == '*') { tail += 2; for (char c; (c = *tail); ++tail) { From 99fd6b674026de3e86b0abb82b307820eaf57129 Mon Sep 17 00:00:00 2001 From: Arimah Date: Tue, 25 Apr 2023 19:12:16 +0200 Subject: [PATCH 2/3] Add comment with link to tail parsing issue --- src/better_sqlite3.cpp | 45 ++++++++++++++++++++------------------- src/better_sqlite3.hpp | 42 ++++++++++++++++++------------------ src/objects/statement.lzz | 1 + 3 files changed, 45 insertions(+), 43 deletions(-) diff --git a/src/better_sqlite3.cpp b/src/better_sqlite3.cpp index ae0c1bcca..2e49b1b57 100644 --- a/src/better_sqlite3.cpp +++ b/src/better_sqlite3.cpp @@ -859,6 +859,7 @@ void Statement::JS_new (v8::FunctionCallbackInfo const & info) if (handle == NULL) { return ThrowRangeError("The supplied SQL string contains no statements"); } + for (char c; (c = *tail); ) { if (IS_SKIPPED(c)) { ++tail; @@ -894,9 +895,9 @@ void Statement::JS_new (v8::FunctionCallbackInfo const & info) info.GetReturnValue().Set(info.This()); } -#line 152 "./src/objects/statement.lzz" +#line 153 "./src/objects/statement.lzz" void Statement::JS_run (v8::FunctionCallbackInfo const & info) -#line 152 "./src/objects/statement.lzz" +#line 153 "./src/objects/statement.lzz" { Statement * stmt = node :: ObjectWrap :: Unwrap < Statement > ( info . This ( ) ) ; ( ( void ) 0 ) ; sqlite3_stmt * handle = stmt -> handle ; Database * db = stmt -> db ; if ( ! db -> GetState ( ) -> open ) return ThrowTypeError ( "The database connection is not open" ) ; if ( db -> GetState ( ) -> busy ) return ThrowTypeError ( "This database connection is busy executing a query" ) ; if ( stmt -> locked ) return ThrowTypeError ( "This statement is busy executing a query" ) ; if ( ! db -> GetState ( ) -> unsafe_mode ) { if ( db -> GetState ( ) -> iterators ) return ThrowTypeError ( "This database connection is busy executing a query" ) ; } ( ( void ) 0 ) ; const bool bound = stmt -> bound ; if ( ! bound ) { Binder binder ( handle ) ; if ( ! binder . Bind ( info , info . Length ( ) , stmt ) ) { sqlite3_clear_bindings ( handle ) ; return ; } ( ( void ) 0 ) ; } else if ( info . Length ( ) > 0 ) { return ThrowTypeError ( "This statement already has bound parameters" ) ; } ( ( void ) 0 ) ; db -> GetState ( ) -> busy = true ; v8 :: Isolate * isolate = info . GetIsolate ( ) ; if ( db -> Log ( isolate , handle ) ) { db -> GetState ( ) -> busy = false ; db -> ThrowDatabaseError ( ) ; if ( ! bound ) { sqlite3_clear_bindings ( handle ) ; } return ; } ( ( void ) 0 ) ; sqlite3* db_handle = db->GetHandle(); @@ -919,9 +920,9 @@ void Statement::JS_run (v8::FunctionCallbackInfo const & info) } db -> GetState ( ) -> busy = false ; db -> ThrowDatabaseError ( ) ; if ( ! bound ) { sqlite3_clear_bindings ( handle ) ; } return ; } -#line 175 "./src/objects/statement.lzz" +#line 176 "./src/objects/statement.lzz" void Statement::JS_get (v8::FunctionCallbackInfo const & info) -#line 175 "./src/objects/statement.lzz" +#line 176 "./src/objects/statement.lzz" { Statement * stmt = node :: ObjectWrap :: Unwrap < Statement > ( info . This ( ) ) ; if ( ! stmt -> returns_data ) return ThrowTypeError ( "This statement does not return data. Use run() instead" ) ; sqlite3_stmt * handle = stmt -> handle ; Database * db = stmt -> db ; if ( ! db -> GetState ( ) -> open ) return ThrowTypeError ( "The database connection is not open" ) ; if ( db -> GetState ( ) -> busy ) return ThrowTypeError ( "This database connection is busy executing a query" ) ; if ( stmt -> locked ) return ThrowTypeError ( "This statement is busy executing a query" ) ; const bool bound = stmt -> bound ; if ( ! bound ) { Binder binder ( handle ) ; if ( ! binder . Bind ( info , info . Length ( ) , stmt ) ) { sqlite3_clear_bindings ( handle ) ; return ; } ( ( void ) 0 ) ; } else if ( info . Length ( ) > 0 ) { return ThrowTypeError ( "This statement already has bound parameters" ) ; } ( ( void ) 0 ) ; db -> GetState ( ) -> busy = true ; v8 :: Isolate * isolate = info . GetIsolate ( ) ; if ( db -> Log ( isolate , handle ) ) { db -> GetState ( ) -> busy = false ; db -> ThrowDatabaseError ( ) ; if ( ! bound ) { sqlite3_clear_bindings ( handle ) ; } return ; } ( ( void ) 0 ) ; int status = sqlite3_step(handle); @@ -936,9 +937,9 @@ void Statement::JS_get (v8::FunctionCallbackInfo const & info) sqlite3_reset(handle); db -> GetState ( ) -> busy = false ; db -> ThrowDatabaseError ( ) ; if ( ! bound ) { sqlite3_clear_bindings ( handle ) ; } return ; } -#line 190 "./src/objects/statement.lzz" +#line 191 "./src/objects/statement.lzz" void Statement::JS_all (v8::FunctionCallbackInfo const & info) -#line 190 "./src/objects/statement.lzz" +#line 191 "./src/objects/statement.lzz" { Statement * stmt = node :: ObjectWrap :: Unwrap < Statement > ( info . This ( ) ) ; if ( ! stmt -> returns_data ) return ThrowTypeError ( "This statement does not return data. Use run() instead" ) ; sqlite3_stmt * handle = stmt -> handle ; Database * db = stmt -> db ; if ( ! db -> GetState ( ) -> open ) return ThrowTypeError ( "The database connection is not open" ) ; if ( db -> GetState ( ) -> busy ) return ThrowTypeError ( "This database connection is busy executing a query" ) ; if ( stmt -> locked ) return ThrowTypeError ( "This statement is busy executing a query" ) ; const bool bound = stmt -> bound ; if ( ! bound ) { Binder binder ( handle ) ; if ( ! binder . Bind ( info , info . Length ( ) , stmt ) ) { sqlite3_clear_bindings ( handle ) ; return ; } ( ( void ) 0 ) ; } else if ( info . Length ( ) > 0 ) { return ThrowTypeError ( "This statement already has bound parameters" ) ; } ( ( void ) 0 ) ; db -> GetState ( ) -> busy = true ; v8 :: Isolate * isolate = info . GetIsolate ( ) ; if ( db -> Log ( isolate , handle ) ) { db -> GetState ( ) -> busy = false ; db -> ThrowDatabaseError ( ) ; if ( ! bound ) { sqlite3_clear_bindings ( handle ) ; } return ; } ( ( void ) 0 ) ; v8 :: Local < v8 :: Context > ctx = isolate -> GetCurrentContext ( ) ; @@ -959,9 +960,9 @@ void Statement::JS_all (v8::FunctionCallbackInfo const & info) if (js_error) db->GetState()->was_js_error = true; db -> GetState ( ) -> busy = false ; db -> ThrowDatabaseError ( ) ; if ( ! bound ) { sqlite3_clear_bindings ( handle ) ; } return ; } -#line 211 "./src/objects/statement.lzz" +#line 212 "./src/objects/statement.lzz" void Statement::JS_iterate (v8::FunctionCallbackInfo const & info) -#line 211 "./src/objects/statement.lzz" +#line 212 "./src/objects/statement.lzz" { Addon * addon = static_cast < Addon * > ( info . Data ( ) . As < v8 :: External > ( ) -> Value ( ) ) ; v8 :: Isolate * isolate = info . GetIsolate ( ) ; @@ -971,9 +972,9 @@ void Statement::JS_iterate (v8::FunctionCallbackInfo const & info) addon->privileged_info = NULL; if (!maybeIterator.IsEmpty()) info.GetReturnValue().Set(maybeIterator.ToLocalChecked()); } -#line 221 "./src/objects/statement.lzz" +#line 222 "./src/objects/statement.lzz" void Statement::JS_bind (v8::FunctionCallbackInfo const & info) -#line 221 "./src/objects/statement.lzz" +#line 222 "./src/objects/statement.lzz" { Statement* stmt = node :: ObjectWrap :: Unwrap (info.This()); if (stmt->bound) return ThrowTypeError("The bind() method can only be invoked once per statement object"); @@ -984,9 +985,9 @@ void Statement::JS_bind (v8::FunctionCallbackInfo const & info) stmt->bound = true; info.GetReturnValue().Set(info.This()); } -#line 232 "./src/objects/statement.lzz" +#line 233 "./src/objects/statement.lzz" void Statement::JS_pluck (v8::FunctionCallbackInfo const & info) -#line 232 "./src/objects/statement.lzz" +#line 233 "./src/objects/statement.lzz" { Statement* stmt = node :: ObjectWrap :: Unwrap (info.This()); if (!stmt->returns_data) return ThrowTypeError("The pluck() method is only for statements that return data"); @@ -997,9 +998,9 @@ void Statement::JS_pluck (v8::FunctionCallbackInfo const & info) stmt->mode = use ? Data::PLUCK : stmt->mode == Data::PLUCK ? Data::FLAT : stmt->mode; info.GetReturnValue().Set(info.This()); } -#line 243 "./src/objects/statement.lzz" +#line 244 "./src/objects/statement.lzz" void Statement::JS_expand (v8::FunctionCallbackInfo const & info) -#line 243 "./src/objects/statement.lzz" +#line 244 "./src/objects/statement.lzz" { Statement* stmt = node :: ObjectWrap :: Unwrap (info.This()); if (!stmt->returns_data) return ThrowTypeError("The expand() method is only for statements that return data"); @@ -1010,9 +1011,9 @@ void Statement::JS_expand (v8::FunctionCallbackInfo const & info) stmt->mode = use ? Data::EXPAND : stmt->mode == Data::EXPAND ? Data::FLAT : stmt->mode; info.GetReturnValue().Set(info.This()); } -#line 254 "./src/objects/statement.lzz" +#line 255 "./src/objects/statement.lzz" void Statement::JS_raw (v8::FunctionCallbackInfo const & info) -#line 254 "./src/objects/statement.lzz" +#line 255 "./src/objects/statement.lzz" { Statement* stmt = node :: ObjectWrap :: Unwrap (info.This()); if (!stmt->returns_data) return ThrowTypeError("The raw() method is only for statements that return data"); @@ -1023,9 +1024,9 @@ void Statement::JS_raw (v8::FunctionCallbackInfo const & info) stmt->mode = use ? Data::RAW : stmt->mode == Data::RAW ? Data::FLAT : stmt->mode; info.GetReturnValue().Set(info.This()); } -#line 265 "./src/objects/statement.lzz" +#line 266 "./src/objects/statement.lzz" void Statement::JS_safeIntegers (v8::FunctionCallbackInfo const & info) -#line 265 "./src/objects/statement.lzz" +#line 266 "./src/objects/statement.lzz" { Statement* stmt = node :: ObjectWrap :: Unwrap (info.This()); if ( stmt -> db -> GetState ( ) -> busy ) return ThrowTypeError ( "This database connection is busy executing a query" ) ; @@ -1034,9 +1035,9 @@ void Statement::JS_safeIntegers (v8::FunctionCallbackInfo const & else { if ( info . Length ( ) <= ( 0 ) || ! info [ 0 ] -> IsBoolean ( ) ) return ThrowTypeError ( "Expected " "first" " argument to be " "a boolean" ) ; stmt -> safe_ints = ( info [ 0 ] . As < v8 :: Boolean > ( ) ) -> Value ( ) ; } info.GetReturnValue().Set(info.This()); } -#line 274 "./src/objects/statement.lzz" +#line 275 "./src/objects/statement.lzz" void Statement::JS_columns (v8::FunctionCallbackInfo const & info) -#line 274 "./src/objects/statement.lzz" +#line 275 "./src/objects/statement.lzz" { Statement* stmt = node :: ObjectWrap :: Unwrap (info.This()); if (!stmt->returns_data) return ThrowTypeError("The columns() method is only for statements that return data"); @@ -1079,9 +1080,9 @@ void Statement::JS_columns (v8::FunctionCallbackInfo const & info) info.GetReturnValue().Set(columns); } -#line 317 "./src/objects/statement.lzz" +#line 318 "./src/objects/statement.lzz" void Statement::JS_busy (v8::Local _, v8::PropertyCallbackInfo const & info) -#line 317 "./src/objects/statement.lzz" +#line 318 "./src/objects/statement.lzz" { Statement* stmt = node :: ObjectWrap :: Unwrap (info.This()); info.GetReturnValue().Set(stmt->alive && stmt->locked); diff --git a/src/better_sqlite3.hpp b/src/better_sqlite3.hpp index 4dcd663b4..c3f9262e0 100644 --- a/src/better_sqlite3.hpp +++ b/src/better_sqlite3.hpp @@ -340,47 +340,47 @@ class Statement : public node::ObjectWrap explicit Statement (Database * db, sqlite3_stmt * handle, sqlite3_uint64 id, bool returns_data); #line 85 "./src/objects/statement.lzz" static void JS_new (v8::FunctionCallbackInfo const & info); -#line 152 "./src/objects/statement.lzz" +#line 153 "./src/objects/statement.lzz" static void JS_run (v8::FunctionCallbackInfo const & info); -#line 175 "./src/objects/statement.lzz" +#line 176 "./src/objects/statement.lzz" static void JS_get (v8::FunctionCallbackInfo const & info); -#line 190 "./src/objects/statement.lzz" +#line 191 "./src/objects/statement.lzz" static void JS_all (v8::FunctionCallbackInfo const & info); -#line 211 "./src/objects/statement.lzz" +#line 212 "./src/objects/statement.lzz" static void JS_iterate (v8::FunctionCallbackInfo const & info); -#line 221 "./src/objects/statement.lzz" +#line 222 "./src/objects/statement.lzz" static void JS_bind (v8::FunctionCallbackInfo const & info); -#line 232 "./src/objects/statement.lzz" +#line 233 "./src/objects/statement.lzz" static void JS_pluck (v8::FunctionCallbackInfo const & info); -#line 243 "./src/objects/statement.lzz" +#line 244 "./src/objects/statement.lzz" static void JS_expand (v8::FunctionCallbackInfo const & info); -#line 254 "./src/objects/statement.lzz" +#line 255 "./src/objects/statement.lzz" static void JS_raw (v8::FunctionCallbackInfo const & info); -#line 265 "./src/objects/statement.lzz" +#line 266 "./src/objects/statement.lzz" static void JS_safeIntegers (v8::FunctionCallbackInfo const & info); -#line 274 "./src/objects/statement.lzz" +#line 275 "./src/objects/statement.lzz" static void JS_columns (v8::FunctionCallbackInfo const & info); -#line 317 "./src/objects/statement.lzz" +#line 318 "./src/objects/statement.lzz" static void JS_busy (v8::Local _, v8::PropertyCallbackInfo const & info); -#line 322 "./src/objects/statement.lzz" - Database * const db; #line 323 "./src/objects/statement.lzz" - sqlite3_stmt * const handle; + Database * const db; #line 324 "./src/objects/statement.lzz" - Extras * const extras; + sqlite3_stmt * const handle; #line 325 "./src/objects/statement.lzz" - bool alive; + Extras * const extras; #line 326 "./src/objects/statement.lzz" - bool locked; + bool alive; #line 327 "./src/objects/statement.lzz" - bool bound; + bool locked; #line 328 "./src/objects/statement.lzz" - bool has_bind_map; + bool bound; #line 329 "./src/objects/statement.lzz" - bool safe_ints; + bool has_bind_map; #line 330 "./src/objects/statement.lzz" - char mode; + bool safe_ints; #line 331 "./src/objects/statement.lzz" + char mode; +#line 332 "./src/objects/statement.lzz" bool const returns_data; }; #line 1 "./src/objects/statement-iterator.lzz" diff --git a/src/objects/statement.lzz b/src/objects/statement.lzz index aaad13591..73dc3a82e 100644 --- a/src/objects/statement.lzz +++ b/src/objects/statement.lzz @@ -113,6 +113,7 @@ private: if (handle == NULL) { return ThrowRangeError("The supplied SQL string contains no statements"); } + // https://github.com/WiseLibs/better-sqlite3/issues/975#issuecomment-1520934678 for (char c; (c = *tail); ) { if (IS_SKIPPED(c)) { ++tail; From 36cc72aa82226adbaa0fa57fed22a0f9606e173b Mon Sep 17 00:00:00 2001 From: Arimah Date: Thu, 27 Apr 2023 22:31:25 +0200 Subject: [PATCH 3/3] Fix missing increment in tail parser + tests --- src/better_sqlite3.cpp | 51 ++++++++++++++++++++----------------- src/better_sqlite3.hpp | 42 +++++++++++++++--------------- src/objects/statement.lzz | 7 +++-- test/13.database.prepare.js | 14 ++++++++++ 4 files changed, 67 insertions(+), 47 deletions(-) diff --git a/src/better_sqlite3.cpp b/src/better_sqlite3.cpp index 2e49b1b57..b0a0f8303 100644 --- a/src/better_sqlite3.cpp +++ b/src/better_sqlite3.cpp @@ -869,14 +869,17 @@ void Statement::JS_new (v8::FunctionCallbackInfo const & info) tail += 2; for (char c; (c = *tail); ++tail) { if (c == '*' && tail[1] == '/') { - tail += 1; + tail += 2; break; } } } else if (c == '-' && tail[1] == '-') { tail += 2; for (char c; (c = *tail); ++tail) { - if (c == '\n') break; + if (c == '\n') { + ++tail; + break; + } } } else { sqlite3_finalize(handle); @@ -895,9 +898,9 @@ void Statement::JS_new (v8::FunctionCallbackInfo const & info) info.GetReturnValue().Set(info.This()); } -#line 153 "./src/objects/statement.lzz" +#line 156 "./src/objects/statement.lzz" void Statement::JS_run (v8::FunctionCallbackInfo const & info) -#line 153 "./src/objects/statement.lzz" +#line 156 "./src/objects/statement.lzz" { Statement * stmt = node :: ObjectWrap :: Unwrap < Statement > ( info . This ( ) ) ; ( ( void ) 0 ) ; sqlite3_stmt * handle = stmt -> handle ; Database * db = stmt -> db ; if ( ! db -> GetState ( ) -> open ) return ThrowTypeError ( "The database connection is not open" ) ; if ( db -> GetState ( ) -> busy ) return ThrowTypeError ( "This database connection is busy executing a query" ) ; if ( stmt -> locked ) return ThrowTypeError ( "This statement is busy executing a query" ) ; if ( ! db -> GetState ( ) -> unsafe_mode ) { if ( db -> GetState ( ) -> iterators ) return ThrowTypeError ( "This database connection is busy executing a query" ) ; } ( ( void ) 0 ) ; const bool bound = stmt -> bound ; if ( ! bound ) { Binder binder ( handle ) ; if ( ! binder . Bind ( info , info . Length ( ) , stmt ) ) { sqlite3_clear_bindings ( handle ) ; return ; } ( ( void ) 0 ) ; } else if ( info . Length ( ) > 0 ) { return ThrowTypeError ( "This statement already has bound parameters" ) ; } ( ( void ) 0 ) ; db -> GetState ( ) -> busy = true ; v8 :: Isolate * isolate = info . GetIsolate ( ) ; if ( db -> Log ( isolate , handle ) ) { db -> GetState ( ) -> busy = false ; db -> ThrowDatabaseError ( ) ; if ( ! bound ) { sqlite3_clear_bindings ( handle ) ; } return ; } ( ( void ) 0 ) ; sqlite3* db_handle = db->GetHandle(); @@ -920,9 +923,9 @@ void Statement::JS_run (v8::FunctionCallbackInfo const & info) } db -> GetState ( ) -> busy = false ; db -> ThrowDatabaseError ( ) ; if ( ! bound ) { sqlite3_clear_bindings ( handle ) ; } return ; } -#line 176 "./src/objects/statement.lzz" +#line 179 "./src/objects/statement.lzz" void Statement::JS_get (v8::FunctionCallbackInfo const & info) -#line 176 "./src/objects/statement.lzz" +#line 179 "./src/objects/statement.lzz" { Statement * stmt = node :: ObjectWrap :: Unwrap < Statement > ( info . This ( ) ) ; if ( ! stmt -> returns_data ) return ThrowTypeError ( "This statement does not return data. Use run() instead" ) ; sqlite3_stmt * handle = stmt -> handle ; Database * db = stmt -> db ; if ( ! db -> GetState ( ) -> open ) return ThrowTypeError ( "The database connection is not open" ) ; if ( db -> GetState ( ) -> busy ) return ThrowTypeError ( "This database connection is busy executing a query" ) ; if ( stmt -> locked ) return ThrowTypeError ( "This statement is busy executing a query" ) ; const bool bound = stmt -> bound ; if ( ! bound ) { Binder binder ( handle ) ; if ( ! binder . Bind ( info , info . Length ( ) , stmt ) ) { sqlite3_clear_bindings ( handle ) ; return ; } ( ( void ) 0 ) ; } else if ( info . Length ( ) > 0 ) { return ThrowTypeError ( "This statement already has bound parameters" ) ; } ( ( void ) 0 ) ; db -> GetState ( ) -> busy = true ; v8 :: Isolate * isolate = info . GetIsolate ( ) ; if ( db -> Log ( isolate , handle ) ) { db -> GetState ( ) -> busy = false ; db -> ThrowDatabaseError ( ) ; if ( ! bound ) { sqlite3_clear_bindings ( handle ) ; } return ; } ( ( void ) 0 ) ; int status = sqlite3_step(handle); @@ -937,9 +940,9 @@ void Statement::JS_get (v8::FunctionCallbackInfo const & info) sqlite3_reset(handle); db -> GetState ( ) -> busy = false ; db -> ThrowDatabaseError ( ) ; if ( ! bound ) { sqlite3_clear_bindings ( handle ) ; } return ; } -#line 191 "./src/objects/statement.lzz" +#line 194 "./src/objects/statement.lzz" void Statement::JS_all (v8::FunctionCallbackInfo const & info) -#line 191 "./src/objects/statement.lzz" +#line 194 "./src/objects/statement.lzz" { Statement * stmt = node :: ObjectWrap :: Unwrap < Statement > ( info . This ( ) ) ; if ( ! stmt -> returns_data ) return ThrowTypeError ( "This statement does not return data. Use run() instead" ) ; sqlite3_stmt * handle = stmt -> handle ; Database * db = stmt -> db ; if ( ! db -> GetState ( ) -> open ) return ThrowTypeError ( "The database connection is not open" ) ; if ( db -> GetState ( ) -> busy ) return ThrowTypeError ( "This database connection is busy executing a query" ) ; if ( stmt -> locked ) return ThrowTypeError ( "This statement is busy executing a query" ) ; const bool bound = stmt -> bound ; if ( ! bound ) { Binder binder ( handle ) ; if ( ! binder . Bind ( info , info . Length ( ) , stmt ) ) { sqlite3_clear_bindings ( handle ) ; return ; } ( ( void ) 0 ) ; } else if ( info . Length ( ) > 0 ) { return ThrowTypeError ( "This statement already has bound parameters" ) ; } ( ( void ) 0 ) ; db -> GetState ( ) -> busy = true ; v8 :: Isolate * isolate = info . GetIsolate ( ) ; if ( db -> Log ( isolate , handle ) ) { db -> GetState ( ) -> busy = false ; db -> ThrowDatabaseError ( ) ; if ( ! bound ) { sqlite3_clear_bindings ( handle ) ; } return ; } ( ( void ) 0 ) ; v8 :: Local < v8 :: Context > ctx = isolate -> GetCurrentContext ( ) ; @@ -960,9 +963,9 @@ void Statement::JS_all (v8::FunctionCallbackInfo const & info) if (js_error) db->GetState()->was_js_error = true; db -> GetState ( ) -> busy = false ; db -> ThrowDatabaseError ( ) ; if ( ! bound ) { sqlite3_clear_bindings ( handle ) ; } return ; } -#line 212 "./src/objects/statement.lzz" +#line 215 "./src/objects/statement.lzz" void Statement::JS_iterate (v8::FunctionCallbackInfo const & info) -#line 212 "./src/objects/statement.lzz" +#line 215 "./src/objects/statement.lzz" { Addon * addon = static_cast < Addon * > ( info . Data ( ) . As < v8 :: External > ( ) -> Value ( ) ) ; v8 :: Isolate * isolate = info . GetIsolate ( ) ; @@ -972,9 +975,9 @@ void Statement::JS_iterate (v8::FunctionCallbackInfo const & info) addon->privileged_info = NULL; if (!maybeIterator.IsEmpty()) info.GetReturnValue().Set(maybeIterator.ToLocalChecked()); } -#line 222 "./src/objects/statement.lzz" +#line 225 "./src/objects/statement.lzz" void Statement::JS_bind (v8::FunctionCallbackInfo const & info) -#line 222 "./src/objects/statement.lzz" +#line 225 "./src/objects/statement.lzz" { Statement* stmt = node :: ObjectWrap :: Unwrap (info.This()); if (stmt->bound) return ThrowTypeError("The bind() method can only be invoked once per statement object"); @@ -985,9 +988,9 @@ void Statement::JS_bind (v8::FunctionCallbackInfo const & info) stmt->bound = true; info.GetReturnValue().Set(info.This()); } -#line 233 "./src/objects/statement.lzz" +#line 236 "./src/objects/statement.lzz" void Statement::JS_pluck (v8::FunctionCallbackInfo const & info) -#line 233 "./src/objects/statement.lzz" +#line 236 "./src/objects/statement.lzz" { Statement* stmt = node :: ObjectWrap :: Unwrap (info.This()); if (!stmt->returns_data) return ThrowTypeError("The pluck() method is only for statements that return data"); @@ -998,9 +1001,9 @@ void Statement::JS_pluck (v8::FunctionCallbackInfo const & info) stmt->mode = use ? Data::PLUCK : stmt->mode == Data::PLUCK ? Data::FLAT : stmt->mode; info.GetReturnValue().Set(info.This()); } -#line 244 "./src/objects/statement.lzz" +#line 247 "./src/objects/statement.lzz" void Statement::JS_expand (v8::FunctionCallbackInfo const & info) -#line 244 "./src/objects/statement.lzz" +#line 247 "./src/objects/statement.lzz" { Statement* stmt = node :: ObjectWrap :: Unwrap (info.This()); if (!stmt->returns_data) return ThrowTypeError("The expand() method is only for statements that return data"); @@ -1011,9 +1014,9 @@ void Statement::JS_expand (v8::FunctionCallbackInfo const & info) stmt->mode = use ? Data::EXPAND : stmt->mode == Data::EXPAND ? Data::FLAT : stmt->mode; info.GetReturnValue().Set(info.This()); } -#line 255 "./src/objects/statement.lzz" +#line 258 "./src/objects/statement.lzz" void Statement::JS_raw (v8::FunctionCallbackInfo const & info) -#line 255 "./src/objects/statement.lzz" +#line 258 "./src/objects/statement.lzz" { Statement* stmt = node :: ObjectWrap :: Unwrap (info.This()); if (!stmt->returns_data) return ThrowTypeError("The raw() method is only for statements that return data"); @@ -1024,9 +1027,9 @@ void Statement::JS_raw (v8::FunctionCallbackInfo const & info) stmt->mode = use ? Data::RAW : stmt->mode == Data::RAW ? Data::FLAT : stmt->mode; info.GetReturnValue().Set(info.This()); } -#line 266 "./src/objects/statement.lzz" +#line 269 "./src/objects/statement.lzz" void Statement::JS_safeIntegers (v8::FunctionCallbackInfo const & info) -#line 266 "./src/objects/statement.lzz" +#line 269 "./src/objects/statement.lzz" { Statement* stmt = node :: ObjectWrap :: Unwrap (info.This()); if ( stmt -> db -> GetState ( ) -> busy ) return ThrowTypeError ( "This database connection is busy executing a query" ) ; @@ -1035,9 +1038,9 @@ void Statement::JS_safeIntegers (v8::FunctionCallbackInfo const & else { if ( info . Length ( ) <= ( 0 ) || ! info [ 0 ] -> IsBoolean ( ) ) return ThrowTypeError ( "Expected " "first" " argument to be " "a boolean" ) ; stmt -> safe_ints = ( info [ 0 ] . As < v8 :: Boolean > ( ) ) -> Value ( ) ; } info.GetReturnValue().Set(info.This()); } -#line 275 "./src/objects/statement.lzz" +#line 278 "./src/objects/statement.lzz" void Statement::JS_columns (v8::FunctionCallbackInfo const & info) -#line 275 "./src/objects/statement.lzz" +#line 278 "./src/objects/statement.lzz" { Statement* stmt = node :: ObjectWrap :: Unwrap (info.This()); if (!stmt->returns_data) return ThrowTypeError("The columns() method is only for statements that return data"); @@ -1080,9 +1083,9 @@ void Statement::JS_columns (v8::FunctionCallbackInfo const & info) info.GetReturnValue().Set(columns); } -#line 318 "./src/objects/statement.lzz" +#line 321 "./src/objects/statement.lzz" void Statement::JS_busy (v8::Local _, v8::PropertyCallbackInfo const & info) -#line 318 "./src/objects/statement.lzz" +#line 321 "./src/objects/statement.lzz" { Statement* stmt = node :: ObjectWrap :: Unwrap (info.This()); info.GetReturnValue().Set(stmt->alive && stmt->locked); diff --git a/src/better_sqlite3.hpp b/src/better_sqlite3.hpp index c3f9262e0..0c0bcfd02 100644 --- a/src/better_sqlite3.hpp +++ b/src/better_sqlite3.hpp @@ -340,47 +340,47 @@ class Statement : public node::ObjectWrap explicit Statement (Database * db, sqlite3_stmt * handle, sqlite3_uint64 id, bool returns_data); #line 85 "./src/objects/statement.lzz" static void JS_new (v8::FunctionCallbackInfo const & info); -#line 153 "./src/objects/statement.lzz" +#line 156 "./src/objects/statement.lzz" static void JS_run (v8::FunctionCallbackInfo const & info); -#line 176 "./src/objects/statement.lzz" +#line 179 "./src/objects/statement.lzz" static void JS_get (v8::FunctionCallbackInfo const & info); -#line 191 "./src/objects/statement.lzz" +#line 194 "./src/objects/statement.lzz" static void JS_all (v8::FunctionCallbackInfo const & info); -#line 212 "./src/objects/statement.lzz" +#line 215 "./src/objects/statement.lzz" static void JS_iterate (v8::FunctionCallbackInfo const & info); -#line 222 "./src/objects/statement.lzz" +#line 225 "./src/objects/statement.lzz" static void JS_bind (v8::FunctionCallbackInfo const & info); -#line 233 "./src/objects/statement.lzz" +#line 236 "./src/objects/statement.lzz" static void JS_pluck (v8::FunctionCallbackInfo const & info); -#line 244 "./src/objects/statement.lzz" +#line 247 "./src/objects/statement.lzz" static void JS_expand (v8::FunctionCallbackInfo const & info); -#line 255 "./src/objects/statement.lzz" +#line 258 "./src/objects/statement.lzz" static void JS_raw (v8::FunctionCallbackInfo const & info); -#line 266 "./src/objects/statement.lzz" +#line 269 "./src/objects/statement.lzz" static void JS_safeIntegers (v8::FunctionCallbackInfo const & info); -#line 275 "./src/objects/statement.lzz" +#line 278 "./src/objects/statement.lzz" static void JS_columns (v8::FunctionCallbackInfo const & info); -#line 318 "./src/objects/statement.lzz" +#line 321 "./src/objects/statement.lzz" static void JS_busy (v8::Local _, v8::PropertyCallbackInfo const & info); -#line 323 "./src/objects/statement.lzz" +#line 326 "./src/objects/statement.lzz" Database * const db; -#line 324 "./src/objects/statement.lzz" +#line 327 "./src/objects/statement.lzz" sqlite3_stmt * const handle; -#line 325 "./src/objects/statement.lzz" +#line 328 "./src/objects/statement.lzz" Extras * const extras; -#line 326 "./src/objects/statement.lzz" +#line 329 "./src/objects/statement.lzz" bool alive; -#line 327 "./src/objects/statement.lzz" +#line 330 "./src/objects/statement.lzz" bool locked; -#line 328 "./src/objects/statement.lzz" +#line 331 "./src/objects/statement.lzz" bool bound; -#line 329 "./src/objects/statement.lzz" +#line 332 "./src/objects/statement.lzz" bool has_bind_map; -#line 330 "./src/objects/statement.lzz" +#line 333 "./src/objects/statement.lzz" bool safe_ints; -#line 331 "./src/objects/statement.lzz" +#line 334 "./src/objects/statement.lzz" char mode; -#line 332 "./src/objects/statement.lzz" +#line 335 "./src/objects/statement.lzz" bool const returns_data; }; #line 1 "./src/objects/statement-iterator.lzz" diff --git a/src/objects/statement.lzz b/src/objects/statement.lzz index 73dc3a82e..5e2c80570 100644 --- a/src/objects/statement.lzz +++ b/src/objects/statement.lzz @@ -123,14 +123,17 @@ private: tail += 2; for (char c; (c = *tail); ++tail) { if (c == '*' && tail[1] == '/') { - tail += 1; + tail += 2; break; } } } else if (c == '-' && tail[1] == '-') { tail += 2; for (char c; (c = *tail); ++tail) { - if (c == '\n') break; + if (c == '\n') { + ++tail; + break; + } } } else { sqlite3_finalize(handle); diff --git a/test/13.database.prepare.js b/test/13.database.prepare.js index 2cbe71175..4643da8af 100644 --- a/test/13.database.prepare.js +++ b/test/13.database.prepare.js @@ -37,6 +37,12 @@ describe('Database#prepare()', function () { expect(() => this.db.prepare('CREATE TABLE people (name TEXT);CREATE TABLE animals (name TEXT)')).to.throw(RangeError); expect(() => this.db.prepare('CREATE TABLE people (name TEXT);/')).to.throw(RangeError); expect(() => this.db.prepare('CREATE TABLE people (name TEXT);-')).to.throw(RangeError); + expect(() => this.db.prepare('CREATE TABLE people (name TEXT);--\n/')).to.throw(RangeError); + expect(() => this.db.prepare('CREATE TABLE people (name TEXT);--\nSELECT 123')).to.throw(RangeError); + expect(() => this.db.prepare('CREATE TABLE people (name TEXT);-- comment\nSELECT 123')).to.throw(RangeError); + expect(() => this.db.prepare('CREATE TABLE people (name TEXT);/**/-')).to.throw(RangeError); + expect(() => this.db.prepare('CREATE TABLE people (name TEXT);/**/SELECT 123')).to.throw(RangeError); + expect(() => this.db.prepare('CREATE TABLE people (name TEXT);/* comment */SELECT 123')).to.throw(RangeError); }); it('should create a prepared Statement object', function () { const stmt1 = this.db.prepare('CREATE TABLE people (name TEXT) '); @@ -57,4 +63,12 @@ describe('Database#prepare()', function () { assertStmt(this.db.prepare('BEGIN EXCLUSIVE'), 'BEGIN EXCLUSIVE', this.db, false, false); assertStmt(this.db.prepare('DELETE FROM data RETURNING *'), 'DELETE FROM data RETURNING *', this.db, true, false); }); + it('should create a prepared Statement object ignoring trailing comments and whitespace', function () { + assertStmt(this.db.prepare('SELECT 555; '), 'SELECT 555; ', this.db, true, true); + assertStmt(this.db.prepare('SELECT 555;-- comment'), 'SELECT 555;-- comment', this.db, true, true); + assertStmt(this.db.prepare('SELECT 555;--abc\n--de\n--f'), 'SELECT 555;--abc\n--de\n--f', this.db, true, true); + assertStmt(this.db.prepare('SELECT 555;/* comment */'), 'SELECT 555;/* comment */', this.db, true, true); + assertStmt(this.db.prepare('SELECT 555;/* comment */-- comment'), 'SELECT 555;/* comment */-- comment', this.db, true, true); + assertStmt(this.db.prepare('SELECT 555;-- comment\n/* comment */'), 'SELECT 555;-- comment\n/* comment */', this.db, true, true); + }); });