From 744cfe9f9eadfd176c6822f84217a9743693b16d Mon Sep 17 00:00:00 2001 From: ff137 Date: Mon, 3 Jun 2024 16:37:43 +0200 Subject: [PATCH 01/15] :construction: add order_by and descending argument to scan Signed-off-by: ff137 --- askar-storage/src/any.rs | 14 ++++++++---- askar-storage/src/backend/db_utils.rs | 14 ++++++++++++ askar-storage/src/backend/mod.rs | 13 +++++++++++- askar-storage/src/backend/postgres/mod.rs | 12 +++++++++-- askar-storage/src/backend/sqlite/mod.rs | 10 ++++++++- askar-storage/tests/utils/mod.rs | 26 +++++++++++++++++++++-- src/ffi/store.rs | 4 +++- src/store.rs | 4 ++++ 8 files changed, 86 insertions(+), 11 deletions(-) diff --git a/askar-storage/src/any.rs b/askar-storage/src/any.rs index c1e4e5dc0..45206a471 100644 --- a/askar-storage/src/any.rs +++ b/askar-storage/src/any.rs @@ -72,9 +72,12 @@ impl Backend for WrapBackend { tag_filter: Option, offset: Option, limit: Option, + order_by: Option, + descending: Option, ) -> BoxFuture<'_, Result, Error>> { - self.0 - .scan(profile, kind, category, tag_filter, offset, limit) + self.0.scan( + profile, kind, category, tag_filter, offset, limit, order_by, descending, + ) } #[inline] @@ -142,9 +145,12 @@ impl Backend for AnyBackend { tag_filter: Option, offset: Option, limit: Option, + order_by: Option, + descending: Option, ) -> BoxFuture<'_, Result, Error>> { - self.0 - .scan(profile, kind, category, tag_filter, offset, limit) + self.0.scan( + profile, kind, category, tag_filter, offset, limit, order_by, descending, + ) } #[inline] diff --git a/askar-storage/src/backend/db_utils.rs b/askar-storage/src/backend/db_utils.rs index 03c3e3400..37c01f719 100644 --- a/askar-storage/src/backend/db_utils.rs +++ b/askar-storage/src/backend/db_utils.rs @@ -453,6 +453,17 @@ pub trait QueryPrepare { } query } + + fn order_by_query<'q>(mut query: String, order_by: Option, descending: Option) -> String { + if let Some(order_by) = order_by { + query.push_str(" ORDER BY "); + query.push_str(&order_by); + if descending.is_some_and(|x| x) { + query.push_str(" DESC"); + } + } + query + } } pub fn replace_arg_placeholders( @@ -625,6 +636,8 @@ pub fn extend_query<'q, Q: QueryPrepare>( tag_filter: Option<(String, Vec>)>, offset: Option, limit: Option, + order_by: Option, + descending: Option, ) -> Result where i64: for<'e> Encode<'e, Q::DB> + Type, @@ -636,6 +649,7 @@ where query.push_str(" AND "); // assumes WHERE already occurs query.push_str(&filter_clause); }; + query = Q::order_by_query(query, order_by, descending); if offset.is_some() || limit.is_some() { query = Q::limit_query(query, args, offset, limit); }; diff --git a/askar-storage/src/backend/mod.rs b/askar-storage/src/backend/mod.rs index 36a3c2851..9840e824b 100644 --- a/askar-storage/src/backend/mod.rs +++ b/askar-storage/src/backend/mod.rs @@ -54,6 +54,8 @@ pub trait Backend: Debug + Send + Sync { tag_filter: Option, offset: Option, limit: Option, + order_by: Option, + descending: Option, ) -> BoxFuture<'_, Result, Error>>; /// Create a new session against the store @@ -185,7 +187,16 @@ pub async fn copy_profile( to_profile: &str, ) -> Result<(), Error> { let scan = from_backend - .scan(Some(from_profile.into()), None, None, None, None, None) + .scan( + Some(from_profile.into()), + None, + None, + None, + None, + None, + None, + None, + ) .await?; if let Err(e) = to_backend.create_profile(Some(to_profile.into())).await { if e.kind() != ErrorKind::Duplicate { diff --git a/askar-storage/src/backend/postgres/mod.rs b/askar-storage/src/backend/postgres/mod.rs index 229fa8b1f..d3e559b50 100644 --- a/askar-storage/src/backend/postgres/mod.rs +++ b/askar-storage/src/backend/postgres/mod.rs @@ -268,6 +268,8 @@ impl Backend for PostgresBackend { tag_filter: Option, offset: Option, limit: Option, + order_by: Option, + descending: Option, ) -> BoxFuture<'_, Result, Error>> { Box::pin(async move { let session = self.session(profile, false)?; @@ -282,6 +284,8 @@ impl Backend for PostgresBackend { tag_filter, offset, limit, + order_by, + descending, false, ); let stream = scan.then(move |enc_rows| { @@ -348,7 +352,7 @@ impl BackendSession for DbSession { .await?; params.push(enc_category); let query = - extend_query::(COUNT_QUERY, &mut params, tag_filter, None, None)?; + extend_query::(COUNT_QUERY, &mut params, tag_filter, None, None, None, None)?; let mut active = acquire_session(&mut *self).await?; let count = sqlx::query_scalar_with(query.as_str(), params) .fetch_one(active.connection_mut()) @@ -440,6 +444,8 @@ impl BackendSession for DbSession { tag_filter, None, limit, + None, + None, for_update, ); pin!(scan); @@ -752,6 +758,8 @@ fn perform_scan( tag_filter: Option, offset: Option, limit: Option, + order_by: Option, + descending: Option, for_update: bool, ) -> impl Stream, Error>> + '_ { try_stream! { @@ -772,7 +780,7 @@ fn perform_scan( } }).await?; params.push(enc_category); - let mut query = extend_query::(SCAN_QUERY, &mut params, tag_filter, offset, limit)?; + let mut query = extend_query::(SCAN_QUERY, &mut params, tag_filter, offset, limit, order_by, descending)?; if for_update { query.push_str(" FOR NO KEY UPDATE"); } diff --git a/askar-storage/src/backend/sqlite/mod.rs b/askar-storage/src/backend/sqlite/mod.rs index 90509faf5..d1b6ef354 100644 --- a/askar-storage/src/backend/sqlite/mod.rs +++ b/askar-storage/src/backend/sqlite/mod.rs @@ -262,6 +262,8 @@ impl Backend for SqliteBackend { tag_filter: Option, offset: Option, limit: Option, + order_by: Option, + descending: Option, ) -> BoxFuture<'_, Result, Error>> { Box::pin(async move { let session = self.session(profile, false)?; @@ -276,6 +278,8 @@ impl Backend for SqliteBackend { tag_filter, offset, limit, + order_by, + descending, ); let stream = scan.then(move |enc_rows| { let category = category.clone(); @@ -413,6 +417,8 @@ impl BackendSession for DbSession { tag_filter, None, limit, + None, + None, ); pin!(scan); let mut enc_rows = vec![]; @@ -703,6 +709,8 @@ fn perform_scan( tag_filter: Option, offset: Option, limit: Option, + order_by: Option, + descending: Option, ) -> impl Stream, Error>> + '_ { try_stream! { let mut params = QueryParams::new(); @@ -720,7 +728,7 @@ fn perform_scan( } }).await?; params.push(enc_category); - let query = extend_query::(SCAN_QUERY, &mut params, tag_filter, offset, limit)?; + let query = extend_query::(SCAN_QUERY, &mut params, tag_filter, offset, limit, order_by, descending)?; let mut batch = Vec::with_capacity(PAGE_SIZE); diff --git a/askar-storage/tests/utils/mod.rs b/askar-storage/tests/utils/mod.rs index 7e2c5173c..09f81cdbf 100644 --- a/askar-storage/tests/utils/mod.rs +++ b/askar-storage/tests/utils/mod.rs @@ -489,6 +489,8 @@ pub async fn db_scan(db: AnyBackend) { tag_filter, offset, limit, + order_by, + descending, ) .await .expect(ERR_SCAN); @@ -506,6 +508,8 @@ pub async fn db_scan(db: AnyBackend) { tag_filter, offset, limit, + order_by, + descending, ) .await .expect(ERR_SCAN); @@ -879,7 +883,16 @@ pub async fn db_import_scan(db: AnyBackend) { let copy = db.create_profile(None).await.expect(ERR_PROFILE); let mut copy_conn = db.session(Some(copy.clone()), true).expect(ERR_SESSION); let records = db - .scan(None, Some(EntryKind::Item), None, None, None, None) + .scan( + None, + Some(EntryKind::Item), + None, + None, + None, + None, + None, + None, + ) .await .expect(ERR_SCAN); copy_conn @@ -889,7 +902,16 @@ pub async fn db_import_scan(db: AnyBackend) { copy_conn.close(true).await.expect(ERR_COMMIT); let mut scan = db - .scan(Some(copy), Some(EntryKind::Item), None, None, None, None) + .scan( + Some(copy), + Some(EntryKind::Item), + None, + None, + None, + None, + None, + None, + ) .await .expect(ERR_SCAN); diff --git a/src/ffi/store.rs b/src/ffi/store.rs index 1c41509e1..f821f0533 100644 --- a/src/ffi/store.rs +++ b/src/ffi/store.rs @@ -547,6 +547,8 @@ pub extern "C" fn askar_scan_start( tag_filter: FfiStr<'_>, offset: i64, limit: i64, + order_by: Option, + descending: Option, cb: Option, cb_id: CallbackId, ) -> ErrorCode { @@ -568,7 +570,7 @@ pub extern "C" fn askar_scan_start( spawn_ok(async move { let result = async { let store = handle.load().await?; - let scan = store.scan(profile, category, tag_filter, Some(offset), if limit < 0 { None }else {Some(limit)}).await?; + let scan = store.scan(profile, category, tag_filter, Some(offset), if limit < 0 { None }else {Some(limit)}, order_by, descending).await?; Ok(FFI_SCANS.insert(handle, scan).await) }.await; cb.resolve(result); diff --git a/src/store.rs b/src/store.rs index 59e024e4d..e0abec08d 100644 --- a/src/store.rs +++ b/src/store.rs @@ -125,6 +125,8 @@ impl Store { tag_filter: Option, offset: Option, limit: Option, + order_by: Option, + descending: Option, ) -> Result, Error> { Ok(self .0 @@ -135,6 +137,8 @@ impl Store { tag_filter, offset, limit, + order_by, + descending, ) .await?) } From 29018ee5e7fe54abe3359398d98e3325082e6fde Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 26 Jul 2024 10:39:13 +0300 Subject: [PATCH 02/15] :art: format Signed-off-by: ff137 --- askar-storage/src/backend/db_utils.rs | 6 +++++- askar-storage/src/backend/postgres/mod.rs | 11 +++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/askar-storage/src/backend/db_utils.rs b/askar-storage/src/backend/db_utils.rs index 37c01f719..5e81a8690 100644 --- a/askar-storage/src/backend/db_utils.rs +++ b/askar-storage/src/backend/db_utils.rs @@ -454,7 +454,11 @@ pub trait QueryPrepare { query } - fn order_by_query<'q>(mut query: String, order_by: Option, descending: Option) -> String { + fn order_by_query<'q>( + mut query: String, + order_by: Option, + descending: Option, + ) -> String { if let Some(order_by) = order_by { query.push_str(" ORDER BY "); query.push_str(&order_by); diff --git a/askar-storage/src/backend/postgres/mod.rs b/askar-storage/src/backend/postgres/mod.rs index d3e559b50..600a17919 100644 --- a/askar-storage/src/backend/postgres/mod.rs +++ b/askar-storage/src/backend/postgres/mod.rs @@ -351,8 +351,15 @@ impl BackendSession for DbSession { }) .await?; params.push(enc_category); - let query = - extend_query::(COUNT_QUERY, &mut params, tag_filter, None, None, None, None)?; + let query = extend_query::( + COUNT_QUERY, + &mut params, + tag_filter, + None, + None, + None, + None, + )?; let mut active = acquire_session(&mut *self).await?; let count = sqlx::query_scalar_with(query.as_str(), params) .fetch_one(active.connection_mut()) From f03ac9fb2f74b941b4219d231b2a0ebe74a7d7d3 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 26 Jul 2024 11:12:54 +0300 Subject: [PATCH 03/15] :art: use Ffi types for order_by and descending Signed-off-by: ff137 --- src/ffi/store.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ffi/store.rs b/src/ffi/store.rs index f821f0533..525f86309 100644 --- a/src/ffi/store.rs +++ b/src/ffi/store.rs @@ -547,11 +547,14 @@ pub extern "C" fn askar_scan_start( tag_filter: FfiStr<'_>, offset: i64, limit: i64, - order_by: Option, - descending: Option, + order_by: FfiStr<'_>, + descending: i8, cb: Option, cb_id: CallbackId, ) -> ErrorCode { + let order_by = order_by.into_opt_string(); + let descending = descending != 0; // Convert to bool + catch_err! { trace!("Scan store start"); let cb = cb.ok_or_else(|| err_msg!("No callback provided"))?; From 93bcae634c194f78236ee010e3c80529cdceb995 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 26 Jul 2024 11:14:32 +0300 Subject: [PATCH 04/15] :art: replace Option[bool] with bool Signed-off-by: ff137 --- askar-storage/src/any.rs | 4 ++-- askar-storage/src/backend/db_utils.rs | 10 +++------- askar-storage/src/backend/mod.rs | 4 ++-- askar-storage/src/backend/postgres/mod.rs | 10 ++++++---- askar-storage/src/backend/sqlite/mod.rs | 19 ++++++++++++++----- askar-storage/tests/utils/mod.rs | 4 ++-- src/store.rs | 2 +- 7 files changed, 30 insertions(+), 23 deletions(-) diff --git a/askar-storage/src/any.rs b/askar-storage/src/any.rs index 45206a471..7bce4b18c 100644 --- a/askar-storage/src/any.rs +++ b/askar-storage/src/any.rs @@ -73,7 +73,7 @@ impl Backend for WrapBackend { offset: Option, limit: Option, order_by: Option, - descending: Option, + descending: bool, ) -> BoxFuture<'_, Result, Error>> { self.0.scan( profile, kind, category, tag_filter, offset, limit, order_by, descending, @@ -146,7 +146,7 @@ impl Backend for AnyBackend { offset: Option, limit: Option, order_by: Option, - descending: Option, + descending: bool, ) -> BoxFuture<'_, Result, Error>> { self.0.scan( profile, kind, category, tag_filter, offset, limit, order_by, descending, diff --git a/askar-storage/src/backend/db_utils.rs b/askar-storage/src/backend/db_utils.rs index 5e81a8690..188ae6487 100644 --- a/askar-storage/src/backend/db_utils.rs +++ b/askar-storage/src/backend/db_utils.rs @@ -454,15 +454,11 @@ pub trait QueryPrepare { query } - fn order_by_query<'q>( - mut query: String, - order_by: Option, - descending: Option, - ) -> String { + fn order_by_query<'q>(mut query: String, order_by: Option, descending: bool) -> String { if let Some(order_by) = order_by { query.push_str(" ORDER BY "); query.push_str(&order_by); - if descending.is_some_and(|x| x) { + if descending { query.push_str(" DESC"); } } @@ -641,7 +637,7 @@ pub fn extend_query<'q, Q: QueryPrepare>( offset: Option, limit: Option, order_by: Option, - descending: Option, + descending: bool, ) -> Result where i64: for<'e> Encode<'e, Q::DB> + Type, diff --git a/askar-storage/src/backend/mod.rs b/askar-storage/src/backend/mod.rs index 9840e824b..3f4f0e4a2 100644 --- a/askar-storage/src/backend/mod.rs +++ b/askar-storage/src/backend/mod.rs @@ -55,7 +55,7 @@ pub trait Backend: Debug + Send + Sync { offset: Option, limit: Option, order_by: Option, - descending: Option, + descending: bool, ) -> BoxFuture<'_, Result, Error>>; /// Create a new session against the store @@ -195,7 +195,7 @@ pub async fn copy_profile( None, None, None, - None, + false, ) .await?; if let Err(e) = to_backend.create_profile(Some(to_profile.into())).await { diff --git a/askar-storage/src/backend/postgres/mod.rs b/askar-storage/src/backend/postgres/mod.rs index 600a17919..e2c8b0626 100644 --- a/askar-storage/src/backend/postgres/mod.rs +++ b/askar-storage/src/backend/postgres/mod.rs @@ -269,7 +269,7 @@ impl Backend for PostgresBackend { offset: Option, limit: Option, order_by: Option, - descending: Option, + descending: bool, ) -> BoxFuture<'_, Result, Error>> { Box::pin(async move { let session = self.session(profile, false)?; @@ -358,7 +358,7 @@ impl BackendSession for DbSession { None, None, None, - None, + false, )?; let mut active = acquire_session(&mut *self).await?; let count = sqlx::query_scalar_with(query.as_str(), params) @@ -452,7 +452,7 @@ impl BackendSession for DbSession { None, limit, None, - None, + false, for_update, ); pin!(scan); @@ -496,6 +496,8 @@ impl BackendSession for DbSession { tag_filter, None, None, + None, + false, )?; let mut active = acquire_session(&mut *self).await?; @@ -766,7 +768,7 @@ fn perform_scan( offset: Option, limit: Option, order_by: Option, - descending: Option, + descending: bool, for_update: bool, ) -> impl Stream, Error>> + '_ { try_stream! { diff --git a/askar-storage/src/backend/sqlite/mod.rs b/askar-storage/src/backend/sqlite/mod.rs index d1b6ef354..1955d3fe5 100644 --- a/askar-storage/src/backend/sqlite/mod.rs +++ b/askar-storage/src/backend/sqlite/mod.rs @@ -263,7 +263,7 @@ impl Backend for SqliteBackend { offset: Option, limit: Option, order_by: Option, - descending: Option, + descending: bool, ) -> BoxFuture<'_, Result, Error>> { Box::pin(async move { let session = self.session(profile, false)?; @@ -334,8 +334,15 @@ impl BackendSession for DbSession { }) .await?; params.push(enc_category); - let query = - extend_query::(COUNT_QUERY, &mut params, tag_filter, None, None)?; + let query = extend_query::( + COUNT_QUERY, + &mut params, + tag_filter, + None, + None, + None, + false, + )?; let mut active = acquire_session(&mut *self).await?; let count = sqlx::query_scalar_with(query.as_str(), params) .fetch_one(active.connection_mut()) @@ -418,7 +425,7 @@ impl BackendSession for DbSession { None, limit, None, - None, + false, ); pin!(scan); let mut enc_rows = vec![]; @@ -461,6 +468,8 @@ impl BackendSession for DbSession { tag_filter, None, None, + None, + false, )?; let mut active = acquire_session(&mut *self).await?; @@ -710,7 +719,7 @@ fn perform_scan( offset: Option, limit: Option, order_by: Option, - descending: Option, + descending: bool, ) -> impl Stream, Error>> + '_ { try_stream! { let mut params = QueryParams::new(); diff --git a/askar-storage/tests/utils/mod.rs b/askar-storage/tests/utils/mod.rs index 09f81cdbf..6604847e4 100644 --- a/askar-storage/tests/utils/mod.rs +++ b/askar-storage/tests/utils/mod.rs @@ -891,7 +891,7 @@ pub async fn db_import_scan(db: AnyBackend) { None, None, None, - None, + false, ) .await .expect(ERR_SCAN); @@ -910,7 +910,7 @@ pub async fn db_import_scan(db: AnyBackend) { None, None, None, - None, + false, ) .await .expect(ERR_SCAN); diff --git a/src/store.rs b/src/store.rs index e0abec08d..7c00c7fc8 100644 --- a/src/store.rs +++ b/src/store.rs @@ -126,7 +126,7 @@ impl Store { offset: Option, limit: Option, order_by: Option, - descending: Option, + descending: bool, ) -> Result, Error> { Ok(self .0 From 1dadd5a2527d1856bdb3d988871417493e05eaca Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 26 Jul 2024 11:34:20 +0300 Subject: [PATCH 05/15] :art: fix order_by and descending reference Signed-off-by: ff137 --- askar-storage/tests/utils/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/askar-storage/tests/utils/mod.rs b/askar-storage/tests/utils/mod.rs index 6604847e4..0d83d599b 100644 --- a/askar-storage/tests/utils/mod.rs +++ b/askar-storage/tests/utils/mod.rs @@ -489,8 +489,8 @@ pub async fn db_scan(db: AnyBackend) { tag_filter, offset, limit, - order_by, - descending, + None, + false, ) .await .expect(ERR_SCAN); @@ -508,8 +508,8 @@ pub async fn db_scan(db: AnyBackend) { tag_filter, offset, limit, - order_by, - descending, + None, + false, ) .await .expect(ERR_SCAN); From a38d6dcf06d12c3c448e03a28536ecd86799cd96 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 26 Jul 2024 11:35:13 +0300 Subject: [PATCH 06/15] :sparkles: add order_by and descending options to python Scan object Signed-off-by: ff137 --- .../python/aries_askar/bindings/__init__.py | 6 ++- wrappers/python/aries_askar/store.py | 39 +++++++++++++++++-- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/wrappers/python/aries_askar/bindings/__init__.py b/wrappers/python/aries_askar/bindings/__init__.py index 065a8273e..d7b5f7e9f 100644 --- a/wrappers/python/aries_askar/bindings/__init__.py +++ b/wrappers/python/aries_askar/bindings/__init__.py @@ -455,17 +455,21 @@ async def scan_start( tag_filter: Optional[Union[str, dict]] = None, offset: Optional[int] = None, limit: Optional[int] = None, + order_by: Optional[str] = None, + descending: bool = False, ) -> ScanHandle: """Create a new Scan against the Store.""" return await invoke_async( "askar_scan_start", - (StoreHandle, FfiStr, FfiStr, FfiJson, c_int64, c_int64), + (StoreHandle, FfiStr, FfiStr, FfiJson, c_int64, c_int64, FfiStr, c_int8), handle, profile, category, tag_filter, offset or 0, limit if limit is not None else -1, + order_by, + descending, return_type=ScanHandle, ) diff --git a/wrappers/python/aries_askar/store.py b/wrappers/python/aries_askar/store.py index f92002a6b..c68253703 100644 --- a/wrappers/python/aries_askar/store.py +++ b/wrappers/python/aries_askar/store.py @@ -247,9 +247,20 @@ def __init__( tag_filter: Union[str, dict] = None, offset: int = None, limit: int = None, + order_by: Optional[str] = None, + descending: bool = False, ): """Initialize the Scan instance.""" - self._params = (store, profile, category, tag_filter, offset, limit) + self._params = ( + store, + profile, + category, + tag_filter, + offset, + limit, + order_by, + descending, + ) self._handle: ScanHandle = None self._buffer: IterEntryList = None @@ -265,14 +276,30 @@ def __aiter__(self): async def __anext__(self): """Fetch the next scan result during async iteration.""" if self._handle is None: - (store, profile, category, tag_filter, offset, limit) = self._params + ( + store, + profile, + category, + tag_filter, + offset, + limit, + order_by, + descending, + ) = self._params self._params = None if not store.handle: raise AskarError( AskarErrorCode.WRAPPER, "Cannot scan from closed store" ) self._handle = await bindings.scan_start( - store.handle, profile, category, tag_filter, offset, limit + store.handle, + profile, + category, + tag_filter, + offset, + limit, + order_by, + descending, ) list_handle = await bindings.scan_next(self._handle) self._buffer = iter(EntryList(list_handle)) if list_handle else None @@ -428,9 +455,13 @@ def scan( offset: int = None, limit: int = None, profile: str = None, + order_by: Optional[str] = None, + descending: bool = False, ) -> Scan: """Start a new record scan.""" - return Scan(self, profile, category, tag_filter, offset, limit) + return Scan( + self, profile, category, tag_filter, offset, limit, order_by, descending + ) def session(self, profile: str = None) -> "OpenSession": """Open a new session on the store without starting a transaction.""" From cf5787e630da7b64d07ccaccbf21f883e814b81d Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 26 Jul 2024 15:01:12 +0300 Subject: [PATCH 07/15] :sparkles: add order_by and descending options to scan in javascript wrapper Signed-off-by: ff137 --- .../aries-askar-nodejs/src/NodeJSAriesAskar.ts | 5 ++++- .../aries-askar-nodejs/src/library/bindings.ts | 13 ++++++++++++- .../aries-askar-react-native/cpp/ariesAskar.cpp | 4 +++- .../cpp/include/libaries_askar.h | 2 ++ .../aries-askar-shared/src/ariesAskar/AriesAskar.ts | 2 ++ .../packages/aries-askar-shared/src/store/Scan.ts | 10 ++++++++++ .../packages/aries-askar-shared/src/store/Store.ts | 2 ++ 7 files changed, 35 insertions(+), 3 deletions(-) diff --git a/wrappers/javascript/packages/aries-askar-nodejs/src/NodeJSAriesAskar.ts b/wrappers/javascript/packages/aries-askar-nodejs/src/NodeJSAriesAskar.ts index 8f221e302..a7775760c 100644 --- a/wrappers/javascript/packages/aries-askar-nodejs/src/NodeJSAriesAskar.ts +++ b/wrappers/javascript/packages/aries-askar-nodejs/src/NodeJSAriesAskar.ts @@ -787,7 +787,8 @@ export class NodeJSAriesAskar implements AriesAskar { } public async scanStart(options: ScanStartOptions): Promise { - const { category, limit, offset, profile, storeHandle, tagFilter } = serializeArguments(options) + const { category, descending, limit, offset, orderBy, profile, storeHandle, tagFilter } = + serializeArguments(options) const handle = await this.promisifyWithResponse( (cb, cbId) => this.nativeAriesAskar.askar_scan_start( @@ -797,6 +798,8 @@ export class NodeJSAriesAskar implements AriesAskar { tagFilter, +offset || 0, +limit || -1, + orderBy, + descending, cb, cbId, ), diff --git a/wrappers/javascript/packages/aries-askar-nodejs/src/library/bindings.ts b/wrappers/javascript/packages/aries-askar-nodejs/src/library/bindings.ts index e1007d5e9..7cbc962a4 100644 --- a/wrappers/javascript/packages/aries-askar-nodejs/src/library/bindings.ts +++ b/wrappers/javascript/packages/aries-askar-nodejs/src/library/bindings.ts @@ -136,7 +136,18 @@ export const nativeBindings = { askar_scan_next: [FFI_ERROR_CODE, [FFI_SCAN_HANDLE, FFI_CALLBACK_PTR, FFI_CALLBACK_ID]], askar_scan_start: [ FFI_ERROR_CODE, - [FFI_STORE_HANDLE, FFI_STRING, FFI_STRING, FFI_STRING, FFI_INT64, FFI_INT64, FFI_CALLBACK_PTR, FFI_CALLBACK_ID], + [ + FFI_STORE_HANDLE, + FFI_STRING, + FFI_STRING, + FFI_STRING, + FFI_INT64, + FFI_INT64, + FFI_STRING, + FFI_INT8, + FFI_CALLBACK_PTR, + FFI_CALLBACK_ID, + ], ], askar_session_close: [FFI_ERROR_CODE, [FFI_SESSION_HANDLE, FFI_INT8, FFI_CALLBACK_PTR, FFI_CALLBACK_ID]], diff --git a/wrappers/javascript/packages/aries-askar-react-native/cpp/ariesAskar.cpp b/wrappers/javascript/packages/aries-askar-react-native/cpp/ariesAskar.cpp index 864877e80..9ae5acd57 100644 --- a/wrappers/javascript/packages/aries-askar-react-native/cpp/ariesAskar.cpp +++ b/wrappers/javascript/packages/aries-askar-react-native/cpp/ariesAskar.cpp @@ -502,6 +502,8 @@ jsi::Value scanStart(jsi::Runtime &rt, jsi::Object options) { auto profile = jsiToValue(rt, options, "profile", true); auto offset = jsiToValue(rt, options, "offset", true); auto limit = jsiToValue(rt, options, "limit", true); + auto orderBy = jsiToValue(rt, options, "orderBy", true); + auto descending = jsiToValue(rt, options, "descending"); jsi::Function cb = options.getPropertyAsFunction(rt, "cb"); State *state = new State(&cb); @@ -510,7 +512,7 @@ jsi::Value scanStart(jsi::Runtime &rt, jsi::Object options) { ErrorCode code = askar_scan_start( storeHandle, profile.length() ? profile.c_str() : nullptr, category.c_str(), tagFilter.length() ? tagFilter.c_str() : nullptr, - offset, limit, callbackWithResponse, CallbackId(state)); + offset, limit, orderBy, descending, callbackWithResponse, CallbackId(state)); return createReturnValue(rt, code, nullptr); }; diff --git a/wrappers/javascript/packages/aries-askar-react-native/cpp/include/libaries_askar.h b/wrappers/javascript/packages/aries-askar-react-native/cpp/include/libaries_askar.h index 103dc47fe..6a8cb52b3 100644 --- a/wrappers/javascript/packages/aries-askar-react-native/cpp/include/libaries_askar.h +++ b/wrappers/javascript/packages/aries-askar-react-native/cpp/include/libaries_askar.h @@ -440,6 +440,8 @@ ErrorCode askar_scan_start(StoreHandle handle, FfiStr tag_filter, int64_t offset, int64_t limit, + FfiStr order_by, + int8_t descending, void (*cb)(CallbackId cb_id, ErrorCode err, ScanHandle handle), CallbackId cb_id); diff --git a/wrappers/javascript/packages/aries-askar-shared/src/ariesAskar/AriesAskar.ts b/wrappers/javascript/packages/aries-askar-shared/src/ariesAskar/AriesAskar.ts index 06d9d5ae2..34d43abd9 100644 --- a/wrappers/javascript/packages/aries-askar-shared/src/ariesAskar/AriesAskar.ts +++ b/wrappers/javascript/packages/aries-askar-shared/src/ariesAskar/AriesAskar.ts @@ -211,6 +211,8 @@ export type ScanStartOptions = { tagFilter?: Record offset?: number limit?: number + orderBy?: string + descending: boolean } export type SessionCloseOptions = { diff --git a/wrappers/javascript/packages/aries-askar-shared/src/store/Scan.ts b/wrappers/javascript/packages/aries-askar-shared/src/store/Scan.ts index fb9ca2144..529b11800 100644 --- a/wrappers/javascript/packages/aries-askar-shared/src/store/Scan.ts +++ b/wrappers/javascript/packages/aries-askar-shared/src/store/Scan.ts @@ -16,11 +16,15 @@ export class Scan { private tagFilter?: Record private offset?: number private limit?: number + private orderBy?: string + private descending: boolean public constructor({ category, limit, offset, + orderBy, + descending, profile, tagFilter, store, @@ -30,6 +34,8 @@ export class Scan { tagFilter?: Record offset?: number limit?: number + orderBy?: string + descending: boolean store: Store }) { this.category = category @@ -37,6 +43,8 @@ export class Scan { this.tagFilter = tagFilter this.offset = offset this.limit = limit + this.orderBy = orderBy + this.descending = descending this.store = store } @@ -51,6 +59,8 @@ export class Scan { storeHandle: this.store.handle, limit: this.limit, offset: this.offset, + orderBy: this.orderBy, + descending: this.descending, tagFilter: this.tagFilter, profile: this.profile, category: this.category, diff --git a/wrappers/javascript/packages/aries-askar-shared/src/store/Store.ts b/wrappers/javascript/packages/aries-askar-shared/src/store/Store.ts index 1b4fd134c..ac442674b 100644 --- a/wrappers/javascript/packages/aries-askar-shared/src/store/Store.ts +++ b/wrappers/javascript/packages/aries-askar-shared/src/store/Store.ts @@ -120,6 +120,8 @@ export class Store { tagFilter?: Record offset?: number limit?: number + orderBy?: string + descending: boolean profile?: string }) { return new Scan({ ...options, store: this }) From aee343eed91ce0d30dcfd611a016361466ed4801 Mon Sep 17 00:00:00 2001 From: ff137 Date: Thu, 15 Aug 2024 15:01:38 +0300 Subject: [PATCH 08/15] :sparkles: implement OrderBy as Enum, with Default impl Signed-off-by: ff137 --- askar-storage/src/any.rs | 5 +++-- askar-storage/src/backend/db_utils.rs | 4 +++- askar-storage/src/backend/mod.rs | 14 +++++++++++++- askar-storage/src/backend/postgres/mod.rs | 5 +++-- askar-storage/src/backend/sqlite/mod.rs | 5 +++-- src/ffi/store.rs | 8 +++++++- src/store.rs | 4 ++-- 7 files changed, 34 insertions(+), 11 deletions(-) diff --git a/askar-storage/src/any.rs b/askar-storage/src/any.rs index 7bce4b18c..25c39e6a9 100644 --- a/askar-storage/src/any.rs +++ b/askar-storage/src/any.rs @@ -4,6 +4,7 @@ use std::{fmt::Debug, sync::Arc}; use super::{Backend, BackendSession, ManageBackend}; use crate::{ + backend::OrderBy, entry::{Entry, EntryKind, EntryOperation, EntryTag, Scan, TagFilter}, error::Error, future::BoxFuture, @@ -72,7 +73,7 @@ impl Backend for WrapBackend { tag_filter: Option, offset: Option, limit: Option, - order_by: Option, + order_by: Option, descending: bool, ) -> BoxFuture<'_, Result, Error>> { self.0.scan( @@ -145,7 +146,7 @@ impl Backend for AnyBackend { tag_filter: Option, offset: Option, limit: Option, - order_by: Option, + order_by: Option, descending: bool, ) -> BoxFuture<'_, Result, Error>> { self.0.scan( diff --git a/askar-storage/src/backend/db_utils.rs b/askar-storage/src/backend/db_utils.rs index 188ae6487..9df9efaa6 100644 --- a/askar-storage/src/backend/db_utils.rs +++ b/askar-storage/src/backend/db_utils.rs @@ -18,6 +18,8 @@ use crate::{ }, }; +use super::OrderBy; + /// cbindgen:ignore pub const PAGE_SIZE: usize = 32; @@ -636,7 +638,7 @@ pub fn extend_query<'q, Q: QueryPrepare>( tag_filter: Option<(String, Vec>)>, offset: Option, limit: Option, - order_by: Option, + order_by: Option, descending: bool, ) -> Result where diff --git a/askar-storage/src/backend/mod.rs b/askar-storage/src/backend/mod.rs index 3f4f0e4a2..959647de8 100644 --- a/askar-storage/src/backend/mod.rs +++ b/askar-storage/src/backend/mod.rs @@ -22,6 +22,18 @@ pub mod postgres; /// Sqlite database support pub mod sqlite; +/// Enum to support custom ordering in record queries +#[derive(Debug)] +pub enum OrderBy { + /// Order by ID field + Id, +} + +impl Default for OrderBy { + fn default() -> Self { + OrderBy::Id + } +} /// Represents a generic backend implementation pub trait Backend: Debug + Send + Sync { /// The type of session managed by this backend @@ -54,7 +66,7 @@ pub trait Backend: Debug + Send + Sync { tag_filter: Option, offset: Option, limit: Option, - order_by: Option, + order_by: Option, descending: bool, ) -> BoxFuture<'_, Result, Error>>; diff --git a/askar-storage/src/backend/postgres/mod.rs b/askar-storage/src/backend/postgres/mod.rs index e2c8b0626..19bcebd8a 100644 --- a/askar-storage/src/backend/postgres/mod.rs +++ b/askar-storage/src/backend/postgres/mod.rs @@ -25,6 +25,7 @@ use super::{ Backend, BackendSession, }; use crate::{ + backend::OrderBy, entry::{EncEntryTag, Entry, EntryKind, EntryOperation, EntryTag, Scan, TagFilter}, error::Error, future::{unblock, BoxFuture}, @@ -268,7 +269,7 @@ impl Backend for PostgresBackend { tag_filter: Option, offset: Option, limit: Option, - order_by: Option, + order_by: Option, descending: bool, ) -> BoxFuture<'_, Result, Error>> { Box::pin(async move { @@ -767,7 +768,7 @@ fn perform_scan( tag_filter: Option, offset: Option, limit: Option, - order_by: Option, + order_by: Option, descending: bool, for_update: bool, ) -> impl Stream, Error>> + '_ { diff --git a/askar-storage/src/backend/sqlite/mod.rs b/askar-storage/src/backend/sqlite/mod.rs index 1955d3fe5..1a0f1a056 100644 --- a/askar-storage/src/backend/sqlite/mod.rs +++ b/askar-storage/src/backend/sqlite/mod.rs @@ -24,6 +24,7 @@ use super::{ Backend, BackendSession, }; use crate::{ + backend::OrderBy, entry::{EncEntryTag, Entry, EntryKind, EntryOperation, EntryTag, Scan, TagFilter}, error::Error, future::{unblock, BoxFuture}, @@ -262,7 +263,7 @@ impl Backend for SqliteBackend { tag_filter: Option, offset: Option, limit: Option, - order_by: Option, + order_by: Option, descending: bool, ) -> BoxFuture<'_, Result, Error>> { Box::pin(async move { @@ -718,7 +719,7 @@ fn perform_scan( tag_filter: Option, offset: Option, limit: Option, - order_by: Option, + order_by: Option, descending: bool, ) -> impl Stream, Error>> + '_ { try_stream! { diff --git a/src/ffi/store.rs b/src/ffi/store.rs index 525f86309..eb2d1fce3 100644 --- a/src/ffi/store.rs +++ b/src/ffi/store.rs @@ -1,5 +1,6 @@ use std::{collections::BTreeMap, ffi::CString, os::raw::c_char, ptr, str::FromStr, sync::Arc}; +use askar_storage::backend::OrderBy; use async_lock::{Mutex as TryMutex, MutexGuardArc as TryMutexGuard, RwLock}; use ffi_support::{rust_string_to_c, ByteBuffer, FfiStr}; use once_cell::sync::Lazy; @@ -552,7 +553,12 @@ pub extern "C" fn askar_scan_start( cb: Option, cb_id: CallbackId, ) -> ErrorCode { - let order_by = order_by.into_opt_string(); + let order_by_str = order_by.as_opt_str().map(|s| s.to_lowercase()); + let order_by = match order_by_str.as_deref() { + Some("id") => Some(OrderBy::Id), + Some(_) => return ErrorCode::Unsupported, + None => None, + }; let descending = descending != 0; // Convert to bool catch_err! { diff --git a/src/store.rs b/src/store.rs index 7c00c7fc8..2cea28095 100644 --- a/src/store.rs +++ b/src/store.rs @@ -1,4 +1,4 @@ -use askar_storage::backend::copy_profile; +use askar_storage::backend::{copy_profile, OrderBy}; use crate::{ error::Error, @@ -125,7 +125,7 @@ impl Store { tag_filter: Option, offset: Option, limit: Option, - order_by: Option, + order_by: Option, descending: bool, ) -> Result, Error> { Ok(self From 974e996ef03bb438c184c5e396312e3a221c3c59 Mon Sep 17 00:00:00 2001 From: ff137 Date: Thu, 15 Aug 2024 15:07:11 +0300 Subject: [PATCH 09/15] :art: order_by_query now takes OrderBy enum directly, instead of Option. Signed-off-by: ff137 --- askar-storage/src/backend/db_utils.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/askar-storage/src/backend/db_utils.rs b/askar-storage/src/backend/db_utils.rs index 9df9efaa6..a4ee3461c 100644 --- a/askar-storage/src/backend/db_utils.rs +++ b/askar-storage/src/backend/db_utils.rs @@ -456,13 +456,13 @@ pub trait QueryPrepare { query } - fn order_by_query<'q>(mut query: String, order_by: Option, descending: bool) -> String { - if let Some(order_by) = order_by { + fn order_by_query<'q>(mut query: String, order_by: OrderBy, descending: bool) -> String { query.push_str(" ORDER BY "); - query.push_str(&order_by); + match order_by { + OrderBy::Id => query.push_str("id"), + } if descending { query.push_str(" DESC"); - } } query } From 1a37edf66aeb8729c09c1052027b950258b6e2bf Mon Sep 17 00:00:00 2001 From: ff137 Date: Thu, 15 Aug 2024 15:08:01 +0300 Subject: [PATCH 10/15] :sparkles: Only add ordering, and limit/offset, if the query starts with SELECT Signed-off-by: ff137 --- askar-storage/src/backend/db_utils.rs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/askar-storage/src/backend/db_utils.rs b/askar-storage/src/backend/db_utils.rs index a4ee3461c..51715eeff 100644 --- a/askar-storage/src/backend/db_utils.rs +++ b/askar-storage/src/backend/db_utils.rs @@ -457,12 +457,12 @@ pub trait QueryPrepare { } fn order_by_query<'q>(mut query: String, order_by: OrderBy, descending: bool) -> String { - query.push_str(" ORDER BY "); + query.push_str(" ORDER BY "); match order_by { OrderBy::Id => query.push_str("id"), } - if descending { - query.push_str(" DESC"); + if descending { + query.push_str(" DESC"); } query } @@ -651,10 +651,16 @@ where query.push_str(" AND "); // assumes WHERE already occurs query.push_str(&filter_clause); }; - query = Q::order_by_query(query, order_by, descending); - if offset.is_some() || limit.is_some() { - query = Q::limit_query(query, args, offset, limit); - }; + // Only add ordering, and limit/offset, if the query starts with SELECT + if query.trim_start().to_uppercase().starts_with("SELECT") { + if let Some(order_by_value) = order_by { + query = Q::order_by_query(query, order_by_value, descending); + }; + + if offset.is_some() || limit.is_some() { + query = Q::limit_query(query, args, offset, limit); + }; + } Ok(query) } From 2efcaded66d784e892396635c4b0aabedb8ac3e6 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 16 Aug 2024 23:07:40 +0300 Subject: [PATCH 11/15] :sparkles: expand ordering functionality to fetch_all query Signed-off-by: ff137 --- askar-storage/src/any.rs | 7 +++++-- askar-storage/src/backend/mod.rs | 2 ++ askar-storage/src/backend/postgres/mod.rs | 6 ++++-- askar-storage/src/backend/sqlite/mod.rs | 6 ++++-- src/ffi/store.rs | 12 +++++++++++- src/store.rs | 6 ++++++ .../aries-askar-nodejs/src/NodeJSAriesAskar.ts | 4 +++- .../aries-askar-nodejs/src/library/bindings.ts | 12 +++++++++++- .../aries-askar-react-native/cpp/ariesAskar.cpp | 6 ++++-- .../cpp/include/libaries_askar.h | 2 ++ .../aries-askar-shared/src/ariesAskar/AriesAskar.ts | 2 ++ wrappers/python/aries_askar/bindings/__init__.py | 6 +++++- wrappers/python/aries_askar/store.py | 10 +++++++++- 13 files changed, 68 insertions(+), 13 deletions(-) diff --git a/askar-storage/src/any.rs b/askar-storage/src/any.rs index 25c39e6a9..3a0d8ad61 100644 --- a/askar-storage/src/any.rs +++ b/askar-storage/src/any.rs @@ -214,10 +214,13 @@ impl BackendSession for AnyBackendSession { category: Option<&'q str>, tag_filter: Option, limit: Option, + order_by: Option, + descending: bool, for_update: bool, ) -> BoxFuture<'q, Result, Error>> { - self.0 - .fetch_all(kind, category, tag_filter, limit, for_update) + self.0.fetch_all( + kind, category, tag_filter, limit, order_by, descending, for_update, + ) } /// Remove all matching records from the store diff --git a/askar-storage/src/backend/mod.rs b/askar-storage/src/backend/mod.rs index 959647de8..4393a5dbe 100644 --- a/askar-storage/src/backend/mod.rs +++ b/askar-storage/src/backend/mod.rs @@ -136,6 +136,8 @@ pub trait BackendSession: Debug + Send { category: Option<&'q str>, tag_filter: Option, limit: Option, + order_by: Option, + descending: bool, for_update: bool, ) -> BoxFuture<'q, Result, Error>>; diff --git a/askar-storage/src/backend/postgres/mod.rs b/askar-storage/src/backend/postgres/mod.rs index 19bcebd8a..74241cfa2 100644 --- a/askar-storage/src/backend/postgres/mod.rs +++ b/askar-storage/src/backend/postgres/mod.rs @@ -436,6 +436,8 @@ impl BackendSession for DbSession { category: Option<&'q str>, tag_filter: Option, limit: Option, + order_by: Option, + descending: bool, for_update: bool, ) -> BoxFuture<'q, Result, Error>> { let category = category.map(|c| c.to_string()); @@ -452,8 +454,8 @@ impl BackendSession for DbSession { tag_filter, None, limit, - None, - false, + order_by, + descending, for_update, ); pin!(scan); diff --git a/askar-storage/src/backend/sqlite/mod.rs b/askar-storage/src/backend/sqlite/mod.rs index 1a0f1a056..73f5326b2 100644 --- a/askar-storage/src/backend/sqlite/mod.rs +++ b/askar-storage/src/backend/sqlite/mod.rs @@ -410,6 +410,8 @@ impl BackendSession for DbSession { category: Option<&'q str>, tag_filter: Option, limit: Option, + order_by: Option, + descending: bool, _for_update: bool, ) -> BoxFuture<'q, Result, Error>> { let category = category.map(|c| c.to_string()); @@ -425,8 +427,8 @@ impl BackendSession for DbSession { tag_filter, None, limit, - None, - false, + order_by, + descending, ); pin!(scan); let mut enc_rows = vec![]; diff --git a/src/ffi/store.rs b/src/ffi/store.rs index eb2d1fce3..b9b6fb684 100644 --- a/src/ffi/store.rs +++ b/src/ffi/store.rs @@ -744,10 +744,20 @@ pub extern "C" fn askar_session_fetch_all( category: FfiStr<'_>, tag_filter: FfiStr<'_>, limit: i64, + order_by: FfiStr<'_>, + descending: i8, for_update: i8, cb: Option, cb_id: CallbackId, ) -> ErrorCode { + let order_by_str = order_by.as_opt_str().map(|s| s.to_lowercase()); + let order_by = match order_by_str.as_deref() { + Some("id") => Some(OrderBy::Id), + Some(_) => return ErrorCode::Unsupported, + None => None, + }; + let descending = descending != 0; // Convert to bool + catch_err! { trace!("Count from store"); let cb = cb.ok_or_else(|| err_msg!("No callback provided"))?; @@ -766,7 +776,7 @@ pub extern "C" fn askar_session_fetch_all( spawn_ok(async move { let result = async { let mut session = FFI_SESSIONS.borrow(handle).await?; - session.fetch_all(category.as_deref(), tag_filter, limit, for_update != 0).await + session.fetch_all(category.as_deref(), tag_filter, limit, order_by, descending, for_update != 0).await }.await; cb.resolve(result); }); diff --git a/src/store.rs b/src/store.rs index 2cea28095..e6476f381 100644 --- a/src/store.rs +++ b/src/store.rs @@ -224,6 +224,8 @@ impl Session { category: Option<&str>, tag_filter: Option, limit: Option, + order_by: Option, + descending: bool, for_update: bool, ) -> Result, Error> { Ok(self @@ -233,6 +235,8 @@ impl Session { category, tag_filter, limit, + order_by, + descending, for_update, ) .await?) @@ -453,6 +457,8 @@ impl Session { Some(KmsCategory::CryptoKey.as_str()), tag_filter, limit, + None, + false, for_update, ) .await?; diff --git a/wrappers/javascript/packages/aries-askar-nodejs/src/NodeJSAriesAskar.ts b/wrappers/javascript/packages/aries-askar-nodejs/src/NodeJSAriesAskar.ts index a7775760c..83a89c9eb 100644 --- a/wrappers/javascript/packages/aries-askar-nodejs/src/NodeJSAriesAskar.ts +++ b/wrappers/javascript/packages/aries-askar-nodejs/src/NodeJSAriesAskar.ts @@ -838,7 +838,7 @@ export class NodeJSAriesAskar implements AriesAskar { } public async sessionFetchAll(options: SessionFetchAllOptions): Promise { - const { forUpdate, sessionHandle, tagFilter, limit, category } = serializeArguments(options) + const { forUpdate, sessionHandle, tagFilter, limit, orderBy, descending, category } = serializeArguments(options) const handle = await this.promisifyWithResponse( (cb, cbId) => @@ -847,6 +847,8 @@ export class NodeJSAriesAskar implements AriesAskar { category, tagFilter, +limit || -1, + orderBy, + descending, forUpdate, cb, cbId, diff --git a/wrappers/javascript/packages/aries-askar-nodejs/src/library/bindings.ts b/wrappers/javascript/packages/aries-askar-nodejs/src/library/bindings.ts index 7cbc962a4..225f14b46 100644 --- a/wrappers/javascript/packages/aries-askar-nodejs/src/library/bindings.ts +++ b/wrappers/javascript/packages/aries-askar-nodejs/src/library/bindings.ts @@ -161,7 +161,17 @@ export const nativeBindings = { ], askar_session_fetch_all: [ FFI_ERROR_CODE, - [FFI_SESSION_HANDLE, FFI_STRING, FFI_STRING, FFI_INT64, FFI_INT8, FFI_CALLBACK_PTR, FFI_CALLBACK_ID], + [ + FFI_SESSION_HANDLE, + FFI_STRING, + FFI_STRING, + FFI_INT64, + FFI_STRING, + FFI_INT8, + FFI_INT8, + FFI_CALLBACK_PTR, + FFI_CALLBACK_ID + ], ], askar_session_fetch_all_keys: [ FFI_ERROR_CODE, diff --git a/wrappers/javascript/packages/aries-askar-react-native/cpp/ariesAskar.cpp b/wrappers/javascript/packages/aries-askar-react-native/cpp/ariesAskar.cpp index 9ae5acd57..b70646a6c 100644 --- a/wrappers/javascript/packages/aries-askar-react-native/cpp/ariesAskar.cpp +++ b/wrappers/javascript/packages/aries-askar-react-native/cpp/ariesAskar.cpp @@ -333,6 +333,8 @@ jsi::Value sessionFetchAll(jsi::Runtime &rt, jsi::Object options) { auto category = jsiToValue(rt, options, "category"); auto tagFilter = jsiToValue(rt, options, "tagFilter", true); int64_t limit = jsiToValue(rt, options, "limit", true); + auto orderBy = jsiToValue(rt, options, "orderBy", true); + auto descending = jsiToValue(rt, options, "descending"); int8_t forUpdate = jsiToValue(rt, options, "forUpdate"); jsi::Function cb = options.getPropertyAsFunction(rt, "cb"); @@ -341,8 +343,8 @@ jsi::Value sessionFetchAll(jsi::Runtime &rt, jsi::Object options) { ErrorCode code = askar_session_fetch_all( sessionHandle, category.c_str(), - tagFilter.length() ? tagFilter.c_str() : nullptr, limit, forUpdate, - callbackWithResponse, CallbackId(state)); + tagFilter.length() ? tagFilter.c_str() : nullptr, limit, orderBy, descending, + forUpdate, callbackWithResponse, CallbackId(state)); return createReturnValue(rt, code, nullptr); } diff --git a/wrappers/javascript/packages/aries-askar-react-native/cpp/include/libaries_askar.h b/wrappers/javascript/packages/aries-askar-react-native/cpp/include/libaries_askar.h index 6a8cb52b3..3171e1297 100644 --- a/wrappers/javascript/packages/aries-askar-react-native/cpp/include/libaries_askar.h +++ b/wrappers/javascript/packages/aries-askar-react-native/cpp/include/libaries_askar.h @@ -467,6 +467,8 @@ ErrorCode askar_session_fetch_all(SessionHandle handle, FfiStr category, FfiStr tag_filter, int64_t limit, + FfiStr order_by, + int8_t descending, int8_t for_update, void (*cb)(CallbackId cb_id, ErrorCode err, diff --git a/wrappers/javascript/packages/aries-askar-shared/src/ariesAskar/AriesAskar.ts b/wrappers/javascript/packages/aries-askar-shared/src/ariesAskar/AriesAskar.ts index 34d43abd9..f24063a14 100644 --- a/wrappers/javascript/packages/aries-askar-shared/src/ariesAskar/AriesAskar.ts +++ b/wrappers/javascript/packages/aries-askar-shared/src/ariesAskar/AriesAskar.ts @@ -235,6 +235,8 @@ export type SessionFetchAllOptions = { category: string tagFilter?: Record limit?: number + orderBy?: string + descending: boolean forUpdate: boolean } export type SessionFetchAllKeysOptions = { diff --git a/wrappers/python/aries_askar/bindings/__init__.py b/wrappers/python/aries_askar/bindings/__init__.py index d7b5f7e9f..12db3a2cd 100644 --- a/wrappers/python/aries_askar/bindings/__init__.py +++ b/wrappers/python/aries_askar/bindings/__init__.py @@ -308,16 +308,20 @@ async def session_fetch_all( category: Optional[str] = None, tag_filter: Optional[Union[str, dict]] = None, limit: Optional[int] = None, + order_by: Optional[str] = None, + descending: bool = False, for_update: bool = False, ) -> EntryListHandle: """Fetch all matching rows in the Store.""" return await invoke_async( "askar_session_fetch_all", - (SessionHandle, FfiStr, FfiJson, c_int64, c_int8), + (SessionHandle, FfiStr, FfiJson, c_int64, c_int8, FfiStr, c_int8), handle, category, tag_filter, limit if limit is not None else -1, + order_by, + descending, for_update, return_type=EntryListHandle, ) diff --git a/wrappers/python/aries_askar/store.py b/wrappers/python/aries_askar/store.py index c68253703..4030a9f01 100644 --- a/wrappers/python/aries_askar/store.py +++ b/wrappers/python/aries_askar/store.py @@ -548,6 +548,8 @@ async def fetch_all( tag_filter: Union[str, dict] = None, limit: int = None, *, + order_by: Optional[str] = None, + descending: bool = False, for_update: bool = False, ) -> EntryList: """Fetch all records matching a category and tag filter.""" @@ -555,7 +557,13 @@ async def fetch_all( raise AskarError(AskarErrorCode.WRAPPER, "Cannot fetch from closed session") return EntryList( await bindings.session_fetch_all( - self._handle, category, tag_filter, limit, for_update + self._handle, + category, + tag_filter, + limit, + order_by, + descending, + for_update, ) ) From 0a9dd4fb78fcfe5b2d61189d8dfee2a123cb1318 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 16 Aug 2024 23:14:24 +0300 Subject: [PATCH 12/15] :white_check_mark: fix tests Signed-off-by: ff137 --- askar-storage/tests/utils/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/askar-storage/tests/utils/mod.rs b/askar-storage/tests/utils/mod.rs index 0d83d599b..75d36dda8 100644 --- a/askar-storage/tests/utils/mod.rs +++ b/askar-storage/tests/utils/mod.rs @@ -89,6 +89,8 @@ pub async fn db_insert_fetch(db: AnyBackend) { Some(&test_row.category), None, None, + None, + false, false, ) .await @@ -732,6 +734,8 @@ pub async fn db_txn_fetch_for_update(db: AnyBackend) { Some(&test_row.category), None, Some(2), + None, + false, true, ) .await From 6272b1f4cce61b0ffd765fc65ad7407110de3f92 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 16 Aug 2024 23:35:10 +0300 Subject: [PATCH 13/15] :bug: fix arg type order Signed-off-by: ff137 --- wrappers/python/aries_askar/bindings/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrappers/python/aries_askar/bindings/__init__.py b/wrappers/python/aries_askar/bindings/__init__.py index 12db3a2cd..040175c51 100644 --- a/wrappers/python/aries_askar/bindings/__init__.py +++ b/wrappers/python/aries_askar/bindings/__init__.py @@ -315,7 +315,7 @@ async def session_fetch_all( """Fetch all matching rows in the Store.""" return await invoke_async( "askar_session_fetch_all", - (SessionHandle, FfiStr, FfiJson, c_int64, c_int8, FfiStr, c_int8), + (SessionHandle, FfiStr, FfiJson, c_int64, FfiStr, c_int8, c_int8), handle, category, tag_filter, From bd11dfeff4821df983ade3bda7de7e7d2b8b1356 Mon Sep 17 00:00:00 2001 From: ff137 Date: Sat, 17 Aug 2024 00:19:56 +0300 Subject: [PATCH 14/15] :bug: add missing arguments Signed-off-by: ff137 --- .../aries-askar-react-native/src/ReactNativeAriesAskar.ts | 4 ++-- .../packages/aries-askar-shared/src/store/Session.ts | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/wrappers/javascript/packages/aries-askar-react-native/src/ReactNativeAriesAskar.ts b/wrappers/javascript/packages/aries-askar-react-native/src/ReactNativeAriesAskar.ts index c519f26bb..3c1a90dff 100644 --- a/wrappers/javascript/packages/aries-askar-react-native/src/ReactNativeAriesAskar.ts +++ b/wrappers/javascript/packages/aries-askar-react-native/src/ReactNativeAriesAskar.ts @@ -517,10 +517,10 @@ export class ReactNativeAriesAskar implements AriesAskar { } public async sessionFetchAll(options: SessionFetchAllOptions) { - const { category, sessionHandle, forUpdate, limit, tagFilter } = serializeArguments(options) + const { category, sessionHandle, forUpdate, limit, orderBy, descending, tagFilter } = serializeArguments(options) const handle = await this.promisifyWithResponse((cb) => this.handleError( - this.ariesAskar.sessionFetchAll({ cb, category, sessionHandle, forUpdate, limit: limit || -1, tagFilter }), + this.ariesAskar.sessionFetchAll({ cb, category, sessionHandle, forUpdate, limit: limit || -1, orderBy, descending, tagFilter }), ), ) diff --git a/wrappers/javascript/packages/aries-askar-shared/src/store/Session.ts b/wrappers/javascript/packages/aries-askar-shared/src/store/Session.ts index 8d598f6c4..893628b3e 100644 --- a/wrappers/javascript/packages/aries-askar-shared/src/store/Session.ts +++ b/wrappers/javascript/packages/aries-askar-shared/src/store/Session.ts @@ -61,12 +61,16 @@ export class Session { category, forUpdate = false, limit, + orderBy, + descending = false, tagFilter, isJson, }: { category: string tagFilter?: Record limit?: number + orderBy?: string, + descending: boolean, forUpdate?: boolean isJson?: boolean }) { @@ -74,6 +78,8 @@ export class Session { const handle = await ariesAskar.sessionFetchAll({ forUpdate, limit, + orderBy, + descending, tagFilter, sessionHandle: this.handle, category, From 7eb776348b88e4e1c3e6f8ec483f804fdba3edbb Mon Sep 17 00:00:00 2001 From: ff137 Date: Sat, 17 Aug 2024 00:47:55 +0300 Subject: [PATCH 15/15] :art: fix lint errors Signed-off-by: ff137 --- .../aries-askar-nodejs/src/library/bindings.ts | 2 +- .../src/ReactNativeAriesAskar.ts | 11 ++++++++++- .../packages/aries-askar-shared/src/store/Session.ts | 4 ++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/wrappers/javascript/packages/aries-askar-nodejs/src/library/bindings.ts b/wrappers/javascript/packages/aries-askar-nodejs/src/library/bindings.ts index 225f14b46..d2730cbb9 100644 --- a/wrappers/javascript/packages/aries-askar-nodejs/src/library/bindings.ts +++ b/wrappers/javascript/packages/aries-askar-nodejs/src/library/bindings.ts @@ -170,7 +170,7 @@ export const nativeBindings = { FFI_INT8, FFI_INT8, FFI_CALLBACK_PTR, - FFI_CALLBACK_ID + FFI_CALLBACK_ID, ], ], askar_session_fetch_all_keys: [ diff --git a/wrappers/javascript/packages/aries-askar-react-native/src/ReactNativeAriesAskar.ts b/wrappers/javascript/packages/aries-askar-react-native/src/ReactNativeAriesAskar.ts index 3c1a90dff..6209a4d17 100644 --- a/wrappers/javascript/packages/aries-askar-react-native/src/ReactNativeAriesAskar.ts +++ b/wrappers/javascript/packages/aries-askar-react-native/src/ReactNativeAriesAskar.ts @@ -520,7 +520,16 @@ export class ReactNativeAriesAskar implements AriesAskar { const { category, sessionHandle, forUpdate, limit, orderBy, descending, tagFilter } = serializeArguments(options) const handle = await this.promisifyWithResponse((cb) => this.handleError( - this.ariesAskar.sessionFetchAll({ cb, category, sessionHandle, forUpdate, limit: limit || -1, orderBy, descending, tagFilter }), + this.ariesAskar.sessionFetchAll({ + cb, + category, + sessionHandle, + forUpdate, + limit: limit || -1, + orderBy, + descending, + tagFilter, + }), ), ) diff --git a/wrappers/javascript/packages/aries-askar-shared/src/store/Session.ts b/wrappers/javascript/packages/aries-askar-shared/src/store/Session.ts index 893628b3e..493b7bef8 100644 --- a/wrappers/javascript/packages/aries-askar-shared/src/store/Session.ts +++ b/wrappers/javascript/packages/aries-askar-shared/src/store/Session.ts @@ -69,8 +69,8 @@ export class Session { category: string tagFilter?: Record limit?: number - orderBy?: string, - descending: boolean, + orderBy?: string + descending: boolean forUpdate?: boolean isJson?: boolean }) {