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 let _stmt = FinalizeOnDrop(stmt);
167 if rc != SQLITE_OK {
168 return Err(sqlite_error(db, 0, "failed to prepare sqlite statement"));
169 }
170 if stmt.is_null() {
171 return Ok(ExecResult { changes: 0 });
172 }
173
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
194pub fn query_statement(
195 db: *mut sqlite3,
196 sql: &str,
197 params: Option<&[BindParam]>,
198) -> Result<QueryResult> {
199 let c_sql = CString::new(sql).map_err(|err| anyhow!(err.to_string()))?;
200 let mut stmt = ptr::null_mut();
201 let rc = unsafe { sqlite3_prepare_v2(db, c_sql.as_ptr(), -1, &mut stmt, ptr::null_mut()) };
202 let _stmt = FinalizeOnDrop(stmt);
203 if rc != SQLITE_OK {
204 return Err(sqlite_error(db, 0, "failed to prepare sqlite query"));
205 }
206 if stmt.is_null() {
207 return Ok(QueryResult {
208 columns: Vec::new(),
209 rows: Vec::new(),
210 });
211 }
212
213 refuse_oversize_vacuum(db, stmt)?;
214 if let Some(params) = params {
215 bind_params(db, stmt, params)?;
216 }
217
218 let columns = collect_columns(stmt);
219 let mut rows = Vec::new();
220
221 loop {
222 let step_rc = unsafe { sqlite3_step(stmt) };
223 if step_rc == SQLITE_DONE {
224 break;
225 }
226 if step_rc != SQLITE_ROW {
227 return Err(sqlite_error(db, 0, "failed to step sqlite query"));
228 }
229
230 let mut row = Vec::with_capacity(columns.len());
231 for index in 0..columns.len() {
232 row.push(column_value(stmt, index as i32));
233 }
234 rows.push(row);
235 }
236
237 Ok(QueryResult { columns, rows })
238}
239
240pub fn execute_single_statement(
243 db: *mut sqlite3,
244 sql: &str,
245 params: Option<&[BindParam]>,
246 max_result_bytes: Option<u64>,
247) -> Result<ExecuteResult> {
248 let c_sql = CString::new(sql).map_err(|err| anyhow!(err.to_string()))?;
249 let mut stmt = ptr::null_mut();
250 let mut tail = ptr::null();
251 let rc = unsafe { sqlite3_prepare_v2(db, c_sql.as_ptr(), -1, &mut stmt, &mut tail) };
252 let _stmt = FinalizeOnDrop(stmt);
253 if rc != SQLITE_OK {
254 return Err(sqlite_error(
255 db,
256 0,
257 "failed to prepare sqlite execute statement",
258 ));
259 }
260 if has_non_whitespace_tail(tail) {
261 return Err(SqliteStatementError {
262 code: -1,
263 statement_index: 0,
264 message: "sqlite execute only supports a single statement".to_owned(),
265 }
266 .into());
267 }
268 if stmt.is_null() {
269 return Ok(ExecuteResult {
270 columns: Vec::new(),
271 rows: Vec::new(),
272 changes: 0,
273 last_insert_row_id: None,
274 });
275 }
276
277 refuse_oversize_vacuum(db, stmt)?;
278 if let Some(params) = params {
279 bind_params(db, stmt, params)?;
280 }
281
282 let columns = collect_columns(stmt);
283 let mut rows = Vec::new();
284 let mut budget = ResultBudget::new(max_result_bytes);
285 loop {
286 let step_rc = unsafe { sqlite3_step(stmt) };
287 if step_rc == SQLITE_DONE {
288 break;
289 }
290 if step_rc != SQLITE_ROW {
291 return Err(sqlite_error(
292 db,
293 0,
294 "failed to step sqlite execute statement",
295 ));
296 }
297
298 rows.push(read_row(stmt, columns.len(), &mut budget)?);
299 }
300
301 let changes = unsafe { sqlite3_changes(db) as i64 };
302 Ok(ExecuteResult {
303 columns,
304 rows,
305 changes,
306 last_insert_row_id: (changes > 0).then(|| unsafe { sqlite3_last_insert_rowid(db) }),
307 })
308}
309
310pub fn execute_single_statement_profiled(
312 db: *mut sqlite3,
313 sql: &str,
314 params: Option<&[BindParam]>,
315 max_result_bytes: Option<u64>,
316) -> Result<(ExecuteResult, SqliteQueryProfile)> {
317 let c_sql = CString::new(sql).map_err(|err| anyhow!(err.to_string()))?;
318 let mut stmt = ptr::null_mut();
319 let mut tail = ptr::null();
320 let rc = unsafe { sqlite3_prepare_v2(db, c_sql.as_ptr(), -1, &mut stmt, &mut tail) };
321 let _stmt = FinalizeOnDrop(stmt);
322 if rc != SQLITE_OK {
323 return Err(sqlite_error(
324 db,
325 0,
326 "failed to prepare sqlite execute statement",
327 ));
328 }
329 if has_non_whitespace_tail(tail) {
330 return Err(SqliteStatementError {
331 code: -1,
332 statement_index: 0,
333 message: "sqlite execute only supports a single statement".to_owned(),
334 }
335 .into());
336 }
337 if stmt.is_null() {
338 return Ok((
339 ExecuteResult {
340 columns: Vec::new(),
341 rows: Vec::new(),
342 changes: 0,
343 last_insert_row_id: None,
344 },
345 SqliteQueryProfile::default(),
346 ));
347 }
348
349 refuse_oversize_vacuum(db, stmt)?;
350 let mut profile = SqliteQueryProfile::default();
351 if let Some(params) = params {
352 bind_params_profiled(db, stmt, params, &mut profile)?;
353 }
354
355 let columns = collect_columns(stmt);
356 profile.result_columns = columns.len() as u64;
357 let mut rows = Vec::new();
358 let mut budget = ResultBudget::new(max_result_bytes);
359 loop {
360 let step_rc = unsafe { sqlite3_step(stmt) };
361 if step_rc == SQLITE_DONE {
362 break;
363 }
364 if step_rc != SQLITE_ROW {
365 return Err(sqlite_error(
366 db,
367 0,
368 "failed to step sqlite execute statement",
369 ));
370 }
371
372 rows.push(read_row_profiled(
373 stmt,
374 columns.len(),
375 &mut budget,
376 &mut profile,
377 )?);
378 profile.result_rows = profile.result_rows.saturating_add(1);
379 }
380
381 let changes = unsafe { sqlite3_changes(db) as i64 };
382 Ok((
383 ExecuteResult {
384 columns,
385 rows,
386 changes,
387 last_insert_row_id: (changes > 0).then(|| unsafe { sqlite3_last_insert_rowid(db) }),
388 },
389 profile,
390 ))
391}
392
393pub fn exec_statements(
396 db: *mut sqlite3,
397 sql: &str,
398 max_result_bytes: Option<u64>,
399) -> Result<QueryResult> {
400 let c_sql = CString::new(sql).map_err(|err| anyhow!(err.to_string()))?;
401 let mut remaining = c_sql.as_ptr();
402 let mut statement_index = 0_u32;
403 let mut final_result = QueryResult {
404 columns: Vec::new(),
405 rows: Vec::new(),
406 };
407
408 while unsafe { *remaining } != 0 {
409 let mut stmt = ptr::null_mut();
410 let mut tail = ptr::null();
411 let rc = unsafe { sqlite3_prepare_v2(db, remaining, -1, &mut stmt, &mut tail) };
412 let _stmt = FinalizeOnDrop(stmt);
413 if rc != SQLITE_OK {
414 return Err(sqlite_error(
415 db,
416 statement_index,
417 "failed to prepare sqlite exec statement",
418 ));
419 }
420
421 if stmt.is_null() {
422 if tail == remaining {
423 break;
424 }
425 remaining = tail;
426 continue;
427 }
428
429 if unsafe { sqlite3_column_count(stmt) } > 0 {
432 final_result = QueryResult {
433 columns: Vec::new(),
434 rows: Vec::new(),
435 };
436 }
437
438 refuse_oversize_vacuum(db, stmt)?;
439 let columns = collect_columns(stmt);
440 let mut rows = Vec::new();
441 let mut budget = ResultBudget::new(max_result_bytes);
442 loop {
443 let step_rc = unsafe { sqlite3_step(stmt) };
444 if step_rc == SQLITE_DONE {
445 break;
446 }
447 if step_rc != SQLITE_ROW {
448 return Err(sqlite_error(
449 db,
450 statement_index,
451 "failed to step sqlite exec statement",
452 ));
453 }
454
455 rows.push(read_row(stmt, columns.len(), &mut budget)?);
456 }
457
458 if !columns.is_empty() || !rows.is_empty() {
459 final_result = QueryResult { columns, rows };
460 }
461
462 if tail == remaining {
463 break;
464 }
465 remaining = tail;
466 statement_index = statement_index.saturating_add(1);
467 }
468
469 Ok(final_result)
470}
471
472pub fn exec_statements_profiled(
474 db: *mut sqlite3,
475 sql: &str,
476 max_result_bytes: Option<u64>,
477) -> Result<(QueryResult, SqliteQueryProfile)> {
478 let c_sql = CString::new(sql).map_err(|err| anyhow!(err.to_string()))?;
479 let mut remaining = c_sql.as_ptr();
480 let mut statement_index = 0_u32;
481 let mut final_result = QueryResult {
482 columns: Vec::new(),
483 rows: Vec::new(),
484 };
485 let mut final_profile = SqliteQueryProfile::default();
486
487 while unsafe { *remaining } != 0 {
488 let mut stmt = ptr::null_mut();
489 let mut tail = ptr::null();
490 let rc = unsafe { sqlite3_prepare_v2(db, remaining, -1, &mut stmt, &mut tail) };
491 let _stmt = FinalizeOnDrop(stmt);
492 if rc != SQLITE_OK {
493 return Err(sqlite_error(
494 db,
495 statement_index,
496 "failed to prepare sqlite exec statement",
497 ));
498 }
499
500 if stmt.is_null() {
501 if tail == remaining {
502 break;
503 }
504 remaining = tail;
505 continue;
506 }
507
508 if unsafe { sqlite3_column_count(stmt) } > 0 {
511 final_result = QueryResult {
512 columns: Vec::new(),
513 rows: Vec::new(),
514 };
515 }
516
517 refuse_oversize_vacuum(db, stmt)?;
518 let columns = collect_columns(stmt);
519 let mut profile = SqliteQueryProfile {
520 result_columns: columns.len() as u64,
521 ..Default::default()
522 };
523 let mut rows = Vec::new();
524 let mut budget = ResultBudget::new(max_result_bytes);
525 loop {
526 let step_rc = unsafe { sqlite3_step(stmt) };
527 if step_rc == SQLITE_DONE {
528 break;
529 }
530 if step_rc != SQLITE_ROW {
531 return Err(sqlite_error(
532 db,
533 statement_index,
534 "failed to step sqlite exec statement",
535 ));
536 }
537
538 rows.push(read_row_profiled(
539 stmt,
540 columns.len(),
541 &mut budget,
542 &mut profile,
543 )?);
544 profile.result_rows = profile.result_rows.saturating_add(1);
545 }
546
547 if !columns.is_empty() || !rows.is_empty() {
548 final_result = QueryResult { columns, rows };
549 final_profile = profile;
550 }
551
552 if tail == remaining {
553 break;
554 }
555 remaining = tail;
556 statement_index = statement_index.saturating_add(1);
557 }
558
559 Ok((final_result, final_profile))
560}
561
562fn bind_params(
563 db: *mut sqlite3,
564 stmt: *mut libsqlite3_sys::sqlite3_stmt,
565 params: &[BindParam],
566) -> Result<()> {
567 for (index, param) in params.iter().enumerate() {
568 bind_param(db, stmt, (index + 1) as i32, param)?;
569 }
570
571 Ok(())
572}
573
574fn bind_params_profiled(
575 db: *mut sqlite3,
576 stmt: *mut libsqlite3_sys::sqlite3_stmt,
577 params: &[BindParam],
578 profile: &mut SqliteQueryProfile,
579) -> Result<()> {
580 for (index, param) in params.iter().enumerate() {
581 profile.bind_count = profile.bind_count.saturating_add(1);
582 profile.bind_logical_bytes = profile
583 .bind_logical_bytes
584 .saturating_add(logical_bind_bytes(param));
585 bind_param(db, stmt, (index + 1) as i32, param)?;
586 }
587
588 Ok(())
589}
590
591fn bind_param(
592 db: *mut sqlite3,
593 stmt: *mut libsqlite3_sys::sqlite3_stmt,
594 bind_index: i32,
595 param: &BindParam,
596) -> Result<()> {
597 let rc = match param {
598 BindParam::Null => unsafe { sqlite3_bind_null(stmt, bind_index) },
599 BindParam::Integer(value) => unsafe { sqlite3_bind_int64(stmt, bind_index, *value) },
600 BindParam::Float(value) => unsafe { sqlite3_bind_double(stmt, bind_index, *value) },
601 BindParam::Text(value) => unsafe {
602 sqlite3_bind_text(
603 stmt,
604 bind_index,
605 value.as_ptr() as *const c_char,
606 value.len() as i32,
607 SQLITE_TRANSIENT(),
608 )
609 },
610 BindParam::Blob(value) => unsafe {
611 sqlite3_bind_blob(
612 stmt,
613 bind_index,
614 value.as_ptr() as *const _,
615 value.len() as i32,
616 SQLITE_TRANSIENT(),
617 )
618 },
619 };
620
621 if rc != SQLITE_OK {
622 return Err(sqlite_error(db, 0, "failed to bind sqlite parameter"));
623 }
624
625 Ok(())
626}
627
628fn collect_columns(stmt: *mut libsqlite3_sys::sqlite3_stmt) -> Vec<String> {
629 let column_count = unsafe { sqlite3_column_count(stmt) };
630 (0..column_count)
631 .map(|index| unsafe {
632 let name_ptr = sqlite3_column_name(stmt, index);
633 if name_ptr.is_null() {
634 String::new()
635 } else {
636 CStr::from_ptr(name_ptr).to_string_lossy().into_owned()
637 }
638 })
639 .collect()
640}
641
642fn column_value(stmt: *mut libsqlite3_sys::sqlite3_stmt, index: i32) -> ColumnValue {
643 match unsafe { sqlite3_column_type(stmt, index) } {
644 SQLITE_NULL => ColumnValue::Null,
645 SQLITE_INTEGER => ColumnValue::Integer(unsafe { sqlite3_column_int64(stmt, index) }),
646 SQLITE_FLOAT => ColumnValue::Float(unsafe { sqlite3_column_double(stmt, index) }),
647 SQLITE_TEXT => {
648 let text_ptr = unsafe { sqlite3_column_text(stmt, index) };
649 if text_ptr.is_null() {
650 ColumnValue::Null
651 } else {
652 let text_len = unsafe { sqlite3_column_bytes(stmt, index) } as usize;
653 let text = String::from_utf8_lossy(unsafe {
654 std::slice::from_raw_parts(text_ptr as *const u8, text_len)
655 })
656 .into_owned();
657 ColumnValue::Text(text)
658 }
659 }
660 SQLITE_BLOB => {
661 let blob_ptr = unsafe { sqlite3_column_blob(stmt, index) };
662 let blob_len = unsafe { sqlite3_column_bytes(stmt, index) } as usize;
663 if blob_len == 0 {
666 ColumnValue::Blob(Vec::new())
667 } else if blob_ptr.is_null() {
668 ColumnValue::Null
669 } else {
670 let blob = unsafe { std::slice::from_raw_parts(blob_ptr as *const u8, blob_len) };
671 ColumnValue::Blob(blob.to_vec())
672 }
673 }
674 _ => ColumnValue::Null,
675 }
676}
677
678fn column_value_profiled(
679 stmt: *mut libsqlite3_sys::sqlite3_stmt,
680 index: i32,
681 profile: &mut SqliteQueryProfile,
682) -> ColumnValue {
683 let mut logical_bytes = 0_u64;
684 let value = match unsafe { sqlite3_column_type(stmt, index) } {
685 SQLITE_NULL => ColumnValue::Null,
686 SQLITE_INTEGER => {
687 logical_bytes = 8;
688 ColumnValue::Integer(unsafe { sqlite3_column_int64(stmt, index) })
689 }
690 SQLITE_FLOAT => {
691 logical_bytes = 8;
692 ColumnValue::Float(unsafe { sqlite3_column_double(stmt, index) })
693 }
694 SQLITE_TEXT => {
695 let text_ptr = unsafe { sqlite3_column_text(stmt, index) };
696 if text_ptr.is_null() {
697 ColumnValue::Null
698 } else {
699 let text_len = unsafe { sqlite3_column_bytes(stmt, index) } as usize;
700 logical_bytes = text_len as u64;
701 let text = String::from_utf8_lossy(unsafe {
702 std::slice::from_raw_parts(text_ptr as *const u8, text_len)
703 })
704 .into_owned();
705 ColumnValue::Text(text)
706 }
707 }
708 SQLITE_BLOB => {
709 let blob_ptr = unsafe { sqlite3_column_blob(stmt, index) };
710 let blob_len = unsafe { sqlite3_column_bytes(stmt, index) } as usize;
711 if blob_len == 0 {
714 ColumnValue::Blob(Vec::new())
715 } else if blob_ptr.is_null() {
716 ColumnValue::Null
717 } else {
718 logical_bytes = blob_len as u64;
719 let blob = unsafe { std::slice::from_raw_parts(blob_ptr as *const u8, blob_len) };
720 ColumnValue::Blob(blob.to_vec())
721 }
722 }
723 _ => ColumnValue::Null,
724 };
725 profile.result_logical_bytes = profile.result_logical_bytes.saturating_add(logical_bytes);
726 value
727}
728
729fn logical_bind_bytes(param: &BindParam) -> u64 {
730 match param {
731 BindParam::Null => 0,
732 BindParam::Integer(_) | BindParam::Float(_) => 8,
733 BindParam::Text(value) => value.len() as u64,
734 BindParam::Blob(value) => value.len() as u64,
735 }
736}
737
738fn refuse_oversize_vacuum(db: *mut sqlite3, stmt: *mut sqlite3_stmt) -> Result<()> {
747 let sql = unsafe { sqlite3_sql(stmt) };
748 if sql.is_null() {
749 return Ok(());
750 }
751 let sql = unsafe { CStr::from_ptr(sql) }.to_string_lossy();
752 if !is_in_place_vacuum(&sql) {
753 return Ok(());
754 }
755
756 let mut max_pages: i64 = 0;
757 let rc = unsafe {
758 sqlite3_file_control(
759 db,
760 c"main".as_ptr(),
761 FCNTL_COMMIT_PAGE_CAP,
762 (&mut max_pages as *mut i64).cast(),
763 )
764 };
765 if rc != SQLITE_OK {
766 return Ok(());
767 }
768
769 let (Some(page_count), Some(freelist_count), Some(page_size)) = (
770 pragma_i64(db, c"PRAGMA main.page_count;"),
771 pragma_i64(db, c"PRAGMA main.freelist_count;"),
772 pragma_i64(db, c"PRAGMA main.page_size;"),
773 ) else {
774 return Ok(());
775 };
776 let in_use_pages = page_count.saturating_sub(freelist_count);
777 if in_use_pages > max_pages {
778 return Err(SqliteTransactionTooLargeError {
779 max_pages: max_pages as usize,
780 page_size: page_size as usize,
781 }
782 .into());
783 }
784
785 Ok(())
786}
787
788pub(crate) fn is_in_place_vacuum(sql: &str) -> bool {
791 let words = sql_words(sql);
792 let mut words = words.iter();
793 if !words
794 .next()
795 .is_some_and(|word| word.eq_ignore_ascii_case("vacuum"))
796 {
797 return false;
798 }
799
800 match words.next() {
801 None => true,
802 Some(word) if word.eq_ignore_ascii_case("into") => false,
803 Some(schema) => {
804 let schema = schema.trim_matches(|c| matches!(c, '"' | '`' | '[' | ']' | '\''));
805 schema.eq_ignore_ascii_case("main")
806 && !words.any(|word| word.eq_ignore_ascii_case("into"))
807 }
808 }
809}
810
811fn sql_words(sql: &str) -> Vec<&str> {
814 let mut words = Vec::new();
815 let mut rest = sql;
816 loop {
817 rest = rest.trim_start_matches(|c: char| c.is_ascii_whitespace() || c == ';');
818 if rest.is_empty() {
819 return words;
820 }
821 if let Some(comment) = rest.strip_prefix("--") {
822 rest = comment.find('\n').map_or("", |end| &comment[end + 1..]);
823 continue;
824 }
825 if let Some(comment) = rest.strip_prefix("/*") {
826 rest = comment.find("*/").map_or("", |end| &comment[end + 2..]);
827 continue;
828 }
829 let end = rest
830 .char_indices()
831 .find(|(index, c)| {
832 c.is_ascii_whitespace()
833 || *c == ';' || rest[*index..].starts_with("--")
834 || rest[*index..].starts_with("/*")
835 })
836 .map_or(rest.len(), |(index, _)| index);
837 words.push(&rest[..end]);
838 rest = &rest[end..];
839 }
840}
841
842fn pragma_i64(db: *mut sqlite3, sql: &CStr) -> Option<i64> {
844 let mut stmt = ptr::null_mut();
845 let rc = unsafe { sqlite3_prepare_v2(db, sql.as_ptr(), -1, &mut stmt, ptr::null_mut()) };
846 let _stmt = FinalizeOnDrop(stmt);
847 if rc != SQLITE_OK || stmt.is_null() {
848 return None;
849 }
850 if unsafe { sqlite3_step(stmt) } == SQLITE_ROW {
851 Some(unsafe { sqlite3_column_int64(stmt, 0) })
852 } else {
853 None
854 }
855}
856
857struct FinalizeOnDrop(*mut sqlite3_stmt);
860
861impl Drop for FinalizeOnDrop {
862 fn drop(&mut self) {
863 unsafe {
866 sqlite3_finalize(self.0);
867 }
868 }
869}
870
871fn has_non_whitespace_tail(tail: *const c_char) -> bool {
872 if tail.is_null() {
873 return false;
874 }
875
876 let bytes = unsafe { CStr::from_ptr(tail).to_bytes() };
877 bytes.iter().any(|byte| !byte.is_ascii_whitespace())
878}
879
880fn sqlite_error(db: *mut sqlite3, statement_index: u32, context: &str) -> anyhow::Error {
881 let (code, detail) = unsafe {
882 if db.is_null() {
883 (-1, "unknown sqlite error".to_string())
884 } else {
885 (
886 sqlite3_extended_errcode(db),
887 CStr::from_ptr(sqlite3_errmsg(db))
888 .to_string_lossy()
889 .into_owned(),
890 )
891 }
892 };
893 SqliteStatementError {
894 code,
895 statement_index,
896 message: format!("{context}: {detail}"),
897 }
898 .into()
899}
900
901