1use std::error::Error;
2use std::ffi::{CStr, CString};
3use std::fmt;
4use std::os::raw::c_char;
5use std::ptr;
6
7use anyhow::{Result, anyhow};
8use libsqlite3_sys::{
9 SQLITE_BLOB, SQLITE_DONE, SQLITE_FLOAT, SQLITE_INTEGER, SQLITE_NULL, SQLITE_OK, SQLITE_ROW,
10 SQLITE_TEXT, SQLITE_TRANSIENT, sqlite3, sqlite3_bind_blob, sqlite3_bind_double,
11 sqlite3_bind_int64, sqlite3_bind_null, sqlite3_bind_text, sqlite3_changes, sqlite3_column_blob,
12 sqlite3_column_bytes, sqlite3_column_count, sqlite3_column_double, sqlite3_column_int64,
13 sqlite3_column_name, sqlite3_column_text, sqlite3_column_type, sqlite3_errmsg,
14 sqlite3_extended_errcode, sqlite3_file_control, sqlite3_finalize, sqlite3_last_insert_rowid,
15 sqlite3_prepare_v2, sqlite3_sql, sqlite3_step, sqlite3_stmt,
16};
17pub use sqlite_types::{BindParam, ColumnValue, ExecResult, ExecuteResult, QueryResult};
18
19use crate::vfs::{FCNTL_COMMIT_PAGE_CAP, SqliteTransactionTooLargeError};
20
21#[derive(Clone, Copy, Debug, Default)]
22pub struct SqliteQueryProfile {
23 pub bind_count: u64,
24 pub bind_logical_bytes: u64,
25 pub result_rows: u64,
26 pub result_columns: u64,
27 pub result_logical_bytes: u64,
28}
29
30#[derive(Debug)]
31pub struct SqliteStatementError {
32 pub code: i32,
33 pub statement_index: u32,
34 pub message: String,
35}
36
37impl fmt::Display for SqliteStatementError {
38 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39 f.write_str(&self.message)
40 }
41}
42
43impl Error for SqliteStatementError {}
44
45const RESULT_VALUE_BYTES: u64 = 8;
47
48#[derive(Debug)]
51pub struct SqliteResultTooLargeError {
52 pub max_bytes: u64,
53}
54
55impl fmt::Display for SqliteResultTooLargeError {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 write!(
58 f,
59 "sqlite response too large: the result passes the {} byte limit",
60 self.max_bytes
61 )
62 }
63}
64
65impl Error for SqliteResultTooLargeError {}
66
67struct ResultBudget {
71 max_bytes: Option<u64>,
72 used_bytes: u64,
73}
74
75impl ResultBudget {
76 fn new(max_bytes: Option<u64>) -> Self {
77 Self {
78 max_bytes,
79 used_bytes: 0,
80 }
81 }
82
83 fn charge(&mut self, stmt: *mut sqlite3_stmt, index: i32) -> Result<u64> {
87 if self.max_bytes.is_none() {
88 return Ok(0);
89 }
90
91 let value_bytes = match unsafe { sqlite3_column_type(stmt, index) } {
92 SQLITE_TEXT | SQLITE_BLOB => unsafe { sqlite3_column_bytes(stmt, index) }.max(0) as u64,
93 _ => 0,
94 };
95 self.add(RESULT_VALUE_BYTES.saturating_add(value_bytes))?;
96 Ok(value_bytes)
97 }
98
99 fn charge_conversion(&mut self, charged_bytes: u64, value: &ColumnValue) -> Result<()> {
102 if let ColumnValue::Text(text) = value {
103 let text_bytes = text.len() as u64;
104 if text_bytes > charged_bytes {
105 self.add(text_bytes - charged_bytes)?;
106 }
107 }
108 Ok(())
109 }
110
111 fn add(&mut self, bytes: u64) -> Result<()> {
112 let Some(max_bytes) = self.max_bytes else {
113 return Ok(());
114 };
115
116 self.used_bytes = self.used_bytes.saturating_add(bytes);
117 if self.used_bytes > max_bytes {
118 return Err(SqliteResultTooLargeError { max_bytes }.into());
119 }
120
121 Ok(())
122 }
123}
124
125fn read_row(
127 stmt: *mut sqlite3_stmt,
128 column_count: usize,
129 budget: &mut ResultBudget,
130) -> Result<Vec<ColumnValue>> {
131 let mut row = Vec::with_capacity(column_count);
132 for index in 0..column_count {
133 let charged_bytes = budget.charge(stmt, index as i32)?;
134 let value = column_value(stmt, index as i32);
135 budget.charge_conversion(charged_bytes, &value)?;
136 row.push(value);
137 }
138 Ok(row)
139}
140
141fn read_row_profiled(
143 stmt: *mut sqlite3_stmt,
144 column_count: usize,
145 budget: &mut ResultBudget,
146 profile: &mut SqliteQueryProfile,
147) -> Result<Vec<ColumnValue>> {
148 let mut row = Vec::with_capacity(column_count);
149 for index in 0..column_count {
150 let charged_bytes = budget.charge(stmt, index as i32)?;
151 let value = column_value_profiled(stmt, index as i32, profile);
152 budget.charge_conversion(charged_bytes, &value)?;
153 row.push(value);
154 }
155 Ok(row)
156}
157
158pub fn execute_statement(
159 db: *mut sqlite3,
160 sql: &str,
161 params: Option<&[BindParam]>,
162) -> Result<ExecResult> {
163 let c_sql = CString::new(sql).map_err(|err| anyhow!(err.to_string()))?;
164 let mut stmt = ptr::null_mut();
165 let rc = unsafe { sqlite3_prepare_v2(db, c_sql.as_ptr(), -1, &mut stmt, ptr::null_mut()) };
166 if rc != SQLITE_OK {
167 return Err(sqlite_error(db, 0, "failed to prepare sqlite statement"));
168 }
169 if stmt.is_null() {
170 return Ok(ExecResult { changes: 0 });
171 }
172
173 let result = (|| {
174 refuse_oversize_vacuum(db, stmt)?;
175 if let Some(params) = params {
176 bind_params(db, stmt, params)?;
177 }
178
179 loop {
180 let step_rc = unsafe { sqlite3_step(stmt) };
181 if step_rc == SQLITE_DONE {
182 break;
183 }
184 if step_rc != SQLITE_ROW {
185 return Err(sqlite_error(db, 0, "failed to execute sqlite statement"));
186 }
187 }
188
189 Ok(ExecResult {
190 changes: unsafe { sqlite3_changes(db) as i64 },
191 })
192 })();
193
194 unsafe {
195 sqlite3_finalize(stmt);
196 }
197
198 result
199}
200
201pub fn query_statement(
202 db: *mut sqlite3,
203 sql: &str,
204 params: Option<&[BindParam]>,
205) -> Result<QueryResult> {
206 let c_sql = CString::new(sql).map_err(|err| anyhow!(err.to_string()))?;
207 let mut stmt = ptr::null_mut();
208 let rc = unsafe { sqlite3_prepare_v2(db, c_sql.as_ptr(), -1, &mut stmt, ptr::null_mut()) };
209 if rc != SQLITE_OK {
210 return Err(sqlite_error(db, 0, "failed to prepare sqlite query"));
211 }
212 if stmt.is_null() {
213 return Ok(QueryResult {
214 columns: Vec::new(),
215 rows: Vec::new(),
216 });
217 }
218
219 let result = (|| {
220 refuse_oversize_vacuum(db, stmt)?;
221 if let Some(params) = params {
222 bind_params(db, stmt, params)?;
223 }
224
225 let columns = collect_columns(stmt);
226 let mut rows = Vec::new();
227
228 loop {
229 let step_rc = unsafe { sqlite3_step(stmt) };
230 if step_rc == SQLITE_DONE {
231 break;
232 }
233 if step_rc != SQLITE_ROW {
234 return Err(sqlite_error(db, 0, "failed to step sqlite query"));
235 }
236
237 let mut row = Vec::with_capacity(columns.len());
238 for index in 0..columns.len() {
239 row.push(column_value(stmt, index as i32));
240 }
241 rows.push(row);
242 }
243
244 Ok(QueryResult { columns, rows })
245 })();
246
247 unsafe {
248 sqlite3_finalize(stmt);
249 }
250
251 result
252}
253
254pub fn execute_single_statement(
257 db: *mut sqlite3,
258 sql: &str,
259 params: Option<&[BindParam]>,
260 max_result_bytes: Option<u64>,
261) -> Result<ExecuteResult> {
262 let c_sql = CString::new(sql).map_err(|err| anyhow!(err.to_string()))?;
263 let mut stmt = ptr::null_mut();
264 let mut tail = ptr::null();
265 let rc = unsafe { sqlite3_prepare_v2(db, c_sql.as_ptr(), -1, &mut stmt, &mut tail) };
266 if rc != SQLITE_OK {
267 return Err(sqlite_error(
268 db,
269 0,
270 "failed to prepare sqlite execute statement",
271 ));
272 }
273 if has_non_whitespace_tail(tail) {
274 if !stmt.is_null() {
275 unsafe {
276 sqlite3_finalize(stmt);
277 }
278 }
279 return Err(SqliteStatementError {
280 code: -1,
281 statement_index: 0,
282 message: "sqlite execute only supports a single statement".to_owned(),
283 }
284 .into());
285 }
286 if stmt.is_null() {
287 return Ok(ExecuteResult {
288 columns: Vec::new(),
289 rows: Vec::new(),
290 changes: 0,
291 last_insert_row_id: None,
292 });
293 }
294
295 let result = (|| {
296 refuse_oversize_vacuum(db, stmt)?;
297 if let Some(params) = params {
298 bind_params(db, stmt, params)?;
299 }
300
301 let columns = collect_columns(stmt);
302 let mut rows = Vec::new();
303 let mut budget = ResultBudget::new(max_result_bytes);
304 loop {
305 let step_rc = unsafe { sqlite3_step(stmt) };
306 if step_rc == SQLITE_DONE {
307 break;
308 }
309 if step_rc != SQLITE_ROW {
310 return Err(sqlite_error(
311 db,
312 0,
313 "failed to step sqlite execute statement",
314 ));
315 }
316
317 rows.push(read_row(stmt, columns.len(), &mut budget)?);
318 }
319
320 let changes = unsafe { sqlite3_changes(db) as i64 };
321 Ok(ExecuteResult {
322 columns,
323 rows,
324 changes,
325 last_insert_row_id: (changes > 0).then(|| unsafe { sqlite3_last_insert_rowid(db) }),
326 })
327 })();
328
329 unsafe {
330 sqlite3_finalize(stmt);
331 }
332
333 result
334}
335
336pub fn execute_single_statement_profiled(
338 db: *mut sqlite3,
339 sql: &str,
340 params: Option<&[BindParam]>,
341 max_result_bytes: Option<u64>,
342) -> Result<(ExecuteResult, SqliteQueryProfile)> {
343 let c_sql = CString::new(sql).map_err(|err| anyhow!(err.to_string()))?;
344 let mut stmt = ptr::null_mut();
345 let mut tail = ptr::null();
346 let rc = unsafe { sqlite3_prepare_v2(db, c_sql.as_ptr(), -1, &mut stmt, &mut tail) };
347 if rc != SQLITE_OK {
348 return Err(sqlite_error(
349 db,
350 0,
351 "failed to prepare sqlite execute statement",
352 ));
353 }
354 if has_non_whitespace_tail(tail) {
355 if !stmt.is_null() {
356 unsafe {
357 sqlite3_finalize(stmt);
358 }
359 }
360 return Err(SqliteStatementError {
361 code: -1,
362 statement_index: 0,
363 message: "sqlite execute only supports a single statement".to_owned(),
364 }
365 .into());
366 }
367 if stmt.is_null() {
368 return Ok((
369 ExecuteResult {
370 columns: Vec::new(),
371 rows: Vec::new(),
372 changes: 0,
373 last_insert_row_id: None,
374 },
375 SqliteQueryProfile::default(),
376 ));
377 }
378
379 let result = (|| {
380 refuse_oversize_vacuum(db, stmt)?;
381 let mut profile = SqliteQueryProfile::default();
382 if let Some(params) = params {
383 bind_params_profiled(db, stmt, params, &mut profile)?;
384 }
385
386 let columns = collect_columns(stmt);
387 profile.result_columns = columns.len() as u64;
388 let mut rows = Vec::new();
389 let mut budget = ResultBudget::new(max_result_bytes);
390 loop {
391 let step_rc = unsafe { sqlite3_step(stmt) };
392 if step_rc == SQLITE_DONE {
393 break;
394 }
395 if step_rc != SQLITE_ROW {
396 return Err(sqlite_error(
397 db,
398 0,
399 "failed to step sqlite execute statement",
400 ));
401 }
402
403 rows.push(read_row_profiled(
404 stmt,
405 columns.len(),
406 &mut budget,
407 &mut profile,
408 )?);
409 profile.result_rows = profile.result_rows.saturating_add(1);
410 }
411
412 let changes = unsafe { sqlite3_changes(db) as i64 };
413 Ok((
414 ExecuteResult {
415 columns,
416 rows,
417 changes,
418 last_insert_row_id: (changes > 0).then(|| unsafe { sqlite3_last_insert_rowid(db) }),
419 },
420 profile,
421 ))
422 })();
423
424 unsafe {
425 sqlite3_finalize(stmt);
426 }
427
428 result
429}
430
431pub fn exec_statements(
434 db: *mut sqlite3,
435 sql: &str,
436 max_result_bytes: Option<u64>,
437) -> Result<QueryResult> {
438 let c_sql = CString::new(sql).map_err(|err| anyhow!(err.to_string()))?;
439 let mut remaining = c_sql.as_ptr();
440 let mut statement_index = 0_u32;
441 let mut final_result = QueryResult {
442 columns: Vec::new(),
443 rows: Vec::new(),
444 };
445
446 while unsafe { *remaining } != 0 {
447 let mut stmt = ptr::null_mut();
448 let mut tail = ptr::null();
449 let rc = unsafe { sqlite3_prepare_v2(db, remaining, -1, &mut stmt, &mut tail) };
450 if rc != SQLITE_OK {
451 return Err(sqlite_error(
452 db,
453 statement_index,
454 "failed to prepare sqlite exec statement",
455 ));
456 }
457
458 if stmt.is_null() {
459 if tail == remaining {
460 break;
461 }
462 remaining = tail;
463 continue;
464 }
465
466 if unsafe { sqlite3_column_count(stmt) } > 0 {
469 final_result = QueryResult {
470 columns: Vec::new(),
471 rows: Vec::new(),
472 };
473 }
474
475 let result = (|| {
476 refuse_oversize_vacuum(db, stmt)?;
477 let columns = collect_columns(stmt);
478 let mut rows = Vec::new();
479 let mut budget = ResultBudget::new(max_result_bytes);
480 loop {
481 let step_rc = unsafe { sqlite3_step(stmt) };
482 if step_rc == SQLITE_DONE {
483 break;
484 }
485 if step_rc != SQLITE_ROW {
486 return Err(sqlite_error(
487 db,
488 statement_index,
489 "failed to step sqlite exec statement",
490 ));
491 }
492
493 rows.push(read_row(stmt, columns.len(), &mut budget)?);
494 }
495
496 Ok((columns, rows))
497 })();
498
499 unsafe {
500 sqlite3_finalize(stmt);
501 }
502
503 let (columns, rows) = result?;
504 if !columns.is_empty() || !rows.is_empty() {
505 final_result = QueryResult { columns, rows };
506 }
507
508 if tail == remaining {
509 break;
510 }
511 remaining = tail;
512 statement_index = statement_index.saturating_add(1);
513 }
514
515 Ok(final_result)
516}
517
518pub fn exec_statements_profiled(
520 db: *mut sqlite3,
521 sql: &str,
522 max_result_bytes: Option<u64>,
523) -> Result<(QueryResult, SqliteQueryProfile)> {
524 let c_sql = CString::new(sql).map_err(|err| anyhow!(err.to_string()))?;
525 let mut remaining = c_sql.as_ptr();
526 let mut statement_index = 0_u32;
527 let mut final_result = QueryResult {
528 columns: Vec::new(),
529 rows: Vec::new(),
530 };
531 let mut final_profile = SqliteQueryProfile::default();
532
533 while unsafe { *remaining } != 0 {
534 let mut stmt = ptr::null_mut();
535 let mut tail = ptr::null();
536 let rc = unsafe { sqlite3_prepare_v2(db, remaining, -1, &mut stmt, &mut tail) };
537 if rc != SQLITE_OK {
538 return Err(sqlite_error(
539 db,
540 statement_index,
541 "failed to prepare sqlite exec statement",
542 ));
543 }
544
545 if stmt.is_null() {
546 if tail == remaining {
547 break;
548 }
549 remaining = tail;
550 continue;
551 }
552
553 if unsafe { sqlite3_column_count(stmt) } > 0 {
556 final_result = QueryResult {
557 columns: Vec::new(),
558 rows: Vec::new(),
559 };
560 }
561
562 let result = (|| {
563 refuse_oversize_vacuum(db, stmt)?;
564 let columns = collect_columns(stmt);
565 let mut profile = SqliteQueryProfile {
566 result_columns: columns.len() as u64,
567 ..Default::default()
568 };
569 let mut rows = Vec::new();
570 let mut budget = ResultBudget::new(max_result_bytes);
571 loop {
572 let step_rc = unsafe { sqlite3_step(stmt) };
573 if step_rc == SQLITE_DONE {
574 break;
575 }
576 if step_rc != SQLITE_ROW {
577 return Err(sqlite_error(
578 db,
579 statement_index,
580 "failed to step sqlite exec statement",
581 ));
582 }
583
584 rows.push(read_row_profiled(
585 stmt,
586 columns.len(),
587 &mut budget,
588 &mut profile,
589 )?);
590 profile.result_rows = profile.result_rows.saturating_add(1);
591 }
592
593 Ok((columns, rows, profile))
594 })();
595
596 unsafe {
597 sqlite3_finalize(stmt);
598 }
599
600 let (columns, rows, profile) = result?;
601 if !columns.is_empty() || !rows.is_empty() {
602 final_result = QueryResult { columns, rows };
603 final_profile = profile;
604 }
605
606 if tail == remaining {
607 break;
608 }
609 remaining = tail;
610 statement_index = statement_index.saturating_add(1);
611 }
612
613 Ok((final_result, final_profile))
614}
615
616fn bind_params(
617 db: *mut sqlite3,
618 stmt: *mut libsqlite3_sys::sqlite3_stmt,
619 params: &[BindParam],
620) -> Result<()> {
621 for (index, param) in params.iter().enumerate() {
622 bind_param(db, stmt, (index + 1) as i32, param)?;
623 }
624
625 Ok(())
626}
627
628fn bind_params_profiled(
629 db: *mut sqlite3,
630 stmt: *mut libsqlite3_sys::sqlite3_stmt,
631 params: &[BindParam],
632 profile: &mut SqliteQueryProfile,
633) -> Result<()> {
634 for (index, param) in params.iter().enumerate() {
635 profile.bind_count = profile.bind_count.saturating_add(1);
636 profile.bind_logical_bytes = profile
637 .bind_logical_bytes
638 .saturating_add(logical_bind_bytes(param));
639 bind_param(db, stmt, (index + 1) as i32, param)?;
640 }
641
642 Ok(())
643}
644
645fn bind_param(
646 db: *mut sqlite3,
647 stmt: *mut libsqlite3_sys::sqlite3_stmt,
648 bind_index: i32,
649 param: &BindParam,
650) -> Result<()> {
651 let rc = match param {
652 BindParam::Null => unsafe { sqlite3_bind_null(stmt, bind_index) },
653 BindParam::Integer(value) => unsafe { sqlite3_bind_int64(stmt, bind_index, *value) },
654 BindParam::Float(value) => unsafe { sqlite3_bind_double(stmt, bind_index, *value) },
655 BindParam::Text(value) => unsafe {
656 sqlite3_bind_text(
657 stmt,
658 bind_index,
659 value.as_ptr() as *const c_char,
660 value.len() as i32,
661 SQLITE_TRANSIENT(),
662 )
663 },
664 BindParam::Blob(value) => unsafe {
665 sqlite3_bind_blob(
666 stmt,
667 bind_index,
668 value.as_ptr() as *const _,
669 value.len() as i32,
670 SQLITE_TRANSIENT(),
671 )
672 },
673 };
674
675 if rc != SQLITE_OK {
676 return Err(sqlite_error(db, 0, "failed to bind sqlite parameter"));
677 }
678
679 Ok(())
680}
681
682fn collect_columns(stmt: *mut libsqlite3_sys::sqlite3_stmt) -> Vec<String> {
683 let column_count = unsafe { sqlite3_column_count(stmt) };
684 (0..column_count)
685 .map(|index| unsafe {
686 let name_ptr = sqlite3_column_name(stmt, index);
687 if name_ptr.is_null() {
688 String::new()
689 } else {
690 CStr::from_ptr(name_ptr).to_string_lossy().into_owned()
691 }
692 })
693 .collect()
694}
695
696fn column_value(stmt: *mut libsqlite3_sys::sqlite3_stmt, index: i32) -> ColumnValue {
697 match unsafe { sqlite3_column_type(stmt, index) } {
698 SQLITE_NULL => ColumnValue::Null,
699 SQLITE_INTEGER => ColumnValue::Integer(unsafe { sqlite3_column_int64(stmt, index) }),
700 SQLITE_FLOAT => ColumnValue::Float(unsafe { sqlite3_column_double(stmt, index) }),
701 SQLITE_TEXT => {
702 let text_ptr = unsafe { sqlite3_column_text(stmt, index) };
703 if text_ptr.is_null() {
704 ColumnValue::Null
705 } else {
706 let text_len = unsafe { sqlite3_column_bytes(stmt, index) } as usize;
707 let text = String::from_utf8_lossy(unsafe {
708 std::slice::from_raw_parts(text_ptr as *const u8, text_len)
709 })
710 .into_owned();
711 ColumnValue::Text(text)
712 }
713 }
714 SQLITE_BLOB => {
715 let blob_ptr = unsafe { sqlite3_column_blob(stmt, index) };
716 let blob_len = unsafe { sqlite3_column_bytes(stmt, index) } as usize;
717 if blob_len == 0 {
720 ColumnValue::Blob(Vec::new())
721 } else if blob_ptr.is_null() {
722 ColumnValue::Null
723 } else {
724 let blob = unsafe { std::slice::from_raw_parts(blob_ptr as *const u8, blob_len) };
725 ColumnValue::Blob(blob.to_vec())
726 }
727 }
728 _ => ColumnValue::Null,
729 }
730}
731
732fn column_value_profiled(
733 stmt: *mut libsqlite3_sys::sqlite3_stmt,
734 index: i32,
735 profile: &mut SqliteQueryProfile,
736) -> ColumnValue {
737 let mut logical_bytes = 0_u64;
738 let value = match unsafe { sqlite3_column_type(stmt, index) } {
739 SQLITE_NULL => ColumnValue::Null,
740 SQLITE_INTEGER => {
741 logical_bytes = 8;
742 ColumnValue::Integer(unsafe { sqlite3_column_int64(stmt, index) })
743 }
744 SQLITE_FLOAT => {
745 logical_bytes = 8;
746 ColumnValue::Float(unsafe { sqlite3_column_double(stmt, index) })
747 }
748 SQLITE_TEXT => {
749 let text_ptr = unsafe { sqlite3_column_text(stmt, index) };
750 if text_ptr.is_null() {
751 ColumnValue::Null
752 } else {
753 let text_len = unsafe { sqlite3_column_bytes(stmt, index) } as usize;
754 logical_bytes = text_len as u64;
755 let text = String::from_utf8_lossy(unsafe {
756 std::slice::from_raw_parts(text_ptr as *const u8, text_len)
757 })
758 .into_owned();
759 ColumnValue::Text(text)
760 }
761 }
762 SQLITE_BLOB => {
763 let blob_ptr = unsafe { sqlite3_column_blob(stmt, index) };
764 let blob_len = unsafe { sqlite3_column_bytes(stmt, index) } as usize;
765 if blob_len == 0 {
768 ColumnValue::Blob(Vec::new())
769 } else if blob_ptr.is_null() {
770 ColumnValue::Null
771 } else {
772 logical_bytes = blob_len as u64;
773 let blob = unsafe { std::slice::from_raw_parts(blob_ptr as *const u8, blob_len) };
774 ColumnValue::Blob(blob.to_vec())
775 }
776 }
777 _ => ColumnValue::Null,
778 };
779 profile.result_logical_bytes = profile.result_logical_bytes.saturating_add(logical_bytes);
780 value
781}
782
783fn logical_bind_bytes(param: &BindParam) -> u64 {
784 match param {
785 BindParam::Null => 0,
786 BindParam::Integer(_) | BindParam::Float(_) => 8,
787 BindParam::Text(value) => value.len() as u64,
788 BindParam::Blob(value) => value.len() as u64,
789 }
790}
791
792fn refuse_oversize_vacuum(db: *mut sqlite3, stmt: *mut sqlite3_stmt) -> Result<()> {
801 let sql = unsafe { sqlite3_sql(stmt) };
802 if sql.is_null() {
803 return Ok(());
804 }
805 let sql = unsafe { CStr::from_ptr(sql) }.to_string_lossy();
806 if !is_in_place_vacuum(&sql) {
807 return Ok(());
808 }
809
810 let mut max_pages: i64 = 0;
811 let rc = unsafe {
812 sqlite3_file_control(
813 db,
814 c"main".as_ptr(),
815 FCNTL_COMMIT_PAGE_CAP,
816 (&mut max_pages as *mut i64).cast(),
817 )
818 };
819 if rc != SQLITE_OK {
820 return Ok(());
821 }
822
823 let (Some(page_count), Some(freelist_count), Some(page_size)) = (
824 pragma_i64(db, c"PRAGMA main.page_count;"),
825 pragma_i64(db, c"PRAGMA main.freelist_count;"),
826 pragma_i64(db, c"PRAGMA main.page_size;"),
827 ) else {
828 return Ok(());
829 };
830 let in_use_pages = page_count.saturating_sub(freelist_count);
831 if in_use_pages > max_pages {
832 return Err(SqliteTransactionTooLargeError {
833 max_pages: max_pages as usize,
834 page_size: page_size as usize,
835 }
836 .into());
837 }
838
839 Ok(())
840}
841
842pub(crate) fn is_in_place_vacuum(sql: &str) -> bool {
845 let words = sql_words(sql);
846 let mut words = words.iter();
847 if !words
848 .next()
849 .is_some_and(|word| word.eq_ignore_ascii_case("vacuum"))
850 {
851 return false;
852 }
853
854 match words.next() {
855 None => true,
856 Some(word) if word.eq_ignore_ascii_case("into") => false,
857 Some(schema) => {
858 let schema = schema.trim_matches(|c| matches!(c, '"' | '`' | '[' | ']' | '\''));
859 schema.eq_ignore_ascii_case("main")
860 && !words.any(|word| word.eq_ignore_ascii_case("into"))
861 }
862 }
863}
864
865fn sql_words(sql: &str) -> Vec<&str> {
868 let mut words = Vec::new();
869 let mut rest = sql;
870 loop {
871 rest = rest.trim_start_matches(|c: char| c.is_ascii_whitespace() || c == ';');
872 if rest.is_empty() {
873 return words;
874 }
875 if let Some(comment) = rest.strip_prefix("--") {
876 rest = comment.find('\n').map_or("", |end| &comment[end + 1..]);
877 continue;
878 }
879 if let Some(comment) = rest.strip_prefix("/*") {
880 rest = comment.find("*/").map_or("", |end| &comment[end + 2..]);
881 continue;
882 }
883 let end = rest
884 .char_indices()
885 .find(|(index, c)| {
886 c.is_ascii_whitespace()
887 || *c == ';' || rest[*index..].starts_with("--")
888 || rest[*index..].starts_with("/*")
889 })
890 .map_or(rest.len(), |(index, _)| index);
891 words.push(&rest[..end]);
892 rest = &rest[end..];
893 }
894}
895
896fn pragma_i64(db: *mut sqlite3, sql: &CStr) -> Option<i64> {
898 let mut stmt = ptr::null_mut();
899 let rc = unsafe { sqlite3_prepare_v2(db, sql.as_ptr(), -1, &mut stmt, ptr::null_mut()) };
900 if rc != SQLITE_OK || stmt.is_null() {
901 return None;
902 }
903 let value = if unsafe { sqlite3_step(stmt) } == SQLITE_ROW {
904 Some(unsafe { sqlite3_column_int64(stmt, 0) })
905 } else {
906 None
907 };
908 unsafe {
909 sqlite3_finalize(stmt);
910 }
911 value
912}
913
914fn has_non_whitespace_tail(tail: *const c_char) -> bool {
915 if tail.is_null() {
916 return false;
917 }
918
919 let bytes = unsafe { CStr::from_ptr(tail).to_bytes() };
920 bytes.iter().any(|byte| !byte.is_ascii_whitespace())
921}
922
923fn sqlite_error(db: *mut sqlite3, statement_index: u32, context: &str) -> anyhow::Error {
924 let (code, detail) = unsafe {
925 if db.is_null() {
926 (-1, "unknown sqlite error".to_string())
927 } else {
928 (
929 sqlite3_extended_errcode(db),
930 CStr::from_ptr(sqlite3_errmsg(db))
931 .to_string_lossy()
932 .into_owned(),
933 )
934 }
935 };
936 SqliteStatementError {
937 code,
938 statement_index,
939 message: format!("{context}: {detail}"),
940 }
941 .into()
942}
943
944