diff --git a/CLAUDE.md b/CLAUDE.md index d72cf372..320fd20e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -26,9 +26,9 @@ This project uses `mise` for task management. Common commands: ### Testing - Run all tests: `mise run test` -- Run specific test: `mise run test --test ` -- Run tests against specific PostgreSQL version: `mise run test --postgres 14|15|16|17` -- Tests are located in `*_test.sql` files alongside source code +- Run SQLx tests directly: `mise run test:sqlx` +- Run SQLx tests in watch mode: `mise run test:sqlx:watch` +- Tests are located in `tests/sqlx/` using Rust and SQLx framework ### Build System - Dependencies are resolved using `-- REQUIRE:` comments in SQL files @@ -54,7 +54,7 @@ This is the **Encrypt Query Language (EQL)** - a PostgreSQL extension for search - `src/config/` - Configuration management functions - `src/blake3/`, `src/hmac_256/`, `src/bloom_filter/`, `src/ore_*` - Index implementations - `tasks/` - mise task scripts -- `tests/` - Test files (PostgreSQL 14-17 support) +- `tests/sqlx/` - Rust/SQLx test framework (PostgreSQL 14-17 support) - `release/` - Generated SQL installation files ### Key Concepts @@ -65,11 +65,12 @@ This is the **Encrypt Query Language (EQL)** - a PostgreSQL extension for search - **CipherStash Proxy**: Required for encryption/decryption operations ### Testing Infrastructure +- Tests are written in Rust using SQLx, located in `tests/sqlx/` - Tests run against PostgreSQL 14, 15, 16, 17 using Docker containers +- Use `mise run test --postgres 14|15|16|17` to test against a specific version - Container configuration in `tests/docker-compose.yml` -- Test helpers in `tests/test_helpers.sql` +- SQL test fixtures and helpers in `tests/test_helpers.sql` - Database connection: `localhost:7432` (cipherstash/password) -- **Rust/SQLx Tests**: Modern test framework in `tests/sqlx/` (see README there) ## Project Learning & Retrospectives @@ -163,7 +164,6 @@ HTML output is also generated in `docs/api/html/` for local preview only. - SQL files are modular - put operator wrappers in `operators.sql`, implementation in `functions.sql` - All SQL files must have `-- REQUIRE:` dependency declarations -- Test files end with `_test.sql` and live alongside source files - Build system uses `tsort` to resolve dependency order - Supabase build excludes operator classes (not supported) - **Documentation**: All functions/types must have Doxygen comments (see Documentation Standards above) diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index d65cf843..3f0f0faa 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -59,7 +59,7 @@ These are the important files in the repo: └── playground/ <-- Playground enviroment for experimenting with EQL and CipherStash Proxy ``` -Tests live alongside the individual SQL files, with a filename ending with `_test.sql` +Tests are in the `tests/sqlx/` directory using Rust and the SQLx framework. We break SQL into small modules named after what they do. @@ -144,85 +144,19 @@ There are tests for checking EQL against PostgreSQL versions 14–17, that verif - Validating schemas for EQL configuration, encrypted data, and encrypted indexes - Using PostgreSQL operators on encrypted data and indexes (`=`, `<>`, `@>`) +Tests are written in Rust using the SQLx framework and live in `tests/sqlx/`. + The easiest way to run the tests [is in GitHub Actions](./.github/workflows/test-eql.yml): - Automatically whenever there are changes in the `sql/`, `tests/`, or `tasks/` directories - By manually running [the workflow](https://github.com/cipherstash/encrypt-query-language/actions/workflows/test-eql.yml) -This is how the `test-eql.yml` workflow functions: - -```mermaid ---- -title: Testing EQL ---- -stateDiagram-v2 - direction LR - classDef code font-family:monospace; - - - state "🧍 Human makes changes to EQL sources" as changes - state sources_fork <> - state sources_join <> - state "src/*.sql" as source_sql - state "tasks/**/*" as source_tasks - state "tests/**/*" as source_tests - state sources_changed <> - - state "πŸ› οΈ Trigger GitHub Actions workflow test-eql.yml" as build_triggered - state "Matrix: Test EQL SQL components" as matrix - state "Test with Postgres 14" as pg14 - state "Test with Postgres 15" as pg15 - state "Test with Postgres 16" as pg16 - state "Test with Postgres 17" as pg17 - state "Check build results" as check - state if_state <> - - changes --> sources_fork - sources_fork --> source_sql:::code - sources_fork --> source_tests:::code - sources_fork --> source_tasks:::code - source_sql --> sources_join - source_tests --> sources_join - source_tasks --> sources_join - sources_join --> source_changed_check - source_changed_check --> sources_changed - sources_changed --> build_triggered : Some changes - sources_changed --> [*]: No changes - - state "Check source changes" as source_changed_check - - [*] --> changes - - build_triggered --> matrix - - state fork_state <> - matrix --> fork_state - fork_state --> pg14 - fork_state --> pg15 - fork_state --> pg16 - fork_state --> pg17 - - state join_state <> - pg14 --> join_state - pg15 --> join_state - pg16 --> join_state - pg17 --> join_state - - state "βœ… Pass build" as build_pass - state "❌ Fail build" as build_fail - join_state --> check - check --> if_state - if_state --> build_pass: All success - if_state --> build_fail : Any failures - build_pass --> [*] - build_fail --> [*] -``` - You can also [run the tests locally](#running-tests-locally) when doing local development. ### Running tests locally -> [!IMPORTANT] > **Before you run the tests locally** you need to [set up a local dev environment](#set-up-a-local-development-environment). +> [!IMPORTANT] +> **Before you run the tests locally** you need to [set up a local dev environment](#set-up-a-local-development-environment). To run tests locally with PostgreSQL 17: diff --git a/docs/assertion-counts.md b/docs/assertion-counts.md deleted file mode 100644 index 0762bf18..00000000 --- a/docs/assertion-counts.md +++ /dev/null @@ -1,56 +0,0 @@ -# Assertion Count Report - -Generated: 2025-10-27 - -# SQL Test Assertions - -| File | ASSERT | PERFORM assert_* | SELECT checks | Total | -|------|--------|------------------|---------------|-------| -| `src/blake3/compare_test.sql` | 9 | 0 | 0 | 9 | -| `src/bloom_filter/functions_test.sql` | 0 | 2 | 0 | 2 | -| `src/config/config_test.sql` | 26 | 4 | 11 | 41 | -| `src/encrypted/aggregates_test.sql` | 4 | 0 | 2 | 6 | -| `src/encrypted/constraints_test.sql` | 0 | 6 | 0 | 6 | -| `src/encryptindex/functions_test.sql` | 21 | 1 | 19 | 41 | -| `src/hmac_256/compare_test.sql` | 9 | 0 | 0 | 9 | -| `src/hmac_256/functions_test.sql` | 1 | 2 | 0 | 3 | -| `src/jsonb/functions_test.sql` | 4 | 24 | 0 | 28 | -| `src/operators/->>_test.sql` | 0 | 6 | 0 | 6 | -| `src/operators/->_test.sql` | 2 | 9 | 0 | 11 | -| `src/operators/<=_ore_cllw_u64_8_test.sql` | 0 | 3 | 3 | 6 | -| `src/operators/<=_ore_cllw_var_8_test.sql` | 0 | 3 | 3 | 6 | -| `src/operators/<=_test.sql` | 0 | 8 | 4 | 12 | -| `src/operators/<>_ore_cllw_u64_8_test.sql` | 0 | 3 | 0 | 3 | -| `src/operators/<>_ore_cllw_var_8_test.sql` | 0 | 3 | 0 | 3 | -| `src/operators/<>_ore_test.sql` | 0 | 8 | 0 | 8 | -| `src/operators/<>_test.sql` | 0 | 12 | 2 | 14 | -| `src/operators/<@_test.sql` | 0 | 2 | 0 | 2 | -| `src/operators/<_test.sql` | 0 | 12 | 1 | 13 | -| `src/operators/=_ore_cllw_u64_8_test.sql` | 0 | 3 | 3 | 6 | -| `src/operators/=_ore_cllw_var_8_test.sql` | 0 | 3 | 3 | 6 | -| `src/operators/=_ore_test.sql` | 0 | 8 | 4 | 12 | -| `src/operators/=_test.sql` | 0 | 16 | 12 | 28 | -| `src/operators/>=_test.sql` | 0 | 14 | 10 | 24 | -| `src/operators/>_test.sql` | 0 | 12 | 1 | 13 | -| `src/operators/@>_test.sql` | 4 | 2 | 0 | 6 | -| `src/operators/compare_test.sql` | 63 | 0 | 0 | 63 | -| `src/operators/operator_class_test.sql` | 16 | 1 | 24 | 41 | -| `src/operators/order_by_test.sql` | 0 | 16 | 4 | 20 | -| `src/operators/~~_test.sql` | 0 | 10 | 0 | 10 | -| `src/ore_block_u64_8_256/compare_test.sql` | 9 | 0 | 0 | 9 | -| `src/ore_block_u64_8_256/functions_test.sql` | 1 | 5 | 2 | 8 | -| `src/ore_cllw_u64_8/compare_test.sql` | 9 | 0 | 0 | 9 | -| `src/ore_cllw_var_8/compare_test.sql` | 9 | 0 | 0 | 9 | -| `src/ore_cllw_var_8/functions_test.sql` | 0 | 0 | 0 | 0 | -| `src/ste_vec/functions_test.sql` | 18 | 0 | 0 | 18 | -| `src/version_test.sql` | 0 | 1 | 1 | 2 | - -**Total SQL assertions:** 513 across 38 files - -# Rust Test Assertions - -| File | assert* | expect* | is_err/is_ok | Total | -|------|---------|---------|--------------|-------| -| *(no Rust tests yet)* | 0 | 0 | 0 | 0 | - -**Total Rust assertions:** 0 across 0 files diff --git a/docs/test-inventory.md b/docs/test-inventory.md deleted file mode 100644 index a555dc87..00000000 --- a/docs/test-inventory.md +++ /dev/null @@ -1,59 +0,0 @@ -# Test Inventory - SQL to SQLx Migration - -Generated: 2025-10-27 - -| # | SQL Test File | Test Cases | Lines | Status | Rust Test File | Notes | -|---|---------------|------------|-------|--------|----------------|-------| -| 1 | `src/blake3/compare_test.sql` | 1 | 26 | ❌ TODO | `*TBD*` | | -| 2 | `src/bloom_filter/functions_test.sql` | 1 | 14 | ❌ TODO | `*TBD*` | | -| 3 | `src/config/config_test.sql` | 9 | 331 | ❌ TODO | `*TBD*` | | -| 4 | `src/encrypted/aggregates_test.sql` | 1 | 50 | ❌ TODO | `*TBD*` | | -| 5 | `src/encrypted/constraints_test.sql` | 3 | 79 | ❌ TODO | `*TBD*` | | -| 6 | `src/encryptindex/functions_test.sql` | 7 | 290 | ❌ TODO | `*TBD*` | | -| 7 | `src/hmac_256/compare_test.sql` | 1 | 26 | ❌ TODO | `*TBD*` | | -| 8 | `src/hmac_256/functions_test.sql` | 2 | 26 | ❌ TODO | `*TBD*` | | -| 9 | `src/jsonb/functions_test.sql` | 12 | 338 | ❌ TODO | `*TBD*` | | -| 10 | `src/operators/->>_test.sql` | 4 | 68 | ❌ TODO | `*TBD*` | | -| 11 | `src/operators/->_test.sql` | 6 | 118 | ❌ TODO | `*TBD*` | | -| 12 | `src/operators/<=_ore_cllw_u64_8_test.sql` | 1 | 56 | ❌ TODO | `*TBD*` | | -| 13 | `src/operators/<=_ore_cllw_var_8_test.sql` | 1 | 52 | ❌ TODO | `*TBD*` | | -| 14 | `src/operators/<=_test.sql` | 2 | 83 | ❌ TODO | `*TBD*` | | -| 15 | `src/operators/<>_ore_cllw_u64_8_test.sql` | 1 | 56 | ❌ TODO | `*TBD*` | | -| 16 | `src/operators/<>_ore_cllw_var_8_test.sql` | 1 | 55 | ❌ TODO | `*TBD*` | | -| 17 | `src/operators/<>_ore_test.sql` | 2 | 86 | ❌ TODO | `*TBD*` | | -| 18 | `src/operators/<>_test.sql` | 5 | 164 | ❌ TODO | `*TBD*` | | -| 19 | `src/operators/<@_test.sql` | 1 | 43 | ❌ TODO | `*TBD*` | | -| 20 | `src/operators/<_test.sql` | 4 | 158 | ❌ TODO | `*TBD*` | | -| 21 | `src/operators/=_ore_cllw_u64_8_test.sql` | 1 | 55 | ❌ TODO | `*TBD*` | | -| 22 | `src/operators/=_ore_cllw_var_8_test.sql` | 1 | 52 | ❌ TODO | `*TBD*` | | -| 23 | `src/operators/=_ore_test.sql` | 2 | 86 | ❌ TODO | `*TBD*` | | -| 24 | `src/operators/=_test.sql` | 6 | 195 | ❌ TODO | `*TBD*` | | -| 25 | `src/operators/>=_test.sql` | 4 | 174 | ❌ TODO | `*TBD*` | | -| 26 | `src/operators/>_test.sql` | 4 | 158 | ❌ TODO | `*TBD*` | | -| 27 | `src/operators/@>_test.sql` | 3 | 93 | ❌ TODO | `*TBD*` | | -| 28 | `src/operators/compare_test.sql` | 7 | 207 | ❌ TODO | `*TBD*` | | -| 29 | `src/operators/operator_class_test.sql` | 3 | 239 | ❌ TODO | `*TBD*` | | -| 30 | `src/operators/order_by_test.sql` | 3 | 148 | ❌ TODO | `*TBD*` | | -| 31 | `src/operators/~~_test.sql` | 3 | 107 | ❌ TODO | `*TBD*` | | -| 32 | `src/ore_block_u64_8_256/compare_test.sql` | 1 | 27 | ❌ TODO | `*TBD*` | | -| 33 | `src/ore_block_u64_8_256/functions_test.sql` | 3 | 58 | ❌ TODO | `*TBD*` | | -| 34 | `src/ore_cllw_u64_8/compare_test.sql` | 1 | 29 | ❌ TODO | `*TBD*` | | -| 35 | `src/ore_cllw_var_8/compare_test.sql` | 1 | 29 | ❌ TODO | `*TBD*` | | -| 36 | `src/ore_cllw_var_8/functions_test.sql` | 0 | 0 | ❌ TODO | `*TBD*` | | -| 37 | `src/ste_vec/functions_test.sql` | 6 | 132 | ❌ TODO | `*TBD*` | | -| 38 | `src/version_test.sql` | 1 | 9 | ❌ TODO | `*TBD*` | | - -## Summary - -- **Total SQL Test Files:** 38 -- **Total Test Cases:** 115 -- **Total Lines:** 3917 - -## Usage - -Update this inventory as you port tests: -1. Mark status βœ… when Rust test passes -2. Add Rust test file path -3. Add notes for any deviations - -Regenerate: `./tools/generate-test-inventory.sh` diff --git a/docs/test-migration-guide.md b/docs/test-migration-guide.md deleted file mode 100644 index f3e4453d..00000000 --- a/docs/test-migration-guide.md +++ /dev/null @@ -1,151 +0,0 @@ -# SQL to SQLx Test Migration Guide - -This guide explains how to port SQL tests to Rust/SQLx while ensuring complete coverage. - -## Overview - -We have three tools to track coverage during migration: - -1. **Test Inventory** - Which tests have been ported -2. **Assertion Counts** - How many test assertions ported -3. **Function Call Tracking** - Which SQL functions are tested - -## Workflow - -### Before Starting Migration - -1. **Generate baseline coverage:** - - ```bash - # Enable function tracking - psql postgres://cipherstash:password@localhost:7432/postgres -c "ALTER SYSTEM SET track_functions = 'all';" - psql postgres://cipherstash:password@localhost:7432/postgres -c "SELECT pg_reload_conf();" - - # Reset stats and run SQL tests - psql postgres://cipherstash:password@localhost:7432/postgres -c "SELECT pg_stat_reset();" - mise run test - - # Capture baseline - TEST_TYPE=sql ./tools/track-function-calls.sh sql-function-calls.json - ./tools/count-assertions.sh - ./tools/generate-test-inventory.sh - ``` - -2. **Review baseline metrics:** - - 38 SQL test files - - ~3,917 lines of tests - - ~513 assertions - - Function call coverage saved in `sql-function-calls.json` - -### During Migration - -For each SQL test file you port: - -1. **Pick a test from inventory** (`docs/test-inventory.md`) - -2. **Read the SQL test**, understand what it tests - -3. **Write equivalent Rust test** in `rust-tests/tests/` - -4. **Run the Rust test:** - ```bash - cd rust-tests - cargo test -- --nocapture - ``` - -5. **Verify function coverage:** - ```bash - psql postgres://cipherstash:password@localhost:7432/postgres -c "SELECT pg_stat_reset();" - cd rust-tests && cargo test - TEST_TYPE=rust ./tools/track-function-calls.sh rust-function-calls.json - ./tools/compare-function-calls.sh sql-function-calls.json rust-function-calls.json - ``` - -6. **Update test inventory:** - ```bash - ./tools/generate-test-inventory.sh - ``` - - Manually edit `docs/test-inventory.md` to mark test as βœ… Ported. - -7. **Check assertion coverage:** - ```bash - ./tools/compare-assertions.sh - ``` - -8. **Commit** when test passes and coverage verified: - ```bash - git add rust-tests/tests/.rs docs/test-inventory.md - git commit -m "test: port tests from SQL to SQLx" - ``` - -### After Migration Complete - -1. **Verify 100% coverage:** - ```bash - ./tools/check-test-coverage.sh - ``` - -2. **Ensure all checks pass:** - - βœ… All 38 tests marked as ported - - βœ… Rust assertion count β‰₯ SQL assertion count - - βœ… Rust function calls cover same functions as SQL - -3. **Delete SQL tests** (only after verification): - ```bash - # DO NOT DO THIS UNTIL MIGRATION COMPLETE - git rm src/**/*_test.sql - ``` - -## Coverage Metrics Explained - -### Test Inventory - -Shows 1:1 mapping of SQL test files to Rust test files: - -- **Status ❌ TODO** - Not yet ported -- **Status βœ… Ported** - Rust equivalent exists and passes - -### Assertion Counts - -Tracks test thoroughness: - -- **SQL:** ASSERT statements, PERFORM assert_*, SELECT checks -- **Rust:** assert!(), assert_eq!(), .expect() - -Goal: Rust count β‰₯ SQL count - -### Function Call Tracking - -Ensures same code paths exercised: - -- Uses PostgreSQL `pg_stat_user_functions` -- Compares which `eql_v2.*` functions called in SQL vs Rust tests -- Identifies gaps: functions only in SQL tests = missing coverage - -## Troubleshooting - -**Q: Rust test passes but function coverage shows missing functions?** - -A: Your test might not be exercising the same code paths. Review the SQL test to see which functions it calls. - -**Q: Assertion count much lower in Rust?** - -A: You may be using fewer, but more comprehensive assertions. That's OK if function coverage matches. Document in test inventory notes. - -**Q: pg_stat_user_functions not tracking?** - -A: Verify `track_functions = 'all'` in postgresql.conf and PostgreSQL reloaded. - -## Tips - -- **Port tests in related groups** (e.g., all operator tests together) -- **Keep both test suites running** until migration complete -- **Update inventory frequently** to track progress -- **Compare function coverage often** to catch gaps early - -## See Also - -- `tools/check-test-coverage.sh` - Run all coverage checks -- `docs/test-inventory.md` - Current migration status -- `docs/assertion-counts.md` - Detailed assertion breakdown diff --git a/src/blake3/compare_test.sql b/src/blake3/compare_test.sql deleted file mode 100644 index b6a49de7..00000000 --- a/src/blake3/compare_test.sql +++ /dev/null @@ -1,26 +0,0 @@ -\set ON_ERROR_STOP on - -DO $$ - DECLARE - a eql_v2_encrypted; - b eql_v2_encrypted; - c eql_v2_encrypted; - BEGIN - a := create_encrypted_json(1, 'b3'); - b := create_encrypted_json(2, 'b3'); - c := create_encrypted_json(3, 'b3'); - - ASSERT eql_v2.compare_blake3(a, a) = 0; - ASSERT eql_v2.compare_blake3(a, b) = -1; - ASSERT eql_v2.compare_blake3(a, c) = -1; - - ASSERT eql_v2.compare_blake3(b, b) = 0; - ASSERT eql_v2.compare_blake3(b, a) = 1; - ASSERT eql_v2.compare_blake3(b, c) = -1; - - ASSERT eql_v2.compare_blake3(c, c) = 0; - ASSERT eql_v2.compare_blake3(c, b) = 1; - ASSERT eql_v2.compare_blake3(c, a) = 1; - END; -$$ LANGUAGE plpgsql; - diff --git a/src/bloom_filter/functions_test.sql b/src/bloom_filter/functions_test.sql deleted file mode 100644 index 87010453..00000000 --- a/src/bloom_filter/functions_test.sql +++ /dev/null @@ -1,14 +0,0 @@ -\set ON_ERROR_STOP on - -DO $$ - BEGIN - PERFORM assert_result( - 'Extract match index term from encrypted', - 'SELECT eql_v2.bloom_filter(''{"bf": []}''::jsonb)'); - - PERFORM assert_exception( - 'Missing match index term in encrypted raises exception', - 'SELECT eql_v2.bloom_filter(''{}''::jsonb)'); - - END; -$$ LANGUAGE plpgsql; diff --git a/src/config/config_test.sql b/src/config/config_test.sql deleted file mode 100644 index e67b4840..00000000 --- a/src/config/config_test.sql +++ /dev/null @@ -1,349 +0,0 @@ -\set ON_ERROR_STOP on - - --- Create tables for adding configuration -DROP TABLE IF EXISTS users; -CREATE TABLE users -( - id bigint GENERATED ALWAYS AS IDENTITY, - name eql_v2_encrypted, - PRIMARY KEY(id) -); - -DROP TABLE IF EXISTS blah; -CREATE TABLE blah -( - id bigint GENERATED ALWAYS AS IDENTITY, - vtha eql_v2_encrypted, - PRIMARY KEY(id) -); - - --- --- Helper function for assertions --- -DROP FUNCTION IF EXISTS _search_config_exists(text, text, text, text); -CREATE FUNCTION _search_config_exists(table_name text, column_name text, index_name text, state text DEFAULT 'pending') - RETURNS boolean -LANGUAGE sql STRICT PARALLEL SAFE -BEGIN ATOMIC - SELECT EXISTS (SELECT id FROM eql_v2_configuration c - WHERE c.state = state AND - c.data #> array['tables', table_name, column_name, 'indexes'] ? index_name); -END; - - --- ----------------------------------------------- --- Add and remove multiple indexes --- --- ----------------------------------------------- -TRUNCATE TABLE eql_v2_configuration; - -DO $$ - BEGIN - - -- Add indexes - PERFORM eql_v2.add_search_config('users', 'name', 'match', migrating => true); - ASSERT (SELECT _search_config_exists('users', 'name', 'match')); - - -- Add index with cast - PERFORM eql_v2.add_search_config('users', 'name', 'unique', 'int', migrating => true); - ASSERT (SELECT _search_config_exists('users', 'name', 'unique')); - - ASSERT (SELECT EXISTS (SELECT id FROM eql_v2_configuration c - WHERE c.state = 'pending' AND - c.data #> array['tables', 'users', 'name'] ? 'cast_as')); - - -- Match index removed - PERFORM eql_v2.remove_search_config('users', 'name', 'match', migrating => true); - ASSERT NOT (SELECT _search_config_exists('users', 'name', 'match')); - - -- All indexes removed, but column config preserved - PERFORM eql_v2.remove_search_config('users', 'name', 'unique', migrating => true); - ASSERT (SELECT EXISTS (SELECT FROM eql_v2_configuration c WHERE c.state = 'pending')); - ASSERT (SELECT data #> array['tables', 'users', 'name', 'indexes'] = '{}' FROM eql_v2_configuration c WHERE c.state = 'pending'); - - END; -$$ LANGUAGE plpgsql; - - - --- ----------------------------------------------- --- Add and remove multiple indexes from multiple tables --- --- ----------------------------------------------- -TRUNCATE TABLE eql_v2_configuration; - - -DO $$ - BEGIN - - -- Add indexes - PERFORM eql_v2.add_search_config('users', 'name', 'match', migrating => true); - ASSERT (SELECT _search_config_exists('users', 'name', 'match')); - - ASSERT (SELECT EXISTS (SELECT id FROM eql_v2_configuration c - WHERE c.state = 'pending' AND - c.data #> array['tables', 'users', 'name', 'indexes'] ? 'match')); - - -- Add index with cast - PERFORM eql_v2.add_search_config('blah', 'vtha', 'unique', 'int', migrating => true); - ASSERT (SELECT _search_config_exists('blah', 'vtha', 'unique')); - - ASSERT (SELECT EXISTS (SELECT id FROM eql_v2_configuration c - WHERE c.state = 'pending' AND - c.data #> array['tables', 'users', 'name', 'indexes'] ? 'match')); - - - ASSERT (SELECT EXISTS (SELECT id FROM eql_v2_configuration c - WHERE c.state = 'pending' AND - c.data #> array['tables', 'blah', 'vtha', 'indexes'] ? 'unique')); - - - -- Match index removed - PERFORM eql_v2.remove_search_config('users', 'name', 'match', migrating => true); - ASSERT NOT (SELECT _search_config_exists('users', 'name', 'match')); - - -- Match index removed - PERFORM eql_v2.remove_search_config('blah', 'vtha', 'unique', migrating => true); - ASSERT NOT (SELECT _search_config_exists('users', 'vtha', 'unique')); - - -- All indexes removed, but column config preserved - ASSERT (SELECT EXISTS (SELECT FROM eql_v2_configuration c WHERE c.state = 'pending')); - ASSERT (SELECT data #> array['tables', 'blah', 'vtha', 'indexes'] = '{}' FROM eql_v2_configuration c WHERE c.state = 'pending'); - - END; -$$ LANGUAGE plpgsql; - --- SELECT FROM eql_v2_configuration c WHERE c.state = 'pending'; - - --- ----------------------------------------------- --- Add & modify index --- Pending configuration created and contains the path `user/name.match.option` --- ----------------------------------------------- --- TRUNCATE TABLE eql_v2_configuration; - - -DO $$ - BEGIN - PERFORM eql_v2.add_search_config('users', 'name', 'match', migrating => true); - ASSERT (SELECT _search_config_exists('users', 'name', 'match')); - - -- Pending configuration contains the path `user/name.match.option` - PERFORM eql_v2.modify_search_config('users', 'name', 'match', 'int', '{"option": "value"}'::jsonb, migrating => true); - ASSERT (SELECT _search_config_exists('users', 'name', 'match')); - - ASSERT (SELECT EXISTS (SELECT id FROM eql_v2_configuration c - WHERE c.state = 'pending' AND - c.data #> array['tables', 'users', 'name', 'indexes', 'match'] ? 'option')); - - ASSERT (SELECT EXISTS (SELECT id FROM eql_v2_configuration c - WHERE c.state = 'pending' AND - c.data #> array['tables', 'users', 'name'] ? 'cast_as')); - - -- All indexes removed, but column config preserved - PERFORM eql_v2.remove_search_config('users', 'name', 'match', migrating => true); - ASSERT (SELECT EXISTS (SELECT FROM eql_v2_configuration c WHERE c.state = 'pending')); - ASSERT (SELECT data #> array['tables', 'users', 'name', 'indexes'] = '{}' FROM eql_v2_configuration c WHERE c.state = 'pending'); - END; -$$ LANGUAGE plpgsql; - - --- -- ----------------------------------------------- --- -- With existing active config --- -- Adding an index creates a new pending configuration --- -- ----------------------------------------------- -TRUNCATE TABLE eql_v2_configuration; - --- create an active configuration -INSERT INTO eql_v2_configuration (state, data) VALUES ( - 'active', - '{ - "v": 1, - "tables": { - "users": { - "blah": { - "cast_as": "text", - "indexes": { - "match": {} - } - }, - "vtha": { - "cast_as": "text", - "indexes": {} - } - } - } - }'::jsonb -); - --- An encrypting config should exist -DO $$ - BEGIN - ASSERT (SELECT _search_config_exists('users', 'blah', 'match', 'active')); - - PERFORM eql_v2.add_search_config('users', 'name', 'match', migrating => true); - - -- index added to name - ASSERT (SELECT _search_config_exists('users', 'name', 'match' )); - - -- pending is a copy of the active config - -- and the active index still exists - ASSERT (SELECT _search_config_exists('users', 'blah', 'match')); - - END; -$$ LANGUAGE plpgsql; - - --- -- ----------------------------------------------- --- -- Add and remove column --- -- --- -- ----------------------------------------------- -TRUNCATE TABLE eql_v2_configuration; -DO $$ - BEGIN - - PERFORM assert_exception( - 'Cannot add index to column that does not exist', - 'SELECT eql_v2.add_column(''user'', ''name'')'); - - PERFORM assert_no_result( - 'No configuration was created', - 'SELECT * FROM eql_v2_configuration'); - END; -$$ LANGUAGE plpgsql; - - - --- -- ----------------------------------------------- --- -- Add and remove column --- -- --- -- ----------------------------------------------- -TRUNCATE TABLE eql_v2_configuration; -DO $$ - BEGIN - -- reset the table - PERFORM create_table_with_encrypted(); - - PERFORM eql_v2.add_column('encrypted', 'e', migrating => true); - - PERFORM assert_count( - 'Pending configuration was created', - 'SELECT * FROM eql_v2_configuration c WHERE c.state = ''pending''', - 1); - - - PERFORM eql_v2.remove_column('encrypted', 'e', migrating => true); - - PERFORM assert_count( - 'Pending configuration exists but is empty', - 'SELECT * FROM eql_v2_configuration c WHERE c.state = ''pending''', - 1); - - -- Verify the config is empty - ASSERT (SELECT data #> array['tables'] = '{}' FROM eql_v2_configuration c WHERE c.state = 'pending'); - - END; -$$ LANGUAGE plpgsql; - - --- ----------------------------------------------- ---- --- eql_v2_configuration tyoe --- Validate configuration schema --- Try and insert many invalid configurations --- None should exist --- --- ----------------------------------------------- -TRUNCATE TABLE eql_v2_configuration; - -\set ON_ERROR_STOP off -\set ON_ERROR_ROLLBACK on - -DO $$ - BEGIN - RAISE NOTICE '------------------------------------------------------'; - RAISE NOTICE 'eql_v2_configuration constraint tests: 4 errors follow'; - END; -$$ LANGUAGE plpgsql; --- --- No schema version -INSERT INTO eql_v2_configuration (data) VALUES ( - '{ - "tables": { - "users": { - "blah": { - "cast_as": "text", - "indexes": {} - } - } - } - }'::jsonb -); - --- --- Empty tables -INSERT INTO eql_v2_configuration (data) VALUES ( - '{ - "v": 1, - "tables": {} - }'::jsonb -); - - --- --- invalid cast -INSERT INTO eql_v2_configuration (data) VALUES ( - '{ - "v": 1, - "tables": { - "users": { - "blah": { - "cast_as": "regex" - } - } - } - }'::jsonb -); - --- --- invalid index -INSERT INTO eql_v2_configuration (data) VALUES ( - '{ - "v": 1, - "tables": { - "users": { - "blah": { - "cast_as": "text", - "indexes": { - "blah": {} - } - } - } - } - }'::jsonb -); - - --- Pending configuration should not be created; -DO $$ - BEGIN - ASSERT (SELECT NOT EXISTS (SELECT FROM eql_v2_configuration c WHERE c.state = 'pending')); - END; -$$ LANGUAGE plpgsql; - -DO $$ - BEGIN - RAISE NOTICE 'eql_v2_configuration constraint tests: OK'; - RAISE NOTICE '------------------------------------------------------'; - END; -$$ LANGUAGE plpgsql; - - -\set ON_ERROR_STOP on -\set ON_ERROR_ROLLBACK off - - - - diff --git a/src/encrypted/constraints_test.sql b/src/encrypted/constraints_test.sql deleted file mode 100644 index df85ef1f..00000000 --- a/src/encrypted/constraints_test.sql +++ /dev/null @@ -1,139 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); - -DO $$ - BEGIN - -- insert without constraint works - INSERT INTO encrypted(e) VALUES ('{}'::jsonb::eql_v2_encrypted); - - -- delete the data - PERFORM create_table_with_encrypted(); - - -- add constraint - PERFORM eql_v2.add_encrypted_constraint('encrypted', 'e'); - - PERFORM assert_exception( - 'Constraint catches invalid eql_v2_encrypted', - 'INSERT INTO encrypted (e) VALUES (''{}''::jsonb::eql_v2_encrypted)'); - - END; -$$ LANGUAGE plpgsql; - - -DO $$ - BEGIN - -- reset the table - PERFORM create_table_with_encrypted(); - - -- add constraint - PERFORM eql_v2.add_encrypted_constraint('encrypted', 'e'); - - PERFORM assert_exception( - 'Constraint catches invalid eql_v2_encrypted', - 'INSERT INTO encrypted (e) VALUES (''{}''::jsonb::eql_v2_encrypted)'); - - PERFORM eql_v2.remove_encrypted_constraint('encrypted', 'e'); - - PERFORM assert_result( - 'Insert invalid data without constraint', - 'INSERT INTO encrypted (e) VALUES (''{}''::jsonb::eql_v2_encrypted) RETURNING id'); - - END; -$$ LANGUAGE plpgsql; - - --- ----------------------------------------------- --- Adding search config adds the constraint --- --- ----------------------------------------------- -TRUNCATE TABLE eql_v2_configuration; - -DO $$ - BEGIN - -- reset the table - PERFORM create_table_with_encrypted(); - - PERFORM eql_v2.add_search_config('encrypted', 'e', 'match'); - - PERFORM assert_exception( - 'Constraint catches invalid eql_v2_encrypted', - 'INSERT INTO encrypted (e) VALUES (''{}''::jsonb::eql_v2_encrypted)'); - - -- add constraint without error - PERFORM eql_v2.add_encrypted_constraint('encrypted', 'e'); - - PERFORM eql_v2.remove_encrypted_constraint('encrypted', 'e'); - - PERFORM assert_result( - 'Insert invalid data without constraint', - 'INSERT INTO encrypted (e) VALUES (''{}''::jsonb::eql_v2_encrypted) RETURNING id'); - - END; -$$ LANGUAGE plpgsql; - - --- ----------------------------------------------- --- Adding column adds the constraint --- --- ----------------------------------------------- -TRUNCATE TABLE eql_v2_configuration; - -DO $$ - BEGIN - -- reset the table - PERFORM create_table_with_encrypted(); - - PERFORM eql_v2.add_column('encrypted', 'e'); - - PERFORM assert_exception( - 'Constraint catches invalid eql_v2_encrypted', - 'INSERT INTO encrypted (e) VALUES (''{}''::jsonb::eql_v2_encrypted)'); - - -- add constraint without error - PERFORM eql_v2.add_encrypted_constraint('encrypted', 'e'); - - PERFORM eql_v2.remove_encrypted_constraint('encrypted', 'e'); - - PERFORM assert_result( - 'Insert invalid data without constraint', - 'INSERT INTO encrypted (e) VALUES (''{}''::jsonb::eql_v2_encrypted) RETURNING id'); - - END; -$$ LANGUAGE plpgsql; - - --- EQL version is enforced -DO $$ - DECLARE - e eql_v2_encrypted; - BEGIN - - -- reset data - PERFORM create_table_with_encrypted(); - - -- remove the version field - e := create_encrypted_json(1)::jsonb-'v'; - - PERFORM assert_exception( - 'Insert with missing version fails', - format('INSERT INTO encrypted (e) VALUES (%s::jsonb::eql_v2_encrypted) RETURNING id', e)); - - -- set version to 1 - e := create_encrypted_json(1)::jsonb || '{"v": 1}'; - - PERFORM assert_exception( - 'Insert with invalid version fails', - format('INSERT INTO encrypted (e) VALUES (%s::jsonb::eql_v2_encrypted) RETURNING id', e)); - - -- set version to 1 - e := create_encrypted_json(1); - - PERFORM assert_result( - 'Insert with valid version is ok', - format('INSERT INTO encrypted (e) VALUES (%L) RETURNING id', e)); - - - END; -$$ LANGUAGE plpgsql; - diff --git a/src/encryptindex/functions_test.sql b/src/encryptindex/functions_test.sql deleted file mode 100644 index 1044e10a..00000000 --- a/src/encryptindex/functions_test.sql +++ /dev/null @@ -1,290 +0,0 @@ -\set ON_ERROR_STOP on - --- ----------------------------------------------- --- --- Alter table from config --- --- ----------------------------------------------- -TRUNCATE TABLE eql_v2_configuration; - --- Create a table with a plaintext column -DROP TABLE IF EXISTS users; -CREATE TABLE users -( - id bigint GENERATED ALWAYS AS IDENTITY, - name TEXT, - PRIMARY KEY(id) -); - -INSERT INTO eql_v2_configuration (data) VALUES ( - '{ - "v": 1, - "tables": { - "users": { - "name": { - "cast_as": "text", - "indexes": { - "ore": {} - } - } - } - } - }'::jsonb -); - -DO $$ - BEGIN - - -- the column is pending encryptindexing - ASSERT (SELECT EXISTS (SELECT * FROM eql_v2.select_pending_columns() AS c WHERE c.column_name = 'name')); - - -- the target column does not exist - ASSERT (SELECT EXISTS (SELECT * FROM eql_v2.select_target_columns() AS c WHERE c.target_column IS NULL)); - - -- Add the vtha_encrypted column to the table - PERFORM eql_v2.create_encrypted_columns(); - - ASSERT (SELECT EXISTS (SELECT * FROM information_schema.columns s WHERE s.column_name = 'name_encrypted')); - - -- -- rename columns - PERFORM eql_v2.rename_encrypted_columns(); - - ASSERT (SELECT EXISTS (SELECT * FROM information_schema.columns s WHERE s.column_name = 'name_plaintext')); - ASSERT (SELECT EXISTS (SELECT * FROM information_schema.columns s WHERE s.column_name = 'name' and s.udt_name = 'eql_v2_encrypted')); - ASSERT (SELECT NOT EXISTS (SELECT * FROM information_schema.columns s WHERE s.column_name = 'name_encrypted')); - END; -$$ LANGUAGE plpgsql; - - --- ----------------------------------------------- --- Create multiple columns --- --- ----------------------------------------------- -TRUNCATE TABLE eql_v2_configuration; - --- Create a table with multiple plaintext columns -DROP TABLE IF EXISTS users; -CREATE TABLE users -( - id bigint GENERATED ALWAYS AS IDENTITY, - name TEXT, - email INT, - PRIMARY KEY(id) -); - -INSERT INTO eql_v2_configuration (data) VALUES ( - '{ - "v": 1, - "tables": { - "users": { - "name": { - "cast_as": "text", - "indexes": { - "ore": {}, - "unique": {} - } - }, - "email": { - "cast_as": "text", - "indexes": { - "match": {} - } - } - } - } - }'::jsonb -); - -DO $$ - BEGIN - - -- the column is pending encryptindexing - ASSERT (SELECT EXISTS (SELECT * FROM eql_v2.select_pending_columns() AS c WHERE c.column_name = 'name')); - - -- the target column does not exisgt - ASSERT (SELECT EXISTS (SELECT * FROM eql_v2.select_target_columns() AS c WHERE c.target_column IS NULL)); - - -- create column - PERFORM eql_v2.create_encrypted_columns(); - - ASSERT (SELECT EXISTS (SELECT * FROM information_schema.columns s WHERE s.column_name = 'name_encrypted')); - ASSERT (SELECT EXISTS (SELECT * FROM information_schema.columns s WHERE s.column_name = 'email_encrypted')); - END; -$$ LANGUAGE plpgsql; - - --- ----------------------------------------------- --- With existing active config --- and an updated schema --- Start encryptindexing --- The active config is unchanged --- The pending config should now be encrypting --- ----------------------------------------------- -TRUNCATE TABLE eql_v2_configuration; - --- create an active configuration -INSERT INTO eql_v2_configuration (state, data) VALUES ( - 'active', - '{ - "v": 1, - "tables": { - "users": { - "name": { - "cast_as": "text", - "indexes": { - "unique": {} - } - } - } - } - }'::jsonb -); - --- Create a table with plaintext and encrypted columns -DROP TABLE IF EXISTS users; -CREATE TABLE users -( - id bigint GENERATED ALWAYS AS IDENTITY, - name TEXT, - name_encrypted eql_v2_encrypted, - PRIMARY KEY(id) -); - - --- An encrypting config should exist -DO $$ - BEGIN - PERFORM eql_v2.add_search_config('users', 'name_encrypted', 'match', migrating => true); - PERFORM eql_v2.migrate_config(); - ASSERT (SELECT EXISTS (SELECT FROM eql_v2_configuration c WHERE c.state = 'active')); - ASSERT (SELECT EXISTS (SELECT FROM eql_v2_configuration c WHERE c.state = 'encrypting')); - ASSERT (SELECT NOT EXISTS (SELECT FROM eql_v2_configuration c WHERE c.state = 'pending')); - END; -$$ LANGUAGE plpgsql; - - --- Encrypting config without `migrating = true` is immediately active -DO $$ - BEGIN - TRUNCATE TABLE eql_v2_configuration; - PERFORM eql_v2.add_search_config('users', 'name_encrypted', 'match'); - ASSERT (SELECT EXISTS (SELECT FROM eql_v2_configuration c WHERE c.state = 'active')); - END; -$$ LANGUAGE plpgsql; - - --- migrate_config() should raise an exception when no pending configuration exists -DO $$ - BEGIN - TRUNCATE TABLE eql_v2_configuration; - PERFORM eql_v2.add_search_config('users', 'name_encrypted', 'match'); - - PERFORM assert_exception( - 'eql_v2.migrate_config() should raise an exception when no pending configuration exists', - 'SELECT eql_v2.migrate_config()' - ); - END; -$$ LANGUAGE plpgsql; - --- ----------------------------------------------- --- With existing active config and an updated schema using a raw JSONB column --- Start encryptindexing --- The active config is unchanged --- The pending config should now be encrypting --- ----------------------------------------------- -TRUNCATE TABLE eql_v2_configuration; - --- create an active configuration -INSERT INTO eql_v2_configuration (state, data) VALUES ( - 'active', - '{ - "v": 1, - "tables": { - "users": { - "name": { - "cast_as": "text", - "indexes": { - "unique": {} - } - } - } - } - }'::jsonb -); - --- Create a table with plaintext and jsonb column -DROP TABLE IF EXISTS users; -CREATE TABLE users -( - id bigint GENERATED ALWAYS AS IDENTITY, - name TEXT, - name_encrypted eql_v2_encrypted, - PRIMARY KEY(id) -); - - --- An encrypting config should exist -DO $$ - BEGIN - PERFORM eql_v2.add_search_config('users', 'name_encrypted', 'match', migrating => true); - PERFORM eql_v2.migrate_config(); - - ASSERT (SELECT EXISTS (SELECT FROM eql_v2_configuration c WHERE c.state = 'active')); - ASSERT (SELECT EXISTS (SELECT FROM eql_v2_configuration c WHERE c.state = 'encrypting')); - ASSERT (SELECT NOT EXISTS (SELECT FROM eql_v2_configuration c WHERE c.state = 'pending')); - END; -$$ LANGUAGE plpgsql; - - --- ----------------------------------------------- --- With existing active config --- Activate encrypting config --- The active config is now inactive --- The encrypting config should now be active --- ----------------------------------------------- -TRUNCATE TABLE eql_v2_configuration; - --- create an active configuration -INSERT INTO eql_v2_configuration (state, data) VALUES ( - 'active', - '{ - "v": 1, - "tables": { - "users": { - "name": { - "cast_as": "text", - "indexes": { - "unique": {} - } - } - } - } - }'::jsonb -); - - --- Create a table with multiple plaintext columns -DROP TABLE IF EXISTS users; -CREATE TABLE users -( - id bigint GENERATED ALWAYS AS IDENTITY, - name TEXT, - name_encrypted eql_v2_encrypted, - PRIMARY KEY(id) -); - --- An encrypting config should exist -DO $$ - BEGIN - PERFORM eql_v2.add_search_config('users', 'name_encrypted', 'match', migrating => true); - - PERFORM eql_v2.migrate_config(); -- need to encrypt first - PERFORM eql_v2.activate_config(); - - ASSERT (SELECT EXISTS (SELECT FROM eql_v2_configuration c WHERE c.state = 'active')); - ASSERT (SELECT EXISTS (SELECT FROM eql_v2_configuration c WHERE c.state = 'inactive')); - ASSERT (SELECT NOT EXISTS (SELECT FROM eql_v2_configuration c WHERE c.state = 'encrypting')); - ASSERT (SELECT NOT EXISTS (SELECT FROM eql_v2_configuration c WHERE c.state = 'pending')); - - END; -$$ LANGUAGE plpgsql; diff --git a/src/hmac_256/compare_test.sql b/src/hmac_256/compare_test.sql deleted file mode 100644 index 3529bfef..00000000 --- a/src/hmac_256/compare_test.sql +++ /dev/null @@ -1,26 +0,0 @@ -\set ON_ERROR_STOP on - -DO $$ - DECLARE - a eql_v2_encrypted; - b eql_v2_encrypted; - c eql_v2_encrypted; - BEGIN - a := create_encrypted_json(1, 'hm'); - b := create_encrypted_json(2, 'hm'); - c := create_encrypted_json(3, 'hm'); - - ASSERT eql_v2.compare_hmac_256(a, a) = 0; - ASSERT eql_v2.compare_hmac_256(a, b) = -1; - ASSERT eql_v2.compare_hmac_256(a, c) = -1; - - ASSERT eql_v2.compare_hmac_256(b, b) = 0; - ASSERT eql_v2.compare_hmac_256(b, a) = 1; - ASSERT eql_v2.compare_hmac_256(b, c) = -1; - - ASSERT eql_v2.compare_hmac_256(c, c) = 0; - ASSERT eql_v2.compare_hmac_256(c, b) = 1; - ASSERT eql_v2.compare_hmac_256(c, a) = 1; - END; -$$ LANGUAGE plpgsql; - diff --git a/src/hmac_256/functions_test.sql b/src/hmac_256/functions_test.sql deleted file mode 100644 index b4ccfae8..00000000 --- a/src/hmac_256/functions_test.sql +++ /dev/null @@ -1,26 +0,0 @@ -\set ON_ERROR_STOP on - -DO $$ - BEGIN - PERFORM assert_result( - 'Extract hmac_256 index term from encrypted', - 'SELECT eql_v2.hmac_256(''{"hm": "u"}''::jsonb)'); - - PERFORM assert_exception( - 'Missing hmac_256 index term in encrypted raises exception', - 'SELECT eql_v2.hmac_256(''{}''::jsonb)'); - - END; -$$ LANGUAGE plpgsql; - - -DO $$ - DECLARE - e eql_v2_encrypted; - BEGIN - e := create_encrypted_json(1, 'hm'); - - ASSERT eql_v2.has_hmac_256(e); - END; -$$ LANGUAGE plpgsql; - diff --git a/src/jsonb/functions_test.sql b/src/jsonb/functions_test.sql deleted file mode 100644 index 861d4435..00000000 --- a/src/jsonb/functions_test.sql +++ /dev/null @@ -1,338 +0,0 @@ -\set ON_ERROR_STOP on - - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - --- ======================================================================== --- --- Selector &.a[*] --- -> 33743aed3ae636f6bf05cff11ac4b519 --- -DO $$ - BEGIN - - PERFORM seed_encrypted_json(); - PERFORM seed_encrypted(get_array_ste_vec()::eql_v2_encrypted); - - PERFORM assert_result( - 'jsonb_array_elements returns array elements from jsonb_path_query result', - 'SELECT eql_v2.jsonb_array_elements(eql_v2.jsonb_path_query(e, ''f510853730e1c3dbd31b86963f029dd5'')) as e FROM encrypted;'); - - PERFORM assert_count( - 'jsonb_array_elements returns the correct number of array elements from jsonb_path_query result', - 'SELECT eql_v2.jsonb_array_elements(eql_v2.jsonb_path_query(e, ''f510853730e1c3dbd31b86963f029dd5'')) as e FROM encrypted;', - 5); - - PERFORM assert_exception( - 'jsonb_array_elements exception if input is not an array', - 'SELECT eql_v2.jsonb_array_elements(eql_v2.jsonb_path_query(e, ''33743aed3ae636f6bf05cff11ac4b519'')) as e FROM encrypted LIMIT 1;'); - - END; -$$ LANGUAGE plpgsql; - --- --- Selector &.a[*] as eql_v2_encrypted --- -> 33743aed3ae636f6bf05cff11ac4b519 --- -DO $$ -DECLARE - selector eql_v2_encrypted; - - BEGIN - - PERFORM seed_encrypted_json(); - PERFORM seed_encrypted(get_array_ste_vec()::eql_v2_encrypted); - - selector := '{"s": "f510853730e1c3dbd31b86963f029dd5"}'::jsonb::eql_v2_encrypted; - - PERFORM assert_result( - 'jsonb_array_elements returns array elements from jsonb_path_query result using eql_v2_encrypted selector', - format('SELECT eql_v2.jsonb_array_elements_text(eql_v2.jsonb_path_query(e, %L::eql_v2_encrypted)) as e FROM encrypted;', selector)); - - PERFORM assert_count( - 'jsonb_array_elements returns the correct number of array elements from jsonb_path_query result', - format('SELECT eql_v2.jsonb_array_elements_text(eql_v2.jsonb_path_query(e, %L::eql_v2_encrypted)) as e FROM encrypted;', selector), - 5); - - selector := '{"s": "33743aed3ae636f6bf05cff11ac4b519"}'::jsonb::eql_v2_encrypted; - - PERFORM assert_exception( - 'jsonb_array_elements exception if input is not an array', - format('SELECT eql_v2.jsonb_array_elements_text(eql_v2.jsonb_path_query(e, %L::eql_v2_encrypted)) as e FROM encrypted;', selector)); - - END; -$$ LANGUAGE plpgsql; - - --- -- ======================================================================== --- -- --- -- Selector &.a[*] --- -- -> 33743aed3ae636f6bf05cff11ac4b519 --- -- -DO $$ -DECLARE - sv eql_v2_encrypted; - results eql_v2_encrypted[]; - BEGIN - - PERFORM seed_encrypted_json(); - PERFORM seed_encrypted(get_array_ste_vec()::eql_v2_encrypted); - - PERFORM assert_result( - 'jsonb_array_elements_text returns array elements from jsonb_path_query result', - 'SELECT eql_v2.jsonb_array_elements_text(eql_v2.jsonb_path_query(e, ''f510853730e1c3dbd31b86963f029dd5'')) as e FROM encrypted;'); - - PERFORM assert_count( - 'jsonb_array_elements_text returns the correct number of array elements from jsonb_path_query result', - 'SELECT eql_v2.jsonb_array_elements_text(eql_v2.jsonb_path_query(e, ''f510853730e1c3dbd31b86963f029dd5'')) as e FROM encrypted;', - 5); - - PERFORM assert_exception( - 'jsonb_array_elements_text exception if input is not an array', - 'SELECT eql_v2.jsonb_array_elements_text(eql_v2.jsonb_path_query(e, ''33743aed3ae636f6bf05cff11ac4b519'')) as e FROM encrypted LIMIT 1;'); - - END; -$$ LANGUAGE plpgsql; - - --- ======================================================================== --- --- Selector &.a[*] --- -> 33743aed3ae636f6bf05cff11ac4b519 --- -DO $$ -DECLARE - sv eql_v2_encrypted; - results eql_v2_encrypted[]; - BEGIN - - PERFORM seed_encrypted_json(); - PERFORM seed_encrypted(get_array_ste_vec()::eql_v2_encrypted); - - PERFORM assert_result( - 'jsonb_array_length returns array length of jsonb_path_query result', - 'SELECT eql_v2.jsonb_array_length(eql_v2.jsonb_path_query(e, ''f510853730e1c3dbd31b86963f029dd5'')) as e FROM encrypted LIMIT 1;', - '5'); - - PERFORM assert_exception( - 'jsonb_array_length exception if input is not an array', - 'SELECT eql_v2.jsonb_array_length(eql_v2.jsonb_path_query(e, ''33743aed3ae636f6bf05cff11ac4b519'')) as e FROM encrypted LIMIT 1;'); - - END; -$$ LANGUAGE plpgsql; - - - --- -- ======================================================================== --- --- -- "{\"hello\": \"four\", \"n\": 20, \"a\": [1, 2, 3, 4, 5] }", --- --- Selector &.a[*] --- -> 33743aed3ae636f6bf05cff11ac4b519 --- -DO $$ -DECLARE - sv eql_v2_encrypted; - results eql_v2_encrypted[]; - BEGIN - - PERFORM seed_encrypted_json(); - - -- Insert a row with array selector - sv := get_array_ste_vec()::eql_v2_encrypted; - PERFORM seed_encrypted(sv); - - PERFORM assert_count( - 'jsonb_path_query with array selector returns count', - 'SELECT eql_v2.jsonb_path_query_first(e, ''33743aed3ae636f6bf05cff11ac4b519'') as e FROM encrypted;', - 4 - ); - - PERFORM assert_count( - 'jsonb_path_query with array selector returns count', - 'SELECT eql_v2.jsonb_path_query_first(e, ''33743aed3ae636f6bf05cff11ac4b519'') as e FROM encrypted WHERE eql_v2.jsonb_path_query_first(e, ''33743aed3ae636f6bf05cff11ac4b519'') IS NOT NULL;', - 1 - ); - - END; -$$ LANGUAGE plpgsql; - - --- ------------------------------------------------------------------------ - - - --- ------------------------------------------------------------------------ --- --- jsonb_path_query --- - --- Paths --- $ -> bca213de9ccce676fa849ff9c4807963 --- $.hello -> a7cea93975ed8c01f861ccb6bd082784 --- $.n -> 2517068c0d1f9d4d41d2c666211f785e --- --- -DO $$ - BEGIN - PERFORM seed_encrypted_json(); - - PERFORM assert_result( - 'jsonb_path_query', - 'SELECT eql_v2.jsonb_path_query(e, ''2517068c0d1f9d4d41d2c666211f785e'') FROM encrypted LIMIT 1;'); - - PERFORM assert_count( - 'jsonb_path_query returns count', - 'SELECT eql_v2.jsonb_path_query(e, ''2517068c0d1f9d4d41d2c666211f785e'') FROM encrypted;', - 3); - - END; -$$ LANGUAGE plpgsql; - - -DO $$ - DECLARE - result jsonb; - BEGIN - PERFORM seed_encrypted_json(); - - SELECT eql_v2.jsonb_path_query(e, '2517068c0d1f9d4d41d2c666211f785e')::jsonb FROM encrypted LIMIT 1 INTO result; - - ASSERT result ? 'i'; - ASSERT result ? 'v'; - - END; -$$ LANGUAGE plpgsql; - - - -DO $$ - DECLARE - result jsonb; - BEGIN - PERFORM seed_encrypted_json(); - PERFORM seed_encrypted(get_array_ste_vec()::eql_v2_encrypted); - - SELECT eql_v2.jsonb_array_elements(eql_v2.jsonb_path_query(e, 'f510853730e1c3dbd31b86963f029dd5')::jsonb) FROM encrypted INTO result; - - ASSERT result ? 'i'; - ASSERT result ? 'v'; - END; -$$ LANGUAGE plpgsql; - - -DO $$ - BEGIN - - PERFORM seed_encrypted_json(); - - PERFORM assert_result( - 'jsonb_path_exists returns true', - 'SELECT eql_v2.jsonb_path_exists(e, ''2517068c0d1f9d4d41d2c666211f785e'') FROM encrypted LIMIT 1;', - 'true'); - - PERFORM assert_result( - 'jsonb_path_exists returns false', - 'SELECT eql_v2.jsonb_path_exists(e, ''blahvtha'') FROM encrypted LIMIT 1;', - 'false'); - - PERFORM assert_count( - 'jsonb_path_exists returns count', - 'SELECT eql_v2.jsonb_path_exists(e, ''2517068c0d1f9d4d41d2c666211f785e'') FROM encrypted;', - 3); - END; -$$ LANGUAGE plpgsql; - - - --- -- --- -- Selector &.a[*] --- -- -> 33743aed3ae636f6bf05cff11ac4b519 --- -- -DO $$ -DECLARE - sv eql_v2_encrypted; - results eql_v2_encrypted[]; - BEGIN - - -- Insert a row with array selector - sv := get_array_ste_vec()::eql_v2_encrypted; - PERFORM seed_encrypted(sv); - - PERFORM assert_result( - 'jsonb_path_query with array selector', - 'SELECT eql_v2.jsonb_path_query(e, ''f510853730e1c3dbd31b86963f029dd5'') FROM encrypted;'); - - -- An array should be wrapped and returned as a single element - PERFORM assert_count( - 'jsonb_path_query with array selector returns one result', - 'SELECT eql_v2.jsonb_path_query(e, ''f510853730e1c3dbd31b86963f029dd5'') FROM encrypted;', - 1); - END; -$$ LANGUAGE plpgsql; - - - --- -- --- -- Selector &.a[*] --- -- -> 33743aed3ae636f6bf05cff11ac4b519 --- -- -DO $$ - DECLARE - sv eql_v2_encrypted; - results eql_v2_encrypted[]; - BEGIN - - PERFORM seed_encrypted_json(); - - -- Insert a row with array selector - sv := get_array_ste_vec()::eql_v2_encrypted; - PERFORM seed_encrypted(sv); - - PERFORM assert_result( - 'jsonb_path_exists with array selector', - 'SELECT eql_v2.jsonb_path_exists(e, ''f510853730e1c3dbd31b86963f029dd5'') FROM encrypted;'); - - PERFORM assert_count( - 'jsonb_path_exists with array selector returns correct number of records', - 'SELECT eql_v2.jsonb_path_exists(e, ''f510853730e1c3dbd31b86963f029dd5'') FROM encrypted;', - 4); - END; -$$ LANGUAGE plpgsql; - - - --- -- --- -- Selector &.a[*] --- -- -> 33743aed3ae636f6bf05cff11ac4b519 --- -- -DO $$ -DECLARE - sv eql_v2_encrypted; - results eql_v2_encrypted[]; - BEGIN - - PERFORM seed_encrypted_json(); - - -- Insert a row with array selector - sv := get_array_ste_vec()::eql_v2_encrypted; - PERFORM seed_encrypted(sv); - - PERFORM assert_count( - 'jsonb_path_query with array selector returns count', - 'SELECT eql_v2.jsonb_path_query_first(e, ''33743aed3ae636f6bf05cff11ac4b519'') as e FROM encrypted;', - 4 - ); - - PERFORM assert_count( - 'jsonb_path_query with array selector returns count', - 'SELECT eql_v2.jsonb_path_query_first(e, ''33743aed3ae636f6bf05cff11ac4b519'') as e FROM encrypted WHERE eql_v2.jsonb_path_query_first(e, ''33743aed3ae636f6bf05cff11ac4b519'') IS NOT NULL;', - 1 - ); - - END; -$$ LANGUAGE plpgsql; - - diff --git a/src/operators/->>_test.sql b/src/operators/->>_test.sql deleted file mode 100644 index d7b4c889..00000000 --- a/src/operators/->>_test.sql +++ /dev/null @@ -1,68 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - --- --- The ->> operator returns ciphertext matching the selector -DO $$ - BEGIN - PERFORM assert_result( - 'Selector ->> returns at least one eql_v2_encrypted', - 'SELECT e->>''bca213de9ccce676fa849ff9c4807963''::text FROM encrypted;'); - - PERFORM assert_count( - 'Selector ->> returns all eql_v2_encrypted', - 'SELECT e->>''bca213de9ccce676fa849ff9c4807963''::text FROM encrypted;', - 3); - END; -$$ LANGUAGE plpgsql; - - --- --- The ->> operator returns NULL if no matching selector -DO $$ - BEGIN - PERFORM assert_no_result( - 'Unknown selector -> returns null', - 'SELECT e->>''blahvtha''::text FROM encrypted;'); - - END; -$$ LANGUAGE plpgsql; - - --- --- The ->> operator returns ciphertext matching the selector --- NOTE: DEPRECATED BEHAVIOUR --- DO $$ --- BEGIN - --- PERFORM assert_result( --- 'Selector ->> returns all eql_v2_encrypted', --- 'SELECT e->>''bca213de9ccce676fa849ff9c4807963''::text FROM encrypted LIMIT 1;', --- 'mBbLGB9xHAGzLvUj-`@Wmf=IhD87n7r3ir3n!Sk6AKir_YawR=0c>pk(OydB;ntIEXK~c>V&4>)rNkf> operator accepts an eql_v2_encrypted as the selector --- -DO $$ - DECLARE - term text; - BEGIN - term := '{"s": "bca213de9ccce676fa849ff9c4807963"}'; - - PERFORM assert_result( - 'Selector ->> returns at least one eql_v2_encrypted', - format('SELECT e->>%L::jsonb::eql_v2_encrypted FROM encrypted;', term)); - - PERFORM assert_count( - 'Selector ->> returns all eql_v2_encrypted', - format('SELECT e->>%L::jsonb::eql_v2_encrypted FROM encrypted;', term), - 3); - END; -$$ LANGUAGE plpgsql; - diff --git a/src/operators/->_test.sql b/src/operators/->_test.sql deleted file mode 100644 index f741e00c..00000000 --- a/src/operators/->_test.sql +++ /dev/null @@ -1,118 +0,0 @@ -\set ON_ERROR_STOP on - - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - - --- --- The -> operator returns an encrypted matching the selector -DO $$ - BEGIN - PERFORM assert_result( - 'Selector -> returns at least one eql_v2_encrypted', - 'SELECT e->''bca213de9ccce676fa849ff9c4807963''::text FROM encrypted;'); - - PERFORM assert_count( - 'Selector -> returns all eql_v2_encrypted', - 'SELECT e->''bca213de9ccce676fa849ff9c4807963''::text FROM encrypted;', - 3); - END; -$$ LANGUAGE plpgsql; - - --- --- The -> operator returns NULL if no matching selector -DO $$ - BEGIN - PERFORM assert_no_result( - 'Unknown selector -> returns null', - 'SELECT e->''blahvtha''::text FROM encrypted;'); - - END; -$$ LANGUAGE plpgsql; - - - --- --- The -> operator accepts an eql_v2_encrypted as the selector --- -DO $$ - DECLARE - term text; - BEGIN - term := '{"s": "bca213de9ccce676fa849ff9c4807963"}'; - - PERFORM assert_result( - 'Selector -> returns at least one eql_v2_encrypted', - format('SELECT e->%L::jsonb::eql_v2_encrypted FROM encrypted;', term)); - - PERFORM assert_count( - 'Selector -> returns all eql_v2_encrypted', - format('SELECT e->%L::jsonb::eql_v2_encrypted FROM encrypted;', term), - 3); - END; -$$ LANGUAGE plpgsql; - - --- --- encrypted returned from -> operator expression called via eql_v2.ciphertext --- -DO $$ - DECLARE - result eql_v2_encrypted; - BEGIN - PERFORM assert_result( - 'Fetch ciphertext via selector', - 'SELECT eql_v2.ciphertext(e->''2517068c0d1f9d4d41d2c666211f785e''::text) FROM encrypted;'); - - PERFORM assert_count( - 'Fetch ciphertext via selector returns all eql_v2_encrypted', - 'SELECT eql_v2.ciphertext(e->''2517068c0d1f9d4d41d2c666211f785e''::text) FROM encrypted;', - 3); - END; -$$ LANGUAGE plpgsql; - - - --- --- encrypted returned from -> operator expression called via eql_v2.ciphertext --- -DO $$ - DECLARE - result eql_v2_encrypted; - BEGIN - - PERFORM truncate_table_with_encrypted(); - PERFORM seed_encrypted(get_array_ste_vec()::eql_v2_encrypted); - - PERFORM assert_result( - '-> operator with integer returns array', - 'SELECT eql_v2.jsonb_path_query_first(e, ''f510853730e1c3dbd31b86963f029dd5'')->0 as e FROM encrypted'); - - PERFORM assert_count( - '-> operator with integer returns array', - 'SELECT eql_v2.jsonb_path_query_first(e, ''f510853730e1c3dbd31b86963f029dd5'')->0 as e FROM encrypted', - 1); - - END; -$$ LANGUAGE plpgsql; - - --- --- Field accessor returns column metadata --- -DO $$ - DECLARE - result jsonb; - BEGIN - PERFORM seed_encrypted_json(); - - SELECT e->'2517068c0d1f9d4d41d2c666211f785e'::text FROM encrypted LIMIT 1 INTO result; - - ASSERT result ? 'i'; - ASSERT result ? 'v'; - - END; -$$ LANGUAGE plpgsql; diff --git a/src/operators/<=_ore_cllw_u64_8_test.sql b/src/operators/<=_ore_cllw_u64_8_test.sql deleted file mode 100644 index 0db355dc..00000000 --- a/src/operators/<=_ore_cllw_u64_8_test.sql +++ /dev/null @@ -1,57 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - --- ======================================================================== - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- ore_cllw_u64_8 less than or equal to <= --- --- Test data is in form '{"hello": "{one | two | three}", "n": {10 | 20 | 30} }' --- --- Paths --- $ -> bca213de9ccce676fa849ff9c4807963 --- $.hello -> a7cea93975ed8c01f861ccb6bd082784 --- $.n -> 2517068c0d1f9d4d41d2c666211f785e --- --- -DO $$ -DECLARE - sv eql_v2_encrypted; - term eql_v2_encrypted; - BEGIN - - -- This extracts the data associated with the field from the test eql_v2_encrypted - -- json n: 10 - sv := get_numeric_ste_vec_20()::eql_v2_encrypted; - -- extract the term at $.n returned as eql_v2_encrypted - term := sv->'2517068c0d1f9d4d41d2c666211f785e'::text; - - -- -- -- -- $.n - PERFORM assert_result( - format('eql_v2_encrypted <= eql_v2_encrypted with ore_cllw_u64_8 index term'), - format('SELECT e FROM encrypted WHERE (e->''2517068c0d1f9d4d41d2c666211f785e''::text) <= %L::eql_v2_encrypted', term)); - - PERFORM assert_count( - format('eql_v2_encrypted <= eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''2517068c0d1f9d4d41d2c666211f785e''::text <= %L::eql_v2_encrypted', term), - 2); - - -- Check the $.hello path - -- Returned encrypted does not have ore_cllw_u64_8 - -- Falls back to jsonb literal comparison - PERFORM assert_no_result( - format('eql_v2_encrypted <= eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''a7cea93975ed8c01f861ccb6bd082784''::text <= %L::eql_v2_encrypted', term)); - - END; -$$ LANGUAGE plpgsql; - - - -SELECT drop_table_with_encrypted(); \ No newline at end of file diff --git a/src/operators/<=_ore_cllw_var_8_test.sql b/src/operators/<=_ore_cllw_var_8_test.sql deleted file mode 100644 index b8255ae8..00000000 --- a/src/operators/<=_ore_cllw_var_8_test.sql +++ /dev/null @@ -1,53 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- ore_cllw_u64_8 less than or equal to <= --- --- Test data is in form '{"hello": "{one | two | three}", "n": {10 | 20 | 30} }' --- --- Paths --- $ -> bca213de9ccce676fa849ff9c4807963 --- $.hello -> a7cea93975ed8c01f861ccb6bd082784 --- $.n -> 2517068c0d1f9d4d41d2c666211f785e --- --- -DO $$ -DECLARE - sv eql_v2_encrypted; - term eql_v2_encrypted; - BEGIN - - -- This extracts the data associated with the field from the test eql_v2_encrypted - -- json n: 10 - sv := get_numeric_ste_vec_20()::eql_v2_encrypted; - -- extract the term at $.n returned as eql_v2_encrypted - term := sv->'a7cea93975ed8c01f861ccb6bd082784'::text; - - -- -- -- -- $.n - PERFORM assert_result( - format('eql_v2_encrypted <= eql_v2_encrypted with ore_cllw_u64_8 index term'), - format('SELECT e FROM encrypted WHERE (e->''a7cea93975ed8c01f861ccb6bd082784''::text) <= %L::eql_v2_encrypted', term)); - - PERFORM assert_count( - format('eql_v2_encrypted <= eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''a7cea93975ed8c01f861ccb6bd082784''::text <= %L::eql_v2_encrypted', term), - 3); - - -- -- Check the $.n path - -- -- Returned encrypted does not have ore_cllw_u64_8 - PERFORM assert_result( - format('eql_v2_encrypted <= eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''2517068c0d1f9d4d41d2c666211f785e''::text <= %L::eql_v2_encrypted', term)); - - END; -$$ LANGUAGE plpgsql; - - - -SELECT drop_table_with_encrypted(); \ No newline at end of file diff --git a/src/operators/<=_test.sql b/src/operators/<=_test.sql deleted file mode 100644 index d0ac226e..00000000 --- a/src/operators/<=_test.sql +++ /dev/null @@ -1,84 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - --- --- ORE - eql_v2_encrypted <= eql_v2_encrypted --- -DO $$ -DECLARE - e eql_v2_encrypted; - ore_term jsonb; - BEGIN - - -- Record with a Numeric ORE term of 42 - e := create_encrypted_ore_json(42); - PERFORM seed_encrypted(e); - - PERFORM assert_result( - 'eql_v2_encrypted <= eql_v2_encrypted', - format('SELECT e FROM encrypted WHERE e <= %L::eql_v2_encrypted', e)); - - PERFORM assert_count( - format('eql_v2_encrypted <= eql_v2_encrypted'), - format('SELECT e FROM encrypted WHERE e <= %L;', e), - 4); - - e := create_encrypted_ore_json(20); - - PERFORM assert_result( - 'eql_v2_encrypted <= eql_v2_encrypted', - format('SELECT e FROM encrypted WHERE e <= %L::eql_v2_encrypted', e)); - - PERFORM assert_count( - format('eql_v2_encrypted <= eql_v2_encrypted'), - format('SELECT e FROM encrypted WHERE e <= %L;', e), - 2); - END; -$$ LANGUAGE plpgsql; - - --- --- ORE - eql_v2.lte(a eql_v2_encrypted, b eql_v2_encrypted) --- -DO $$ -DECLARE - e eql_v2_encrypted; - ore_term jsonb; - BEGIN - -- Reset data - PERFORM seed_encrypted_json(); - - -- Record with a Numeric ORE term of 42 - e := create_encrypted_ore_json(42); - PERFORM seed_encrypted(e); - - PERFORM assert_result( - 'eql_v2.lte(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.lte(e, %L)', e)); - - -- include - PERFORM assert_count( - 'eql_v2.lte(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.lte(e, %L)', e), - 4); - - -- Record with a Numeric ORE term of 30 - e := create_encrypted_ore_json(30); - - PERFORM assert_result( - 'eql_v2.get(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.lte(e, %L)', e)); - - PERFORM assert_count( - 'eql_v2.get(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.lte(e, %L)', e), - 3); - END; -$$ LANGUAGE plpgsql; - - - -SELECT drop_table_with_encrypted(); \ No newline at end of file diff --git a/src/operators/<>_ore_cllw_u64_8_test.sql b/src/operators/<>_ore_cllw_u64_8_test.sql deleted file mode 100644 index 17881b0e..00000000 --- a/src/operators/<>_ore_cllw_u64_8_test.sql +++ /dev/null @@ -1,57 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - --- ======================================================================== - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- ore_cllw_u64_8 equality --- --- Test data is '{"hello": "world", "n": 42}' - --- Paths --- $ -> bca213de9ccce676fa849ff9c4807963 --- $.hello -> a7cea93975ed8c01f861ccb6bd082784 --- $.n -> 2517068c0d1f9d4d41d2c666211f785e --- --- - -DO $$ -DECLARE - sv eql_v2_encrypted; - term eql_v2_encrypted; - BEGIN - - -- This extracts the data associated with the field from the test eql_v2_encrypted - -- json n: 10 - sv := get_numeric_ste_vec_10()::eql_v2_encrypted; - -- extract the term at $.n returned as eql_v2_encrypted - term := sv->'2517068c0d1f9d4d41d2c666211f785e'::text; - - -- -- -- -- $.n - PERFORM assert_result( - format('eql_v2_encrypted <> eql_v2_encrypted with ore_cllw_u64_8 index term'), - format('SELECT e FROM encrypted WHERE (e->''2517068c0d1f9d4d41d2c666211f785e''::text) <> %L::eql_v2_encrypted', term)); - - PERFORM assert_count( - format('eql_v2_encrypted <> eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''2517068c0d1f9d4d41d2c666211f785e''::text <> %L::eql_v2_encrypted', term), - 2); - - -- -- Check the $.hello path - -- -- Returned encrypted does not have ore_cllw_u64_8 - PERFORM assert_result( - format('eql_v2_encrypted <> eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''a7cea93975ed8c01f861ccb6bd082784''::text <> %L::eql_v2_encrypted', term)); - - END; -$$ LANGUAGE plpgsql; - - - -SELECT drop_table_with_encrypted(); \ No newline at end of file diff --git a/src/operators/<>_ore_cllw_var_8_test.sql b/src/operators/<>_ore_cllw_var_8_test.sql deleted file mode 100644 index 13d3b659..00000000 --- a/src/operators/<>_ore_cllw_var_8_test.sql +++ /dev/null @@ -1,56 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - --- ======================================================================== - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- ore_cllw_u64_8 equality --- --- Test data is '{"hello": "world", "n": 42}' - --- Paths --- $ -> bca213de9ccce676fa849ff9c4807963 --- $.hello -> a7cea93975ed8c01f861ccb6bd082784 --- $.n -> 2517068c0d1f9d4d41d2c666211f785e --- --- -DO $$ -DECLARE - sv eql_v2_encrypted; - term eql_v2_encrypted; - BEGIN - - -- This extracts the data associated with the field from the test eql_v2_encrypted - -- json n: 10 - sv := get_numeric_ste_vec_10()::eql_v2_encrypted; - -- extract the term at $.n returned as eql_v2_encrypted - term := sv->'a7cea93975ed8c01f861ccb6bd082784'::text; - - -- -- -- -- $.n - PERFORM assert_result( - format('eql_v2_encrypted <> eql_v2_encrypted with ore_cllw_var_8 index term'), - format('SELECT e FROM encrypted WHERE (e->''a7cea93975ed8c01f861ccb6bd082784''::text) <> %L::eql_v2_encrypted', term)); - - PERFORM assert_count( - format('eql_v2_encrypted <> eql_v2_encrypted with ore_cllw_var_8 index term'), - format('SELECT e FROM encrypted WHERE e->''a7cea93975ed8c01f861ccb6bd082784''::text <> %L::eql_v2_encrypted', term), - 2); - - -- -- Check the $.n path - -- -- Returned encrypted does not have ore_cllw_var_8 - PERFORM assert_result( - format('eql_v2_encrypted <> eql_v2_encrypted with ore_cllw_var_8 index term'), - format('SELECT e FROM encrypted WHERE e->''2517068c0d1f9d4d41d2c666211f785e''::text <> %L::eql_v2_encrypted', term)); - - END; -$$ LANGUAGE plpgsql; - - - -SELECT drop_table_with_encrypted(); \ No newline at end of file diff --git a/src/operators/<>_ore_test.sql b/src/operators/<>_ore_test.sql deleted file mode 100644 index df68fedf..00000000 --- a/src/operators/<>_ore_test.sql +++ /dev/null @@ -1,87 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - --- --- ORE - eql_v2_encrypted <> eql_v2_encrypted --- -DO $$ -DECLARE - e eql_v2_encrypted; - ore_term jsonb; - BEGIN - - -- Record with a Numeric ORE term of 42 - e := create_encrypted_ore_json(42); - PERFORM seed_encrypted(e); - - PERFORM assert_result( - 'eql_v2_encrypted <> eql_v2_encrypted', - format('SELECT e FROM encrypted WHERE e <> %L::eql_v2_encrypted', e)); - - PERFORM assert_count( - format('eql_v2_encrypted <> eql_v2_encrypted'), - format('SELECT e FROM encrypted WHERE e <> %L;', e), - 3); - - e := create_encrypted_ore_json(20); - - PERFORM assert_result( - 'eql_v2_encrypted <> eql_v2_encrypted', - format('SELECT e FROM encrypted WHERE e <> %L::eql_v2_encrypted', e)); - - PERFORM assert_count( - format('eql_v2_encrypted <> eql_v2_encrypted'), - format('SELECT e FROM encrypted WHERE e <> %L;', e), - 3); - END; -$$ LANGUAGE plpgsql; - - --- --- ORE - eql_v2.gte(a eql_v2_encrypted, b eql_v2_encrypted) --- -DO $$ -DECLARE - e eql_v2_encrypted; - ore_term jsonb; - BEGIN - -- Reset data - PERFORM seed_encrypted_json(); - - -- Record with a Numeric ORE term of 20 - e := create_encrypted_ore_json(20); - PERFORM seed_encrypted(e); - - PERFORM assert_result( - 'eql_v2.neq(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.neq(e, %L)', e)); - - -- include - PERFORM assert_count( - 'eql_v2.neq(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.neq(e, %L)', e), - 2); - - -- Record with a Numeric ORE term of 30 - e := create_encrypted_ore_json(30); - - PERFORM assert_result( - 'eql_v2.get(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.neq(e, %L)', e)); - - PERFORM assert_count( - 'eql_v2.get(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.neq(e, %L)', e), - 3); - END; -$$ LANGUAGE plpgsql; - - - --- ======================================================================== - - -SELECT drop_table_with_encrypted(); \ No newline at end of file diff --git a/src/operators/<>_test.sql b/src/operators/<>_test.sql deleted file mode 100644 index 076f50d0..00000000 --- a/src/operators/<>_test.sql +++ /dev/null @@ -1,165 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - --- --- Unique equality - eql_v2_encrypted <> eql_v2_encrypted --- -DO $$ -DECLARE - e eql_v2_encrypted; - BEGIN - - for i in 1..3 loop - e := create_encrypted_json(i, 'hm'); - - PERFORM assert_count( - format('eql_v2_encrypted <> eql_v2_encrypted with unique index term %s of 3', i), - format('SELECT e FROM encrypted WHERE e <> %L;', e), - 2); - - end loop; - - -- record not in database - e := create_encrypted_json(91347, 'hm'); - - PERFORM assert_no_result( - 'eql_v2_encrypted <> eql_v2_encrypted with no matching record', - format('SELECT e FROM encrypted WHERE e <> %L;', e)); - - END; -$$ LANGUAGE plpgsql; - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- Unique equality - eql_v2.neq(eql_v2_encrypted, eql_v2_encrypted) --- -DO $$ -DECLARE - e eql_v2_encrypted; - BEGIN - - for i in 1..3 loop - e := create_encrypted_json(i, 'hm'); - - PERFORM assert_count( - format('eql_v2.neq(eql_v2_encrypted, eql_v2_encrypted) with unique index term %s of 3', i), - format('SELECT e FROM encrypted WHERE eql_v2.neq(e, %L);', e), - 2); - end loop; - - -- record not in database - e := create_encrypted_json(91347, 'hm'); - - PERFORM assert_no_result( - 'eql_v2_encrypted <> eql_v2_encrypted with no matching record', - format('SELECT e FROM encrypted WHERE e <> %L;', e)); - - END; -$$ LANGUAGE plpgsql; - - - --- ======================================================================== - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- Blake equality - eql_v2_encrypted <> eql_v2_encrypted --- -DO $$ -DECLARE - e eql_v2_encrypted; - BEGIN - - for i in 1..3 loop - e := create_encrypted_json(i, 'b3'); - - PERFORM assert_result( - format('eql_v2_encrypted <> eql_v2_encrypted with blake3 index term %s of 3', i), - format('SELECT e FROM encrypted WHERE e <> %L;', e)); - - end loop; - - -- remove the ore index term - e := create_encrypted_json(91347, 'b3'); - - PERFORM assert_no_result( - 'eql_v2_encrypted <> eql_v2_encrypted with no matching record', - format('SELECT e FROM encrypted WHERE e <> %L;', e)); - - END; -$$ LANGUAGE plpgsql; - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- Blake3 equality - eql_v2.neq(eql_v2_encrypted, eql_v2_encrypted) --- -DO $$ -DECLARE - e eql_v2_encrypted; - BEGIN - - for i in 1..3 loop - e := create_encrypted_json(i, 'b3'); - - PERFORM assert_result( - format('eql_v2.neq(eql_v2_encrypted, eql_v2_encrypted) with unique index term %s of 3', i), - format('SELECT e FROM encrypted WHERE eql_v2.neq(e, %L);', e)); - end loop; - - -- remove the ore index term - e := create_encrypted_json(91347, 'b3'); - - PERFORM assert_no_result( - 'eql_v2.neq(eql_v2_encrypted, eql_v2_encrypted) with no matching record', - format('SELECT e FROM encrypted WHERE eql_v2.neq(e, %L);', e)); - - END; -$$ LANGUAGE plpgsql; - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- Blake3 equality - eql_v2_encrypted = jsonb --- -DO $$ -DECLARE - e jsonb; - BEGIN - for i in 1..3 loop - - -- remove the default - e := create_encrypted_json(i, 'b3'); - - PERFORM assert_result( - format('eql_v2_encrypted = jsonb with unique index term %s of 3', i), - format('SELECT e FROM encrypted WHERE e <> %L::jsonb;', e)); - - PERFORM assert_result( - format('jsonb = eql_v2_encrypted with unique index term %s of 3', i), - format('SELECT e FROM encrypted WHERE %L::jsonb = e', e)); - end loop; - - e := create_encrypted_json(91347, 'b3'); - - PERFORM assert_no_result( - 'eql_v2_encrypted = jsonb with no matching record', - format('SELECT e FROM encrypted WHERE e <> %L::jsonb', e)); - - PERFORM assert_no_result( - 'jsonb = eql_v2_encrypted with no matching record', - format('SELECT e FROM encrypted WHERE %L::jsonb = e', e)); - - END; -$$ LANGUAGE plpgsql; - - -SELECT drop_table_with_encrypted(); \ No newline at end of file diff --git a/src/operators/<@_test.sql b/src/operators/<@_test.sql deleted file mode 100644 index 0f500099..00000000 --- a/src/operators/<@_test.sql +++ /dev/null @@ -1,44 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- ore_cllw_u64_8 equality --- --- Test data is in form '{"hello": "{one | two | three}", "n": {10 | 20 | 30} }' --- --- Paths --- $ -> bca213de9ccce676fa849ff9c4807963 --- $.hello -> a7cea93975ed8c01f861ccb6bd082784 --- $.n -> 2517068c0d1f9d4d41d2c666211f785e --- --- -DO $$ -DECLARE - sv eql_v2_encrypted; - term eql_v2_encrypted; - BEGIN - - -- This extracts the data associated with the field from the test eql_v2_encrypted - sv := get_numeric_ste_vec_10()::eql_v2_encrypted; - -- extract the term at $.n returned as eql_v2_encrypted - term := sv->'a7cea93975ed8c01f861ccb6bd082784'::text; - - -- -- -- -- $.n - PERFORM assert_result( - format('eql_v2_encrypted = eql_v2_encrypted with ore_cllw_u64_8 index term'), - format('SELECT e FROM encrypted WHERE %L::eql_v2_encrypted <@ e', term)); - - PERFORM assert_count( - format('eql_v2_encrypted = eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE %L::eql_v2_encrypted <@ e', term), - 1); - - END; -$$ LANGUAGE plpgsql; - - -SELECT drop_table_with_encrypted(); \ No newline at end of file diff --git a/src/operators/<_test.sql b/src/operators/<_test.sql deleted file mode 100644 index a872cc59..00000000 --- a/src/operators/<_test.sql +++ /dev/null @@ -1,159 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- eql_v2_encrypted < eql_v2_encrypted with ore_cllw_u64_8 index --- --- Test data is in form '{"hello": "{one | two | three}", "n": {10 | 20 | 30} }' --- --- Paths --- $ -> bca213de9ccce676fa849ff9c4807963 --- $.hello -> a7cea93975ed8c01f861ccb6bd082784 --- $.n -> 2517068c0d1f9d4d41d2c666211f785e --- --- -- -DO $$ -DECLARE - sv eql_v2_encrypted; - term eql_v2_encrypted; - BEGIN - - -- This extracts the data associated with the field from the test eql_v2_encrypted - -- json n: 30 - sv := get_numeric_ste_vec_30()::eql_v2_encrypted; - -- extract the term at $.n returned as eql_v2_encrypted - term := sv->'2517068c0d1f9d4d41d2c666211f785e'::text; - - -- -- -- -- $.n - PERFORM assert_result( - format('eql_v2_encrypted < eql_v2_encrypted with ore_cllw_u64_8 index term'), - format('SELECT e FROM encrypted WHERE e->''2517068c0d1f9d4d41d2c666211f785e''::text < %L::eql_v2_encrypted', term)); - - PERFORM assert_count( - format('eql_v2_encrypted < eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''2517068c0d1f9d4d41d2c666211f785e''::text < %L::eql_v2_encrypted', term), - 2); - - -- -- Check the $.hello path - -- -- Returned encrypted does not have ore_cllw_u64_8 - -- Falls back to jsonb literal comparison - PERFORM assert_result( - format('eql_v2_encrypted < eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''a7cea93975ed8c01f861ccb6bd082784''::text < %L::eql_v2_encrypted', term)); - - END; -$$ LANGUAGE plpgsql; - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- eql_v2_encrypted < eql_v2_encrypted with ore_cllw_var_8 index --- --- Test data is in form '{"hello": "{one | two | three}", "n": {10 | 20 | 30} }' --- --- Paths --- $ -> bca213de9ccce676fa849ff9c4807963 --- $.hello -> a7cea93975ed8c01f861ccb6bd082784 --- $.n -> 2517068c0d1f9d4d41d2c666211f785e --- --- -- -DO $$ -DECLARE - sv eql_v2_encrypted; - term eql_v2_encrypted; - BEGIN - - -- This extracts the data associated with the field from the test eql_v2_encrypted - sv := get_numeric_ste_vec_30()::eql_v2_encrypted; - -- extract the term at $.n returned as eql_v2_encrypted - term := sv->'a7cea93975ed8c01f861ccb6bd082784'::text; - - -- -- -- -- $.n - PERFORM assert_result( - format('eql_v2_encrypted < eql_v2_encrypted with ore_cllw_var_8 index term'), - format('SELECT e FROM encrypted WHERE e->''a7cea93975ed8c01f861ccb6bd082784''::text < %L::eql_v2_encrypted', term)); - - PERFORM assert_count( - format('eql_v2_encrypted < eql_v2_encrypted with ore_cllw_var_8 index term'), - format('SELECT e FROM encrypted WHERE e->''a7cea93975ed8c01f861ccb6bd082784''::text < %L::eql_v2_encrypted', term), - 1); - - -- -- Check the $.n path - -- -- Returned encrypted does not have ore_cllw_var_8 - -- Falls back to jsonb literal comparison - PERFORM assert_result( - format('eql_v2_encrypted < eql_v2_encrypted with ore_cllw_var_8 index term'), - format('SELECT e FROM encrypted WHERE e->''2517068c0d1f9d4d41d2c666211f785e''::text < %L::eql_v2_encrypted', term)); - - END; -$$ LANGUAGE plpgsql; - - - --- --- ORE - eql_v2_encrypted < eql_v2_encrypted --- -DO $$ -DECLARE - e eql_v2_encrypted; - ore_term eql_v2_encrypted; - BEGIN - SELECT ore.e FROM ore WHERE id = 42 INTO ore_term; - - PERFORM assert_count( - 'eql_v2_encrypted < eql_v2_encrypted', - format('SELECT id FROM ore WHERE e < %L ORDER BY e DESC', ore_term), - 41); - - -- Record with a Numeric ORE term of 1 - e := create_encrypted_ore_json(1); - - PERFORM assert_no_result( - format('eql_v2_encrypted < eql_v2_encrypted'), - format('SELECT e FROM encrypted WHERE e < %L;', e)); - - -- Record with a Numeric ORE term of 42 - e := create_encrypted_ore_json(42); - - PERFORM assert_result( - 'eql_v2_encrypted < eql_v2_encrypted', - format('SELECT e FROM encrypted WHERE e < %L', e)); - - PERFORM assert_count( - 'eql_v2_encrypted < eql_v2_encrypted', - format('SELECT e FROM encrypted WHERE e < %L', e), - 3); - END; -$$ LANGUAGE plpgsql; - - --- -- --- -- ORE - eql_v2.lt(a eql_v2_encrypted, b eql_v2_encrypted) --- -- -DO $$ -DECLARE - e eql_v2_encrypted; - ore_term jsonb; - BEGIN - -- Record with a Numeric ORE term of 42 - e := create_encrypted_ore_json(42); - - PERFORM assert_result( - 'eql_v2.lt(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.lt(e, %L)', e)); - - PERFORM assert_count( - 'eql_v2.lt(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.lt(e, %L)', e), - 3); - END; -$$ LANGUAGE plpgsql; - - -SELECT drop_table_with_encrypted(); \ No newline at end of file diff --git a/src/operators/=_ore_cllw_u64_8_test.sql b/src/operators/=_ore_cllw_u64_8_test.sql deleted file mode 100644 index 6498214a..00000000 --- a/src/operators/=_ore_cllw_u64_8_test.sql +++ /dev/null @@ -1,56 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - --- ======================================================================== - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- ore_cllw_u64_8 equality --- --- Test data is in form '{"hello": "{one | two | three}", "n": {10 | 20 | 30} }' --- --- Paths --- $ -> bca213de9ccce676fa849ff9c4807963 --- $.hello -> a7cea93975ed8c01f861ccb6bd082784 --- $.n -> 2517068c0d1f9d4d41d2c666211f785e --- --- -DO $$ -DECLARE - sv eql_v2_encrypted; - term eql_v2_encrypted; - BEGIN - - -- This extracts the data associated with the field from the test eql_v2_encrypted - -- json n: 10 - sv := get_numeric_ste_vec_10()::eql_v2_encrypted; - -- extract the term at $.n returned as eql_v2_encrypted - term := sv->'2517068c0d1f9d4d41d2c666211f785e'::text; - - -- -- -- -- $.n - PERFORM assert_result( - format('eql_v2_encrypted = eql_v2_encrypted with ore_cllw_u64_8 index term'), - format('SELECT e FROM encrypted WHERE (e->''2517068c0d1f9d4d41d2c666211f785e''::text) = %L::eql_v2_encrypted', term)); - - PERFORM assert_count( - format('eql_v2_encrypted = eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''2517068c0d1f9d4d41d2c666211f785e''::text = %L::eql_v2_encrypted', term), - 1); - - -- -- Check the $.hello path - -- -- Returned encrypted does not have ore_cllw_u64_8 - PERFORM assert_no_result( - format('eql_v2_encrypted = eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''a7cea93975ed8c01f861ccb6bd082784''::text = %L::eql_v2_encrypted', term)); - - END; -$$ LANGUAGE plpgsql; - - - -SELECT drop_table_with_encrypted(); \ No newline at end of file diff --git a/src/operators/=_ore_cllw_var_8_test.sql b/src/operators/=_ore_cllw_var_8_test.sql deleted file mode 100644 index 1eb7ee8b..00000000 --- a/src/operators/=_ore_cllw_var_8_test.sql +++ /dev/null @@ -1,53 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- ore_cllw_u64_8 equality --- --- Test data is in form '{"hello": "{one | two | three}", "n": {10 | 20 | 30} }' --- --- Paths --- $ -> bca213de9ccce676fa849ff9c4807963 --- $.hello -> a7cea93975ed8c01f861ccb6bd082784 --- $.n -> 2517068c0d1f9d4d41d2c666211f785e --- --- -DO $$ -DECLARE - sv eql_v2_encrypted; - term eql_v2_encrypted; - BEGIN - - -- This extracts the data associated with the field from the test eql_v2_encrypted - -- json n: 10 - sv := get_numeric_ste_vec_10()::eql_v2_encrypted; - -- extract the term at $.n returned as eql_v2_encrypted - term := sv->'a7cea93975ed8c01f861ccb6bd082784'::text; - - -- -- -- -- $.n - PERFORM assert_result( - format('eql_v2_encrypted = eql_v2_encrypted with ore_cllw_u64_8 index term'), - format('SELECT e FROM encrypted WHERE (e->''a7cea93975ed8c01f861ccb6bd082784''::text) = %L::eql_v2_encrypted', term)); - - PERFORM assert_count( - format('eql_v2_encrypted = eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''a7cea93975ed8c01f861ccb6bd082784''::text = %L::eql_v2_encrypted', term), - 1); - - -- -- Check the $.n path - -- -- Returned encrypted does not have ore_cllw_u64_8 - PERFORM assert_no_result( - format('eql_v2_encrypted = eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''2517068c0d1f9d4d41d2c666211f785e''::text = %L::eql_v2_encrypted', term)); - - END; -$$ LANGUAGE plpgsql; - - - -SELECT drop_table_with_encrypted(); \ No newline at end of file diff --git a/src/operators/=_ore_test.sql b/src/operators/=_ore_test.sql deleted file mode 100644 index 2f75a8d0..00000000 --- a/src/operators/=_ore_test.sql +++ /dev/null @@ -1,87 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - --- --- ORE - eql_v2_encrypted = eql_v2_encrypted --- -DO $$ -DECLARE - e eql_v2_encrypted; - ore_term jsonb; - BEGIN - - -- Record with a Numeric ORE term of 42 - e := create_encrypted_ore_json(42); - PERFORM seed_encrypted(e); - - PERFORM assert_result( - 'eql_v2_encrypted = eql_v2_encrypted', - format('SELECT e FROM encrypted WHERE e = %L::eql_v2_encrypted', e)); - - PERFORM assert_count( - format('eql_v2_encrypted = eql_v2_encrypted'), - format('SELECT e FROM encrypted WHERE e = %L;', e), - 1); - - e := create_encrypted_ore_json(20); - - PERFORM assert_result( - 'eql_v2_encrypted = eql_v2_encrypted', - format('SELECT e FROM encrypted WHERE e = %L::eql_v2_encrypted', e)); - - PERFORM assert_count( - format('eql_v2_encrypted = eql_v2_encrypted'), - format('SELECT e FROM encrypted WHERE e = %L;', e), - 1); - END; -$$ LANGUAGE plpgsql; - - --- --- ORE - eql_v2.gte(a eql_v2_encrypted, b eql_v2_encrypted) --- -DO $$ -DECLARE - e eql_v2_encrypted; - ore_term jsonb; - BEGIN - -- Reset data - PERFORM seed_encrypted_json(); - - -- Record with a Numeric ORE term of 20 - e := create_encrypted_ore_json(20); - PERFORM seed_encrypted(e); - - PERFORM assert_result( - 'eql_v2.eq(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.eq(e, %L)', e)); - - -- include - PERFORM assert_count( - 'eql_v2.eq(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.eq(e, %L)', e), - 2); - - -- Record with a Numeric ORE term of 30 - e := create_encrypted_ore_json(30); - - PERFORM assert_result( - 'eql_v2.get(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.eq(e, %L)', e)); - - PERFORM assert_count( - 'eql_v2.get(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.eq(e, %L)', e), - 1); - END; -$$ LANGUAGE plpgsql; - - - --- ======================================================================== - - -SELECT drop_table_with_encrypted(); \ No newline at end of file diff --git a/src/operators/=_test.sql b/src/operators/=_test.sql deleted file mode 100644 index 1f39e717..00000000 --- a/src/operators/=_test.sql +++ /dev/null @@ -1,196 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - --- --- Unique equality - eql_v2_encrypted = eql_v2_encrypted --- -DO $$ -DECLARE - e eql_v2_encrypted; - BEGIN - - for i in 1..3 loop - e := create_encrypted_json(i, 'hm'); - - PERFORM assert_result( - format('eql_v2_encrypted = eql_v2_encrypted with unique index term %s of 3', i), - format('SELECT e FROM encrypted WHERE e = %L;', e)); - - end loop; - - -- remove the ore index term - e := create_encrypted_json(91347, 'hm'); - - PERFORM assert_no_result( - 'eql_v2_encrypted = eql_v2_encrypted with no matching record', - format('SELECT e FROM encrypted WHERE e = %L;', e)); - - END; -$$ LANGUAGE plpgsql; - - --- --- Unique equality - eql_v2.eq(eql_v2_encrypted, eql_v2_encrypted) --- -DO $$ -DECLARE - e eql_v2_encrypted; - BEGIN - - for i in 1..3 loop - e := create_encrypted_json(i)::jsonb-'ob'; - - PERFORM assert_result( - format('eql_v2.eq(eql_v2_encrypted, eql_v2_encrypted) with unique index term %s of 3', i), - format('SELECT e FROM encrypted WHERE eql_v2.eq(e, %L);', e)); - end loop; - - -- remove the ore index term - e := create_encrypted_json(91347)::jsonb-'ob'; - - PERFORM assert_no_result( - 'eql_v2.eq(eql_v2_encrypted, eql_v2_encrypted) with no matching record', - format('SELECT e FROM encrypted WHERE eql_v2.eq(e, %L);', e)); - - END; -$$ LANGUAGE plpgsql; - - --- --- Unique equality - eql_v2_encrypted = jsonb --- -DO $$ -DECLARE - e jsonb; - BEGIN - for i in 1..3 loop - - -- remove the default - e := create_encrypted_json(i)::jsonb-'ob'; - - PERFORM assert_result( - format('eql_v2_encrypted = jsonb with unique index term %s of 3', i), - format('SELECT e FROM encrypted WHERE e = %L::jsonb;', e)); - - PERFORM assert_result( - format('jsonb = eql_v2_encrypted with unique index term %s of 3', i), - format('SELECT e FROM encrypted WHERE %L::jsonb = e', e)); - end loop; - - e := create_encrypted_json(91347)::jsonb-'ob'; - - PERFORM assert_no_result( - 'eql_v2_encrypted = jsonb with no matching record', - format('SELECT e FROM encrypted WHERE e = %L::jsonb', e)); - - PERFORM assert_no_result( - 'jsonb = eql_v2_encrypted with no matching record', - format('SELECT e FROM encrypted WHERE %L::jsonb = e', e)); - - END; -$$ LANGUAGE plpgsql; - - - --- ======================================================================== - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- Blake equality - eql_v2_encrypted = eql_v2_encrypted --- -DO $$ -DECLARE - e eql_v2_encrypted; - BEGIN - - for i in 1..3 loop - e := create_encrypted_json(i, 'b3'); - - PERFORM assert_result( - format('eql_v2_encrypted = eql_v2_encrypted with unique index term %s of 3', i), - format('SELECT e FROM encrypted WHERE e = %L;', e)); - - end loop; - - -- remove the ore index term - e := create_encrypted_json(91347, 'b3'); - - PERFORM assert_no_result( - 'eql_v2_encrypted = eql_v2_encrypted with no matching record', - format('SELECT e FROM encrypted WHERE e = %L;', e)); - - END; -$$ LANGUAGE plpgsql; - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- Blake3 equality - eql_v2.eq(eql_v2_encrypted, eql_v2_encrypted) --- -DO $$ -DECLARE - e eql_v2_encrypted; - BEGIN - - for i in 1..3 loop - e := create_encrypted_json(i, 'b3'); - - PERFORM assert_result( - format('eql_v2.eq(eql_v2_encrypted, eql_v2_encrypted) with unique index term %s of 3', i), - format('SELECT e FROM encrypted WHERE eql_v2.eq(e, %L);', e)); - end loop; - - -- remove the ore index term - e := create_encrypted_json(91347, 'b3'); - - PERFORM assert_no_result( - 'eql_v2.eq(eql_v2_encrypted, eql_v2_encrypted) with no matching record', - format('SELECT e FROM encrypted WHERE eql_v2.eq(e, %L);', e)); - - END; -$$ LANGUAGE plpgsql; - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- Blake3 equality - eql_v2_encrypted = jsonb --- -DO $$ -DECLARE - e jsonb; - BEGIN - for i in 1..3 loop - - -- remove the default - e := create_encrypted_json(i, 'b3'); - - PERFORM assert_result( - format('eql_v2_encrypted = jsonb with unique index term %s of 3', i), - format('SELECT e FROM encrypted WHERE e = %L::jsonb;', e)); - - PERFORM assert_result( - format('jsonb = eql_v2_encrypted with unique index term %s of 3', i), - format('SELECT e FROM encrypted WHERE %L::jsonb = e', e)); - end loop; - - e := create_encrypted_json(91347, 'b3'); - - PERFORM assert_no_result( - 'eql_v2_encrypted = jsonb with no matching record', - format('SELECT e FROM encrypted WHERE e = %L::jsonb', e)); - - PERFORM assert_no_result( - 'jsonb = eql_v2_encrypted with no matching record', - format('SELECT e FROM encrypted WHERE %L::jsonb = e', e)); - - END; -$$ LANGUAGE plpgsql; - - -SELECT drop_table_with_encrypted(); \ No newline at end of file diff --git a/src/operators/>=_test.sql b/src/operators/>=_test.sql deleted file mode 100644 index 82e22e8e..00000000 --- a/src/operators/>=_test.sql +++ /dev/null @@ -1,175 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- eql_v2_encrypted < eql_v2_encrypted with ore_cllw_var_8 index --- --- Test data is in form '{"hello": "{one | two | three}", "n": {10 | 20 | 30} }' --- --- Paths --- $ -> bca213de9ccce676fa849ff9c4807963 --- $.hello -> a7cea93975ed8c01f861ccb6bd082784 --- $.n -> 2517068c0d1f9d4d41d2c666211f785e --- --- -- -DO $$ -DECLARE - sv eql_v2_encrypted; - term eql_v2_encrypted; - BEGIN - - -- This extracts the data associated with the field from the test eql_v2_encrypted - -- json n: 30 - sv := get_numeric_ste_vec_10()::eql_v2_encrypted; - -- extract the term at $.n returned as eql_v2_encrypted - term := sv->'2517068c0d1f9d4d41d2c666211f785e'::text; - - -- -- -- -- $.n - PERFORM assert_result( - format('eql_v2_encrypted >= eql_v2_encrypted with ore_cllw_u64_8 index term'), - format('SELECT e FROM encrypted WHERE e->''2517068c0d1f9d4d41d2c666211f785e''::text >= %L::eql_v2_encrypted', term)); - - PERFORM assert_count( - format('eql_v2_encrypted >= eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''2517068c0d1f9d4d41d2c666211f785e''::text >= %L::eql_v2_encrypted', term), - 3); - - -- -- Check the $.hello path - -- -- Returned encrypted does not have ore_cllw_u64_8 - -- Falls back to jsonb literal comparison - PERFORM assert_result( - format('eql_v2_encrypted >= eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''a7cea93975ed8c01f861ccb6bd082784''::text >= %L::eql_v2_encrypted', term)); - - END; -$$ LANGUAGE plpgsql; - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- eql_v2_encrypted < eql_v2_encrypted with ore_cllw_var_8 index --- --- Test data is in form '{"hello": "{one | two | three}", "n": {10 | 20 | 30} }' --- --- Paths --- $ -> bca213de9ccce676fa849ff9c4807963 --- $.hello -> a7cea93975ed8c01f861ccb6bd082784 --- $.n -> 2517068c0d1f9d4d41d2c666211f785e --- --- -- -DO $$ -DECLARE - sv eql_v2_encrypted; - term eql_v2_encrypted; - BEGIN - - -- This extracts the data associated with the field from the test eql_v2_encrypted - -- json n: 30 - sv := get_numeric_ste_vec_10()::eql_v2_encrypted; - -- extract the term at $.n returned as eql_v2_encrypted - term := sv->'a7cea93975ed8c01f861ccb6bd082784'::text; - - -- -- -- -- $.n - PERFORM assert_result( - format('eql_v2_encrypted >= eql_v2_encrypted with ore_cllw_u64_8 index term'), - format('SELECT e FROM encrypted WHERE e->''a7cea93975ed8c01f861ccb6bd082784''::text >= %L::eql_v2_encrypted', term)); - - PERFORM assert_count( - format('eql_v2_encrypted >= eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''a7cea93975ed8c01f861ccb6bd082784''::text >= %L::eql_v2_encrypted', term), - 3); - - -- -- Check the $.hello path - -- -- Returned encrypted does not have ore_cllw_u64_8 - -- Falls back to jsonb literal comparison - PERFORM assert_result( - format('eql_v2_encrypted >= eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''2517068c0d1f9d4d41d2c666211f785e''::text >= %L::eql_v2_encrypted', term)); - - END; -$$ LANGUAGE plpgsql; - - --- --- ORE - eql_v2_encrypted >= eql_v2_encrypted --- -DO $$ -DECLARE - e eql_v2_encrypted; - ore_term jsonb; - BEGIN - - -- Record with a Numeric ORE term of 12 - e := create_encrypted_ore_json(12); - PERFORM seed_encrypted(e); - - PERFORM assert_result( - 'eql_v2_encrypted >= eql_v2_encrypted', - format('SELECT e FROM encrypted WHERE e >= %L', e)); - - PERFORM assert_count( - format('eql_v2_encrypted >= eql_v2_encrypted'), - format('SELECT e FROM encrypted WHERE e >= %L;', e), - 3); - - e := create_encrypted_ore_json(20); - - PERFORM assert_result( - 'eql_v2_encrypted >= eql_v2_encrypted', - format('SELECT e FROM encrypted WHERE e >= %L::eql_v2_encrypted', e)); - - PERFORM assert_count( - format('eql_v2_encrypted >= eql_v2_encrypted'), - format('SELECT e FROM encrypted WHERE e >= %L;', e), - 2); - END; -$$ LANGUAGE plpgsql; - - --- --- ORE - eql_v2.gte(a eql_v2_encrypted, b eql_v2_encrypted) --- -DO $$ -DECLARE - e eql_v2_encrypted; - ore_term jsonb; - BEGIN - -- Reset data - PERFORM seed_encrypted_json(); - - -- Record with a Numeric ORE term of 42 - e := create_encrypted_ore_json(42); - PERFORM seed_encrypted(e); - - PERFORM assert_result( - 'eql_v2.gte(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.gte(e, %L)', e)); - - -- include - PERFORM assert_count( - 'eql_v2.gte(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.gte(e, %L)', e), - 1); - - -- Record with a Numeric ORE term of 30 - e := create_encrypted_ore_json(30); - - PERFORM assert_result( - 'eql_v2.get(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.gte(e, %L)', e)); - - PERFORM assert_count( - 'eql_v2.get(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.gte(e, %L)', e), - 2); - END; -$$ LANGUAGE plpgsql; - - -SELECT drop_table_with_encrypted(); \ No newline at end of file diff --git a/src/operators/>_test.sql b/src/operators/>_test.sql deleted file mode 100644 index d2429509..00000000 --- a/src/operators/>_test.sql +++ /dev/null @@ -1,159 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- eql_v2_encrypted < eql_v2_encrypted with ore_cllw_u64_8 index --- --- Test data is in form '{"hello": "{one | two | three}", "n": {10 | 20 | 30} }' --- --- Paths --- $ -> bca213de9ccce676fa849ff9c4807963 --- $.hello -> a7cea93975ed8c01f861ccb6bd082784 --- $.n -> 2517068c0d1f9d4d41d2c666211f785e --- --- -- -DO $$ -DECLARE - sv eql_v2_encrypted; - term eql_v2_encrypted; - BEGIN - - -- This extracts the data associated with the field from the test eql_v2_encrypted - -- json n: 30 - sv := get_numeric_ste_vec_20()::eql_v2_encrypted; - -- extract the term at $.n returned as eql_v2_encrypted - term := sv->'2517068c0d1f9d4d41d2c666211f785e'::text; - - -- -- -- -- $.n - PERFORM assert_result( - format('eql_v2_encrypted > eql_v2_encrypted with ore_cllw_u64_8 index term'), - format('SELECT e FROM encrypted WHERE e->''2517068c0d1f9d4d41d2c666211f785e''::text > %L::eql_v2_encrypted', term)); - - PERFORM assert_count( - format('eql_v2_encrypted > eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''2517068c0d1f9d4d41d2c666211f785e''::text > %L::eql_v2_encrypted', term), - 1); - - -- Check the $.hello path - -- Returned encrypted does not have ore_cllw_u64_8 - -- Falls back to jsonb literal comparison - PERFORM assert_result( - format('eql_v2_encrypted > eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''a7cea93975ed8c01f861ccb6bd082784''::text > %L::eql_v2_encrypted', term)); - - END; -$$ LANGUAGE plpgsql; - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- eql_v2_encrypted < eql_v2_encrypted with ore_cllw_var_8 index --- --- Test data is in form '{"hello": "{one | two | three}", "n": {10 | 20 | 30} }' --- --- Paths --- $ -> bca213de9ccce676fa849ff9c4807963 --- $.hello -> a7cea93975ed8c01f861ccb6bd082784 --- $.n -> 2517068c0d1f9d4d41d2c666211f785e --- --- -- -DO $$ -DECLARE - sv eql_v2_encrypted; - term eql_v2_encrypted; - BEGIN - - -- This extracts the data associated with the field from the test eql_v2_encrypted - -- json n: 30 - sv := get_numeric_ste_vec_30()::eql_v2_encrypted; - -- extract the term at $.n returned as eql_v2_encrypted - term := sv->'a7cea93975ed8c01f861ccb6bd082784'::text; - - -- -- -- -- $.n - PERFORM assert_result( - format('eql_v2_encrypted > eql_v2_encrypted with ore_cllw_u64_8 index term'), - format('SELECT e FROM encrypted WHERE e->''a7cea93975ed8c01f861ccb6bd082784''::text > %L::eql_v2_encrypted', term)); - - PERFORM assert_count( - format('eql_v2_encrypted > eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''a7cea93975ed8c01f861ccb6bd082784''::text > %L::eql_v2_encrypted', term), - 1); - - -- Check the $.hello path - -- Returned encrypted does not have ore_cllw_var_8 - -- Falls back to jsonb literal comparison - PERFORM assert_no_result( - format('eql_v2_encrypted > eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''2517068c0d1f9d4d41d2c666211f785e''::text > %L::eql_v2_encrypted', term)); - - END; -$$ LANGUAGE plpgsql; - - --- --- ORE - eql_v2_encrypted > eql_v2_encrypted --- -DO $$ -DECLARE - e eql_v2_encrypted; - ore_term eql_v2_encrypted; - BEGIN - SELECT ore.e FROM ore WHERE id = 42 INTO ore_term; - - PERFORM assert_count( - 'eql_v2_encrypted > eql_v2_encrypted', - format('SELECT id FROM ore WHERE e > %L ORDER BY e DESC', ore_term), - 57); - - -- Record with a Numeric ORE term of 1 - e := create_encrypted_ore_json(99); - - PERFORM assert_no_result( - format('eql_v2_encrypted > eql_v2_encrypted'), - format('SELECT e FROM encrypted WHERE e > %L;', e)); - - -- Record with a Numeric ORE term of 42 - e := create_encrypted_ore_json(17); - - PERFORM assert_result( - 'eql_v2_encrypted > eql_v2_encrypted', - format('SELECT e FROM encrypted WHERE e > %L', e)); - - PERFORM assert_count( - 'eql_v2_encrypted > eql_v2_encrypted', - format('SELECT e FROM encrypted WHERE e > %L', e), - 2); - END; -$$ LANGUAGE plpgsql; - - --- -- --- -- ORE - eql_v2.lt(a eql_v2_encrypted, b eql_v2_encrypted) --- -- -DO $$ -DECLARE - e eql_v2_encrypted; - ore_term jsonb; - BEGIN - -- Record with a Numeric ORE term of 42 - e := create_encrypted_ore_json(21); - - PERFORM assert_result( - 'eql_v2.lt(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.gt(e, %L)', e)); - - PERFORM assert_count( - 'eql_v2.lt(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.gt(e, %L)', e), - 1); - END; -$$ LANGUAGE plpgsql; - - -SELECT drop_table_with_encrypted(); \ No newline at end of file diff --git a/src/operators/@>_test.sql b/src/operators/@>_test.sql deleted file mode 100644 index 82494fec..00000000 --- a/src/operators/@>_test.sql +++ /dev/null @@ -1,94 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- eql_v2_encrypted contains itself --- --- -DO $$ - DECLARE - a eql_v2_encrypted; - b eql_v2_encrypted; - BEGIN - - a := get_numeric_ste_vec_10()::eql_v2_encrypted; - b := get_numeric_ste_vec_10()::eql_v2_encrypted; - - ASSERT a @> b; - ASSERT b @> a; - END; -$$ LANGUAGE plpgsql; - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- eql_v2_encrypted contains a term --- --- -DO $$ - DECLARE - a eql_v2_encrypted; - b eql_v2_encrypted; - term eql_v2_encrypted; - BEGIN - - a := get_numeric_ste_vec_10()::eql_v2_encrypted; - b := get_numeric_ste_vec_10()::eql_v2_encrypted; - - -- $.n - term := b->'2517068c0d1f9d4d41d2c666211f785e'::text; - - ASSERT a @> term; - - ASSERT NOT term @> a; - END; -$$ LANGUAGE plpgsql; - - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- ore_cllw_u64_8 equality --- --- Test data is in form '{"hello": "{one | two | three}", "n": {10 | 20 | 30} }' --- --- Paths --- $ -> bca213de9ccce676fa849ff9c4807963 --- $.hello -> a7cea93975ed8c01f861ccb6bd082784 --- $.n -> 2517068c0d1f9d4d41d2c666211f785e --- --- -DO $$ -DECLARE - sv eql_v2_encrypted; - term eql_v2_encrypted; - BEGIN - - -- This extracts the data associated with the field from the test eql_v2_encrypted - sv := get_numeric_ste_vec_10()::eql_v2_encrypted; - -- extract the term at $.n returned as eql_v2_encrypted - term := sv->'a7cea93975ed8c01f861ccb6bd082784'::text; - - -- -- -- -- $.n - PERFORM assert_result( - format('eql_v2_encrypted = eql_v2_encrypted with ore_cllw_u64_8 index term'), - format('SELECT e FROM encrypted WHERE e @> %L::eql_v2_encrypted', term)); - - PERFORM assert_count( - format('eql_v2_encrypted = eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e @> %L::eql_v2_encrypted', term), - 1); - - END; -$$ LANGUAGE plpgsql; - - - -SELECT drop_table_with_encrypted(); \ No newline at end of file diff --git a/src/operators/compare_test.sql b/src/operators/compare_test.sql deleted file mode 100644 index c55c1b31..00000000 --- a/src/operators/compare_test.sql +++ /dev/null @@ -1,207 +0,0 @@ -\set ON_ERROR_STOP on - - --- Compare compare_ore_cllw_var_8 -DO $$ - DECLARE - a eql_v2_encrypted; - b eql_v2_encrypted; - c eql_v2_encrypted; - BEGIN - - -- {"hello": "world{N}"} - -- $.hello: d90b97b5207d30fe867ca816ed0fe4a7 - a := eql_v2.jsonb_path_query(create_encrypted_ste_vec_json(1), 'd90b97b5207d30fe867ca816ed0fe4a7'); - b := eql_v2.jsonb_path_query(create_encrypted_ste_vec_json(2), 'd90b97b5207d30fe867ca816ed0fe4a7'); - c := eql_v2.jsonb_path_query(create_encrypted_ste_vec_json(3), 'd90b97b5207d30fe867ca816ed0fe4a7'); - - ASSERT eql_v2.compare(a, a) = 0; - ASSERT eql_v2.compare(a, b) = -1; - ASSERT eql_v2.compare(a, c) = -1; - - ASSERT eql_v2.compare(b, b) = 0; - ASSERT eql_v2.compare(b, a) = 1; - ASSERT eql_v2.compare(b, c) = -1; - - ASSERT eql_v2.compare(c, c) = 0; - ASSERT eql_v2.compare(c, b) = 1; - ASSERT eql_v2.compare(c, a) = 1; - END; -$$ LANGUAGE plpgsql; - - --- Compare compare_ore_cllw_var_8 -DO $$ - DECLARE - a eql_v2_encrypted; - b eql_v2_encrypted; - c eql_v2_encrypted; - BEGIN - - -- {"number": {N}} - -- $.number: 3dba004f4d7823446e7cb71f6681b344 - a := eql_v2.jsonb_path_query(create_encrypted_ste_vec_json(1), '3dba004f4d7823446e7cb71f6681b344'); - b := eql_v2.jsonb_path_query(create_encrypted_ste_vec_json(5), '3dba004f4d7823446e7cb71f6681b344'); - c := eql_v2.jsonb_path_query(create_encrypted_ste_vec_json(10), '3dba004f4d7823446e7cb71f6681b344'); - - ASSERT eql_v2.compare(a, a) = 0; - ASSERT eql_v2.compare(a, b) = -1; - ASSERT eql_v2.compare(a, c) = -1; - - ASSERT eql_v2.compare(b, b) = 0; - ASSERT eql_v2.compare(b, a) = 1; - ASSERT eql_v2.compare(b, c) = -1; - - ASSERT eql_v2.compare(c, c) = 0; - ASSERT eql_v2.compare(c, b) = 1; - ASSERT eql_v2.compare(c, a) = 1; - END; -$$ LANGUAGE plpgsql; - - --- Compare ore_block_u64_8_256 -DO $$ - DECLARE - a eql_v2_encrypted; - b eql_v2_encrypted; - c eql_v2_encrypted; - BEGIN - - a := create_encrypted_ore_json(1); - b := create_encrypted_ore_json(21); - c := create_encrypted_ore_json(42); - - ASSERT eql_v2.compare(a, a) = 0; - ASSERT eql_v2.compare(a, b) = -1; - ASSERT eql_v2.compare(a, c) = -1; - - ASSERT eql_v2.compare(b, b) = 0; - ASSERT eql_v2.compare(b, a) = 1; - ASSERT eql_v2.compare(b, c) = -1; - - ASSERT eql_v2.compare(c, c) = 0; - ASSERT eql_v2.compare(c, b) = 1; - ASSERT eql_v2.compare(c, a) = 1; - END; -$$ LANGUAGE plpgsql; - - --- Compare blake3 -DO $$ - DECLARE - a eql_v2_encrypted; - b eql_v2_encrypted; - c eql_v2_encrypted; - BEGIN - a := create_encrypted_json(1, 'b3'); - b := create_encrypted_json(2, 'b3'); - c := create_encrypted_json(3, 'b3'); - - ASSERT eql_v2.compare(a, a) = 0; - ASSERT eql_v2.compare(a, b) = -1; - ASSERT eql_v2.compare(a, c) = -1; - - ASSERT eql_v2.compare(b, b) = 0; - ASSERT eql_v2.compare(b, a) = 1; - ASSERT eql_v2.compare(b, c) = -1; - - ASSERT eql_v2.compare(c, c) = 0; - ASSERT eql_v2.compare(c, b) = 1; - ASSERT eql_v2.compare(c, a) = 1; - END; -$$ LANGUAGE plpgsql; - - --- Compare hmac_256 -DO $$ - DECLARE - a eql_v2_encrypted; - b eql_v2_encrypted; - c eql_v2_encrypted; - BEGIN - a := create_encrypted_json(1, 'hm'); - b := create_encrypted_json(2, 'hm'); - c := create_encrypted_json(3, 'hm'); - - ASSERT eql_v2.compare(a, a) = 0; - ASSERT eql_v2.compare(a, b) = -1; - ASSERT eql_v2.compare(a, c) = -1; - - ASSERT eql_v2.compare(b, b) = 0; - ASSERT eql_v2.compare(b, a) = 1; - ASSERT eql_v2.compare(b, c) = -1; - - ASSERT eql_v2.compare(c, c) = 0; - ASSERT eql_v2.compare(c, b) = 1; - ASSERT eql_v2.compare(c, a) = 1; - END; -$$ LANGUAGE plpgsql; - - - --- Compare with no index terms --- This is a fallback to literal comparison of the encrypted data -DO $$ - DECLARE - a eql_v2_encrypted; - b eql_v2_encrypted; - c eql_v2_encrypted; - BEGIN - a := '{"a": 1}'::jsonb::eql_v2_encrypted; - b := '{"b": 2}'::jsonb::eql_v2_encrypted; - c := '{"c": 3}'::jsonb::eql_v2_encrypted; - - ASSERT eql_v2.compare(a, a) = 0; - ASSERT eql_v2.compare(a, b) = -1; - ASSERT eql_v2.compare(a, c) = -1; - - ASSERT eql_v2.compare(b, b) = 0; - ASSERT eql_v2.compare(b, a) = 1; - ASSERT eql_v2.compare(b, c) = -1; - - ASSERT eql_v2.compare(c, c) = 0; - ASSERT eql_v2.compare(c, b) = 1; - ASSERT eql_v2.compare(c, a) = 1; - END; -$$ LANGUAGE plpgsql; - - --- --- Compare hmac_256 when record has a `null` index of higher precedence --- TEST COVERAGE FOR BUG FIX --- --- ORE Block indexes `ob` are used in compare before hmac_256 indexes. --- If the index term is null `{"ob": null}` it should not be used --- Comparing two `null` values is evaluated as equality and hilarity ensues --- - -DO $$ - DECLARE - a eql_v2_encrypted; - b eql_v2_encrypted; - c eql_v2_encrypted; - BEGIN - -- generate with `hm` index - a := create_encrypted_json(1, 'hm'); - -- append `null` index - a := '{"ob": null}'::jsonb || a::jsonb; - - b := create_encrypted_json(2, 'hm'); - b := '{"ob": null}'::jsonb || b::jsonb; - - c := create_encrypted_json(3, 'hm'); - c := '{"ob": null}'::jsonb || c::jsonb; - - ASSERT eql_v2.compare(a, a) = 0; - ASSERT eql_v2.compare(a, b) = -1; - ASSERT eql_v2.compare(a, c) = -1; - - ASSERT eql_v2.compare(b, b) = 0; - ASSERT eql_v2.compare(b, a) = 1; - ASSERT eql_v2.compare(b, c) = -1; - - ASSERT eql_v2.compare(c, c) = 0; - ASSERT eql_v2.compare(c, b) = 1; - ASSERT eql_v2.compare(c, a) = 1; - END; -$$ LANGUAGE plpgsql; diff --git a/src/operators/operator_class_test.sql b/src/operators/operator_class_test.sql deleted file mode 100644 index efb836cb..00000000 --- a/src/operators/operator_class_test.sql +++ /dev/null @@ -1,239 +0,0 @@ -\set ON_ERROR_STOP on - --- --- ORE GROUP BY --- -DO $$ - BEGIN - - PERFORM create_table_with_encrypted(); - - -- Copy ORE data into encrypted - INSERT INTO encrypted(e) SELECT e FROM ore WHERE ore.id=42; - INSERT INTO encrypted(e) SELECT e FROM ore WHERE ore.id=42; - INSERT INTO encrypted(e) SELECT e FROM ore WHERE ore.id=42; - INSERT INTO encrypted(e) SELECT e FROM ore WHERE ore.id=42; - INSERT INTO encrypted(e) SELECT e FROM ore WHERE ore.id=99; - INSERT INTO encrypted(e) SELECT e FROM ore WHERE ore.id=99; - - -- Should be the rows with value of 42 - PERFORM assert_id( - 'GROUP BY eql_v2_encrypted', - 'SELECT count(id) FROM encrypted GROUP BY e ORDER BY count(id) DESC', - 4); - END; -$$ LANGUAGE plpgsql; - --- --- Confirm index used correctly --- -DO $$ - DECLARE - ore_term eql_v2_encrypted; - result text; - BEGIN - - PERFORM create_table_with_encrypted(); - - EXECUTE 'EXPLAIN ANALYZE SELECT e::jsonb FROM encrypted WHERE e = ''("{\"ob\": \"abc\"}")'';' into result; - - -- PERFORM eql_v2.log('', result); - - IF position('Bitmap Heap Scan on encrypted' in result) > 0 THEN - RAISE EXCEPTION 'Unexpected Bitmap Heap Scan: %', result; - ELSE - ASSERT true; - END IF; - - -- Add index - CREATE INDEX ON encrypted (e eql_v2.encrypted_operator_class); - - SELECT ore.e FROM ore WHERE id = 42 INTO ore_term; - EXECUTE format('EXPLAIN ANALYZE SELECT e::jsonb FROM encrypted WHERE e = %L::eql_v2_encrypted;', ore_term) into result; - - IF position('Bitmap Heap Scan on encrypted' in result) > 0 THEN - ASSERT true; - ELSE - RAISE EXCEPTION 'Expected Bitmap Heap Scan: %', result; - END IF; - - -- INDEX WILL BE USED - EXECUTE 'EXPLAIN ANALYZE SELECT e::jsonb FROM encrypted WHERE e = ''("{\"hm\": \"abc\"}")'';' into result; - - IF position('Bitmap Heap Scan on encrypted' in result) > 0 THEN - ASSERT true; - ELSE - RAISE EXCEPTION 'Expected Bitmap Heap Scan: %', result; - END IF; - - --- - EXECUTE 'EXPLAIN ANALYZE SELECT e::jsonb FROM encrypted WHERE e = ''("{\"blah\": \"vtha\"}")'';' into result; - - IF position('Bitmap Heap Scan on encrypted' in result) > 0 THEN - ASSERT true; - ELSE - RAISE EXCEPTION 'Expected Bitmap Heap Scan: %', result; - END IF; - - END; -$$ LANGUAGE plpgsql; - - - --- --- Adding index to table where values do not have an appropriate search term --- -DO $$ - DECLARE - ore_term eql_v2_encrypted; - result text; - BEGIN - - PERFORM create_table_with_encrypted(); - - INSERT INTO encrypted (e) VALUES ('("{\"bf\": \"[1, 2, 3]\"}")'); - - -- Add index - CREATE INDEX encrypted_index ON encrypted (e eql_v2.encrypted_operator_class); - - ANALYZE encrypted; - - EXECUTE 'EXPLAIN ANALYZE SELECT e::jsonb FROM encrypted WHERE e = ''("{\"bf\": \"[1,2,3]\"}")'';' into result; - - IF position('Seq Scan on encrypted' in result) > 0 THEN - ASSERT true; - ELSE - RAISE EXCEPTION 'Unexpected Seq Scan: %', result; - END IF; - - -- NO INDEX WILL BE USED - EXECUTE 'EXPLAIN ANALYZE SELECT e::jsonb FROM encrypted WHERE e = ''("{\"hm\": \"abc\"}")'';' into result; - - IF position('Seq Scan on encrypted' in result) > 0 THEN - ASSERT true; - ELSE - RAISE EXCEPTION 'Unexpected Seq Scan: %', result; - END IF; - - INSERT INTO encrypted (e) VALUES ('("{\"hm\": \"abc\"}")'); - INSERT INTO encrypted (e) VALUES ('("{\"hm\": \"def\"}")'); - INSERT INTO encrypted (e) VALUES ('("{\"hm\": \"ghi\"}")'); - - ANALYZE encrypted; - - -- STILL NO INDEX WILL BE USED - EXECUTE 'EXPLAIN ANALYZE SELECT e::jsonb FROM encrypted WHERE e = ''("{\"hm\": \"abc\"}")'';' into result; - - IF position('Seq Scan on encrypted' in result) > 0 THEN - ASSERT true; - ELSE - RAISE EXCEPTION 'Unexpected Seq Scan: %', result; - END IF; - - DROP INDEX encrypted_index; - CREATE INDEX encrypted_index ON encrypted (e eql_v2.encrypted_operator_class); - - ANALYZE encrypted; - - EXECUTE 'EXPLAIN ANALYZE SELECT e::jsonb FROM encrypted WHERE e = ''("{\"hm\": \"abc\"}")'';' into result; - - -- -- AND STILL NOPE - IF position('Seq Scan on encrypted' in result) > 0 THEN - ASSERT true; - ELSE - RAISE EXCEPTION 'Unexpected Seq Scan: %', result; - END IF; - - - TRUNCATE encrypted; - DROP INDEX encrypted_index; - CREATE INDEX encrypted_index ON encrypted (e eql_v2.encrypted_operator_class); - - INSERT INTO encrypted (e) VALUES ('("{\"hm\": \"abc\"}")'); - INSERT INTO encrypted (e) VALUES ('("{\"hm\": \"def\"}")'); - INSERT INTO encrypted (e) VALUES ('("{\"hm\": \"ghi\"}")'); - INSERT INTO encrypted (e) VALUES ('("{\"hm\": \"jkl\"}")'); - INSERT INTO encrypted (e) VALUES ('("{\"hm\": \"mno\"}")'); - - -- Literal row type type thing - EXECUTE 'EXPLAIN ANALYZE SELECT e::jsonb FROM encrypted WHERE e = ''("{\"hm\": \"abc\"}")'';' into result; - - IF position('Index Only Scan using encrypted' in result) > 0 THEN - ASSERT true; - ELSE - RAISE EXCEPTION 'Expected Index Only Scan: %', result; - END IF; - - -- Cast to jsonb - EXECUTE 'EXPLAIN ANALYZE SELECT e::jsonb FROM encrypted WHERE e = ''{"hm": "abc"}''::jsonb;' into result; - - -- INDEX IS NOT USED - IF position('Seq Scan on encrypted' in result) > 0 THEN - ASSERT true; - ELSE - RAISE EXCEPTION 'Unexpected Seq Scan: %', result; - END IF; - - -- Cast to jsonb to eql_v2_encrypted - EXECUTE 'EXPLAIN ANALYZE SELECT e::jsonb FROM encrypted WHERE e = ''{"hm": "abc"}''::jsonb::eql_v2_encrypted;' into result; - - IF position('Index Only Scan using encrypted' in result) > 0 THEN - ASSERT true; - ELSE - RAISE EXCEPTION 'Expected Index Only Scan: %', result; - END IF; - - -- Cast to text to eql_v2_encrypted - EXECUTE 'EXPLAIN ANALYZE SELECT e::jsonb FROM encrypted WHERE e = ''{"hm": "abc"}''::text::eql_v2_encrypted;' into result; - - IF position('Index Only Scan using encrypted' in result) > 0 THEN - ASSERT true; - ELSE - RAISE EXCEPTION 'Expected Index Only Scan: %', result; - END IF; - - -- Use to_encrypted with jsonb - EXECUTE 'EXPLAIN ANALYZE SELECT e::jsonb FROM encrypted WHERE e = eql_v2.to_encrypted(''{"hm": "abc"}''::jsonb);' into result; - - IF position('Index Only Scan using encrypted' in result) > 0 THEN - ASSERT true; - ELSE - RAISE EXCEPTION 'Expected Index Only Scan: %', result; - END IF; - - -- Use to_encrypted with text - EXECUTE 'EXPLAIN ANALYZE SELECT e::jsonb FROM encrypted WHERE e = eql_v2.to_encrypted(''{"hm": "abc"}'');' into result; - - - IF position('Index Only Scan using encrypted' in result) > 0 THEN - ASSERT true; - ELSE - RAISE EXCEPTION 'Expected Index Only Scan: %', result; - END IF; - - -- - SELECT ore.e FROM ore WHERE id = 42 INTO ore_term; - EXECUTE format('EXPLAIN ANALYZE SELECT e::jsonb FROM encrypted WHERE e = %L::eql_v2_encrypted;', ore_term) into result; - - IF position('Index Only Scan using encrypted' in result) > 0 THEN - ASSERT true; - ELSE - RAISE EXCEPTION 'Expected Index Only Scan: %', result; - END IF; - - - TRUNCATE encrypted; - PERFORM seed_encrypted_json(); - - EXECUTE 'EXPLAIN ANALYZE SELECT e::jsonb FROM encrypted WHERE e = ''("{\"blah\": \"vtha\"}")'';' into result; - - IF position('Index Only Scan using encrypted' in result) > 0 THEN - ASSERT true; - ELSE - RAISE EXCEPTION 'Expected Index Only Scan: %', result; - END IF; - - END; -$$ LANGUAGE plpgsql; - - diff --git a/src/operators/order_by_test.sql b/src/operators/order_by_test.sql deleted file mode 100644 index 35e7207f..00000000 --- a/src/operators/order_by_test.sql +++ /dev/null @@ -1,149 +0,0 @@ -\set ON_ERROR_STOP on - - - --- --- ORE - ORDER BY without function --- -DO $$ -DECLARE - e eql_v2_encrypted; - ore_term eql_v2_encrypted; - BEGIN - -- Pull a record from the ore table with value of "42" - SELECT ore.e FROM ore WHERE id = 42 INTO ore_term; - - -- lt - PERFORM assert_count( - 'ORDER BY eql_v2.order_by(e) DESC', - format('SELECT id FROM ore WHERE e < %L ORDER BY e DESC', ore_term), - 41); - - PERFORM assert_result( - 'ORDER BY e DESC returns correct record', - format('SELECT id FROM ore WHERE e < %L ORDER BY e DESC LIMIT 1', ore_term), - '41'); - - PERFORM assert_result( - 'ORDER BY e ASC', - format('SELECT id FROM ore WHERE e < %L ORDER BY e ASC LIMIT 1', ore_term), - '1'); - - -- gt - PERFORM assert_count( - 'ORDER BY eql_v2.order_by(e) DESC', - format('SELECT id FROM ore WHERE e > %L ORDER BY e ASC', ore_term), - 57); - - PERFORM assert_result( - 'ORDER BY e DESC returns correct record', - format('SELECT id FROM ore WHERE e > %L ORDER BY e DESC LIMIT 1', ore_term), - '99'); - - PERFORM assert_result( - 'ORDER BY e ASC', - format('SELECT id FROM ore WHERE e > %L ORDER BY e ASC LIMIT 1', ore_term), - '43'); - - END; -$$ LANGUAGE plpgsql; - - -SELECT create_table_with_encrypted(); - --- --- ORE - ORDER BY NULL handling --- -DO $$ -DECLARE - e eql_v2_encrypted; - ore_term eql_v2_encrypted; - BEGIN - - -- Insert records with NULL values - -- record with ID=1 and e=NULL - INSERT INTO encrypted(e) VALUES (NULL::jsonb::eql_v2_encrypted); - - - -- Pull records from the ore table and insert - SELECT ore.e FROM ore WHERE id = 42 INTO ore_term; - -- record with ID=2 and e=42 - INSERT INTO encrypted(e) VALUES (ore_term); - - - SELECT ore.e FROM ore WHERE id = 3 INTO ore_term; - -- record with ID=3 and e=3 - INSERT INTO encrypted(e) VALUES (ore_term); - - - -- record with ID=4 and e=NULL - INSERT INTO encrypted(e) VALUES (NULL::jsonb::eql_v2_encrypted); - - PERFORM assert_result( - 'ORDER BY encrypted', - format('SELECT id FROM encrypted ORDER BY e'), - '3'); - - PERFORM assert_result( - 'ORDER BY encrypted ASC', - format('SELECT id FROM encrypted ORDER BY e ASC'), - '3'); - - PERFORM assert_result( - 'ORDER BY eql_v2.order_by(e) ASC NULLS FIRST', - format('SELECT id FROM encrypted ORDER BY e ASC NULLS FIRST'), - '1'); - - PERFORM assert_result( - 'ORDER BY eql_v2.order_by(e) ASC NULLS LAST', - format('SELECT id FROM encrypted ORDER BY e ASC NULLS LAST'), - '3'); - - -- NULLS FIRST when DESC - PERFORM assert_result( - 'ORDER BY encrypted DESC', - format('SELECT id FROM encrypted ORDER BY e DESC'), - '1'); - - PERFORM assert_result( - 'ORDER BY eql_v2.order_by(e) DESC NULLS FIRST', - format('SELECT id FROM encrypted ORDER BY e DESC NULLS FIRST'), - '1'); - - PERFORM assert_result( - 'ORDER BY eql_v2.order_by(e) DESC NULLS LAST', - format('SELECT id FROM encrypted ORDER BY e DESC NULLS LAST'), - '2'); - - END; -$$ LANGUAGE plpgsql; - - --- --- ORE - ORDER BY ore_block_u64_8_256(eql_v2_encrypted) - -DO $$ -DECLARE - e eql_v2_encrypted; - ore_term eql_v2_encrypted; - BEGIN - SELECT ore.e FROM ore WHERE id = 42 INTO ore_term; - - PERFORM assert_count( - 'ORDER BY eql_v2.order_by(e) DESC', - format('SELECT id FROM ore WHERE e < %L ORDER BY eql_v2.order_by(e) DESC', ore_term), - 41); - - PERFORM assert_result( - 'ORDER BY eql_v2.order_by(e) DESC returns correct record', - format('SELECT id FROM ore WHERE e < %L ORDER BY eql_v2.order_by(e) DESC LIMIT 1', ore_term), - '41'); - - PERFORM assert_result( - 'ORDER BY eql_v2.order_by(e) ASC', - format('SELECT id FROM ore WHERE e < %L ORDER BY eql_v2.order_by(e) ASC LIMIT 1', ore_term), - '1'); - END; -$$ LANGUAGE plpgsql; - -SELECT drop_table_with_encrypted(); \ No newline at end of file diff --git a/src/operators/~~_test.sql b/src/operators/~~_test.sql deleted file mode 100644 index 6bb05461..00000000 --- a/src/operators/~~_test.sql +++ /dev/null @@ -1,108 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - --- --- Match - eql_v2_encrypted ~~ eql_v2_encrypted --- -DO $$ -DECLARE - e eql_v2_encrypted; - BEGIN - - for i in 1..3 loop - e := create_encrypted_json(i); - - PERFORM assert_result( - format('eql_v2_encrypted ~~ eql_v2_encrypted %s of 3', i), - format('SELECT e FROM encrypted WHERE e ~~ %L;', e)); - - PERFORM assert_result( - format('eql_v2_encrypted LIKE eql_v2_encrypted %s of 3', i), - format('SELECT e FROM encrypted WHERE e LIKE %L;', e)); - - end loop; - - -- Partial match - e := create_encrypted_json('bf')::jsonb || '{"bf": [10, 11]}'; - - PERFORM assert_result( - 'eql_v2_encrypted ~~ eql_v2_encrypted with partial match', - format('SELECT e FROM encrypted WHERE e ~~ %L;', e)); - - PERFORM assert_result( - 'eql_v2_encrypted LIKE eql_v2_encrypted with partial match', - format('SELECT e FROM encrypted WHERE e LIKE %L;', e)); - - END; -$$ LANGUAGE plpgsql; - - --- --- Match - eql_v2_encrypted ~~* eql_v2_encrypted --- -DO $$ -DECLARE - e eql_v2_encrypted; - BEGIN - - for i in 1..3 loop - e := create_encrypted_json(i, 'bf'); - - PERFORM assert_result( - format('eql_v2_encrypted ~~* eql_v2_encrypted %s of 3', i), - format('SELECT e FROM encrypted WHERE e ~~* %L;', e)); - - PERFORM assert_result( - format('eql_v2_encrypted LIKE eql_v2_encrypted %s of 3', i), - format('SELECT e FROM encrypted WHERE e ILIKE %L;', e)); - - end loop; - - -- Partial match - e := create_encrypted_json('bf')::jsonb || '{"bf": [10, 11]}'; - - PERFORM assert_result( - 'eql_v2_encrypted ~~* eql_v2_encrypted with partial match', - format('SELECT e FROM encrypted WHERE e ~~* %L;', e)); - - PERFORM assert_result( - 'eql_v2_encrypted LIKE eql_v2_encrypted with partial match', - format('SELECT e FROM encrypted WHERE e ILIKE %L;', e)); - - END; -$$ LANGUAGE plpgsql; - - --- --- Match - eql_v2.bloom_filter(eql_v2_encrypted, eql_v2_encrypted) --- -DO $$ -DECLARE - e eql_v2_encrypted; - BEGIN - - for i in 1..3 loop - e := create_encrypted_json(i, 'bf'); - - PERFORM assert_result( - format('eql_v2.like(eql_v2_encrypted, eql_v2_encrypted)', i), - format('SELECT e FROM encrypted WHERE eql_v2.like(e, %L);', e)); - - end loop; - - -- Partial match - e := create_encrypted_json('bf')::jsonb || '{"bf": [10, 11]}'; - - PERFORM assert_result( - 'eql_v2.like(eql_v2_encrypted, eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.like(e, %L);', e)); - - END; -$$ LANGUAGE plpgsql; - - - - -SELECT drop_table_with_encrypted(); \ No newline at end of file diff --git a/src/ore_block_u64_8_256/compare_test.sql b/src/ore_block_u64_8_256/compare_test.sql deleted file mode 100644 index 83c90939..00000000 --- a/src/ore_block_u64_8_256/compare_test.sql +++ /dev/null @@ -1,27 +0,0 @@ -\set ON_ERROR_STOP on - -DO $$ - DECLARE - a eql_v2_encrypted; - b eql_v2_encrypted; - c eql_v2_encrypted; - BEGIN - - a := create_encrypted_ore_json(1); - b := create_encrypted_ore_json(21); - c := create_encrypted_ore_json(42); - - ASSERT eql_v2.compare_ore_block_u64_8_256(a, a) = 0; - ASSERT eql_v2.compare_ore_block_u64_8_256(a, b) = -1; - ASSERT eql_v2.compare_ore_block_u64_8_256(a, c) = -1; - - ASSERT eql_v2.compare_ore_block_u64_8_256(b, b) = 0; - ASSERT eql_v2.compare_ore_block_u64_8_256(b, a) = 1; - ASSERT eql_v2.compare_ore_block_u64_8_256(b, c) = -1; - - ASSERT eql_v2.compare_ore_block_u64_8_256(c, c) = 0; - ASSERT eql_v2.compare_ore_block_u64_8_256(c, b) = 1; - ASSERT eql_v2.compare_ore_block_u64_8_256(c, a) = 1; - END; -$$ LANGUAGE plpgsql; - diff --git a/src/ore_block_u64_8_256/functions_test.sql b/src/ore_block_u64_8_256/functions_test.sql deleted file mode 100644 index 0ecfaa22..00000000 --- a/src/ore_block_u64_8_256/functions_test.sql +++ /dev/null @@ -1,58 +0,0 @@ -\set ON_ERROR_STOP on - -DO $$ - BEGIN - PERFORM assert_result( - 'Extract ore index term from encrypted', - 'SELECT eql_v2.ore_block_u64_8_256(''{"ob": []}''::jsonb)'); - - PERFORM assert_exception( - 'Missing ore index term in encrypted raises exception', - 'SELECT eql_v2.ore_block_u64_8_256(''{}''::jsonb)'); - END; -$$ LANGUAGE plpgsql; - - - -DO $$ - DECLARE - ore_term eql_v2_encrypted; - BEGIN - SELECT ore.e FROM ore WHERE id = 42 INTO ore_term; - - ASSERT eql_v2.has_ore_block_u64_8_256(ore_term); - - END; -$$ LANGUAGE plpgsql; - - - --- --- ORE - ORDER BY ore_block_u64_8_256(eql_v2_encrypted) --- SKIP THIS TEST --- Individual term operators are currently disabled because we may not need them anymore - - --- DO $$ --- DECLARE --- e eql_v2_encrypted; --- ore_term eql_v2_encrypted; --- BEGIN --- SELECT ore.e FROM ore WHERE id = 42 INTO ore_term; - --- PERFORM assert_count( --- 'ORDER BY eql_v2.ore_block_u64_8_256(e) DESC', --- format('SELECT id FROM ore WHERE e < %L ORDER BY eql_v2.ore_block_u64_8_256(e) DESC', ore_term), --- 41); - --- PERFORM assert_result( --- 'ORDER BY eql_v2.ore_block_u64_8_256(e) DESC returns correct record', --- format('SELECT id FROM ore WHERE e < %L ORDER BY eql_v2.ore_block_u64_8_256(e) DESC LIMIT 1', ore_term), --- '41'); - --- PERFORM assert_result( --- 'ORDER BY eql_v2.ore_block_u64_8_256(e) ASC', --- format('SELECT id FROM ore WHERE e < %L ORDER BY eql_v2.ore_block_u64_8_256(e) ASC LIMIT 1', ore_term), --- '1'); --- END; --- $$ LANGUAGE plpgsql; diff --git a/src/ore_cllw_u64_8/compare_test.sql b/src/ore_cllw_u64_8/compare_test.sql deleted file mode 100644 index d9219b9f..00000000 --- a/src/ore_cllw_u64_8/compare_test.sql +++ /dev/null @@ -1,29 +0,0 @@ -\set ON_ERROR_STOP on - -DO $$ - DECLARE - a eql_v2_encrypted; - b eql_v2_encrypted; - c eql_v2_encrypted; - BEGIN - - -- {"number": {N}} - -- $.number: 3dba004f4d7823446e7cb71f6681b344 - a := eql_v2.jsonb_path_query(create_encrypted_ste_vec_json(1), '3dba004f4d7823446e7cb71f6681b344'); - b := eql_v2.jsonb_path_query(create_encrypted_ste_vec_json(5), '3dba004f4d7823446e7cb71f6681b344'); - c := eql_v2.jsonb_path_query(create_encrypted_ste_vec_json(10), '3dba004f4d7823446e7cb71f6681b344'); - - ASSERT eql_v2.compare_ore_cllw_u64_8(a, a) = 0; - ASSERT eql_v2.compare_ore_cllw_u64_8(a, b) = -1; - ASSERT eql_v2.compare_ore_cllw_u64_8(a, c) = -1; - - ASSERT eql_v2.compare_ore_cllw_u64_8(b, b) = 0; - ASSERT eql_v2.compare_ore_cllw_u64_8(b, a) = 1; - ASSERT eql_v2.compare_ore_cllw_u64_8(b, c) = -1; - - ASSERT eql_v2.compare_ore_cllw_u64_8(c, c) = 0; - ASSERT eql_v2.compare_ore_cllw_u64_8(c, b) = 1; - ASSERT eql_v2.compare_ore_cllw_u64_8(c, a) = 1; - END; -$$ LANGUAGE plpgsql; - diff --git a/src/ore_cllw_var_8/compare_test.sql b/src/ore_cllw_var_8/compare_test.sql deleted file mode 100644 index fa9af427..00000000 --- a/src/ore_cllw_var_8/compare_test.sql +++ /dev/null @@ -1,29 +0,0 @@ -\set ON_ERROR_STOP on - -DO $$ - DECLARE - a eql_v2_encrypted; - b eql_v2_encrypted; - c eql_v2_encrypted; - BEGIN - - -- {"hello": "world{N}"} - -- $.hello: d90b97b5207d30fe867ca816ed0fe4a7 - a := eql_v2.jsonb_path_query(create_encrypted_ste_vec_json(1), 'd90b97b5207d30fe867ca816ed0fe4a7'); - b := eql_v2.jsonb_path_query(create_encrypted_ste_vec_json(2), 'd90b97b5207d30fe867ca816ed0fe4a7'); - c := eql_v2.jsonb_path_query(create_encrypted_ste_vec_json(3), 'd90b97b5207d30fe867ca816ed0fe4a7'); - - ASSERT eql_v2.compare_ore_cllw_var_8(a, a) = 0; - ASSERT eql_v2.compare_ore_cllw_var_8(a, b) = -1; - ASSERT eql_v2.compare_ore_cllw_var_8(a, c) = -1; - - ASSERT eql_v2.compare_ore_cllw_var_8(b, b) = 0; - ASSERT eql_v2.compare_ore_cllw_var_8(b, a) = 1; - ASSERT eql_v2.compare_ore_cllw_var_8(b, c) = -1; - - ASSERT eql_v2.compare_ore_cllw_var_8(c, c) = 0; - ASSERT eql_v2.compare_ore_cllw_var_8(c, b) = 1; - ASSERT eql_v2.compare_ore_cllw_var_8(c, a) = 1; - END; -$$ LANGUAGE plpgsql; - diff --git a/src/ore_cllw_var_8/functions_test.sql b/src/ore_cllw_var_8/functions_test.sql deleted file mode 100644 index e69de29b..00000000 diff --git a/src/ste_vec/functions_test.sql b/src/ste_vec/functions_test.sql deleted file mode 100644 index 620262b5..00000000 --- a/src/ste_vec/functions_test.sql +++ /dev/null @@ -1,132 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - -DO $$ - DECLARE - e eql_v2_encrypted; - sv eql_v2_encrypted[]; - BEGIN - - SELECT encrypted.e FROM encrypted LIMIT 1 INTO e; - - sv := eql_v2.ste_vec(e); - ASSERT array_length(sv, 1) = 3; - - -- eql_v2_encrypted that IS a ste_vec element - e := get_numeric_ste_vec_10()::eql_v2_encrypted; - - sv := eql_v2.ste_vec(e); - ASSERT array_length(sv, 1) = 3; - - END; -$$ LANGUAGE plpgsql; - - -DO $$ - DECLARE - e eql_v2_encrypted; - BEGIN - e := '{ "a": 1 }'::jsonb::eql_v2_encrypted; - ASSERT eql_v2.is_ste_vec_array(e); - - e := '{ "a": 0 }'::jsonb::eql_v2_encrypted; - ASSERT NOT eql_v2.is_ste_vec_array(e); - - e := '{ }'::jsonb::eql_v2_encrypted; - ASSERT NOT eql_v2.is_ste_vec_array(e); - END; -$$ LANGUAGE plpgsql; - - -DO $$ - DECLARE - e jsonb; - BEGIN - -- extract the ste_vec array item as an eeql_v2_encrypted - e := eql_v2.to_ste_vec_value('{ "i": "i", "v": 2, "sv": [ { "ocf": "ocf" }] }'::jsonb); - - ASSERT e ? 'i'; - ASSERT e ? 'v'; - ASSERT e ? 'ocf'; - - -- Returns the original if not an stevec value - e := eql_v2.to_ste_vec_value('{ "i": "i", "v": 2, "b3": "b3" }'::jsonb); - - ASSERT e ? 'i'; - ASSERT e ? 'v'; - ASSERT e ? 'b3'; - - END; -$$ LANGUAGE plpgsql; - - - -DO $$ - DECLARE - e eql_v2_encrypted; - sv eql_v2_encrypted[]; - BEGIN - e := '{ "sv": [1] }'::jsonb::eql_v2_encrypted; - ASSERT eql_v2.is_ste_vec_value(e); - - e := '{ "sv": [] }'::jsonb::eql_v2_encrypted; - ASSERT NOT eql_v2.is_ste_vec_value(e); - - e := '{ }'::jsonb::eql_v2_encrypted; - ASSERT NOT eql_v2.is_ste_vec_value(e); - - END; -$$ LANGUAGE plpgsql; - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- eql_v2_encrypted contains itself --- --- -DO $$ - DECLARE - a eql_v2_encrypted; - b eql_v2_encrypted; - BEGIN - - a := get_numeric_ste_vec_10()::eql_v2_encrypted; - b := get_numeric_ste_vec_10()::eql_v2_encrypted; - - ASSERT eql_v2.ste_vec_contains(a, b); - ASSERT eql_v2.ste_vec_contains(b, a); - - END; -$$ LANGUAGE plpgsql; - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- eql_v2_encrypted contains a term --- --- -DO $$ - DECLARE - a eql_v2_encrypted; - b eql_v2_encrypted; - term eql_v2_encrypted; - BEGIN - - a := get_numeric_ste_vec_10()::eql_v2_encrypted; - b := get_numeric_ste_vec_10()::eql_v2_encrypted; - - -- $.n - - term := b->('2517068c0d1f9d4d41d2c666211f785e'::text); - - ASSERT eql_v2.ste_vec_contains(a, term); - - ASSERT NOT eql_v2.ste_vec_contains(term, a); - END; -$$ LANGUAGE plpgsql; - diff --git a/src/version_test.sql b/src/version_test.sql deleted file mode 100644 index cb717454..00000000 --- a/src/version_test.sql +++ /dev/null @@ -1,9 +0,0 @@ -\set ON_ERROR_STOP on - -DO $$ - BEGIN - PERFORM assert_result( - 'eql_v2.version()', - 'SELECT true WHERE eql_v2.version() = ''DEV'''); - END; -$$ LANGUAGE plpgsql; diff --git a/tasks/test.sh b/tasks/test.sh index 415b2428..dc9f43ec 100755 --- a/tasks/test.sh +++ b/tasks/test.sh @@ -1,6 +1,5 @@ #!/usr/bin/env bash -#MISE description="Run all tests (legacy SQL + SQLx Rust)" -#USAGE flag "--test " help="Test to run" default="false" +#MISE description="Run all tests (SQLx Rust)" #USAGE flag "--postgres " help="PostgreSQL version to test against" default="17" { #USAGE choices "14" "15" "16" "17" #USAGE } @@ -11,7 +10,7 @@ set -euo pipefail POSTGRES_VERSION=${usage_postgres} echo "==========================================" -echo "Running Complete EQL Test Suite" +echo "Running EQL Test Suite" echo "PostgreSQL Version: $POSTGRES_VERSION" echo "==========================================" echo "" @@ -26,21 +25,14 @@ mise run --output prefix build --force # Run lints on sqlx tests echo "" echo "==============================================" -echo "1/3: Running linting checks on SQLx Rust tests" +echo "1/2: Running linting checks on SQLx Rust tests" echo "==============================================" mise run --output prefix test:lint -# Run legacy SQL tests -echo "" -echo "==============================================" -echo "2/3: Running Legacy SQL Tests" -echo "==============================================" -mise run --output prefix test:legacy --postgres ${POSTGRES_VERSION} - # Run SQLx Rust tests echo "" echo "==============================================" -echo "3/3: Running SQLx Rust Tests" +echo "2/2: Running SQLx Rust Tests" echo "==============================================" mise run --output prefix test:sqlx @@ -51,6 +43,5 @@ echo "==============================================" echo "" echo "Summary:" echo " βœ“ SQLx Rust lint checks" -echo " βœ“ Legacy SQL tests" echo " βœ“ SQLx Rust tests" echo "" diff --git a/tasks/test/legacy.sh b/tasks/test/legacy.sh deleted file mode 100755 index 4c087b3b..00000000 --- a/tasks/test/legacy.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env bash -#MISE description="Run legacy SQL tests (inline test files)" -#USAGE flag "--test " help="Specific test file pattern to run" default="false" -#USAGE flag "--postgres " help="PostgreSQL version to test against" default="17" { -#USAGE choices "14" "15" "16" "17" -#USAGE } - -set -euo pipefail - -POSTGRES_VERSION=${usage_postgres} - -connection_url=postgresql://${POSTGRES_USER:-$USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} -container_name=postgres-${POSTGRES_VERSION} - -# Check postgres is running (script will exit if not) -source "$(dirname "$0")/../postgres/check_container.sh" ${POSTGRES_VERSION} - -run_test () { - echo - echo '###############################################' - echo "# Running Test: ${1}" - echo '###############################################' - echo - - cat $1 | docker exec -i ${container_name} psql --variable ON_ERROR_STOP=1 $connection_url -f- -} - -# Reset database -mise run reset --force --postgres ${POSTGRES_VERSION} - -echo -echo '###############################################' -echo '# Installing release/cipherstash-encrypt.sql' -echo '###############################################' -echo - -# Install -cat release/cipherstash-encrypt.sql | docker exec -i ${container_name} psql ${connection_url} -f- - - -cat tests/test_helpers.sql | docker exec -i ${container_name} psql ${connection_url} -f- -cat tests/ore.sql | docker exec -i ${container_name} psql ${connection_url} -f- -cat tests/ste_vec.sql | docker exec -i ${container_name} psql ${connection_url} -f- - - -if [ $usage_test = "false" ]; then - find src -type f -path "*_test.sql" | while read -r sql_file; do - echo $sql_file - run_test $sql_file - done -else - find src -type f -path "*$usage_test*" | while read -r sql_file; do - run_test $sql_file - done -fi - -echo -echo '###############################################' -echo "# βœ…ALL TESTS PASSED " -echo '###############################################' -echo diff --git a/src/encrypted/aggregates_test.sql b/tests/sqlx/fixtures/aggregate_minmax_data.sql similarity index 86% rename from src/encrypted/aggregates_test.sql rename to tests/sqlx/fixtures/aggregate_minmax_data.sql index 68bd4ea5..c635f88d 100644 --- a/src/encrypted/aggregates_test.sql +++ b/tests/sqlx/fixtures/aggregate_minmax_data.sql @@ -1,13 +1,18 @@ -\set ON_ERROR_STOP on +-- Fixture: aggregate_minmax_data.sql +-- Test data for eql_v2.min() and eql_v2.max() aggregate functions +-- +-- Creates table with encrypted integer data including NULL values +-- to test aggregate functions on encrypted columns --- create table +-- Create table (drop first for idempotency) +DROP TABLE IF EXISTS agg_test; CREATE TABLE agg_test ( plain_int integer, enc_int eql_v2_encrypted ); --- Add data. These are saved from the psql query output connected to Proxy. +-- Add data. These are encrypted values from the SQL test file. -- Decrypted `enc_int` value is the same as the `plain_int` value in the same row. INSERT INTO agg_test (plain_int, enc_int) VALUES ( @@ -31,20 +36,3 @@ INSERT INTO agg_test (plain_int, enc_int) VALUES '{"c": "mBbLa7Cm?&jvpfcv1d3hep>s)76qzUbwUky&M&C3mjDG_os-_y0MRaMGl@&p#AOuusN|3Lu=mBCcg_V{&N2hzy", "i": {"c": "encrypted_int4", "t": "encrypted"}, "k": "ct", "bf": null, "ob": ["ccccccccb06565ebd23d6a4c3eee512713175e673c6d995ff5d9b1d3492fe8eb289c3eb95029025f5b71fc6e06632b4a1302980e433361c7999724dbdd052739258d9444b0fbd43cc61368e60f4b0d5aeca2aa85c1c89933b53afffcc4eb0632dca75f632bb9bc792d1dbd6bced6253291f0db134552d384e9e378f4f5890c31ca9d115965a0e8fb2c3c60ccce84ffc03bddb22b27a1ce278eec118496fd23f083ebb21bb4b83b89eda8c0bdea50debc5ec4f2b2d91b63a80d39386194ad9d129bee2f5168341cb41ed26dc03466cac5e2dbe7336fdb74c0d37d63b396033ce60002c9950f5ac2970dacf4caace2eef5b81544df88a7ef2a8d69550d25d39c678c8e43a3dcc2857018a2c979b45c6b19dabd28ae7388d62916e6742763d6484d1b45154e6c8e6a66e02b03f64b67ddef24747dded32e226e3a93d5d1a92d11e760403cad04a0dd07c14da336a409739e8bbeb3b3d6b92117fa2d2c941da4996ea61b29ca3fffb4594ddbeab7105a1b4c5e422ec5ab8154db545103d8c2889be2e4591198912446d8b33b8708a4cc959a1e0957dcae6a50c3"], "v": 1}'::jsonb::eql_v2_encrypted ) ; - --- run normal cases -DO $$ - BEGIN - -- min null finds null - ASSERT ((SELECT eql_v2.min(enc_int) FROM agg_test where enc_int IS NULL) IS NULL); - - -- min enc_int finds the minimum (1) - ASSERT ((SELECT enc_int FROM agg_test WHERE plain_int = 1) = (SELECT eql_v2.min(enc_int) FROM agg_test)); - - -- max null finds null - ASSERT ((SELECT eql_v2.max(enc_int) FROM agg_test where enc_int IS NULL) IS NULL); - - -- max enc_int finds the maximum (5) - ASSERT ((SELECT enc_int FROM agg_test WHERE plain_int = 5) = (SELECT eql_v2.max(enc_int) FROM agg_test)); - END; -$$ LANGUAGE plpgsql; diff --git a/tests/sqlx/src/helpers.rs b/tests/sqlx/src/helpers.rs index 6335ef73..381ce8f1 100644 --- a/tests/sqlx/src/helpers.rs +++ b/tests/sqlx/src/helpers.rs @@ -82,3 +82,88 @@ pub async fn get_ore_encrypted_as_jsonb(pool: &PgPool, id: i32) -> Result Result { + let sql = format!("SELECT e::text FROM ste_vec WHERE id = {}", id); + let row = sqlx::query(&sql) + .fetch_one(pool) + .await + .with_context(|| format!("fetching ste_vec encrypted value for id={}", id))?; + + let result: Option = row + .try_get(0) + .with_context(|| format!("extracting text column for id={}", id))?; + + result.with_context(|| format!("ste_vec table returned NULL for id={}", id)) +} + +/// Extract selector term using SQL helper functions +/// +/// Uses the get_numeric_ste_vec_*() helper functions to extract a selector term. +/// This matches the SQL test pattern exactly. +/// +/// # Arguments +/// * `pool` - Database connection pool +/// * `value` - Which STE vec value to use (10, 20, 30, or 42) +/// * `selector` - Selector hash to extract (e.g., Selectors::N, Selectors::HELLO) +/// +/// # Example +/// ```ignore +/// // Extract $.n selector from n=30 test data +/// let term = get_ste_vec_selector_term(&pool, 30, Selectors::N).await?; +/// ``` +pub async fn get_ste_vec_selector_term( + pool: &PgPool, + value: i32, + selector: &str, +) -> Result { + // Call the appropriate get_numeric_ste_vec_*() function + let func_name = match value { + 10 => "get_numeric_ste_vec_10", + 20 => "get_numeric_ste_vec_20", + 30 => "get_numeric_ste_vec_30", + 42 => "get_numeric_ste_vec_42", + _ => { + return Err(anyhow::anyhow!( + "Invalid value: {}. Must be 10, 20, 30, or 42", + value + )) + } + }; + + // SQL equivalent: sv := get_numeric_ste_vec_30()::eql_v2_encrypted; + // term := sv->'2517068c0d1f9d4d41d2c666211f785e'::text; + let sql = format!( + "SELECT ({}()::eql_v2_encrypted -> '{}'::text)::text", + func_name, selector + ); + + let row = sqlx::query(&sql).fetch_one(pool).await.with_context(|| { + format!( + "extracting selector '{}' from ste_vec value={}", + selector, value + ) + })?; + + let result: Option = row.try_get(0).with_context(|| { + format!( + "getting text column for selector '{}' from value={}", + selector, value + ) + })?; + + result.with_context(|| { + format!( + "selector extraction returned NULL for selector='{}', value={}", + selector, value + ) + }) +} diff --git a/tests/sqlx/src/lib.rs b/tests/sqlx/src/lib.rs index 6ea784dd..7c81cdd3 100644 --- a/tests/sqlx/src/lib.rs +++ b/tests/sqlx/src/lib.rs @@ -10,7 +10,10 @@ pub mod index_types; pub mod selectors; pub use assertions::QueryAssertion; -pub use helpers::{get_encrypted_term, get_ore_encrypted, get_ore_encrypted_as_jsonb}; +pub use helpers::{ + get_encrypted_term, get_ore_encrypted, get_ore_encrypted_as_jsonb, get_ste_vec_encrypted, + get_ste_vec_selector_term, +}; pub use index_types as IndexTypes; pub use selectors::Selectors; diff --git a/tests/sqlx/tests/aggregate_tests.rs b/tests/sqlx/tests/aggregate_tests.rs index 624d0915..9d7b5245 100644 --- a/tests/sqlx/tests/aggregate_tests.rs +++ b/tests/sqlx/tests/aggregate_tests.rs @@ -1,45 +1,67 @@ //! Aggregate function tests //! -//! Tests COUNT, MAX, MIN with encrypted data +//! Tests COUNT, MAX, MIN with encrypted data including eql_v2.min() and eql_v2.max() use anyhow::Result; use sqlx::PgPool; #[sqlx::test] async fn count_aggregate_on_encrypted_column(pool: PgPool) -> Result<()> { - // Test: COUNT works with encrypted columns + // Test: COUNT works on encrypted columns (counts non-NULL encrypted values) - let count: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM ore") + let count: i64 = sqlx::query_scalar("SELECT COUNT(e) FROM ore") .fetch_one(&pool) .await?; - assert_eq!(count, 99, "should count all ORE records"); + assert_eq!(count, 99, "should count all non-NULL encrypted values"); Ok(()) } #[sqlx::test] async fn max_aggregate_on_encrypted_column(pool: PgPool) -> Result<()> { - // Test: MAX returns highest value with ORE + // Test: eql_v2.max() returns highest encrypted value + // The ore table has id and e columns where e is the encrypted version of id + // So eql_v2.max(e) should return the encrypted value corresponding to id=99 - let max_id: i64 = sqlx::query_scalar("SELECT MAX(id) FROM ore WHERE id <= 50") + // Get the expected max value (encrypted value where id = 99) + let expected: String = sqlx::query_scalar("SELECT e::text FROM ore WHERE id = 99") .fetch_one(&pool) .await?; - assert_eq!(max_id, 50, "MAX should return 50"); + // Get the actual max from eql_v2.max() + let actual: String = sqlx::query_scalar("SELECT eql_v2.max(e)::text FROM ore") + .fetch_one(&pool) + .await?; + + assert_eq!( + actual, expected, + "eql_v2.max(e) should return the encrypted value where id = 99 (maximum)" + ); Ok(()) } #[sqlx::test] async fn min_aggregate_on_encrypted_column(pool: PgPool) -> Result<()> { - // Test: MIN returns lowest value with ORE + // Test: eql_v2.min() returns lowest encrypted value + // The ore table has id and e columns where e is the encrypted version of id + // So eql_v2.min(e) should return the encrypted value corresponding to id=1 + + // Get the expected min value (encrypted value where id = 1) + let expected: String = sqlx::query_scalar("SELECT e::text FROM ore WHERE id = 1") + .fetch_one(&pool) + .await?; - let min_id: i64 = sqlx::query_scalar("SELECT MIN(id) FROM ore WHERE id >= 10") + // Get the actual min from eql_v2.min() + let actual: String = sqlx::query_scalar("SELECT eql_v2.min(e)::text FROM ore") .fetch_one(&pool) .await?; - assert_eq!(min_id, 10, "MIN should return 10"); + assert_eq!( + actual, expected, + "eql_v2.min(e) should return the encrypted value where id = 1 (minimum)" + ); Ok(()) } @@ -64,3 +86,89 @@ async fn group_by_with_encrypted_column(pool: PgPool) -> Result<()> { Ok(()) } + +// ========== eql_v2.min() and eql_v2.max() Tests ========== + +#[sqlx::test(fixtures(path = "../fixtures", scripts("aggregate_minmax_data")))] +async fn eql_v2_min_with_null_values(pool: PgPool) -> Result<()> { + // Test: eql_v2.min() on NULL encrypted values returns NULL + // Source SQL: ASSERT ((SELECT eql_v2.min(enc_int) FROM agg_test where enc_int IS NULL) IS NULL); + + let result: Option = + sqlx::query_scalar("SELECT eql_v2.min(enc_int)::text FROM agg_test WHERE enc_int IS NULL") + .fetch_one(&pool) + .await?; + + assert!( + result.is_none(), + "eql_v2.min() should return NULL when querying only NULL values" + ); + + Ok(()) +} + +#[sqlx::test(fixtures(path = "../fixtures", scripts("aggregate_minmax_data")))] +async fn eql_v2_min_finds_minimum_encrypted_value(pool: PgPool) -> Result<()> { + // Test: eql_v2.min() finds the minimum encrypted value (plain_int = 1) + // Source SQL: ASSERT ((SELECT enc_int FROM agg_test WHERE plain_int = 1) = (SELECT eql_v2.min(enc_int) FROM agg_test)); + + // Get the expected minimum value (plain_int = 1) + let expected: String = + sqlx::query_scalar("SELECT enc_int::text FROM agg_test WHERE plain_int = 1") + .fetch_one(&pool) + .await?; + + // Get the actual minimum from eql_v2.min() + let actual: String = sqlx::query_scalar("SELECT eql_v2.min(enc_int)::text FROM agg_test") + .fetch_one(&pool) + .await?; + + assert_eq!( + actual, expected, + "eql_v2.min() should return the encrypted value where plain_int = 1 (minimum)" + ); + + Ok(()) +} + +#[sqlx::test(fixtures(path = "../fixtures", scripts("aggregate_minmax_data")))] +async fn eql_v2_max_with_null_values(pool: PgPool) -> Result<()> { + // Test: eql_v2.max() on NULL encrypted values returns NULL + // Source SQL: ASSERT ((SELECT eql_v2.max(enc_int) FROM agg_test where enc_int IS NULL) IS NULL); + + let result: Option = + sqlx::query_scalar("SELECT eql_v2.max(enc_int)::text FROM agg_test WHERE enc_int IS NULL") + .fetch_one(&pool) + .await?; + + assert!( + result.is_none(), + "eql_v2.max() should return NULL when querying only NULL values" + ); + + Ok(()) +} + +#[sqlx::test(fixtures(path = "../fixtures", scripts("aggregate_minmax_data")))] +async fn eql_v2_max_finds_maximum_encrypted_value(pool: PgPool) -> Result<()> { + // Test: eql_v2.max() finds the maximum encrypted value (plain_int = 5) + // Source SQL: ASSERT ((SELECT enc_int FROM agg_test WHERE plain_int = 5) = (SELECT eql_v2.max(enc_int) FROM agg_test)); + + // Get the expected maximum value (plain_int = 5) + let expected: String = + sqlx::query_scalar("SELECT enc_int::text FROM agg_test WHERE plain_int = 5") + .fetch_one(&pool) + .await?; + + // Get the actual maximum from eql_v2.max() + let actual: String = sqlx::query_scalar("SELECT eql_v2.max(enc_int)::text FROM agg_test") + .fetch_one(&pool) + .await?; + + assert_eq!( + actual, expected, + "eql_v2.max() should return the encrypted value where plain_int = 5 (maximum)" + ); + + Ok(()) +} diff --git a/tests/sqlx/tests/comparison_tests.rs b/tests/sqlx/tests/comparison_tests.rs index 12ba9c0e..6fbe8212 100644 --- a/tests/sqlx/tests/comparison_tests.rs +++ b/tests/sqlx/tests/comparison_tests.rs @@ -3,7 +3,10 @@ //! Tests EQL comparison operators with ORE (Order-Revealing Encryption) use anyhow::{Context, Result}; -use eql_tests::{get_ore_encrypted, get_ore_encrypted_as_jsonb, QueryAssertion}; +use eql_tests::{ + get_ore_encrypted, get_ore_encrypted_as_jsonb, get_ste_vec_selector_term, QueryAssertion, + Selectors, +}; use sqlx::{PgPool, Row}; /// Helper to execute create_encrypted_json SQL function @@ -308,3 +311,392 @@ async fn greater_than_or_equal_jsonb_gte_encrypted(pool: PgPool) -> Result<()> { Ok(()) } + +// ============================================================================ +// Selector-based Comparison Tests +// ============================================================================ +// Tests for extracting subterms with e->'selector' and comparing them +// Covers ore_cllw_u64_8 and ore_cllw_var_8 index types with fallback behavior + +#[sqlx::test] +async fn selector_less_than_with_ore_cllw_u64_8(pool: PgPool) -> Result<()> { + // Test: e->'selector' < term with ore_cllw_u64_8 index + // + // Uses test data created by seed_encrypted_json() helper which creates: + // - Three records with n=10, n=20, n=30 + // - ore_cllw_u64_8 index on $.n selector + + // Create table and seed with test data + sqlx::query("SELECT create_table_with_encrypted()") + .execute(&pool) + .await?; + + sqlx::query("SELECT seed_encrypted_json()") + .execute(&pool) + .await?; + + // Extract $.n selector term from n=30 test data + let term = get_ste_vec_selector_term(&pool, 30, Selectors::N).await?; + + // Query: e->'$.n' < term(30) + // Should return 2 records (n=10 and n=20) + let sql = format!( + "SELECT e FROM encrypted WHERE e->'{}'::text < '{}'::eql_v2_encrypted", + Selectors::N, + term + ); + + QueryAssertion::new(&pool, &sql).count(2).await; + + Ok(()) +} + +#[sqlx::test] +async fn selector_less_than_with_ore_cllw_u64_8_fallback(pool: PgPool) -> Result<()> { + // Test: e->'selector' < term fallback when index missing + // + // Tests that comparison falls back to JSONB literal comparison + // when the requested index type is not present on the selector + + sqlx::query("SELECT create_table_with_encrypted()") + .execute(&pool) + .await?; + + sqlx::query("SELECT seed_encrypted_json()") + .execute(&pool) + .await?; + + // Extract $.n selector term from n=30 test data + let term = get_ste_vec_selector_term(&pool, 30, Selectors::N).await?; + + // Query with $.hello selector (which has ore_cllw_var_8, not ore_cllw_u64_8) + // Falls back to JSONB literal comparison - should still return results + let sql = format!( + "SELECT e FROM encrypted WHERE e->'{}'::text < '{}'::eql_v2_encrypted", + Selectors::HELLO, + term + ); + + QueryAssertion::new(&pool, &sql).returns_rows().await; + + Ok(()) +} + +#[sqlx::test] +async fn selector_less_than_with_ore_cllw_var_8(pool: PgPool) -> Result<()> { + // Test: e->'selector' < term with ore_cllw_var_8 index + // + // STE vec test data has ore_cllw_var_8 on $.hello selector (a7cea93975ed8c01f861ccb6bd082784) + // Extract $.hello from ste_vec id=3 and compare + + sqlx::query("SELECT create_table_with_encrypted()") + .execute(&pool) + .await?; + + sqlx::query("SELECT seed_encrypted_json()") + .execute(&pool) + .await?; + + // Extract $.hello selector term from n=30 test data (corresponds to "three") + let term = get_ste_vec_selector_term(&pool, 30, Selectors::HELLO).await?; + + // Query: e->'$.hello' < term(from ste_vec 3) + // Should return 1 record (ste_vec id=1, since "world 1" < "world 3") + let sql = format!( + "SELECT e FROM encrypted WHERE e->'{}'::text < '{}'::eql_v2_encrypted", + Selectors::HELLO, + term + ); + + QueryAssertion::new(&pool, &sql).count(1).await; + + Ok(()) +} + +#[sqlx::test] +async fn selector_greater_than_with_ore_cllw_u64_8(pool: PgPool) -> Result<()> { + // Test: e->'selector' > term with ore_cllw_u64_8 index + // + // Extract $.n from ste_vec id=2 (n=20 value) and find records > 20 + + sqlx::query("SELECT create_table_with_encrypted()") + .execute(&pool) + .await?; + + sqlx::query("SELECT seed_encrypted_json()") + .execute(&pool) + .await?; + + // Extract $.n selector term from n=20 test data + let term = get_ste_vec_selector_term(&pool, 20, Selectors::N).await?; + + // Query: e->'$.n' > term(20) + // Should return 1 record (n=30) + let sql = format!( + "SELECT e FROM encrypted WHERE e->'{}'::text > '{}'::eql_v2_encrypted", + Selectors::N, + term + ); + + QueryAssertion::new(&pool, &sql).count(1).await; + + Ok(()) +} + +#[sqlx::test] +async fn selector_greater_than_with_ore_cllw_u64_8_fallback(pool: PgPool) -> Result<()> { + // Test: e->'selector' > term fallback when index missing + + sqlx::query("SELECT create_table_with_encrypted()") + .execute(&pool) + .await?; + + sqlx::query("SELECT seed_encrypted_json()") + .execute(&pool) + .await?; + + // Extract $.n selector term from n=20 test data + let term = get_ste_vec_selector_term(&pool, 20, Selectors::N).await?; + + // Query with $.hello selector (falls back to JSONB comparison) + let sql = format!( + "SELECT e FROM encrypted WHERE e->'{}'::text > '{}'::eql_v2_encrypted", + Selectors::HELLO, + term + ); + + QueryAssertion::new(&pool, &sql).returns_rows().await; + + Ok(()) +} + +#[sqlx::test] +async fn selector_greater_than_with_ore_cllw_var_8(pool: PgPool) -> Result<()> { + // Test: e->'selector' > term with ore_cllw_var_8 index + + sqlx::query("SELECT create_table_with_encrypted()") + .execute(&pool) + .await?; + + sqlx::query("SELECT seed_encrypted_json()") + .execute(&pool) + .await?; + + // Extract $.hello selector term from n=30 test data (corresponds to "three") + let term = get_ste_vec_selector_term(&pool, 30, Selectors::HELLO).await?; + + // Query: e->'$.hello' > term + // Should return 1 record + let sql = format!( + "SELECT e FROM encrypted WHERE e->'{}'::text > '{}'::eql_v2_encrypted", + Selectors::HELLO, + term + ); + + QueryAssertion::new(&pool, &sql).count(1).await; + + Ok(()) +} + +#[sqlx::test] +async fn selector_greater_than_with_ore_cllw_var_8_fallback(pool: PgPool) -> Result<()> { + // Test: e->'selector' > term fallback to JSONB comparison + // + // Tests fallback when selector doesn't have ore_cllw_var_8 + + sqlx::query("SELECT create_table_with_encrypted()") + .execute(&pool) + .await?; + + sqlx::query("SELECT seed_encrypted_json()") + .execute(&pool) + .await?; + + // Extract $.hello selector term from n=30 test data + let term = get_ste_vec_selector_term(&pool, 30, Selectors::HELLO).await?; + + // Query with $.n selector (which has ore_cllw_u64_8, not ore_cllw_var_8) + // Falls back to JSONB literal comparison - should not return results + // because the extracted term is a string selector and $.n expects numeric + let sql = format!( + "SELECT e FROM encrypted WHERE e->'{}'::text > '{}'::eql_v2_encrypted", + Selectors::N, + term + ); + + // The fallback query succeeds but returns no results due to type mismatch + QueryAssertion::new(&pool, &sql).count(0).await; + + Ok(()) +} + +#[sqlx::test] +async fn selector_less_than_or_equal_with_ore_cllw_u64_8(pool: PgPool) -> Result<()> { + // Test: e->'selector' <= term with ore_cllw_u64_8 index + // + // Extract $.n from ste_vec id=2 (n=20) and find records <= 20 + + sqlx::query("SELECT create_table_with_encrypted()") + .execute(&pool) + .await?; + + sqlx::query("SELECT seed_encrypted_json()") + .execute(&pool) + .await?; + + // Extract $.n selector term from n=20 test data + let term = get_ste_vec_selector_term(&pool, 20, Selectors::N).await?; + + // Query: e->'$.n' <= term(20) + // Should return 2 records (n=10 and n=20) + let sql = format!( + "SELECT e FROM encrypted WHERE e->'{}'::text <= '{}'::eql_v2_encrypted", + Selectors::N, + term + ); + + QueryAssertion::new(&pool, &sql).count(2).await; + + Ok(()) +} + +#[sqlx::test] +async fn selector_less_than_or_equal_with_ore_cllw_u64_8_fallback(pool: PgPool) -> Result<()> { + // Test: e->'selector' <= term fallback when index missing + + sqlx::query("SELECT create_table_with_encrypted()") + .execute(&pool) + .await?; + + sqlx::query("SELECT seed_encrypted_json()") + .execute(&pool) + .await?; + + // Extract $.n selector term from n=20 test data + let term = get_ste_vec_selector_term(&pool, 20, Selectors::N).await?; + + // Query with $.hello selector (falls back to JSONB comparison) + // The extracted term is numeric but $.hello selector expects string + let sql = format!( + "SELECT e FROM encrypted WHERE e->'{}'::text <= '{}'::eql_v2_encrypted", + Selectors::HELLO, + term + ); + + // SQL test behavior: fallback succeeds but returns no results due to type mismatch + QueryAssertion::new(&pool, &sql).count(0).await; + + Ok(()) +} + +#[sqlx::test] +async fn selector_greater_than_or_equal_with_ore_cllw_u64_8(pool: PgPool) -> Result<()> { + // Test: e->'selector' >= term with ore_cllw_u64_8 index + // + // Extract $.n from ste_vec id=1 (n=10) and find records >= 10 + + sqlx::query("SELECT create_table_with_encrypted()") + .execute(&pool) + .await?; + + sqlx::query("SELECT seed_encrypted_json()") + .execute(&pool) + .await?; + + // Extract $.n selector term from n=10 test data + let term = get_ste_vec_selector_term(&pool, 10, Selectors::N).await?; + + // Query: e->'$.n' >= term(10) + // Should return 3 records (n=10, n=20, n=30) + let sql = format!( + "SELECT e FROM encrypted WHERE e->'{}'::text >= '{}'::eql_v2_encrypted", + Selectors::N, + term + ); + + QueryAssertion::new(&pool, &sql).count(3).await; + + Ok(()) +} + +#[sqlx::test] +async fn selector_greater_than_or_equal_with_ore_cllw_u64_8_fallback(pool: PgPool) -> Result<()> { + // Test: e->'selector' >= term fallback when index missing + + sqlx::query("SELECT create_table_with_encrypted()") + .execute(&pool) + .await?; + + sqlx::query("SELECT seed_encrypted_json()") + .execute(&pool) + .await?; + + // Extract $.n selector term from n=10 test data + let term = get_ste_vec_selector_term(&pool, 10, Selectors::N).await?; + + // Query with $.hello selector (falls back to JSONB comparison) + let sql = format!( + "SELECT e FROM encrypted WHERE e->'{}'::text >= '{}'::eql_v2_encrypted", + Selectors::HELLO, + term + ); + + QueryAssertion::new(&pool, &sql).returns_rows().await; + + Ok(()) +} + +#[sqlx::test] +async fn selector_greater_than_or_equal_with_ore_cllw_var_8(pool: PgPool) -> Result<()> { + // Test: e->'selector' >= term with ore_cllw_var_8 index + + sqlx::query("SELECT create_table_with_encrypted()") + .execute(&pool) + .await?; + + sqlx::query("SELECT seed_encrypted_json()") + .execute(&pool) + .await?; + + // Extract $.hello selector term from n=10 test data (corresponds to "one") + let term = get_ste_vec_selector_term(&pool, 10, Selectors::HELLO).await?; + + // Query: e->'$.hello' >= term + // Should return 3 records (all have "world X" >= "world 1") + let sql = format!( + "SELECT e FROM encrypted WHERE e->'{}'::text >= '{}'::eql_v2_encrypted", + Selectors::HELLO, + term + ); + + QueryAssertion::new(&pool, &sql).count(3).await; + + Ok(()) +} + +#[sqlx::test] +async fn selector_greater_than_or_equal_with_ore_cllw_var_8_fallback(pool: PgPool) -> Result<()> { + // Test: e->'selector' >= term fallback to JSONB comparison + + sqlx::query("SELECT create_table_with_encrypted()") + .execute(&pool) + .await?; + + sqlx::query("SELECT seed_encrypted_json()") + .execute(&pool) + .await?; + + // Extract $.hello selector term from n=10 test data + let term = get_ste_vec_selector_term(&pool, 10, Selectors::HELLO).await?; + + // Query with $.n selector (falls back to JSONB comparison) + let sql = format!( + "SELECT e FROM encrypted WHERE e->'{}'::text >= '{}'::eql_v2_encrypted", + Selectors::N, + term + ); + + QueryAssertion::new(&pool, &sql).returns_rows().await; + + Ok(()) +} diff --git a/tests/sqlx/tests/constraint_tests.rs b/tests/sqlx/tests/constraint_tests.rs index 15428677..3f6f7f20 100644 --- a/tests/sqlx/tests/constraint_tests.rs +++ b/tests/sqlx/tests/constraint_tests.rs @@ -221,3 +221,169 @@ async fn foreign_key_constraint_with_encrypted(pool: PgPool) -> Result<()> { Ok(()) } + +// ======================================================================== +// EQL-Specific Constraint Tests +// ======================================================================== + +#[sqlx::test(fixtures(path = "../fixtures", scripts("encrypted_json")))] +async fn add_encrypted_constraint_prevents_invalid_data(pool: PgPool) -> Result<()> { + // Test: eql_v2.add_encrypted_constraint() adds validation to encrypted column + + // First, verify that insert without constraint works (even with invalid empty JSONB) + sqlx::query("INSERT INTO encrypted (e) VALUES ('{}'::jsonb::eql_v2_encrypted)") + .execute(&pool) + .await?; + + let count: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM encrypted") + .fetch_one(&pool) + .await?; + + assert_eq!( + count, 4, + "Should have 4 records (3 from fixture + 1 invalid)" + ); + + // Delete the invalid data and reset + sqlx::query("DELETE FROM encrypted WHERE e = '{}'::jsonb::eql_v2_encrypted") + .execute(&pool) + .await?; + + // Add the encrypted constraint + sqlx::query("SELECT eql_v2.add_encrypted_constraint('encrypted', 'e')") + .execute(&pool) + .await?; + + // Now attempt to insert invalid data - should fail + let result = sqlx::query("INSERT INTO encrypted (e) VALUES ('{}'::jsonb::eql_v2_encrypted)") + .execute(&pool) + .await; + + assert!( + result.is_err(), + "Constraint should prevent insert of invalid eql_v2_encrypted (empty JSONB)" + ); + + // Verify count unchanged after failed insert + let final_count: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM encrypted") + .fetch_one(&pool) + .await?; + + assert_eq!( + final_count, 3, + "Should still have 3 records after constraint prevented invalid insert" + ); + + Ok(()) +} + +#[sqlx::test(fixtures(path = "../fixtures", scripts("encrypted_json")))] +async fn remove_encrypted_constraint_allows_invalid_data(pool: PgPool) -> Result<()> { + // Test: eql_v2.remove_encrypted_constraint() removes validation from encrypted column + + // Add the encrypted constraint first + sqlx::query("SELECT eql_v2.add_encrypted_constraint('encrypted', 'e')") + .execute(&pool) + .await?; + + // Verify constraint is working - invalid data should be rejected + let result = sqlx::query("INSERT INTO encrypted (e) VALUES ('{}'::jsonb::eql_v2_encrypted)") + .execute(&pool) + .await; + + assert!( + result.is_err(), + "Constraint should prevent insert of invalid eql_v2_encrypted" + ); + + // Remove the constraint + sqlx::query("SELECT eql_v2.remove_encrypted_constraint('encrypted', 'e')") + .execute(&pool) + .await?; + + // Now invalid data should be allowed + sqlx::query("INSERT INTO encrypted (e) VALUES ('{}'::jsonb::eql_v2_encrypted)") + .execute(&pool) + .await?; + + let count: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM encrypted") + .fetch_one(&pool) + .await?; + + assert_eq!( + count, 4, + "Should have 4 records (3 valid + 1 invalid after constraint removed)" + ); + + Ok(()) +} + +#[sqlx::test(fixtures(path = "../fixtures", scripts("encrypted_json")))] +async fn version_metadata_validation_on_insert(pool: PgPool) -> Result<()> { + // Test: EQL version metadata (v field) is enforced on insert + // + // Note: The SQL test doesn't explicitly add a constraint, which suggests + // version validation is built into the eql_v2_encrypted type itself or + // is enforced automatically. However, for this test we need to ensure + // the constraint exists to validate version fields. + + // Add encrypted constraint to enable version validation + sqlx::query("SELECT eql_v2.add_encrypted_constraint('encrypted', 'e')") + .execute(&pool) + .await?; + + // Create a valid encrypted value with version removed + // We'll get a valid encrypted JSON and remove the 'v' field + let encrypted_without_version: String = + sqlx::query_scalar("SELECT (create_encrypted_json(1)::jsonb - 'v')::text") + .fetch_one(&pool) + .await?; + + // Attempt to insert without version field - should fail + let result = sqlx::query(&format!( + "INSERT INTO encrypted (e) VALUES ('{}'::jsonb::eql_v2_encrypted)", + encrypted_without_version + )) + .execute(&pool) + .await; + + assert!( + result.is_err(), + "Insert should fail when version field is missing" + ); + + // Create encrypted value with invalid version (v=1 instead of v=2) + let encrypted_invalid_version: String = + sqlx::query_scalar("SELECT (create_encrypted_json(1)::jsonb || '{\"v\": 1}')::text") + .fetch_one(&pool) + .await?; + + // Attempt to insert with invalid version - should fail + let result = sqlx::query(&format!( + "INSERT INTO encrypted (e) VALUES ('{}'::jsonb::eql_v2_encrypted)", + encrypted_invalid_version + )) + .execute(&pool) + .await; + + assert!( + result.is_err(), + "Insert should fail when version field is invalid (v=1)" + ); + + // Insert with valid version (v=2) should succeed + sqlx::query("INSERT INTO encrypted (e) VALUES (create_encrypted_json(1))") + .execute(&pool) + .await?; + + let count: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM encrypted") + .fetch_one(&pool) + .await?; + + assert_eq!( + count, 4, + "Should have 4 records after successful insert with valid version" + ); + + Ok(()) +} diff --git a/tests/sqlx/tests/jsonb_path_operators_tests.rs b/tests/sqlx/tests/jsonb_path_operators_tests.rs index 93c0a761..e8875cf3 100644 --- a/tests/sqlx/tests/jsonb_path_operators_tests.rs +++ b/tests/sqlx/tests/jsonb_path_operators_tests.rs @@ -4,6 +4,7 @@ use anyhow::Result; use eql_tests::{QueryAssertion, Selectors}; +use serde_json; use sqlx::{PgPool, Row}; #[sqlx::test(fixtures(path = "../fixtures", scripts("encrypted_json")))] @@ -93,3 +94,136 @@ async fn double_arrow_in_where_clause(pool: PgPool) -> Result<()> { Ok(()) } + +#[sqlx::test(fixtures(path = "../fixtures", scripts("encrypted_json")))] +async fn arrow_operator_returns_metadata_fields(pool: PgPool) -> Result<()> { + // Test: e -> 'selector' returns JSONB with 'i' (index) and 'v' (version) metadata fields. + // This verifies that the arrow operator returns the full encrypted metadata structure, + // not just the value. The metadata includes the index term ('i') and version ('v'). + // + // NOTE: This test uses raw SQLx instead of QueryAssertion because we need to verify + // specific JSONB field presence. QueryAssertion is designed for row count and basic + // value assertions, but doesn't support introspecting JSONB object structure. + + let sql = format!( + "SELECT (e -> '{}'::text)::jsonb FROM encrypted LIMIT 1", + Selectors::N + ); + + let result: serde_json::Value = sqlx::query_scalar(&sql).fetch_one(&pool).await?; + + assert!(result.is_object(), "-> operator should return JSONB object"); + let obj = result + .as_object() + .expect("Result should be a JSONB object after is_object() check"); + assert!( + obj.contains_key("i"), + "Result should contain 'i' (index metadata) field" + ); + assert!( + obj.contains_key("v"), + "Result should contain 'v' (version) field" + ); + + Ok(()) +} + +#[sqlx::test(fixtures(path = "../fixtures", scripts("encrypted_json")))] +async fn ciphertext_function_extracts_from_arrow_result(pool: PgPool) -> Result<()> { + // Test: eql_v2.ciphertext(e -> 'selector') extracts ciphertext value + // + // The ciphertext() function extracts the 'c' field from the encrypted JSONB structure. + // When combined with the -> operator, it allows extracting ciphertext from nested paths. + + let sql = format!( + "SELECT eql_v2.ciphertext(e -> '{}'::text) FROM encrypted LIMIT 1", + Selectors::N + ); + + // Should return ciphertext value (a text string) + QueryAssertion::new(&pool, &sql).returns_rows().await; + + Ok(()) +} + +#[sqlx::test(fixtures(path = "../fixtures", scripts("encrypted_json")))] +async fn ciphertext_function_returns_all_rows(pool: PgPool) -> Result<()> { + // Test: eql_v2.ciphertext() returns ciphertext for all encrypted rows + + let sql = format!( + "SELECT eql_v2.ciphertext(e -> '{}'::text) FROM encrypted", + Selectors::N + ); + + // All 3 records have $.n path, should return 3 ciphertext values + QueryAssertion::new(&pool, &sql).count(3).await; + + Ok(()) +} + +#[sqlx::test(fixtures(path = "../fixtures", scripts("encrypted_json")))] +async fn arrow_operator_with_encrypted_selector(pool: PgPool) -> Result<()> { + // Test: e -> eql_v2_encrypted selector (encrypted selector) + // + // The -> operator can accept an eql_v2_encrypted value as the selector. + // The selector is created from JSONB with structure: {"s": "selector_hash"} + + let encrypted_selector = Selectors::as_encrypted(Selectors::ROOT); + let sql = format!( + "SELECT e -> '{}'::jsonb::eql_v2_encrypted FROM encrypted LIMIT 1", + encrypted_selector + ); + + QueryAssertion::new(&pool, &sql).returns_rows().await; + + Ok(()) +} + +#[sqlx::test(fixtures(path = "../fixtures", scripts("encrypted_json")))] +async fn arrow_operator_with_encrypted_selector_all_rows(pool: PgPool) -> Result<()> { + // Test: e -> eql_v2_encrypted selector returns all matching rows + + let encrypted_selector = Selectors::as_encrypted(Selectors::ROOT); + let sql = format!( + "SELECT e -> '{}'::jsonb::eql_v2_encrypted FROM encrypted", + encrypted_selector + ); + + // All 3 records should have the root selector + QueryAssertion::new(&pool, &sql).count(3).await; + + Ok(()) +} + +#[sqlx::test(fixtures(path = "../fixtures", scripts("encrypted_json")))] +async fn double_arrow_operator_with_encrypted_selector(pool: PgPool) -> Result<()> { + // Test: e ->> eql_v2_encrypted selector (encrypted selector) + // + // The ->> operator can also accept an eql_v2_encrypted value as the selector. + + let encrypted_selector = Selectors::as_encrypted(Selectors::ROOT); + let sql = format!( + "SELECT e ->> '{}'::jsonb::eql_v2_encrypted FROM encrypted LIMIT 1", + encrypted_selector + ); + + QueryAssertion::new(&pool, &sql).returns_rows().await; + + Ok(()) +} + +#[sqlx::test(fixtures(path = "../fixtures", scripts("encrypted_json")))] +async fn double_arrow_operator_with_encrypted_selector_all_rows(pool: PgPool) -> Result<()> { + // Test: e ->> eql_v2_encrypted selector returns all matching rows + + let encrypted_selector = Selectors::as_encrypted(Selectors::ROOT); + let sql = format!( + "SELECT e ->> '{}'::jsonb::eql_v2_encrypted FROM encrypted", + encrypted_selector + ); + + // All 3 records should have the root selector + QueryAssertion::new(&pool, &sql).count(3).await; + + Ok(()) +} diff --git a/tests/sqlx/tests/order_by_tests.rs b/tests/sqlx/tests/order_by_tests.rs index d27b1828..f90ec559 100644 --- a/tests/sqlx/tests/order_by_tests.rs +++ b/tests/sqlx/tests/order_by_tests.rs @@ -118,3 +118,274 @@ async fn order_by_asc_with_greater_than_returns_lowest(pool: PgPool) -> Result<( Ok(()) } + +// NULL ordering tests + +#[sqlx::test] +async fn order_by_asc_nulls_first_returns_null_record_first(pool: PgPool) -> Result<()> { + // Test: ORDER BY e ASC NULLS FIRST returns NULL values first + // Setup: Create table with NULLs and encrypted values + // - ID=1: NULL + // - ID=2: 42 + // - ID=3: 3 + // - ID=4: NULL + // Expected: ORDER BY e ASC NULLS FIRST returns id=1 first + + // Create test table + sqlx::query("CREATE TABLE encrypted(id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, e eql_v2_encrypted)") + .execute(&pool) + .await?; + + // Insert NULL + sqlx::query("INSERT INTO encrypted(e) VALUES (NULL::jsonb::eql_v2_encrypted)") + .execute(&pool) + .await?; + + // Insert id=42 from ore table + let ore_42 = get_ore_encrypted(&pool, 42).await?; + sqlx::query(&format!( + "INSERT INTO encrypted(e) VALUES ('{}'::eql_v2_encrypted)", + ore_42 + )) + .execute(&pool) + .await?; + + // Insert id=3 from ore table + let ore_3 = get_ore_encrypted(&pool, 3).await?; + sqlx::query(&format!( + "INSERT INTO encrypted(e) VALUES ('{}'::eql_v2_encrypted)", + ore_3 + )) + .execute(&pool) + .await?; + + // Insert another NULL + sqlx::query("INSERT INTO encrypted(e) VALUES (NULL::jsonb::eql_v2_encrypted)") + .execute(&pool) + .await?; + + // Test: NULLS FIRST should return a NULL row first + // Use tie-breaker (id) to ensure deterministic ordering among NULL rows + let sql = "SELECT id FROM encrypted ORDER BY e ASC NULLS FIRST, id"; + let row = sqlx::query(sql).fetch_one(&pool).await?; + let first_id: i64 = row.try_get(0)?; + assert_eq!( + first_id, 1, + "ORDER BY e ASC NULLS FIRST, id should return NULL value with lowest id (id=1) first" + ); + + Ok(()) +} + +#[sqlx::test] +async fn order_by_asc_nulls_last_returns_smallest_value_first(pool: PgPool) -> Result<()> { + // Test: ORDER BY e ASC NULLS LAST returns smallest non-NULL value first + // Setup: Same as previous test + // Expected: ORDER BY e ASC NULLS LAST returns id=3 (value=3) first + + // Create test table + sqlx::query("CREATE TABLE encrypted(id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, e eql_v2_encrypted)") + .execute(&pool) + .await?; + + // Insert NULL + sqlx::query("INSERT INTO encrypted(e) VALUES (NULL::jsonb::eql_v2_encrypted)") + .execute(&pool) + .await?; + + // Insert id=42 from ore table + let ore_42 = get_ore_encrypted(&pool, 42).await?; + sqlx::query(&format!( + "INSERT INTO encrypted(e) VALUES ('{}'::eql_v2_encrypted)", + ore_42 + )) + .execute(&pool) + .await?; + + // Insert id=3 from ore table + let ore_3 = get_ore_encrypted(&pool, 3).await?; + sqlx::query(&format!( + "INSERT INTO encrypted(e) VALUES ('{}'::eql_v2_encrypted)", + ore_3 + )) + .execute(&pool) + .await?; + + // Insert another NULL + sqlx::query("INSERT INTO encrypted(e) VALUES (NULL::jsonb::eql_v2_encrypted)") + .execute(&pool) + .await?; + + // Test: NULLS LAST should return id=3 (smallest value) + let sql = "SELECT id FROM encrypted ORDER BY e ASC NULLS LAST"; + let row = sqlx::query(sql).fetch_one(&pool).await?; + let first_id: i64 = row.try_get(0)?; + assert_eq!( + first_id, 3, + "ORDER BY e ASC NULLS LAST should return smallest non-NULL value (id=3) first" + ); + + Ok(()) +} + +#[sqlx::test] +async fn order_by_desc_nulls_first_returns_null_value_first(pool: PgPool) -> Result<()> { + // Test: ORDER BY e DESC NULLS FIRST returns NULL values first + // Expected: ORDER BY e DESC NULLS FIRST returns id=1 first + + // Create test table + sqlx::query("CREATE TABLE encrypted(id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, e eql_v2_encrypted)") + .execute(&pool) + .await?; + + // Insert NULL + sqlx::query("INSERT INTO encrypted(e) VALUES (NULL::jsonb::eql_v2_encrypted)") + .execute(&pool) + .await?; + + // Insert id=42 from ore table + let ore_42 = get_ore_encrypted(&pool, 42).await?; + sqlx::query(&format!( + "INSERT INTO encrypted(e) VALUES ('{}'::eql_v2_encrypted)", + ore_42 + )) + .execute(&pool) + .await?; + + // Insert id=3 from ore table + let ore_3 = get_ore_encrypted(&pool, 3).await?; + sqlx::query(&format!( + "INSERT INTO encrypted(e) VALUES ('{}'::eql_v2_encrypted)", + ore_3 + )) + .execute(&pool) + .await?; + + // Insert another NULL + sqlx::query("INSERT INTO encrypted(e) VALUES (NULL::jsonb::eql_v2_encrypted)") + .execute(&pool) + .await?; + + // Test: DESC NULLS FIRST should return a NULL row first + // Use tie-breaker (id) to ensure deterministic ordering among NULL rows + let sql = "SELECT id FROM encrypted ORDER BY e DESC NULLS FIRST, id"; + let row = sqlx::query(sql).fetch_one(&pool).await?; + let first_id: i64 = row.try_get(0)?; + assert_eq!( + first_id, 1, + "ORDER BY e DESC NULLS FIRST, id should return NULL value with lowest id (id=1) first" + ); + + Ok(()) +} + +#[sqlx::test] +async fn order_by_desc_nulls_last_returns_largest_value_first(pool: PgPool) -> Result<()> { + // Test: ORDER BY e DESC NULLS LAST returns largest non-NULL value first + // Expected: ORDER BY e DESC NULLS LAST returns id=2 (value=42) first + + // Create test table + sqlx::query("CREATE TABLE encrypted(id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, e eql_v2_encrypted)") + .execute(&pool) + .await?; + + // Insert NULL + sqlx::query("INSERT INTO encrypted(e) VALUES (NULL::jsonb::eql_v2_encrypted)") + .execute(&pool) + .await?; + + // Insert id=42 from ore table + let ore_42 = get_ore_encrypted(&pool, 42).await?; + sqlx::query(&format!( + "INSERT INTO encrypted(e) VALUES ('{}'::eql_v2_encrypted)", + ore_42 + )) + .execute(&pool) + .await?; + + // Insert id=3 from ore table + let ore_3 = get_ore_encrypted(&pool, 3).await?; + sqlx::query(&format!( + "INSERT INTO encrypted(e) VALUES ('{}'::eql_v2_encrypted)", + ore_3 + )) + .execute(&pool) + .await?; + + // Insert another NULL + sqlx::query("INSERT INTO encrypted(e) VALUES (NULL::jsonb::eql_v2_encrypted)") + .execute(&pool) + .await?; + + // Test: DESC NULLS LAST should return id=2 (largest value) + let sql = "SELECT id FROM encrypted ORDER BY e DESC NULLS LAST"; + let row = sqlx::query(sql).fetch_one(&pool).await?; + let first_id: i64 = row.try_get(0)?; + assert_eq!( + first_id, 2, + "ORDER BY e DESC NULLS LAST should return largest non-NULL value (id=2) first" + ); + + Ok(()) +} + +// eql_v2.order_by() helper function tests + +#[sqlx::test] +async fn order_by_helper_function_desc_returns_correct_count(pool: PgPool) -> Result<()> { + // Test: ORDER BY eql_v2.order_by(e) DESC with WHERE e < 42 + // Expected: Returns 41 records + + let ore_term = get_ore_encrypted(&pool, 42).await?; + + let sql = format!( + "SELECT id FROM ore WHERE e < '{}'::eql_v2_encrypted ORDER BY eql_v2.order_by(e) DESC", + ore_term + ); + + QueryAssertion::new(&pool, &sql).count(41).await; + + Ok(()) +} + +#[sqlx::test] +async fn order_by_helper_function_desc_returns_highest_value_first(pool: PgPool) -> Result<()> { + // Test: ORDER BY eql_v2.order_by(e) DESC LIMIT 1 returns id=41 + + let ore_term = get_ore_encrypted(&pool, 42).await?; + + let sql = format!( + "SELECT id FROM ore WHERE e < '{}'::eql_v2_encrypted ORDER BY eql_v2.order_by(e) DESC LIMIT 1", + ore_term + ); + + let row = sqlx::query(&sql).fetch_one(&pool).await?; + let id: i64 = row.try_get(0)?; + assert_eq!( + id, 41, + "ORDER BY eql_v2.order_by(e) DESC should return id=41 (highest value < 42) first" + ); + + Ok(()) +} + +#[sqlx::test] +async fn order_by_helper_function_asc_returns_lowest_value_first(pool: PgPool) -> Result<()> { + // Test: ORDER BY eql_v2.order_by(e) ASC LIMIT 1 returns id=1 + + let ore_term = get_ore_encrypted(&pool, 42).await?; + + let sql = format!( + "SELECT id FROM ore WHERE e < '{}'::eql_v2_encrypted ORDER BY eql_v2.order_by(e) ASC LIMIT 1", + ore_term + ); + + let row = sqlx::query(&sql).fetch_one(&pool).await?; + let id: i64 = row.try_get(0)?; + assert_eq!( + id, 1, + "ORDER BY eql_v2.order_by(e) ASC should return id=1 (lowest value < 42) first" + ); + + Ok(()) +}