@@ -31,8 +31,11 @@ pub struct VirtualStatement {
31
31
/// there are no more statements to execute and `reset()` must be called
32
32
index : Option < usize > ,
33
33
34
- /// tail of the most recently prepared SQL statement within this container
35
- tail : Bytes ,
34
+ /// The full input SQL.
35
+ sql : Arc < str > ,
36
+
37
+ /// The byte offset of the next statement to prepare in `sql`.
38
+ tail_offset : usize ,
36
39
37
40
/// underlying sqlite handles for each inner statement
38
41
/// a SQL query string in SQLite is broken up into N statements
@@ -44,6 +47,9 @@ pub struct VirtualStatement {
44
47
45
48
// each set of column names
46
49
pub ( crate ) column_names : SmallVec < [ Arc < HashMap < UStr , usize > > ; 1 ] > ,
50
+
51
+ /// Offsets into `sql` for each statement.
52
+ pub ( crate ) sql_offsets : SmallVec < [ usize ; 1 ] > ,
47
53
}
48
54
49
55
pub struct PreparedStatement < ' a > {
@@ -53,9 +59,7 @@ pub struct PreparedStatement<'a> {
53
59
}
54
60
55
61
impl VirtualStatement {
56
- pub ( crate ) fn new ( mut query : & str , persistent : bool ) -> Result < Self , Error > {
57
- query = query. trim ( ) ;
58
-
62
+ pub ( crate ) fn new ( query : Arc < str > , persistent : bool ) -> Result < Self , Error > {
59
63
if query. len ( ) > i32:: max_value ( ) as usize {
60
64
return Err ( err_protocol ! (
61
65
"query string must be smaller than {} bytes" ,
@@ -65,11 +69,13 @@ impl VirtualStatement {
65
69
66
70
Ok ( Self {
67
71
persistent,
68
- tail : Bytes :: from ( String :: from ( query) ) ,
72
+ sql : query,
73
+ tail_offset : 0 ,
69
74
handles : SmallVec :: with_capacity ( 1 ) ,
70
75
index : None ,
71
76
columns : SmallVec :: with_capacity ( 1 ) ,
72
77
column_names : SmallVec :: with_capacity ( 1 ) ,
78
+ sql_offsets : SmallVec :: with_capacity ( 1 ) ,
73
79
} )
74
80
}
75
81
@@ -84,11 +90,33 @@ impl VirtualStatement {
84
90
. or ( Some ( 0 ) ) ;
85
91
86
92
while self . handles . len ( ) <= self . index . unwrap_or ( 0 ) {
87
- if self . tail . is_empty ( ) {
93
+ let sql_offset = self . tail_offset ;
94
+
95
+ let query = self . sql . get ( sql_offset..) . unwrap_or ( "" ) ;
96
+
97
+ if query. is_empty ( ) {
88
98
return Ok ( None ) ;
89
99
}
90
100
91
- if let Some ( statement) = prepare ( conn. as_ptr ( ) , & mut self . tail , self . persistent ) ? {
101
+ let ( consumed, maybe_statement) = try_prepare (
102
+ conn. as_ptr ( ) ,
103
+ query,
104
+ self . persistent ,
105
+ ) . map_err ( |mut e| {
106
+ // `sqlite3_offset()` returns the offset into the passed string,
107
+ // but we want the offset into the original SQL string.
108
+ e. add_offset ( sql_offset) ;
109
+ e. find_error_pos ( & self . sql ) ;
110
+ e
111
+ } ) ?;
112
+
113
+ self . tail_offset = self . tail_offset
114
+ . checked_add ( consumed)
115
+ // Highly unlikely, but since we're dealing with `unsafe` here
116
+ // it's best not to fool around.
117
+ . ok_or_else ( || Error :: Protocol ( format ! ( "overflow adding {n:?} bytes to tail_offset {tail_offset:?}" ) ) ) ?;
118
+
119
+ if let Some ( statement) = maybe_statement {
92
120
let num = statement. column_count ( ) ;
93
121
94
122
let mut columns = Vec :: with_capacity ( num) ;
@@ -112,6 +140,7 @@ impl VirtualStatement {
112
140
self . handles . push ( statement) ;
113
141
self . columns . push ( Arc :: new ( columns) ) ;
114
142
self . column_names . push ( Arc :: new ( column_names) ) ;
143
+ self . sql_offsets . push ( sql_offset) ;
115
144
}
116
145
}
117
146
@@ -140,11 +169,13 @@ impl VirtualStatement {
140
169
}
141
170
}
142
171
143
- fn prepare (
172
+ /// Attempt to prepare one statement, returning the number of bytes consumed from `sql`,
173
+ /// and the statement handle if successful.
174
+ fn try_prepare (
144
175
conn : * mut sqlite3 ,
145
- query : & mut Bytes ,
176
+ query : & str ,
146
177
persistent : bool ,
147
- ) -> Result < Option < StatementHandle > , Error > {
178
+ ) -> Result < ( usize , Option < StatementHandle > ) , SqliteError > {
148
179
let mut flags = 0 ;
149
180
150
181
// For some reason, when building with the `sqlcipher` feature enabled
@@ -158,40 +189,37 @@ fn prepare(
158
189
flags |= SQLITE_PREPARE_PERSISTENT as u32 ;
159
190
}
160
191
161
- while !query. is_empty ( ) {
162
- let mut statement_handle: * mut sqlite3_stmt = null_mut ( ) ;
163
- let mut tail: * const c_char = null ( ) ;
164
-
165
- let query_ptr = query. as_ptr ( ) as * const c_char ;
166
- let query_len = query. len ( ) as i32 ;
167
-
168
- // <https://www.sqlite.org/c3ref/prepare.html>
169
- let status = unsafe {
170
- sqlite3_prepare_v3 (
171
- conn,
172
- query_ptr,
173
- query_len,
174
- flags,
175
- & mut statement_handle,
176
- & mut tail,
177
- )
178
- } ;
179
-
180
- if status != SQLITE_OK {
181
- return Err ( SqliteError :: new ( conn) . into ( ) ) ;
182
- }
183
-
184
- // tail should point to the first byte past the end of the first SQL
185
- // statement in zSql. these routines only compile the first statement,
186
- // so tail is left pointing to what remains un-compiled.
192
+ let mut statement_handle: * mut sqlite3_stmt = null_mut ( ) ;
193
+ let mut tail_ptr: * const c_char = null ( ) ;
194
+
195
+ let query_ptr = query. as_ptr ( ) as * const c_char ;
196
+ let query_len = query. len ( ) as i32 ;
197
+
198
+ // <https://www.sqlite.org/c3ref/prepare.html>
199
+ let status = unsafe {
200
+ sqlite3_prepare_v3 (
201
+ conn,
202
+ query_ptr,
203
+ query_len,
204
+ flags,
205
+ & mut statement_handle,
206
+ & mut tail_ptr,
207
+ )
208
+ } ;
209
+
210
+ if status != SQLITE_OK {
211
+ // Note: `offset` and `error_pos` will be updated in `VirtualStatement::prepare_next()`.
212
+ return Err ( SqliteError :: new ( conn) ) ;
213
+ }
187
214
188
- let n = ( tail as usize ) - ( query_ptr as usize ) ;
189
- query. advance ( n) ;
215
+ // tail should point to the first byte past the end of the first SQL
216
+ // statement in zSql. these routines only compile the first statement,
217
+ // so tail is left pointing to what remains un-compiled.
190
218
191
- if let Some ( handle) = NonNull :: new ( statement_handle) {
192
- return Ok ( Some ( StatementHandle :: new ( handle) ) ) ;
193
- }
194
- }
219
+ let consumed = ( tail_ptr as usize ) - ( query_ptr as usize ) ;
195
220
196
- Ok ( None )
221
+ Ok ( (
222
+ consumed,
223
+ NonNull :: new ( statement_handle) . map ( StatementHandle :: new) ,
224
+ ) )
197
225
}
0 commit comments