From e13589717ea30f143c58e143ebc1dcc46ce94309 Mon Sep 17 00:00:00 2001
From: Pratik Chowdhury <pratikc@live.co.uk>
Date: Mon, 10 Jan 2022 02:48:56 -0800
Subject: [PATCH 01/19] README: document winsqlite3

https://blogs.windows.com/windowsdeveloper/2017/02/06/using-sqlite-databases-uwp-apps/

Makes it easier to find out abou the awesome WinSQLite3 feature
---
 README.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/README.md b/README.md
index 52c3fb1..665c29a 100644
--- a/README.md
+++ b/README.md
@@ -115,6 +115,7 @@ features](https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-s
 * `extra_check` fail when a query passed to execute is readonly or has a column count > 0.
 * `column_decltype` provides `columns()` method for Statements and Rows; omit if linking to a version of SQLite/SQLCipher compiled with `-DSQLITE_OMIT_DECLTYPE`.
 * `collation` exposes [`sqlite3_create_collation_v2`](https://sqlite.org/c3ref/create_collation.html).
+* [`winsqlite3`](https://blogs.windows.com/windowsdeveloper/2017/02/06/using-sqlite-databases-uwp-apps/) allows linking against the SQLite present in newer versions of Windows
 
 ## Notes on building rusqlite and libsqlite3-sys
 

From 6eb9d0f6aa7cd7add135aaf36c952c58e96ef56e Mon Sep 17 00:00:00 2001
From: Pratik Chowdhury <pratikc@live.co.uk>
Date: Mon, 10 Jan 2022 09:11:21 -0800
Subject: [PATCH 02/19] Remove winsqlite link to Microsoft article.

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 665c29a..a304f62 100644
--- a/README.md
+++ b/README.md
@@ -115,7 +115,7 @@ features](https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-s
 * `extra_check` fail when a query passed to execute is readonly or has a column count > 0.
 * `column_decltype` provides `columns()` method for Statements and Rows; omit if linking to a version of SQLite/SQLCipher compiled with `-DSQLITE_OMIT_DECLTYPE`.
 * `collation` exposes [`sqlite3_create_collation_v2`](https://sqlite.org/c3ref/create_collation.html).
-* [`winsqlite3`](https://blogs.windows.com/windowsdeveloper/2017/02/06/using-sqlite-databases-uwp-apps/) allows linking against the SQLite present in newer versions of Windows
+* `winsqlite3` allows linking against the SQLite present in newer versions of Windows
 
 ## Notes on building rusqlite and libsqlite3-sys
 

From 7132e4d7cc09a2889ce0cdb53344d34a12ed1170 Mon Sep 17 00:00:00 2001
From: polyrand <37962604+d0l-phin@users.noreply.github.com>
Date: Fri, 21 Jan 2022 11:21:47 +0100
Subject: [PATCH 03/19] Fix typo in libsqlite flags variable

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 52c3fb1..39b53fc 100644
--- a/README.md
+++ b/README.md
@@ -134,7 +134,7 @@ You can adjust this behavior in a number of ways:
   version = "0.26.0"
   features = ["bundled"]
   ```
-* When using any of the `bundled` features, the build script will honor `SQLITE_MAX_VARIABLE_NUMBER` and `SQLITE_MAX_EXPR_DEPTH` variables. It will also honor a `LIBSQLITE_FLAGS` variable, which can have a format like `"-USQLITE_ALPHA -DSQLITE_BETA SQLITE_GAMMA ..."`. That would disable the `SQLITE_ALPHA` flag, and set the `SQLITE_BETA` and `SQLITE_GAMMA` flags. (The initial `-D` can be omitted, as on the last one.)
+* When using any of the `bundled` features, the build script will honor `SQLITE_MAX_VARIABLE_NUMBER` and `SQLITE_MAX_EXPR_DEPTH` variables. It will also honor a `LIBSQLITE3_FLAGS` variable, which can have a format like `"-USQLITE_ALPHA -DSQLITE_BETA SQLITE_GAMMA ..."`. That would disable the `SQLITE_ALPHA` flag, and set the `SQLITE_BETA` and `SQLITE_GAMMA` flags. (The initial `-D` can be omitted, as on the last one.)
 * When using `bundled-sqlcipher` (and not also using `bundled-sqlcipher-vendored-openssl`), `libsqlite3-sys` will need to
   link against crypto libraries on the system. If the build script can find a `libcrypto` from OpenSSL or LibreSSL (it will consult `OPENSSL_LIB_DIR`/`OPENSSL_INCLUDE_DIR` and `OPENSSL_DIR` environment variables), it will use that. If building on and for Macs, and none of those variables are set, it will use the system's SecurityFramework instead.
 

From 69a40526d5bc5c48b08ee3c3ffc2659e0e0985a8 Mon Sep 17 00:00:00 2001
From: gwenn <gtreguier@gmail.com>
Date: Sun, 13 Mar 2022 17:31:07 +0100
Subject: [PATCH 04/19] Introduce SqlInputError with offset

---
 src/error.rs            | 55 ++++++++++++++++++++++++++++++++++++++++-
 src/inner_connection.rs |  6 +++--
 src/statement.rs        | 17 +++++++++++++
 3 files changed, 75 insertions(+), 3 deletions(-)

diff --git a/src/error.rs b/src/error.rs
index 129f697..9964274 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -128,6 +128,17 @@ pub enum Error {
     #[cfg(feature = "blob")]
     #[cfg_attr(docsrs, doc(cfg(feature = "blob")))]
     BlobSizeError,
+    /// Error referencing a specific token in the input SQL
+    #[cfg(feature = "modern_sqlite")] // 3.38.0
+    #[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))]
+    SqlInputError {
+        /// error code
+        error: ffi::Error,
+        /// error message
+        msg: String,
+        /// byte offset of the start of invalid token
+        offset: c_int,
+    },
 }
 
 impl PartialEq for Error {
@@ -172,6 +183,19 @@ impl PartialEq for Error {
             }
             #[cfg(feature = "blob")]
             (Error::BlobSizeError, Error::BlobSizeError) => true,
+            #[cfg(feature = "modern_sqlite")]
+            (
+                Error::SqlInputError {
+                    error: e1,
+                    msg: s1,
+                    offset: o1,
+                },
+                Error::SqlInputError {
+                    error: e2,
+                    msg: s2,
+                    offset: o2,
+                },
+            ) => e1 == e2 && s1 == s2 && o1 == o2,
             (..) => false,
         }
     }
@@ -281,9 +305,12 @@ impl fmt::Display for Error {
             #[cfg(feature = "functions")]
             Error::GetAuxWrongType => write!(f, "get_aux called with wrong type"),
             Error::MultipleStatement => write!(f, "Multiple statements provided"),
-
             #[cfg(feature = "blob")]
             Error::BlobSizeError => "Blob size is insufficient".fmt(f),
+            #[cfg(feature = "modern_sqlite")]
+            Error::SqlInputError {
+                ref msg, offset, ..
+            } => write!(f, "{} at offset {}", msg, offset),
         }
     }
 }
@@ -331,6 +358,8 @@ impl error::Error for Error {
 
             #[cfg(feature = "blob")]
             Error::BlobSizeError => None,
+            #[cfg(feature = "modern_sqlite")]
+            Error::SqlInputError { ref error, .. } => Some(error),
         }
     }
 }
@@ -352,6 +381,30 @@ pub unsafe fn error_from_handle(db: *mut ffi::sqlite3, code: c_int) -> Error {
     error_from_sqlite_code(code, message)
 }
 
+#[cold]
+#[cfg(not(feature = "modern_sqlite"))]
+pub unsafe fn error_with_offset(db: *mut ffi::sqlite3, code: c_int) -> Error {
+    error_from_handle(db, code)
+}
+
+#[cold]
+#[cfg(feature = "modern_sqlite")] // 3.38.0
+pub unsafe fn error_with_offset(db: *mut ffi::sqlite3, code: c_int) -> Error {
+    if db.is_null() {
+        error_from_sqlite_code(code, None)
+    } else {
+        let error = ffi::Error::new(code);
+        let msg = errmsg_to_string(ffi::sqlite3_errmsg(db));
+        if ffi::ErrorCode::Unknown == error.code {
+            let offset = ffi::sqlite3_error_offset(db);
+            if offset >= 0 {
+                return Error::SqlInputError { error, msg, offset };
+            }
+        }
+        Error::SqliteFailure(error, Some(msg))
+    }
+}
+
 pub fn check(code: c_int) -> Result<()> {
     if code != crate::ffi::SQLITE_OK {
         Err(crate::error::error_from_sqlite_code(code, None))
diff --git a/src/inner_connection.rs b/src/inner_connection.rs
index 0ea630e..daf4100 100644
--- a/src/inner_connection.rs
+++ b/src/inner_connection.rs
@@ -10,7 +10,7 @@ use std::sync::{Arc, Mutex};
 use super::ffi;
 use super::str_for_sqlite;
 use super::{Connection, InterruptHandle, OpenFlags, Result};
-use crate::error::{error_from_handle, error_from_sqlite_code, Error};
+use crate::error::{error_from_handle, error_from_sqlite_code, error_with_offset, Error};
 use crate::raw_statement::RawStatement;
 use crate::statement::Statement;
 use crate::version::version_number;
@@ -255,7 +255,9 @@ impl InnerConnection {
             rc
         };
         // If there is an error, *ppStmt is set to NULL.
-        self.decode_result(r)?;
+        if r != ffi::SQLITE_OK {
+            return Err(unsafe { error_with_offset(self.db, r) });
+        }
         // If the input text contains no SQL (if the input is an empty string or a
         // comment) then *ppStmt is set to NULL.
         let c_stmt: *mut ffi::sqlite3_stmt = c_stmt;
diff --git a/src/statement.rs b/src/statement.rs
index 9a84cbd..7c5fe2f 100644
--- a/src/statement.rs
+++ b/src/statement.rs
@@ -1508,4 +1508,21 @@ mod test {
         assert_eq!(expected, actual);
         Ok(())
     }
+
+    #[test]
+    #[cfg(feature = "modern_sqlite")]
+    fn test_error_offset() -> Result<()> {
+        use crate::ffi::ErrorCode;
+        let db = Connection::open_in_memory()?;
+        let r = db.execute_batch("SELECT CURRENT_TIMESTANP;");
+        assert!(r.is_err());
+        match r.unwrap_err() {
+            Error::SqlInputError { error, offset, .. } => {
+                assert_eq!(error.code, ErrorCode::Unknown);
+                assert_eq!(offset, 7);
+            }
+            err => panic!("Unexpected error {}", err),
+        }
+        Ok(())
+    }
 }

From fedf6b41ae2a7391edabf9a7262104c1c3caaacf Mon Sep 17 00:00:00 2001
From: gwenn <gtreguier@gmail.com>
Date: Thu, 7 Apr 2022 09:15:55 +0200
Subject: [PATCH 05/19] Use sqlite_error_code where possible

---
 src/busy.rs | 12 +++++-------
 src/lib.rs  | 12 ++++--------
 2 files changed, 9 insertions(+), 15 deletions(-)

diff --git a/src/busy.rs b/src/busy.rs
index b394d01..7297f20 100644
--- a/src/busy.rs
+++ b/src/busy.rs
@@ -90,7 +90,7 @@ mod test {
     use std::thread;
     use std::time::Duration;
 
-    use crate::{Connection, Error, ErrorCode, Result, TransactionBehavior};
+    use crate::{Connection, ErrorCode, Result, TransactionBehavior};
 
     #[test]
     fn test_default_busy() -> Result<()> {
@@ -101,12 +101,10 @@ mod test {
         let tx1 = db1.transaction_with_behavior(TransactionBehavior::Exclusive)?;
         let db2 = Connection::open(&path)?;
         let r: Result<()> = db2.query_row("PRAGMA schema_version", [], |_| unreachable!());
-        match r.unwrap_err() {
-            Error::SqliteFailure(err, _) => {
-                assert_eq!(err.code, ErrorCode::DatabaseBusy);
-            }
-            err => panic!("Unexpected error {}", err),
-        }
+        assert_eq!(
+            r.unwrap_err().sqlite_error_code(),
+            Some(ErrorCode::DatabaseBusy)
+        );
         tx1.rollback()
     }
 
diff --git a/src/lib.rs b/src/lib.rs
index 070b1a7..4dbb819 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1742,14 +1742,10 @@ mod test {
 
         let result: Result<Vec<i32>> = stmt.query([])?.map(|r| r.get(0)).collect();
 
-        match result.unwrap_err() {
-            Error::SqliteFailure(err, _) => {
-                assert_eq!(err.code, ErrorCode::OperationInterrupted);
-            }
-            err => {
-                panic!("Unexpected error {}", err);
-            }
-        }
+        assert_eq!(
+            result.unwrap_err().sqlite_error_code(),
+            Some(ErrorCode::OperationInterrupted)
+        );
         Ok(())
     }
 

From 214a6244f921db2c30fb76dc1a049b240cc67991 Mon Sep 17 00:00:00 2001
From: gwenn <gtreguier@gmail.com>
Date: Thu, 7 Apr 2022 09:17:07 +0200
Subject: [PATCH 06/19] Rustfmt

---
 src/lib.rs | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/src/lib.rs b/src/lib.rs
index 4dbb819..b2e798f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -384,9 +384,8 @@ impl Connection {
     ///
     /// - Open the database for both reading or writing.
     /// - Create the database if one does not exist at the path.
-    /// - Allow the filename to be interpreted as a URI (see
-    ///   <https://www.sqlite.org/uri.html#uri_filenames_in_sqlite> for
-    ///   details).
+    /// - Allow the filename to be interpreted as a URI (see <https://www.sqlite.org/uri.html#uri_filenames_in_sqlite>
+    ///   for details).
     /// - Disables the use of a per-connection mutex.
     ///
     ///     Rusqlite enforces thread-safety at compile time, so additional

From ad911efd15eb177dd9829a13d3721968d55aeedd Mon Sep 17 00:00:00 2001
From: gwenn <gtreguier@gmail.com>
Date: Thu, 7 Apr 2022 09:17:42 +0200
Subject: [PATCH 07/19] Replace .err().unwrap() by .unwrap_err()

---
 src/lib.rs       |  2 +-
 src/types/mod.rs | 62 +++++++++++++++++-------------------------------
 2 files changed, 23 insertions(+), 41 deletions(-)

diff --git a/src/lib.rs b/src/lib.rs
index b2e798f..418a1b7 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1288,7 +1288,7 @@ mod test {
         let filename = "no_such_file.db";
         let result = Connection::open_with_flags(filename, OpenFlags::SQLITE_OPEN_READ_ONLY);
         assert!(result.is_err());
-        let err = result.err().unwrap();
+        let err = result.unwrap_err();
         if let Error::SqliteFailure(e, Some(msg)) = err {
             assert_eq!(ErrorCode::CannotOpen, e.code);
             assert_eq!(ffi::SQLITE_CANTOPEN, e.extended_code);
diff --git a/src/types/mod.rs b/src/types/mod.rs
index 4e524b2..a5c73d5 100644
--- a/src/types/mod.rs
+++ b/src/types/mod.rs
@@ -272,85 +272,67 @@ mod test {
         // check some invalid types
 
         // 0 is actually a blob (Vec<u8>)
-        assert!(is_invalid_column_type(
-            row.get::<_, c_int>(0).err().unwrap()
-        ));
-        assert!(is_invalid_column_type(
-            row.get::<_, c_int>(0).err().unwrap()
-        ));
+        assert!(is_invalid_column_type(row.get::<_, c_int>(0).unwrap_err()));
+        assert!(is_invalid_column_type(row.get::<_, c_int>(0).unwrap_err()));
         assert!(is_invalid_column_type(row.get::<_, i64>(0).err().unwrap()));
         assert!(is_invalid_column_type(
-            row.get::<_, c_double>(0).err().unwrap()
-        ));
-        assert!(is_invalid_column_type(
-            row.get::<_, String>(0).err().unwrap()
+            row.get::<_, c_double>(0).unwrap_err()
         ));
+        assert!(is_invalid_column_type(row.get::<_, String>(0).unwrap_err()));
         #[cfg(feature = "time")]
         assert!(is_invalid_column_type(
-            row.get::<_, time::OffsetDateTime>(0).err().unwrap()
+            row.get::<_, time::OffsetDateTime>(0).unwrap_err()
         ));
         assert!(is_invalid_column_type(
-            row.get::<_, Option<c_int>>(0).err().unwrap()
+            row.get::<_, Option<c_int>>(0).unwrap_err()
         ));
 
         // 1 is actually a text (String)
-        assert!(is_invalid_column_type(
-            row.get::<_, c_int>(1).err().unwrap()
-        ));
+        assert!(is_invalid_column_type(row.get::<_, c_int>(1).unwrap_err()));
         assert!(is_invalid_column_type(row.get::<_, i64>(1).err().unwrap()));
         assert!(is_invalid_column_type(
-            row.get::<_, c_double>(1).err().unwrap()
+            row.get::<_, c_double>(1).unwrap_err()
         ));
         assert!(is_invalid_column_type(
-            row.get::<_, Vec<u8>>(1).err().unwrap()
+            row.get::<_, Vec<u8>>(1).unwrap_err()
         ));
         assert!(is_invalid_column_type(
-            row.get::<_, Option<c_int>>(1).err().unwrap()
+            row.get::<_, Option<c_int>>(1).unwrap_err()
         ));
 
         // 2 is actually an integer
+        assert!(is_invalid_column_type(row.get::<_, String>(2).unwrap_err()));
         assert!(is_invalid_column_type(
-            row.get::<_, String>(2).err().unwrap()
+            row.get::<_, Vec<u8>>(2).unwrap_err()
         ));
         assert!(is_invalid_column_type(
-            row.get::<_, Vec<u8>>(2).err().unwrap()
-        ));
-        assert!(is_invalid_column_type(
-            row.get::<_, Option<String>>(2).err().unwrap()
+            row.get::<_, Option<String>>(2).unwrap_err()
         ));
 
         // 3 is actually a float (c_double)
-        assert!(is_invalid_column_type(
-            row.get::<_, c_int>(3).err().unwrap()
-        ));
+        assert!(is_invalid_column_type(row.get::<_, c_int>(3).unwrap_err()));
         assert!(is_invalid_column_type(row.get::<_, i64>(3).err().unwrap()));
+        assert!(is_invalid_column_type(row.get::<_, String>(3).unwrap_err()));
         assert!(is_invalid_column_type(
-            row.get::<_, String>(3).err().unwrap()
+            row.get::<_, Vec<u8>>(3).unwrap_err()
         ));
         assert!(is_invalid_column_type(
-            row.get::<_, Vec<u8>>(3).err().unwrap()
-        ));
-        assert!(is_invalid_column_type(
-            row.get::<_, Option<c_int>>(3).err().unwrap()
+            row.get::<_, Option<c_int>>(3).unwrap_err()
         ));
 
         // 4 is actually NULL
-        assert!(is_invalid_column_type(
-            row.get::<_, c_int>(4).err().unwrap()
-        ));
+        assert!(is_invalid_column_type(row.get::<_, c_int>(4).unwrap_err()));
         assert!(is_invalid_column_type(row.get::<_, i64>(4).err().unwrap()));
         assert!(is_invalid_column_type(
-            row.get::<_, c_double>(4).err().unwrap()
+            row.get::<_, c_double>(4).unwrap_err()
         ));
+        assert!(is_invalid_column_type(row.get::<_, String>(4).unwrap_err()));
         assert!(is_invalid_column_type(
-            row.get::<_, String>(4).err().unwrap()
-        ));
-        assert!(is_invalid_column_type(
-            row.get::<_, Vec<u8>>(4).err().unwrap()
+            row.get::<_, Vec<u8>>(4).unwrap_err()
         ));
         #[cfg(feature = "time")]
         assert!(is_invalid_column_type(
-            row.get::<_, time::OffsetDateTime>(4).err().unwrap()
+            row.get::<_, time::OffsetDateTime>(4).unwrap_err()
         ));
         Ok(())
     }

From 4d06d2222f0d7a13be54f4886bd6e92b9ae2db4d Mon Sep 17 00:00:00 2001
From: Nikolai Vazquez <hello@nikolaivazquez.com>
Date: Sun, 17 Apr 2022 06:00:15 -0400
Subject: [PATCH 08/19] Add `Connection::release_memory` method

This is behind a new `release_memory` feature flag. It simply calls
`sqlite3_db_release_memory` on the raw handle.
---
 Cargo.toml |  2 ++
 src/lib.rs | 15 +++++++++++++++
 2 files changed, 17 insertions(+)

diff --git a/Cargo.toml b/Cargo.toml
index 5616c99..55bb288 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -43,6 +43,8 @@ collation = []
 functions = ["libsqlite3-sys/min_sqlite_version_3_7_7"]
 # sqlite3_log: 3.6.23 (2010-03-09)
 trace = ["libsqlite3-sys/min_sqlite_version_3_6_23"]
+# sqlite3_db_release_memory: 3.7.10 (2012-01-16)
+release_memory = ["libsqlite3-sys/min_sqlite_version_3_7_16"]
 bundled = ["libsqlite3-sys/bundled", "modern_sqlite"]
 bundled-sqlcipher = ["libsqlite3-sys/bundled-sqlcipher", "bundled"]
 bundled-sqlcipher-vendored-openssl = ["libsqlite3-sys/bundled-sqlcipher-vendored-openssl", "bundled-sqlcipher"]
diff --git a/src/lib.rs b/src/lib.rs
index 418a1b7..2d968c4 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -595,6 +595,21 @@ impl Connection {
         self.path.as_deref()
     }
 
+    /// Attempts to free as much heap memory as possible from the database
+    /// connection.
+    ///
+    /// This calls [`sqlite3_db_release_memory`](https://www.sqlite.org/c3ref/db_release_memory.html).
+    #[inline]
+    #[cfg(feature = "release_memory")]
+    pub fn release_memory(&self) -> Result<()> {
+        unsafe {
+            match crate::ffi::sqlite3_db_release_memory(self.handle()) {
+                ffi::SQLITE_OK => Ok(()),
+                error => Err(error_from_sqlite_code(error)),
+            }
+        }
+    }
+
     /// Convenience method to prepare and execute a single SQL statement with
     /// named parameter(s).
     ///

From 95941f6378ab96b9ac786ca74ea34d0758259b3d Mon Sep 17 00:00:00 2001
From: Max Unsted <maxunsted@gmail.com>
Date: Sun, 17 Apr 2022 20:34:30 +0100
Subject: [PATCH 09/19] derive Eq for Type

---
 src/types/mod.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/types/mod.rs b/src/types/mod.rs
index a5c73d5..4269a81 100644
--- a/src/types/mod.rs
+++ b/src/types/mod.rs
@@ -110,7 +110,7 @@ pub struct Null;
 
 /// SQLite data types.
 /// See [Fundamental Datatypes](https://sqlite.org/c3ref/c_blob.html).
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
 pub enum Type {
     /// NULL
     Null,

From 26c444341c249c48fa8f038f8e801774d634b898 Mon Sep 17 00:00:00 2001
From: gwenn <gtreguier@gmail.com>
Date: Mon, 18 Apr 2022 13:17:11 +0200
Subject: [PATCH 10/19] Do not use Connection::handle()

https://docs.rs/rusqlite/latest/rusqlite/struct.Connection.html#method.handle
```
You should not need to use this function...
```
And use `decode_result`...
---
 src/inner_connection.rs | 6 ++++++
 src/lib.rs              | 7 +------
 2 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/src/inner_connection.rs b/src/inner_connection.rs
index bd90e28..06f0ce6 100644
--- a/src/inner_connection.rs
+++ b/src/inner_connection.rs
@@ -360,6 +360,12 @@ impl InnerConnection {
             )),
         }
     }
+
+    #[inline]
+    #[cfg(feature = "release_memory")]
+    pub fn release_memory(&self) -> Result<()> {
+        self.decode_result(unsafe { ffi::sqlite3_db_release_memory(self.db) })
+    }
 }
 
 impl Drop for InnerConnection {
diff --git a/src/lib.rs b/src/lib.rs
index 2d968c4..0f813da 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -602,12 +602,7 @@ impl Connection {
     #[inline]
     #[cfg(feature = "release_memory")]
     pub fn release_memory(&self) -> Result<()> {
-        unsafe {
-            match crate::ffi::sqlite3_db_release_memory(self.handle()) {
-                ffi::SQLITE_OK => Ok(()),
-                error => Err(error_from_sqlite_code(error)),
-            }
-        }
+        self.db.borrow_mut().release_memory()
     }
 
     /// Convenience method to prepare and execute a single SQL statement with

From 616781957ec559d2d4d45016955d124106f1f77e Mon Sep 17 00:00:00 2001
From: gwenn <gtreguier@gmail.com>
Date: Wed, 20 Apr 2022 17:08:00 +0200
Subject: [PATCH 11/19] Upgrade uuid dependency

---
 Cargo.toml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml
index 55bb288..ef5e4c8 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -121,7 +121,7 @@ lazy_static = { version = "1.4", optional = true }
 fallible-iterator = "0.2"
 fallible-streaming-iterator = "0.1"
 memchr = "2.3"
-uuid = { version = "0.8", optional = true }
+uuid = { version = "1.0", optional = true }
 smallvec = "1.6.1"
 
 [dev-dependencies]
@@ -129,7 +129,7 @@ doc-comment = "0.3"
 tempfile = "3.1.0"
 lazy_static = "1.4"
 regex = "1.3"
-uuid = { version = "0.8", features = ["v4"] }
+uuid = { version = "1.0", features = ["v4"] }
 unicase = "2.6.0"
 # Use `bencher` over criterion because it builds much faster and we don't have
 # many benchmarks

From c15fa489adabe31a2f0149055d7a76ff667b7457 Mon Sep 17 00:00:00 2001
From: gwenn <gtreguier@gmail.com>
Date: Thu, 21 Apr 2022 13:34:20 +0200
Subject: [PATCH 12/19] Bundled SQLCipher is now based on SQLite > 3.35

---
 src/lib.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/lib.rs b/src/lib.rs
index 0f813da..bb998fa 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -2098,7 +2098,7 @@ mod test {
     }
 
     #[test]
-    #[cfg(all(feature = "bundled", not(feature = "bundled-sqlcipher")))] // SQLite >= 3.35.0
+    #[cfg(feature = "modern_sqlite")]
     fn test_returning() -> Result<()> {
         let db = Connection::open_in_memory()?;
         db.execute_batch("CREATE TABLE foo(x INTEGER PRIMARY KEY)")?;

From 8370970b0b503e9f0ba4065932dbdad2ca6b4d12 Mon Sep 17 00:00:00 2001
From: gwenn <gtreguier@gmail.com>
Date: Thu, 21 Apr 2022 15:14:08 +0200
Subject: [PATCH 13/19] Keep track of SQL input

---
 src/error.rs            | 28 ++++++++++++++++++++--------
 src/inner_connection.rs |  2 +-
 src/statement.rs        |  2 +-
 3 files changed, 22 insertions(+), 10 deletions(-)

diff --git a/src/error.rs b/src/error.rs
index 6764157..fd2dae5 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -136,6 +136,8 @@ pub enum Error {
         error: ffi::Error,
         /// error message
         msg: String,
+        /// SQL input
+        sql: String,
         /// byte offset of the start of invalid token
         offset: c_int,
     },
@@ -187,15 +189,17 @@ impl PartialEq for Error {
             (
                 Error::SqlInputError {
                     error: e1,
-                    msg: s1,
+                    msg: m1,
+                    sql: s1,
                     offset: o1,
                 },
                 Error::SqlInputError {
                     error: e2,
-                    msg: s2,
+                    msg: m2,
+                    sql: s2,
                     offset: o2,
                 },
-            ) => e1 == e2 && s1 == s2 && o1 == o2,
+            ) => e1 == e2 && m1 == m2 && s1 == s2 && o1 == o2,
             (..) => false,
         }
     }
@@ -309,8 +313,11 @@ impl fmt::Display for Error {
             Error::BlobSizeError => "Blob size is insufficient".fmt(f),
             #[cfg(feature = "modern_sqlite")]
             Error::SqlInputError {
-                ref msg, offset, ..
-            } => write!(f, "{} at offset {}", msg, offset),
+                ref msg,
+                offset,
+                ref sql,
+                ..
+            } => write!(f, "{} in {} at offset {}", msg, sql, offset),
         }
     }
 }
@@ -402,13 +409,13 @@ pub unsafe fn error_from_handle(db: *mut ffi::sqlite3, code: c_int) -> Error {
 
 #[cold]
 #[cfg(not(feature = "modern_sqlite"))]
-pub unsafe fn error_with_offset(db: *mut ffi::sqlite3, code: c_int) -> Error {
+pub unsafe fn error_with_offset(db: *mut ffi::sqlite3, code: c_int, sql: &str) -> Error {
     error_from_handle(db, code)
 }
 
 #[cold]
 #[cfg(feature = "modern_sqlite")] // 3.38.0
-pub unsafe fn error_with_offset(db: *mut ffi::sqlite3, code: c_int) -> Error {
+pub unsafe fn error_with_offset(db: *mut ffi::sqlite3, code: c_int, sql: &str) -> Error {
     if db.is_null() {
         error_from_sqlite_code(code, None)
     } else {
@@ -417,7 +424,12 @@ pub unsafe fn error_with_offset(db: *mut ffi::sqlite3, code: c_int) -> Error {
         if ffi::ErrorCode::Unknown == error.code {
             let offset = ffi::sqlite3_error_offset(db);
             if offset >= 0 {
-                return Error::SqlInputError { error, msg, offset };
+                return Error::SqlInputError {
+                    error,
+                    msg,
+                    sql: sql.to_owned(),
+                    offset,
+                };
             }
         }
         Error::SqliteFailure(error, Some(msg))
diff --git a/src/inner_connection.rs b/src/inner_connection.rs
index f239e48..08ce18f 100644
--- a/src/inner_connection.rs
+++ b/src/inner_connection.rs
@@ -257,7 +257,7 @@ impl InnerConnection {
         };
         // If there is an error, *ppStmt is set to NULL.
         if r != ffi::SQLITE_OK {
-            return Err(unsafe { error_with_offset(self.db, r) });
+            return Err(unsafe { error_with_offset(self.db, r, sql) });
         }
         // If the input text contains no SQL (if the input is an empty string or a
         // comment) then *ppStmt is set to NULL.
diff --git a/src/statement.rs b/src/statement.rs
index d44816f..f147868 100644
--- a/src/statement.rs
+++ b/src/statement.rs
@@ -1537,7 +1537,7 @@ mod test {
     }
 
     #[test]
-    #[cfg(feature = "modern_sqlite")]
+    #[cfg(all(feature = "modern_sqlite", not(feature = "bundled-sqlcipher")))] // SQLite >= 3.38.0
     fn test_error_offset() -> Result<()> {
         use crate::ffi::ErrorCode;
         let db = Connection::open_in_memory()?;

From 8f40fd1cf3fd88ac12362a8c9b35493c0b02411c Mon Sep 17 00:00:00 2001
From: gwenn <gtreguier@gmail.com>
Date: Thu, 21 Apr 2022 15:26:46 +0200
Subject: [PATCH 14/19] Fix build errors with SQLCipher

---
 src/error.rs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/error.rs b/src/error.rs
index fd2dae5..7401b7a 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -408,13 +408,13 @@ pub unsafe fn error_from_handle(db: *mut ffi::sqlite3, code: c_int) -> Error {
 }
 
 #[cold]
-#[cfg(not(feature = "modern_sqlite"))]
-pub unsafe fn error_with_offset(db: *mut ffi::sqlite3, code: c_int, sql: &str) -> Error {
+#[cfg(not(all(feature = "modern_sqlite", not(feature = "bundled-sqlcipher"))))] // SQLite >= 3.38.0
+pub unsafe fn error_with_offset(db: *mut ffi::sqlite3, code: c_int, _sql: &str) -> Error {
     error_from_handle(db, code)
 }
 
 #[cold]
-#[cfg(feature = "modern_sqlite")] // 3.38.0
+#[cfg(all(feature = "modern_sqlite", not(feature = "bundled-sqlcipher")))] // SQLite >= 3.38.0
 pub unsafe fn error_with_offset(db: *mut ffi::sqlite3, code: c_int, sql: &str) -> Error {
     if db.is_null() {
         error_from_sqlite_code(code, None)

From c313cd770dc813200bc36b9d2a724de5f1daadc3 Mon Sep 17 00:00:00 2001
From: gwenn <gtreguier@gmail.com>
Date: Fri, 22 Apr 2022 10:33:39 +0200
Subject: [PATCH 15/19] Params for 28-length arrays

---
 src/params.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/params.rs b/src/params.rs
index d37fa6c..ce818dd 100644
--- a/src/params.rs
+++ b/src/params.rs
@@ -327,7 +327,7 @@ macro_rules! impl_for_array_ref {
 // don't really think it matters -- users who hit that can use `params!` anyway.
 impl_for_array_ref!(
     1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
-    18 19 20 21 22 23 24 25 26 27 29 30 31 32
+    18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
 );
 
 /// Adapter type which allows any iterator over [`ToSql`] values to implement

From c3c6c713917d2bd7193eba15846b3213dec1e1b8 Mon Sep 17 00:00:00 2001
From: gwenn <gtreguier@gmail.com>
Date: Sat, 30 Apr 2022 07:23:21 +0000
Subject: [PATCH 16/19] Upgrade SQLite bundled version to 3.38.3

---
 .../sqlite3/bindgen_bundled_version.rs        |   6 +-
 libsqlite3-sys/sqlite3/sqlite3.c              | 128 +++++++++++++-----
 libsqlite3-sys/sqlite3/sqlite3.h              |   6 +-
 libsqlite3-sys/upgrade.sh                     |   2 +-
 4 files changed, 103 insertions(+), 39 deletions(-)

diff --git a/libsqlite3-sys/sqlite3/bindgen_bundled_version.rs b/libsqlite3-sys/sqlite3/bindgen_bundled_version.rs
index 6fbe8ef..a2d5fde 100644
--- a/libsqlite3-sys/sqlite3/bindgen_bundled_version.rs
+++ b/libsqlite3-sys/sqlite3/bindgen_bundled_version.rs
@@ -1,9 +1,9 @@
 /* automatically generated by rust-bindgen 0.59.2 */
 
-pub const SQLITE_VERSION: &[u8; 7usize] = b"3.38.2\0";
-pub const SQLITE_VERSION_NUMBER: i32 = 3038002;
+pub const SQLITE_VERSION: &[u8; 7usize] = b"3.38.3\0";
+pub const SQLITE_VERSION_NUMBER: i32 = 3038003;
 pub const SQLITE_SOURCE_ID: &[u8; 85usize] =
-    b"2022-03-26 13:51:10 d33c709cc0af66bc5b6dc6216eba9f1f0b40960b9ae83694c986fbf4c1d6f08f\0";
+    b"2022-04-27 12:03:15 9547e2c38a1c6f751a77d4d796894dec4dc5d8f5d79b1cd39e1ffc50df7b3be4\0";
 pub const SQLITE_OK: i32 = 0;
 pub const SQLITE_ERROR: i32 = 1;
 pub const SQLITE_INTERNAL: i32 = 2;
diff --git a/libsqlite3-sys/sqlite3/sqlite3.c b/libsqlite3-sys/sqlite3/sqlite3.c
index 0b227f0..4f3dc68 100644
--- a/libsqlite3-sys/sqlite3/sqlite3.c
+++ b/libsqlite3-sys/sqlite3/sqlite3.c
@@ -1,6 +1,6 @@
 /******************************************************************************
 ** This file is an amalgamation of many separate C source files from SQLite
-** version 3.38.2.  By combining all the individual C code files into this
+** version 3.38.3.  By combining all the individual C code files into this
 ** single large file, the entire code can be compiled as a single translation
 ** unit.  This allows many compilers to do optimizations that would not be
 ** possible if the files were compiled separately.  Performance improvements
@@ -452,9 +452,9 @@ extern "C" {
 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
 ** [sqlite_version()] and [sqlite_source_id()].
 */
-#define SQLITE_VERSION        "3.38.2"
-#define SQLITE_VERSION_NUMBER 3038002
-#define SQLITE_SOURCE_ID      "2022-03-26 13:51:10 d33c709cc0af66bc5b6dc6216eba9f1f0b40960b9ae83694c986fbf4c1d6f08f"
+#define SQLITE_VERSION        "3.38.3"
+#define SQLITE_VERSION_NUMBER 3038003
+#define SQLITE_SOURCE_ID      "2022-04-27 12:03:15 9547e2c38a1c6f751a77d4d796894dec4dc5d8f5d79b1cd39e1ffc50df7b3be4"
 
 /*
 ** CAPI3REF: Run-Time Library Version Numbers
@@ -19929,6 +19929,7 @@ SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr*);
 SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr*, u8);
 SQLITE_PRIVATE int sqlite3ExprIsConstantOrGroupBy(Parse*, Expr*, ExprList*);
 SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr*,int);
+SQLITE_PRIVATE int sqlite3ExprIsTableConstraint(Expr*,const SrcItem*);
 #ifdef SQLITE_ENABLE_CURSOR_HINTS
 SQLITE_PRIVATE int sqlite3ExprContainsSubquery(Expr*);
 #endif
@@ -29337,8 +29338,9 @@ SQLITE_PRIVATE char *sqlite3DbSpanDup(sqlite3 *db, const char *zStart, const cha
 ** Free any prior content in *pz and replace it with a copy of zNew.
 */
 SQLITE_PRIVATE void sqlite3SetString(char **pz, sqlite3 *db, const char *zNew){
+  char *z = sqlite3DbStrDup(db, zNew);
   sqlite3DbFree(db, *pz);
-  *pz = sqlite3DbStrDup(db, zNew);
+  *pz = z;
 }
 
 /*
@@ -67764,6 +67766,8 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){
         ** fragmented bytes within the page. */
         memcpy(&aData[iAddr], &aData[pc], 2);
         aData[hdr+7] += (u8)x;
+        testcase( pc+x>maxPC );
+        return &aData[pc];
       }else if( x+pc > maxPC ){
         /* This slot extends off the end of the usable part of the page */
         *pRc = SQLITE_CORRUPT_PAGE(pPg);
@@ -71963,7 +71967,7 @@ SQLITE_PRIVATE int sqlite3BtreeIndexMoveto(
     assert( lwr==upr+1 || (pPage->intKey && !pPage->leaf) );
     assert( pPage->isInit );
     if( pPage->leaf ){
-      assert( pCur->ix<pCur->pPage->nCell );
+      assert( pCur->ix<pCur->pPage->nCell || CORRUPT_DB );
       pCur->ix = (u16)idx;
       *pRes = c;
       rc = SQLITE_OK;
@@ -74487,7 +74491,7 @@ static int balance_nonroot(
     iOvflSpace += sz;
     assert( sz<=pBt->maxLocal+23 );
     assert( iOvflSpace <= (int)pBt->pageSize );
-    for(k=0; b.ixNx[k]<=i && ALWAYS(k<NB*2); k++){}
+    for(k=0; b.ixNx[k]<=j && ALWAYS(k<NB*2); k++){}
     pSrcEnd = b.apEnd[k];
     if( SQLITE_WITHIN(pSrcEnd, pCell, pCell+sz) ){
       rc = SQLITE_CORRUPT_BKPT;
@@ -78052,7 +78056,11 @@ SQLITE_PRIVATE int sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){
   assert( !sqlite3VdbeMemIsRowSet(pMem) );
   assert( desiredEnc==SQLITE_UTF8 || desiredEnc==SQLITE_UTF16LE
            || desiredEnc==SQLITE_UTF16BE );
-  if( !(pMem->flags&MEM_Str) || pMem->enc==desiredEnc ){
+  if( !(pMem->flags&MEM_Str) ){
+    pMem->enc = desiredEnc;
+    return SQLITE_OK;
+  }
+  if( pMem->enc==desiredEnc ){
     return SQLITE_OK;
   }
   assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
@@ -104759,6 +104767,38 @@ SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr *p, int iCur){
   return exprIsConst(p, 3, iCur);
 }
 
+/*
+** Check pExpr to see if it is an invariant constraint on data source pSrc.
+** This is an optimization.  False negatives will perhaps cause slower
+** queries, but false positives will yield incorrect answers.  So when in
+** double, return 0.
+**
+** To be an invariant constraint, the following must be true:
+**
+**   (1)  pExpr cannot refer to any table other than pSrc->iCursor.
+**
+**   (2)  pExpr cannot use subqueries or non-deterministic functions.
+**
+**   (*)  ** Not applicable to this branch **
+**
+**   (4)  If pSrc is the right operand of a LEFT JOIN, then...
+**         (4a)  pExpr must come from an ON clause..
+**         (4b)  and specifically the ON clause associated with the LEFT JOIN.
+**
+**   (5)  If pSrc is not the right operand of a LEFT JOIN or the left
+**        operand of a RIGHT JOIN, then pExpr must be from the WHERE
+**        clause, not an ON clause.
+*/
+SQLITE_PRIVATE int sqlite3ExprIsTableConstraint(Expr *pExpr, const SrcItem *pSrc){
+  if( pSrc->fg.jointype & JT_LEFT ){
+    if( !ExprHasProperty(pExpr, EP_FromJoin) ) return 0;    /* rule (4a) */
+    if( pExpr->w.iRightJoinTable!=pSrc->iCursor ) return 0; /* rule (4b) */
+  }else{
+    if( ExprHasProperty(pExpr, EP_FromJoin) ) return 0;     /* rule (5) */
+  }
+  return sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor);  /* rules (1), (2) */
+}
+
 
 /*
 ** sqlite3WalkExpr() callback used by sqlite3ExprIsConstantOrGroupBy().
@@ -139042,8 +139082,7 @@ static int pushDownWhereTerms(
   Parse *pParse,        /* Parse context (for malloc() and error reporting) */
   Select *pSubq,        /* The subquery whose WHERE clause is to be augmented */
   Expr *pWhere,         /* The WHERE clause of the outer query */
-  int iCursor,          /* Cursor number of the subquery */
-  int isLeftJoin        /* True if pSubq is the right term of a LEFT JOIN */
+  SrcItem *pSrc         /* The subquery term of the outer FROM clause */
 ){
   Expr *pNew;
   int nChng = 0;
@@ -139078,10 +139117,11 @@ static int pushDownWhereTerms(
     return 0; /* restriction (3) */
   }
   while( pWhere->op==TK_AND ){
-    nChng += pushDownWhereTerms(pParse, pSubq, pWhere->pRight,
-                                iCursor, isLeftJoin);
+    nChng += pushDownWhereTerms(pParse, pSubq, pWhere->pRight, pSrc);
     pWhere = pWhere->pLeft;
   }
+
+#if 0  /* Legacy code. Checks now done by sqlite3ExprIsTableConstraint() */
   if( isLeftJoin
    && (ExprHasProperty(pWhere,EP_FromJoin)==0
          || pWhere->w.iRightJoinTable!=iCursor)
@@ -139093,7 +139133,9 @@ static int pushDownWhereTerms(
   ){
     return 0; /* restriction (5) */
   }
-  if( sqlite3ExprIsTableConstant(pWhere, iCursor) ){
+#endif
+
+  if( sqlite3ExprIsTableConstraint(pWhere, pSrc) ){
     nChng++;
     pSubq->selFlags |= SF_PushDown;
     while( pSubq ){
@@ -139101,8 +139143,8 @@ static int pushDownWhereTerms(
       pNew = sqlite3ExprDup(pParse->db, pWhere, 0);
       unsetJoinExpr(pNew, -1);
       x.pParse = pParse;
-      x.iTable = iCursor;
-      x.iNewTable = iCursor;
+      x.iTable = pSrc->iCursor;
+      x.iNewTable = pSrc->iCursor;
       x.isLeftJoin = 0;
       x.pEList = pSubq->pEList;
       pNew = substExpr(&x, pNew);
@@ -140884,8 +140926,7 @@ SQLITE_PRIVATE int sqlite3Select(
     if( OptimizationEnabled(db, SQLITE_PushDown)
      && (pItem->fg.isCte==0
          || (pItem->u2.pCteUse->eM10d!=M10d_Yes && pItem->u2.pCteUse->nUse<2))
-     && pushDownWhereTerms(pParse, pSub, p->pWhere, pItem->iCursor,
-                           (pItem->fg.jointype & JT_OUTER)!=0)
+     && pushDownWhereTerms(pParse, pSub, p->pWhere, pItem)
     ){
 #if SELECTTRACE_ENABLED
       if( sqlite3SelectTrace & 0x100 ){
@@ -152810,8 +152851,7 @@ static SQLITE_NOINLINE void constructAutomaticIndex(
     ** WHERE clause (or the ON clause of a LEFT join) that constrain which
     ** rows of the target table (pSrc) that can be used. */
     if( (pTerm->wtFlags & TERM_VIRTUAL)==0
-     && ((pSrc->fg.jointype&JT_LEFT)==0 || ExprHasProperty(pExpr,EP_FromJoin))
-     && sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor)
+     && sqlite3ExprIsTableConstraint(pExpr, pSrc)
     ){
       pPartial = sqlite3ExprAnd(pParse, pPartial,
                                 sqlite3ExprDup(pParse->db, pExpr, 0));
@@ -153050,7 +153090,7 @@ static SQLITE_NOINLINE void sqlite3ConstructBloomFilter(
     for(pTerm=pWInfo->sWC.a; pTerm<pWCEnd; pTerm++){
       Expr *pExpr = pTerm->pExpr;
       if( (pTerm->wtFlags & TERM_VIRTUAL)==0
-       && sqlite3ExprIsTableConstant(pExpr, iCur)
+       && sqlite3ExprIsTableConstraint(pExpr, pItem)
       ){
         sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL);
       }
@@ -159970,7 +160010,7 @@ static void windowAggStep(
 
         for(iEnd=sqlite3VdbeCurrentAddr(v); iOp<iEnd; iOp++){
           VdbeOp *pOp = sqlite3VdbeGetOp(v, iOp);
-          if( pOp->opcode==OP_Column && pOp->p1==pWin->iEphCsr ){
+          if( pOp->opcode==OP_Column && pOp->p1==pMWin->iEphCsr ){
             pOp->p1 = csr;
           }
         }
@@ -194288,14 +194328,15 @@ static JsonNode *jsonLookupStep(
         *pzErr = zPath;
         return 0;
       }
+      testcase( nKey==0 );
     }else{
       zKey = zPath;
       for(i=0; zPath[i] && zPath[i]!='.' && zPath[i]!='['; i++){}
       nKey = i;
-    }
-    if( nKey==0 ){
-      *pzErr = zPath;
-      return 0;
+      if( nKey==0 ){
+        *pzErr = zPath;
+        return 0;
+      }
     }
     j = 1;
     for(;;){
@@ -195443,6 +195484,33 @@ static int jsonEachNext(sqlite3_vtab_cursor *cur){
   return SQLITE_OK;
 }
 
+/* Append an object label to the JSON Path being constructed
+** in pStr.
+*/
+static void jsonAppendObjectPathElement(
+  JsonString *pStr,
+  JsonNode *pNode
+){
+  int jj, nn;
+  const char *z;
+  assert( pNode->eType==JSON_STRING );
+  assert( pNode->jnFlags & JNODE_LABEL );
+  assert( pNode->eU==1 );
+  z = pNode->u.zJContent;
+  nn = pNode->n;
+  assert( nn>=2 );
+  assert( z[0]=='"' );
+  assert( z[nn-1]=='"' );
+  if( nn>2 && sqlite3Isalpha(z[1]) ){
+    for(jj=2; jj<nn-1 && sqlite3Isalnum(z[jj]); jj++){}
+    if( jj==nn-1 ){
+      z++;
+      nn -= 2;
+    }
+  }
+  jsonPrintf(nn+2, pStr, ".%.*s", nn, z);
+}
+
 /* Append the name of the path for element i to pStr
 */
 static void jsonEachComputePath(
@@ -195467,10 +195535,7 @@ static void jsonEachComputePath(
   }else{
     assert( pUp->eType==JSON_OBJECT );
     if( (pNode->jnFlags & JNODE_LABEL)==0 ) pNode--;
-    assert( pNode->eType==JSON_STRING );
-    assert( pNode->jnFlags & JNODE_LABEL );
-    assert( pNode->eU==1 );
-    jsonPrintf(pNode->n+1, pStr, ".%.*s", pNode->n-2, pNode->u.zJContent+1);
+    jsonAppendObjectPathElement(pStr, pNode);
   }
 }
 
@@ -195541,8 +195606,7 @@ static int jsonEachColumn(
         if( p->eType==JSON_ARRAY ){
           jsonPrintf(30, &x, "[%d]", p->iRowid);
         }else if( p->eType==JSON_OBJECT ){
-          assert( pThis->eU==1 );
-          jsonPrintf(pThis->n, &x, ".%.*s", pThis->n-2, pThis->u.zJContent+1);
+          jsonAppendObjectPathElement(&x, pThis);
         }
       }
       jsonResult(&x);
@@ -234433,7 +234497,7 @@ static void fts5SourceIdFunc(
 ){
   assert( nArg==0 );
   UNUSED_PARAM2(nArg, apUnused);
-  sqlite3_result_text(pCtx, "fts5: 2022-03-26 13:51:10 d33c709cc0af66bc5b6dc6216eba9f1f0b40960b9ae83694c986fbf4c1d6f08f", -1, SQLITE_TRANSIENT);
+  sqlite3_result_text(pCtx, "fts5: 2022-04-27 12:03:15 9547e2c38a1c6f751a77d4d796894dec4dc5d8f5d79b1cd39e1ffc50df7b3be4", -1, SQLITE_TRANSIENT);
 }
 
 /*
diff --git a/libsqlite3-sys/sqlite3/sqlite3.h b/libsqlite3-sys/sqlite3/sqlite3.h
index 33dbec2..eaa03d4 100644
--- a/libsqlite3-sys/sqlite3/sqlite3.h
+++ b/libsqlite3-sys/sqlite3/sqlite3.h
@@ -146,9 +146,9 @@ extern "C" {
 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
 ** [sqlite_version()] and [sqlite_source_id()].
 */
-#define SQLITE_VERSION        "3.38.2"
-#define SQLITE_VERSION_NUMBER 3038002
-#define SQLITE_SOURCE_ID      "2022-03-26 13:51:10 d33c709cc0af66bc5b6dc6216eba9f1f0b40960b9ae83694c986fbf4c1d6f08f"
+#define SQLITE_VERSION        "3.38.3"
+#define SQLITE_VERSION_NUMBER 3038003
+#define SQLITE_SOURCE_ID      "2022-04-27 12:03:15 9547e2c38a1c6f751a77d4d796894dec4dc5d8f5d79b1cd39e1ffc50df7b3be4"
 
 /*
 ** CAPI3REF: Run-Time Library Version Numbers
diff --git a/libsqlite3-sys/upgrade.sh b/libsqlite3-sys/upgrade.sh
index 42d8eb4..d0f63ca 100755
--- a/libsqlite3-sys/upgrade.sh
+++ b/libsqlite3-sys/upgrade.sh
@@ -9,7 +9,7 @@ export SQLITE3_LIB_DIR="$SCRIPT_DIR/sqlite3"
 export SQLITE3_INCLUDE_DIR="$SQLITE3_LIB_DIR"
 
 # Download and extract amalgamation
-SQLITE=sqlite-amalgamation-3380200
+SQLITE=sqlite-amalgamation-3380300
 curl -O https://sqlite.org/2022/$SQLITE.zip
 unzip -p "$SQLITE.zip" "$SQLITE/sqlite3.c" > "$SQLITE3_LIB_DIR/sqlite3.c"
 unzip -p "$SQLITE.zip" "$SQLITE/sqlite3.h" > "$SQLITE3_LIB_DIR/sqlite3.h"

From e6696097343afc59aacb8d0a14ae9edb75d76c98 Mon Sep 17 00:00:00 2001
From: Lucas Burns <burnsac@me.com>
Date: Thu, 5 May 2022 13:00:49 -0500
Subject: [PATCH 17/19] chore: derive Debug for Savepoint

---
 src/transaction.rs | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/transaction.rs b/src/transaction.rs
index 3afbe1a..cdc4f26 100644
--- a/src/transaction.rs
+++ b/src/transaction.rs
@@ -87,6 +87,7 @@ pub struct Transaction<'conn> {
 ///     sp.commit()
 /// }
 /// ```
+#[derive(Debug)]
 pub struct Savepoint<'conn> {
     conn: &'conn Connection,
     name: String,

From 42acaec9bc825a7608774fb558bd07a572b578c5 Mon Sep 17 00:00:00 2001
From: gwenn <gtreguier@gmail.com>
Date: Thu, 26 May 2022 07:33:37 +0200
Subject: [PATCH 18/19] Remove unused memchr dependency

---
 Cargo.toml | 1 -
 1 file changed, 1 deletion(-)

diff --git a/Cargo.toml b/Cargo.toml
index ef5e4c8..1debfc4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -120,7 +120,6 @@ url = { version = "2.1", optional = true }
 lazy_static = { version = "1.4", optional = true }
 fallible-iterator = "0.2"
 fallible-streaming-iterator = "0.1"
-memchr = "2.3"
 uuid = { version = "1.0", optional = true }
 smallvec = "1.6.1"
 

From 7a6a2d01d433221934a86b4b45058fed7eda96e9 Mon Sep 17 00:00:00 2001
From: gwenn <gtreguier@gmail.com>
Date: Thu, 26 May 2022 07:34:50 +0200
Subject: [PATCH 19/19] Upgrade regexp dev dependency

---
 Cargo.toml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Cargo.toml b/Cargo.toml
index 1debfc4..c6bbc94 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -127,7 +127,7 @@ smallvec = "1.6.1"
 doc-comment = "0.3"
 tempfile = "3.1.0"
 lazy_static = "1.4"
-regex = "1.3"
+regex = "1.5.5"
 uuid = { version = "1.0", features = ["v4"] }
 unicase = "2.6.0"
 # Use `bencher` over criterion because it builds much faster and we don't have