This is an automated email from the ASF dual-hosted git repository. erickguan pushed a commit to branch fix-sqlite-stat in repository https://gitbox.apache.org/repos/asf/opendal.git
commit 52bc23fee84df621d417a73484fffe6495f8206c Author: Erick Guan <[email protected]> AuthorDate: Sat Jul 4 17:18:01 2026 +0800 Fix sqlite stat getting the entire object --- core/services/sqlite/src/backend.rs | 75 +++++++++++++++++++++++++++++++++++-- core/services/sqlite/src/core.rs | 6 +-- 2 files changed, 74 insertions(+), 7 deletions(-) diff --git a/core/services/sqlite/src/backend.rs b/core/services/sqlite/src/backend.rs index 63feabbac..ad1571017 100644 --- a/core/services/sqlite/src/backend.rs +++ b/core/services/sqlite/src/backend.rs @@ -228,10 +228,10 @@ impl Service for SqliteBackend { if p == build_abs_path(&self.root, "") { Ok(RpStat::new(Metadata::new(EntryMode::DIR))) } else { - let bs = self.core.get(&p).await?; - match bs { - Some(bs) => Ok(RpStat::new( - Metadata::new(EntryMode::from_path(&p)).with_content_length(bs.len() as u64), + let length = self.core.get_length(&p).await?; + match length { + Some(length) => Ok(RpStat::new( + Metadata::new(EntryMode::from_path(&p)).with_content_length(length as u64), )), None => { // Check if this might be a directory by looking for keys with this prefix @@ -437,4 +437,71 @@ mod test { assert_eq!(buffer.to_vec(), b"world"); } + + #[tokio::test] + async fn test_sqlite_stat_uses_value_length() { + let core = SqliteCore { + pool: build_client().await, + config: Default::default(), + table: "test".to_string(), + key_field: "key".to_string(), + value_field: "value".to_string(), + }; + let pool = core.get_client().await.unwrap(); + sqlx::query("CREATE TABLE test (key TEXT PRIMARY KEY, value BLOB)") + .execute(pool) + .await + .unwrap(); + + let accessor = SqliteBackend::new(core); + let ctx = OperationContext::new(); + let mut writer = accessor.write(&ctx, "hello", OpWrite::default()).unwrap(); + writer.write(Buffer::from("hello world")).await.unwrap(); + writer.close().await.unwrap(); + + let rp = accessor + .stat(&ctx, "hello", OpStat::default()) + .await + .unwrap(); + + assert_eq!(rp.into_metadata().content_length(), 11); + } + + #[tokio::test] + async fn test_sqlite_stat_returns_byte_length_for_text_value() { + let core = SqliteCore { + pool: build_client().await, + config: Default::default(), + table: "test".to_string(), + key_field: "key".to_string(), + value_field: "value".to_string(), + }; + let pool = core.get_client().await.unwrap(); + sqlx::query("CREATE TABLE test (key TEXT PRIMARY KEY, value BLOB)") + .execute(pool) + .await + .unwrap(); + sqlx::query("INSERT INTO test (key, value) VALUES ($1, $2)") + .bind("hello") + .bind("你好") + .execute(pool) + .await + .unwrap(); + + let accessor = SqliteBackend::new(core); + let ctx = OperationContext::new(); + + let rp = accessor + .stat(&ctx, "hello", OpStat::default()) + .await + .unwrap(); + assert_eq!(rp.into_metadata().content_length(), 6); + + let reader = accessor.read(&ctx, "hello", OpRead::default()).unwrap(); + let (rp, mut stream) = reader.open(BytesRange::from(0_u64..3)).await.unwrap(); + let buffer = stream.read_all().await.unwrap(); + + assert_eq!(rp.into_metadata().unwrap().content_length(), 6); + assert_eq!(buffer.to_vec(), "你".as_bytes()); + } } diff --git a/core/services/sqlite/src/core.rs b/core/services/sqlite/src/core.rs index 9cc82440d..16ee5f68a 100644 --- a/core/services/sqlite/src/core.rs +++ b/core/services/sqlite/src/core.rs @@ -64,7 +64,7 @@ impl SqliteCore { let pool = self.get_client().await?; let value: Option<i64> = sqlx::query_scalar(&format!( - "SELECT LENGTH(`{}`) FROM `{}` WHERE `{}` = $1 LIMIT 1", + "SELECT LENGTH(CAST(`{}` AS BLOB)) FROM `{}` WHERE `{}` = $1 LIMIT 1", self.value_field, self.table, self.key_field )) .bind(path) @@ -91,7 +91,7 @@ impl SqliteCore { let pool = self.get_client().await?; let query = match limit { Some(limit) => format!( - "SELECT SUBSTR(`{}`, {}, {}), LENGTH(`{}`) FROM `{}` WHERE `{}` = $1 LIMIT 1", + "SELECT SUBSTR(CAST(`{}` AS BLOB), {}, {}), LENGTH(CAST(`{}` AS BLOB)) FROM `{}` WHERE `{}` = $1 LIMIT 1", self.value_field, start + 1, limit, @@ -100,7 +100,7 @@ impl SqliteCore { self.key_field ), None => format!( - "SELECT SUBSTR(`{}`, {}), LENGTH(`{}`) FROM `{}` WHERE `{}` = $1 LIMIT 1", + "SELECT SUBSTR(CAST(`{}` AS BLOB), {}), LENGTH(CAST(`{}` AS BLOB)) FROM `{}` WHERE `{}` = $1 LIMIT 1", self.value_field, start + 1, self.value_field,
