Skip to content

Commit 35a97d1

Browse files
authored
Merge pull request #6 from powersync-ja/fix-bind-with-explicit-index
Don't treat parameters with an explicit index as named
2 parents 39b8c83 + ab275c5 commit 35a97d1

File tree

7 files changed

+82
-45
lines changed

7 files changed

+82
-45
lines changed

.changeset/polite-rules-cut.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@powersync/better-sqlite3": minor
3+
---
4+
5+
Don't treat SQL parameters with an explicit index (e.g. `?1`) as a named parameter.

src/better_sqlite3.cpp

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33

44
#include "better_sqlite3.hpp"
5-
#line 165 "./src/util/macros.lzz"
5+
#line 167 "./src/util/macros.lzz"
66
void SetPrototypeGetter(
77
v8::Isolate* isolate,
88
v8::Local<v8::External> data,
@@ -30,7 +30,7 @@ void SetPrototypeGetter(
3030
);
3131
#endif
3232
}
33-
#line 195 "./src/util/macros.lzz"
33+
#line 197 "./src/util/macros.lzz"
3434
#ifndef V8_COMPRESS_POINTERS_IN_SHARED_CAGE
3535
#define SAFE_NEW_BUFFER(env, data, length, finalizeCallback, finalizeHint) node::Buffer::New(env, data, length, finalizeCallback, finalizeHint)
3636
#else
@@ -118,28 +118,28 @@ void ThrowRangeError (char const * message)
118118
#line 40 "./src/util/macros.lzz"
119119
{ v8 :: Isolate * isolate = v8 :: Isolate :: GetCurrent ( ) ; isolate->ThrowException(v8::Exception::RangeError(StringFromUtf8(isolate, message, -1)));
120120
}
121-
#line 117 "./src/util/macros.lzz"
121+
#line 119 "./src/util/macros.lzz"
122122
v8::Local <v8::FunctionTemplate> NewConstructorTemplate (v8::Isolate * isolate, v8::Local <v8::External> data, v8::FunctionCallback func, char const * name)
123-
#line 122 "./src/util/macros.lzz"
123+
#line 124 "./src/util/macros.lzz"
124124
{
125125
v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate, func, data);
126126
t->InstanceTemplate()->SetInternalFieldCount(1);
127127
t->SetClassName(InternalizedFromLatin1(isolate, name));
128128
return t;
129129
}
130-
#line 128 "./src/util/macros.lzz"
130+
#line 130 "./src/util/macros.lzz"
131131
void SetPrototypeMethod (v8::Isolate * isolate, v8::Local <v8::External> data, v8::Local <v8::FunctionTemplate> recv, char const * name, v8::FunctionCallback func)
132-
#line 134 "./src/util/macros.lzz"
132+
#line 136 "./src/util/macros.lzz"
133133
{
134134
v8::HandleScope scope(isolate);
135135
recv->PrototypeTemplate()->Set(
136136
InternalizedFromLatin1(isolate, name),
137137
v8::FunctionTemplate::New(isolate, func, data, v8::Signature::New(isolate, recv))
138138
);
139139
}
140-
#line 141 "./src/util/macros.lzz"
140+
#line 143 "./src/util/macros.lzz"
141141
void SetPrototypeSymbolMethod (v8::Isolate * isolate, v8::Local <v8::External> data, v8::Local <v8::FunctionTemplate> recv, v8::Local <v8::Symbol> symbol, v8::FunctionCallback func)
142-
#line 147 "./src/util/macros.lzz"
142+
#line 149 "./src/util/macros.lzz"
143143
{
144144
v8::HandleScope scope(isolate);
145145
recv->PrototypeTemplate()->Set(
@@ -1020,7 +1020,7 @@ BindMap * Statement::GetBindMap (v8::Isolate * isolate)
10201020
int param_count = sqlite3_bind_parameter_count(handle);
10211021
for (int i = 1; i <= param_count; ++i) {
10221022
const char* name = sqlite3_bind_parameter_name(handle, i);
1023-
if (name != NULL) bind_map->Add(isolate, name + 1, i);
1023+
if ( ( name != NULL && name [ 0 ] != '?' ) ) bind_map->Add(isolate, name + 1, i);
10241024
}
10251025
has_bind_map = true;
10261026
return bind_map;
@@ -2221,12 +2221,19 @@ void Binder::Fail (void (* Throw) (char const *), char const * message)
22212221
int Binder::NextAnonIndex ()
22222222
#line 63 "./src/util/binder.lzz"
22232223
{
2224-
while (sqlite3_bind_parameter_name(handle, ++anon_index) != NULL) {}
2224+
2225+
while (true) {
2226+
const char* name = sqlite3_bind_parameter_name(handle, ++anon_index);
2227+
if (! ( name != NULL && name [ 0 ] != '?' ) ) {
2228+
break;
2229+
}
2230+
}
2231+
22252232
return anon_index;
22262233
}
2227-
#line 69 "./src/util/binder.lzz"
2234+
#line 76 "./src/util/binder.lzz"
22282235
void Binder::BindValue (v8::Isolate * isolate, v8::Local <v8::Value> value, int index)
2229-
#line 69 "./src/util/binder.lzz"
2236+
#line 76 "./src/util/binder.lzz"
22302237
{
22312238
int status = Data::BindValueFromJS(isolate, handle, index, value);
22322239
if (status != SQLITE_OK) {
@@ -2245,9 +2252,9 @@ void Binder::BindValue (v8::Isolate * isolate, v8::Local <v8::Value> value, int
22452252
assert(false);
22462253
}
22472254
}
2248-
#line 90 "./src/util/binder.lzz"
2255+
#line 97 "./src/util/binder.lzz"
22492256
int Binder::BindArray (v8::Isolate * isolate, v8::Local <v8::Array> arr)
2250-
#line 90 "./src/util/binder.lzz"
2257+
#line 97 "./src/util/binder.lzz"
22512258
{
22522259
v8 :: Local < v8 :: Context > ctx = isolate -> GetCurrentContext ( ) ;
22532260
uint32_t length = arr->Length();
@@ -2269,9 +2276,9 @@ int Binder::BindArray (v8::Isolate * isolate, v8::Local <v8::Array> arr)
22692276
}
22702277
return len;
22712278
}
2272-
#line 116 "./src/util/binder.lzz"
2279+
#line 123 "./src/util/binder.lzz"
22732280
int Binder::BindObject (v8::Isolate * isolate, v8::Local <v8::Object> obj, Statement * stmt)
2274-
#line 116 "./src/util/binder.lzz"
2281+
#line 123 "./src/util/binder.lzz"
22752282
{
22762283
v8 :: Local < v8 :: Context > ctx = isolate -> GetCurrentContext ( ) ;
22772284
BindMap* bind_map = stmt->GetBindMap(isolate);
@@ -2308,9 +2315,9 @@ int Binder::BindObject (v8::Isolate * isolate, v8::Local <v8::Object> obj, State
23082315

23092316
return len;
23102317
}
2311-
#line 160 "./src/util/binder.lzz"
2318+
#line 167 "./src/util/binder.lzz"
23122319
Binder::Result Binder::BindArgs (v8::FunctionCallbackInfo <v8 :: Value> const & info, int argc, Statement * stmt)
2313-
#line 160 "./src/util/binder.lzz"
2320+
#line 167 "./src/util/binder.lzz"
23142321
{
23152322
v8 :: Isolate * isolate = info . GetIsolate ( ) ;
23162323
int count = 0;

src/better_sqlite3.hpp

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
#include <node.h>
1717
#include <node_object_wrap.h>
1818
#include <node_buffer.h>
19-
#line 156 "./src/util/macros.lzz"
19+
#line 158 "./src/util/macros.lzz"
2020
void SetPrototypeGetter(
2121
v8::Isolate* isolate,
2222
v8::Local<v8::External> data,
@@ -43,21 +43,21 @@ void ThrowError (char const * message);
4343
void ThrowTypeError (char const * message);
4444
#line 40 "./src/util/macros.lzz"
4545
void ThrowRangeError (char const * message);
46-
#line 103 "./src/util/macros.lzz"
46+
#line 105 "./src/util/macros.lzz"
4747
bool IS_SKIPPED (char c);
48-
#line 108 "./src/util/macros.lzz"
48+
#line 110 "./src/util/macros.lzz"
4949
template <typename T>
50-
#line 108 "./src/util/macros.lzz"
50+
#line 110 "./src/util/macros.lzz"
5151
T * ALLOC_ARRAY (size_t count);
52-
#line 113 "./src/util/macros.lzz"
52+
#line 115 "./src/util/macros.lzz"
5353
template <typename T>
54-
#line 113 "./src/util/macros.lzz"
54+
#line 115 "./src/util/macros.lzz"
5555
void FREE_ARRAY (T * array_pointer);
56-
#line 117 "./src/util/macros.lzz"
56+
#line 119 "./src/util/macros.lzz"
5757
v8::Local <v8::FunctionTemplate> NewConstructorTemplate (v8::Isolate * isolate, v8::Local <v8::External> data, v8::FunctionCallback func, char const * name);
58-
#line 128 "./src/util/macros.lzz"
58+
#line 130 "./src/util/macros.lzz"
5959
void SetPrototypeMethod (v8::Isolate * isolate, v8::Local <v8::External> data, v8::Local <v8::FunctionTemplate> recv, char const * name, v8::FunctionCallback func);
60-
#line 141 "./src/util/macros.lzz"
60+
#line 143 "./src/util/macros.lzz"
6161
void SetPrototypeSymbolMethod (v8::Isolate * isolate, v8::Local <v8::External> data, v8::Local <v8::FunctionTemplate> recv, v8::Local <v8::Symbol> symbol, v8::FunctionCallback func);
6262
#line 1 "./src/util/constants.lzz"
6363
class CS
@@ -830,21 +830,21 @@ class Binder
830830
void Fail (void (* Throw) (char const *), char const * message);
831831
#line 63 "./src/util/binder.lzz"
832832
int NextAnonIndex ();
833-
#line 69 "./src/util/binder.lzz"
833+
#line 76 "./src/util/binder.lzz"
834834
void BindValue (v8::Isolate * isolate, v8::Local <v8::Value> value, int index);
835-
#line 90 "./src/util/binder.lzz"
835+
#line 97 "./src/util/binder.lzz"
836836
int BindArray (v8::Isolate * isolate, v8::Local <v8::Array> arr);
837-
#line 116 "./src/util/binder.lzz"
837+
#line 123 "./src/util/binder.lzz"
838838
int BindObject (v8::Isolate * isolate, v8::Local <v8::Object> obj, Statement * stmt);
839-
#line 160 "./src/util/binder.lzz"
839+
#line 167 "./src/util/binder.lzz"
840840
Result BindArgs (v8::FunctionCallbackInfo <v8 :: Value> const & info, int argc, Statement * stmt);
841-
#line 200 "./src/util/binder.lzz"
841+
#line 207 "./src/util/binder.lzz"
842842
sqlite3_stmt * handle;
843-
#line 201 "./src/util/binder.lzz"
843+
#line 208 "./src/util/binder.lzz"
844844
int param_count;
845-
#line 202 "./src/util/binder.lzz"
845+
#line 209 "./src/util/binder.lzz"
846846
int anon_index;
847-
#line 203 "./src/util/binder.lzz"
847+
#line 210 "./src/util/binder.lzz"
848848
bool success;
849849
};
850850
#line 34 "./src/better_sqlite3.lzz"
@@ -906,25 +906,25 @@ LZZ_INLINE void SetFrozen (v8::Isolate * isolate, v8::Local <v8::Context> ctx, v
906906
{
907907
obj->DefineOwnProperty(ctx, key.Get(isolate), value, static_cast<v8::PropertyAttribute>(v8::DontDelete | v8::ReadOnly)).FromJust();
908908
}
909-
#line 103 "./src/util/macros.lzz"
909+
#line 105 "./src/util/macros.lzz"
910910
LZZ_INLINE bool IS_SKIPPED (char c)
911-
#line 103 "./src/util/macros.lzz"
911+
#line 105 "./src/util/macros.lzz"
912912
{
913913
return c == ' ' || c == ';' || (c >= '\t' && c <= '\r');
914914
}
915-
#line 108 "./src/util/macros.lzz"
915+
#line 110 "./src/util/macros.lzz"
916916
template <typename T>
917-
#line 108 "./src/util/macros.lzz"
917+
#line 110 "./src/util/macros.lzz"
918918
LZZ_INLINE T * ALLOC_ARRAY (size_t count)
919-
#line 108 "./src/util/macros.lzz"
919+
#line 110 "./src/util/macros.lzz"
920920
{
921921
return static_cast<T*>(::operator new[](count * sizeof(T)));
922922
}
923-
#line 113 "./src/util/macros.lzz"
923+
#line 115 "./src/util/macros.lzz"
924924
template <typename T>
925-
#line 113 "./src/util/macros.lzz"
925+
#line 115 "./src/util/macros.lzz"
926926
LZZ_INLINE void FREE_ARRAY (T * array_pointer)
927-
#line 113 "./src/util/macros.lzz"
927+
#line 115 "./src/util/macros.lzz"
928928
{
929929
::operator delete[](array_pointer);
930930
}

src/objects/statement.lzz

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public:
2929
int param_count = sqlite3_bind_parameter_count(handle);
3030
for (int i = 1; i <= param_count; ++i) {
3131
const char* name = sqlite3_bind_parameter_name(handle, i);
32-
if (name != NULL) bind_map->Add(isolate, name + 1, i);
32+
if (IS_NAMED_PARAMETER(name)) bind_map->Add(isolate, name + 1, i);
3333
}
3434
has_bind_map = true;
3535
return bind_map;

src/util/binder.lzz

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,14 @@ private:
6161
}
6262

6363
int NextAnonIndex() {
64-
while (sqlite3_bind_parameter_name(handle, ++anon_index) != NULL) {}
64+
// Find the next parameter index that isn't a named variable.
65+
while (true) {
66+
const char* name = sqlite3_bind_parameter_name(handle, ++anon_index);
67+
if (!IS_NAMED_PARAMETER(name)) {
68+
break;
69+
}
70+
}
71+
6572
return anon_index;
6673
}
6774

src/util/macros.lzz

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ void ThrowRangeError(const char* message) { EasyIsolate; isolate->ThrowException
8888
if (stmt->locked) \
8989
return ThrowTypeError("This statement is busy executing a query")
9090

91+
#define IS_NAMED_PARAMETER(name) (name != NULL && name[0] != '?')
92+
9193
#define first() 0
9294
#define second() 1
9395
#define third() 2

test/22.statement.all.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ describe('Statement#all()', function () {
9999
const rows = [{ a: 'foo', b: 1, c: 3.14, d: Buffer.alloc(4).fill(0xdd), e: null }];
100100
const SQL1 = 'SELECT * FROM entries WHERE a=? AND b=? AND c=? AND d=? AND e IS ?';
101101
const SQL2 = 'SELECT * FROM entries WHERE a=@a AND b=@b AND c=@c AND d=@d AND e IS @e';
102+
const SQL3 = 'SELECT * FROM entries WHERE a=?1 AND b=?1';
102103
let result = this.db.prepare(SQL1).all('foo', 1, 3.14, Buffer.alloc(4).fill(0xdd), null);
103104
expect(result).to.deep.equal(rows);
104105

@@ -114,6 +115,9 @@ describe('Statement#all()', function () {
114115
result = this.db.prepare(SQL2).all({ a: 'foo', b: 1, c: 3.14, d: Buffer.alloc(4).fill(0xaa), e: undefined });
115116
expect(result).to.deep.equal([]);
116117

118+
result = this.db.prepare(SQL3).all(123);
119+
expect(result).to.deep.equal([]);
120+
117121
expect(() =>
118122
this.db.prepare(SQL2).all({ a: 'foo', b: 1, c: 3.14, d: Buffer.alloc(4).fill(0xdd) })
119123
).to.throw(RangeError);
@@ -125,5 +129,17 @@ describe('Statement#all()', function () {
125129
expect(() =>
126130
this.db.prepare(SQL2).all({})
127131
).to.throw(RangeError);
132+
133+
expect(() =>
134+
this.db.prepare(SQL3).all({})
135+
).to.throw(RangeError);
136+
137+
expect(() =>
138+
this.db.prepare(SQL3).all([])
139+
).to.throw(RangeError);
140+
141+
expect(() =>
142+
this.db.prepare(SQL3).all(['one', 'too many'])
143+
).to.throw(RangeError);
128144
});
129145
});

0 commit comments

Comments
 (0)