-
Notifications
You must be signed in to change notification settings - Fork 1
feat(proxy): support OPE index type #390
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -43,8 +43,11 @@ debug = true | |
|
|
||
| [workspace.dependencies] | ||
| sqltk = { version = "0.10.0" } | ||
| cipherstash-client = { version = "0.34.0-alpha.4" } | ||
| cts-common = { version = "0.34.0-alpha.4" } | ||
| # TODO: revert to crates.io once the cipherstash-suite version with `IndexType::Ope` | ||
| # is published. See https://github.com/cipherstash/cipherstash-suite for the | ||
| # OPE-enabled change. | ||
| cipherstash-client = { path = "../cipherstash-suite/packages/cipherstash-client" } | ||
| cts-common = { path = "../cipherstash-suite/packages/cts-common" } | ||
|
Comment on lines
+46
to
+50
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "Path dependencies that leave the repository:"
rg -n 'path\s*=\s*"\.\./cipherstash-suite/'
echo
echo "Bootstrap / CI references to that sibling checkout:"
rg -n -C2 'cipherstash-suite|../cipherstash-suite'Repository: cipherstash/proxy Length of output: 3799 Don't merge sibling path dependencies into Lines 49-50 reference Resolve this before merge by either:
🤖 Prompt for AI Agents |
||
|
|
||
| thiserror = "2.0.9" | ||
| tokio = { version = "1.44.2", features = ["full"] } | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -88,6 +88,18 @@ pub async fn clear_with_client(client: &Client) { | |||||||||||||||||||||||||||||
| client.simple_query(sql).await.unwrap(); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| /// Truncate a single table by name. Useful for tests that own a dedicated | ||||||||||||||||||||||||||||||
| /// fixture table (e.g. the per-test ORE/OPE tables) and don't need to wipe | ||||||||||||||||||||||||||||||
| /// the shared `encrypted`/`plaintext` tables. | ||||||||||||||||||||||||||||||
| pub async fn clear_table_with_client(client: &Client, table: &str) { | ||||||||||||||||||||||||||||||
| let sql = format!("TRUNCATE {}", table); | ||||||||||||||||||||||||||||||
| client.simple_query(&sql).await.unwrap(); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
Comment on lines
+94
to
+97
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Guard table names before building Line 95 interpolates Suggested hardening pub async fn clear_table_with_client(client: &Client, table: &str) {
+ assert!(
+ table
+ .chars()
+ .all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_'),
+ "invalid fixture table name: {table}"
+ );
let sql = format!("TRUNCATE {}", table);
client.simple_query(&sql).await.unwrap();
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| pub async fn clear_table(table: &str) { | ||||||||||||||||||||||||||||||
| clear_table_with_client(&connect_with_tls(PROXY).await, table).await; | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| pub async fn reset_schema() { | ||||||||||||||||||||||||||||||
| let port = std::env::var("CS_DATABASE__PORT") | ||||||||||||||||||||||||||||||
| .map(|s| s.parse().unwrap()) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,165 @@ | ||
| #[cfg(test)] | ||
| mod tests { | ||
| use crate::common::{ | ||
| clear_table, connect_with_tls, interleaved_indices, random_id, trace, PROXY, | ||
| }; | ||
|
|
||
| #[tokio::test] | ||
| async fn map_ope_order_text_asc() { | ||
| trace(); | ||
| let table = "encrypted_ope_order_text_asc"; | ||
| clear_table(table).await; | ||
| let client = connect_with_tls(PROXY).await; | ||
|
|
||
| let values = ["aardvark", "aplomb", "chimera", "chrysalis", "zephyr"]; | ||
|
|
||
| let insert = format!("INSERT INTO {table} (id, encrypted_text) VALUES ($1, $2)"); | ||
| for idx in interleaved_indices(values.len()) { | ||
| client | ||
| .query(&insert, &[&random_id(), &values[idx]]) | ||
| .await | ||
| .unwrap(); | ||
| } | ||
|
|
||
| let select = format!("SELECT encrypted_text FROM {table} ORDER BY encrypted_text"); | ||
| let rows = client.query(&select, &[]).await.unwrap(); | ||
|
|
||
| let actual: Vec<String> = rows.iter().map(|r| r.get(0)).collect(); | ||
| let expected: Vec<String> = values.iter().map(|s| s.to_string()).collect(); | ||
|
|
||
| assert_eq!(actual, expected); | ||
| } | ||
|
|
||
| #[tokio::test] | ||
| async fn map_ope_order_text_desc() { | ||
| trace(); | ||
| let table = "encrypted_ope_order_text_desc"; | ||
| clear_table(table).await; | ||
| let client = connect_with_tls(PROXY).await; | ||
|
|
||
| let values = ["aardvark", "aplomb", "chimera", "chrysalis", "zephyr"]; | ||
|
|
||
| let insert = format!("INSERT INTO {table} (id, encrypted_text) VALUES ($1, $2)"); | ||
| for idx in interleaved_indices(values.len()) { | ||
| client | ||
| .query(&insert, &[&random_id(), &values[idx]]) | ||
| .await | ||
| .unwrap(); | ||
| } | ||
|
|
||
| let select = format!("SELECT encrypted_text FROM {table} ORDER BY encrypted_text DESC"); | ||
| let rows = client.query(&select, &[]).await.unwrap(); | ||
|
|
||
| let actual: Vec<String> = rows.iter().map(|r| r.get(0)).collect(); | ||
| let expected: Vec<String> = values.iter().rev().map(|s| s.to_string()).collect(); | ||
|
|
||
| assert_eq!(actual, expected); | ||
| } | ||
|
|
||
| #[tokio::test] | ||
| async fn map_ope_order_int4_asc() { | ||
| trace(); | ||
| let table = "encrypted_ope_order_int4_asc"; | ||
| clear_table(table).await; | ||
| let client = connect_with_tls(PROXY).await; | ||
|
|
||
| let values: Vec<i32> = vec![-100, -1, 0, 1, 42, 1000, i32::MAX]; | ||
|
|
||
| let insert = format!("INSERT INTO {table} (id, encrypted_int4) VALUES ($1, $2)"); | ||
| for idx in interleaved_indices(values.len()) { | ||
| client | ||
| .query(&insert, &[&random_id(), &values[idx]]) | ||
| .await | ||
| .unwrap(); | ||
| } | ||
|
|
||
| let select = format!("SELECT encrypted_int4 FROM {table} ORDER BY encrypted_int4"); | ||
| let rows = client.query(&select, &[]).await.unwrap(); | ||
|
|
||
| let actual: Vec<i32> = rows.iter().map(|r| r.get(0)).collect(); | ||
| assert_eq!(actual, values); | ||
| } | ||
|
|
||
| #[tokio::test] | ||
| async fn map_ope_order_int4_desc() { | ||
| trace(); | ||
| let table = "encrypted_ope_order_int4_desc"; | ||
| clear_table(table).await; | ||
| let client = connect_with_tls(PROXY).await; | ||
|
|
||
| let values: Vec<i32> = vec![-100, -1, 0, 1, 42, 1000, i32::MAX]; | ||
|
|
||
| let insert = format!("INSERT INTO {table} (id, encrypted_int4) VALUES ($1, $2)"); | ||
| for idx in interleaved_indices(values.len()) { | ||
| client | ||
| .query(&insert, &[&random_id(), &values[idx]]) | ||
| .await | ||
| .unwrap(); | ||
| } | ||
|
|
||
| let select = format!("SELECT encrypted_int4 FROM {table} ORDER BY encrypted_int4 DESC"); | ||
| let rows = client.query(&select, &[]).await.unwrap(); | ||
|
|
||
| let actual: Vec<i32> = rows.iter().map(|r| r.get(0)).collect(); | ||
| let expected: Vec<i32> = values.into_iter().rev().collect(); | ||
| assert_eq!(actual, expected); | ||
| } | ||
|
|
||
| #[tokio::test] | ||
| async fn map_ope_order_nulls_last_by_default() { | ||
| trace(); | ||
| let table = "encrypted_ope_order_nulls_last"; | ||
| clear_table(table).await; | ||
| let client = connect_with_tls(PROXY).await; | ||
|
|
||
| let null_insert = format!("INSERT INTO {table} (id) VALUES ($1)"); | ||
| client.query(&null_insert, &[&random_id()]).await.unwrap(); | ||
|
|
||
| let insert = | ||
| format!("INSERT INTO {table} (id, encrypted_text) VALUES ($1, $2), ($3, $4)"); | ||
| client | ||
| .query(&insert, &[&random_id(), &"a", &random_id(), &"b"]) | ||
| .await | ||
| .unwrap(); | ||
|
|
||
| let select = format!("SELECT encrypted_text FROM {table} ORDER BY encrypted_text"); | ||
| let rows = client.query(&select, &[]).await.unwrap(); | ||
|
|
||
| let actual: Vec<Option<String>> = rows.iter().map(|r| r.get(0)).collect(); | ||
| assert_eq!( | ||
| actual, | ||
| vec![Some("a".into()), Some("b".into()), None], | ||
| "NULLs should sort last by default" | ||
| ); | ||
| } | ||
|
|
||
| #[tokio::test] | ||
| async fn map_ope_order_nulls_first() { | ||
| trace(); | ||
| let table = "encrypted_ope_order_nulls_first"; | ||
| clear_table(table).await; | ||
| let client = connect_with_tls(PROXY).await; | ||
|
|
||
| let insert = | ||
| format!("INSERT INTO {table} (id, encrypted_text) VALUES ($1, $2), ($3, $4)"); | ||
| client | ||
| .query(&insert, &[&random_id(), &"a", &random_id(), &"b"]) | ||
| .await | ||
| .unwrap(); | ||
|
|
||
| let null_insert = format!("INSERT INTO {table} (id) VALUES ($1)"); | ||
| client.query(&null_insert, &[&random_id()]).await.unwrap(); | ||
|
|
||
| let select = format!( | ||
| "SELECT encrypted_text FROM {table} ORDER BY encrypted_text NULLS FIRST" | ||
| ); | ||
| let rows = client.query(&select, &[]).await.unwrap(); | ||
|
|
||
| let actual: Vec<Option<String>> = rows.iter().map(|r| r.get(0)).collect(); | ||
| assert_eq!( | ||
| actual, | ||
| vec![None, Some("a".into()), Some("b".into())], | ||
| "NULLS FIRST should explicitly sort NULLs first" | ||
| ); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make the changelog entry release-facing.
The first sentence is changelog material; the SQL example and
IndexType::Openote read like implementation details. I'd move those details into docs or the PR description so[Unreleased]stays user-facing.As per coding guidelines, "Write changelog entries from the user's perspective, not implementation details."
🤖 Prompt for AI Agents