From 0188985ba47d1e07a8ce935d15961b0ad07ca96f Mon Sep 17 00:00:00 2001 From: Toby Hede Date: Thu, 2 Jan 2025 12:32:56 +1100 Subject: [PATCH 01/32] Add mise tasks --- mise.toml | 19 +++++++++++++++++++ tasks/build.sh | 38 ++++++++++++++++++++++++++++++++++++++ tasks/reset.sh | 10 ++++++++++ tasks/test.sh | 18 ++++++++++++++++++ 4 files changed, 85 insertions(+) create mode 100644 mise.toml create mode 100755 tasks/build.sh create mode 100755 tasks/reset.sh create mode 100755 tasks/test.sh diff --git a/mise.toml b/mise.toml new file mode 100644 index 00000000..efda929d --- /dev/null +++ b/mise.toml @@ -0,0 +1,19 @@ +[settings] +# Config for test environments +# Can be invoked with: mise --env tcp run +# trusted_config_paths = [ +# "./tests/mise.toml", +# "./tests/mise.tcp.toml", +# "./tests/mise.tls.toml", +# ] +[task_config] +includes = ["tasks"] + + +[env] +# Default configuration for running cipherstash-proxy out of the box +CS_DATABASE__NAME = "cipherstash" +CS_DATABASE__USERNAME = "cipherstash" +CS_DATABASE__PASSWORD = "password" +CS_DATABASE__HOST = "localhost" +CS_DATABASE__PORT = "5532" diff --git a/tasks/build.sh b/tasks/build.sh new file mode 100755 index 00000000..ac4e8397 --- /dev/null +++ b/tasks/build.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +#MISE description="Build SQL into single release file" +set -euxo pipefail + +mkdir -p release + +rm -f release/cipherstash-encrypt-uninstall.sql +rm -f release/cipherstash-encrypt.sql + +# Collect all the drops +# In reverse order (tac) so that we drop the constraints before the tables +grep -h -E '^(DROP)' sql/0*-*.sql | tac > release/cipherstash-encrypt-tmp-drop-install.sql +# types are always last +cat sql/666-drop_types.sql >> release/cipherstash-encrypt-tmp-drop-install.sql + + +# Build cipherstash-encrypt.sql +# drop everything first +cat release/cipherstash-encrypt-tmp-drop-install.sql > release/cipherstash-encrypt.sql +# cat the rest of the sql files +cat sql/0*-*.sql >> release/cipherstash-encrypt.sql + +# Collect all the drops +# In reverse order (tac) so that we drop the constraints before the tables +grep -h -E '^(DROP|ALTER DOMAIN [^ ]+ DROP CONSTRAINT)' sql/0*-*.sql | tac > release/cipherstash-encrypt-tmp-drop-uninstall.sql +# types are always last +cat sql/666-drop_types.sql >> release/cipherstash-encrypt-tmp-drop-uninstall.sql + + +# Build cipherstash-encrypt-uninstall.sql +# prepend the drops to the main sql file +cat release/cipherstash-encrypt-tmp-drop-uninstall.sql >> release/cipherstash-encrypt-uninstall.sql +# uninstall renames configuration table +cat sql/666-rename_configuration_table.sql >> release/cipherstash-encrypt-uninstall.sql + +# remove the drop file +rm release/cipherstash-encrypt-tmp-drop-install.sql +rm release/cipherstash-encrypt-tmp-drop-uninstall.sql diff --git a/tasks/reset.sh b/tasks/reset.sh new file mode 100755 index 00000000..c9e95bca --- /dev/null +++ b/tasks/reset.sh @@ -0,0 +1,10 @@ + #!/usr/bin/env bash + #MISE description="Clean install of EQL" + set -euxo pipefail + +# PGPASSWORD=$CS_DATABASE__PASSWORD dropdb --force --if-exists --username ${CS_DATABASE__USERNAME:-$USER} --port $CS_DATABASE__PORT $CS_DATABASE__NAME +# PGPASSWORD=$CS_DATABASE__PASSWORD createdb --username ${CS_DATABASE__USERNAME:-$USER} --port $CS_DATABASE__PORT $CS_DATABASE__NAME + + connection_url=postgresql://${CS_DATABASE__USERNAME:-$USER}:@localhost:$CS_DATABASE__PORT/$CS_DATABASE__NAME + + PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f release/cipherstash-encrypt.sql \ No newline at end of file diff --git a/tasks/test.sh b/tasks/test.sh new file mode 100755 index 00000000..4b9673e2 --- /dev/null +++ b/tasks/test.sh @@ -0,0 +1,18 @@ + #!/usr/bin/env bash + #MISE description="Build, reset and run test" + set -euxo pipefail + + mise run build + mise run reset + + connection_url=postgresql://${CS_DATABASE__USERNAME:-$USER}:@localhost:$CS_DATABASE__PORT/$CS_DATABASE__NAME + + # tests + # PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/core.sql + PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/core-functions.sql + # PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/config.sql + # PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/encryptindex.sql + + # Uninstall + # PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f release/cipherstash-encrypt-uninstall.sql + From cbbbf6e50b9735a5e35b4806214a798a02533938 Mon Sep 17 00:00:00 2001 From: Toby Hede Date: Thu, 2 Jan 2025 12:33:14 +1100 Subject: [PATCH 02/32] Remove just --- .tool-versions | 1 - justfile | 84 -------------------------------------------------- 2 files changed, 85 deletions(-) delete mode 100644 .tool-versions delete mode 100644 justfile diff --git a/.tool-versions b/.tool-versions deleted file mode 100644 index 185ff653..00000000 --- a/.tool-versions +++ /dev/null @@ -1 +0,0 @@ -just 1.36.0 diff --git a/justfile b/justfile deleted file mode 100644 index 7f654628..00000000 --- a/justfile +++ /dev/null @@ -1,84 +0,0 @@ -set dotenv-load -set positional-arguments - - -test: - #!/usr/bin/env bash - set -euxo pipefail - cd "{{justfile_directory()}}" - - just build - just reset - - connection_url=postgresql://${CS_DATABASE__USERNAME:-$USER}:@localhost:$CS_DATABASE__PORT/$CS_DATABASE__NAME - - # tests - PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/core.sql - PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/config.sql - PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/encryptindex.sql - - # Uninstall - PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f release/cipherstash-encrypt-uninstall.sql - - - -build: - #!/usr/bin/env bash - set -euxo pipefail - cd "{{justfile_directory()}}" - - mkdir -p release - - rm -f release/cipherstash-encrypt-uninstall.sql - rm -f release/cipherstash-encrypt.sql - - # Collect all the drops - # In reverse order (tac) so that we drop the constraints before the tables - grep -h -E '^(DROP)' sql/0*-*.sql | tac > release/cipherstash-encrypt-tmp-drop-install.sql - # types are always last - cat sql/666-drop_types.sql >> release/cipherstash-encrypt-tmp-drop-install.sql - - - # Build cipherstash-encrypt.sql - # drop everything first - cat release/cipherstash-encrypt-tmp-drop-install.sql > release/cipherstash-encrypt.sql - # cat the rest of the sql files - cat sql/0*-*.sql >> release/cipherstash-encrypt.sql - - # Collect all the drops - # In reverse order (tac) so that we drop the constraints before the tables - grep -h -E '^(DROP|ALTER DOMAIN [^ ]+ DROP CONSTRAINT)' sql/0*-*.sql | tac > release/cipherstash-encrypt-tmp-drop-uninstall.sql - # types are always last - cat sql/666-drop_types.sql >> release/cipherstash-encrypt-tmp-drop-uninstall.sql - - - # Build cipherstash-encrypt-uninstall.sql - # prepend the drops to the main sql file - cat release/cipherstash-encrypt-tmp-drop-uninstall.sql >> release/cipherstash-encrypt-uninstall.sql - # uninstall renames configuration table - cat sql/666-rename_configuration_table.sql >> release/cipherstash-encrypt-uninstall.sql - - # remove the drop file - rm release/cipherstash-encrypt-tmp-drop-install.sql - rm release/cipherstash-encrypt-tmp-drop-uninstall.sql - - -reset: - #!/usr/bin/env bash - set -euxo pipefail - cd "{{justfile_directory()}}" - - PGPASSWORD=$CS_DATABASE__PASSWORD dropdb --force --if-exists --username ${CS_DATABASE__USERNAME:-$USER} --port $CS_DATABASE__PORT $CS_DATABASE__NAME - PGPASSWORD=$CS_DATABASE__PASSWORD createdb --username ${CS_DATABASE__USERNAME:-$USER} --port $CS_DATABASE__PORT $CS_DATABASE__NAME - - connection_url=postgresql://${CS_DATABASE__USERNAME:-$USER}:@localhost:$CS_DATABASE__PORT/$CS_DATABASE__NAME - - PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f release/cipherstash-encrypt.sql - - -psql: - psql postgresql://$CS_USERNAME:$CS_PASSWORD@localhost:$CS_PORT/$CS_DATABASE__NAME - - -psql_direct: - psql --user $CS_DATABASE__USERNAME --dbname $CS_DATABASE__NAME --port $CS_DATABASE__PORT From a961d9e180aec00517ba0c4648564088f96e096f Mon Sep 17 00:00:00 2001 From: Toby Hede Date: Thu, 2 Jan 2025 14:36:23 +1100 Subject: [PATCH 03/32] Match and unique operators --- sql/010-core-domain-types.sql | 159 ++++++++++++++++ sql/{010-core.sql => 011-core-functions.sql} | 163 +--------------- sql/015-operators-unique.sql | 151 +++++++++++++++ sql/016-operators-match.sql | 105 +++++++++++ tasks/test.sh | 4 +- tests/core.sql | 2 +- tests/operators.sql | 184 +++++++++++++++++++ 7 files changed, 604 insertions(+), 164 deletions(-) create mode 100644 sql/010-core-domain-types.sql rename sql/{010-core.sql => 011-core-functions.sql} (54%) create mode 100644 sql/015-operators-unique.sql create mode 100644 sql/016-operators-match.sql create mode 100644 tests/operators.sql diff --git a/sql/010-core-domain-types.sql b/sql/010-core-domain-types.sql new file mode 100644 index 00000000..8d9ba783 --- /dev/null +++ b/sql/010-core-domain-types.sql @@ -0,0 +1,159 @@ +DROP DOMAIN IF EXISTS cs_match_index_v1; +CREATE DOMAIN cs_match_index_v1 AS smallint[]; + +DROP DOMAIN IF EXISTS cs_unique_index_v1; +CREATE DOMAIN cs_unique_index_v1 AS text; + + +-- cs_encrypted_v1 is a column type and cannot be dropped if in use +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'cs_encrypted_v1') THEN + CREATE DOMAIN cs_encrypted_v1 AS JSONB; + END IF; +END +$$; + + +-- Should include a kind field +DROP FUNCTION IF EXISTS _cs_encrypted_check_k(jsonb); +CREATE FUNCTION _cs_encrypted_check_k(val jsonb) + RETURNS boolean +AS $$ + BEGIN + IF (val->>'k' = ANY('{ct, sv}')) THEN + RETURN true; + END IF; + RAISE 'Invalid kind (%) in Encrypted column. Kind should be one of {ct, sv}', val; + END; +$$ LANGUAGE plpgsql; + + +-- +-- CT payload should include a c field +-- +DROP FUNCTION IF EXISTS _cs_encrypted_check_k_ct(jsonb); +CREATE FUNCTION _cs_encrypted_check_k_ct(val jsonb) + RETURNS boolean +AS $$ + BEGIN + IF (val->>'k' = 'ct') THEN + IF (val ? 'c') THEN + RETURN true; + END IF; + RAISE 'Encrypted column kind (k) of "ct" missing data field (c): %', val; + END IF; + RETURN true; + END; +$$ LANGUAGE plpgsql; + + +-- +-- SV payload should include an sv field +-- +DROP FUNCTION IF EXISTS _cs_encrypted_check_k_sv(jsonb); +CREATE FUNCTION _cs_encrypted_check_k_sv(val jsonb) + RETURNS boolean +AS $$ + BEGIN + IF (val->>'k' = 'sv') THEN + IF (val ? 'sv') THEN + RETURN true; + END IF; + RAISE 'Encrypted column kind (k) of "sv" missing data field (sv): %', val; + END IF; + RETURN true; + END; +$$ LANGUAGE plpgsql; + + +-- Plaintext field should never be present in an encrypted column +DROP FUNCTION IF EXISTS _cs_encrypted_check_p(jsonb); +CREATE FUNCTION _cs_encrypted_check_p(val jsonb) + RETURNS boolean +AS $$ + BEGIN + IF NOT val ? 'p' THEN + RETURN true; + END IF; + RAISE 'Encrypted column includes plaintext (p) field: %', val; + END; +$$ LANGUAGE plpgsql; + +-- Should include an ident field +DROP FUNCTION IF EXISTS _cs_encrypted_check_i(jsonb); +CREATE FUNCTION _cs_encrypted_check_i(val jsonb) + RETURNS boolean +AS $$ + BEGIN + IF val ? 'i' THEN + RETURN true; + END IF; + RAISE 'Encrypted column missing ident (i) field: %', val; + END; +$$ LANGUAGE plpgsql; + +-- Query field should never be present in an encrypted column +DROP FUNCTION IF EXISTS _cs_encrypted_check_q(jsonb); +CREATE FUNCTION _cs_encrypted_check_q(val jsonb) + RETURNS boolean +AS $$ + BEGIN + IF val ? 'q' THEN + RAISE 'Encrypted column includes query (q) field: %', val; + END IF; + RETURN true; + END; +$$ LANGUAGE plpgsql; + +-- Ident field should include table and column +DROP FUNCTION IF EXISTS _cs_encrypted_check_i_ct(jsonb); +CREATE FUNCTION _cs_encrypted_check_i_ct(val jsonb) + RETURNS boolean +AS $$ + BEGIN + IF (val->'i' ?& array['t', 'c']) THEN + RETURN true; + END IF; + RAISE 'Encrypted column ident (i) missing table (t) or column (c) fields: %', val; + END; +$$ LANGUAGE plpgsql; + +-- Should include a version field +DROP FUNCTION IF EXISTS _cs_encrypted_check_v(jsonb); +CREATE FUNCTION _cs_encrypted_check_v(val jsonb) + RETURNS boolean +AS $$ + BEGIN + IF (val ? 'v') THEN + RETURN true; + END IF; + RAISE 'Encrypted column missing version (v) field: %', val; + END; +$$ LANGUAGE plpgsql; + + +DROP FUNCTION IF EXISTS cs_check_encrypted_v1(val jsonb); + +CREATE FUNCTION cs_check_encrypted_v1(val jsonb) + RETURNS BOOLEAN +LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +BEGIN ATOMIC + RETURN ( + _cs_encrypted_check_v(val) AND + _cs_encrypted_check_i(val) AND + _cs_encrypted_check_k(val) AND + _cs_encrypted_check_k_ct(val) AND + _cs_encrypted_check_k_sv(val) AND + _cs_encrypted_check_q(val) AND + _cs_encrypted_check_p(val) + ); +END; + +ALTER DOMAIN cs_encrypted_v1 DROP CONSTRAINT IF EXISTS cs_encrypted_v1_check; + +ALTER DOMAIN cs_encrypted_v1 + ADD CONSTRAINT cs_encrypted_v1_check CHECK ( + cs_check_encrypted_v1(VALUE) +); + diff --git a/sql/010-core.sql b/sql/011-core-functions.sql similarity index 54% rename from sql/010-core.sql rename to sql/011-core-functions.sql index ecf9b6c4..003e6991 100644 --- a/sql/010-core.sql +++ b/sql/011-core-functions.sql @@ -1,164 +1,3 @@ -DROP DOMAIN IF EXISTS cs_match_index_v1; -CREATE DOMAIN cs_match_index_v1 AS smallint[]; - -DROP DOMAIN IF EXISTS cs_unique_index_v1; -CREATE DOMAIN cs_unique_index_v1 AS text; - - --- cs_encrypted_v1 is a column type and cannot be dropped if in use -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'cs_encrypted_v1') THEN - CREATE DOMAIN cs_encrypted_v1 AS JSONB; - END IF; -END -$$; - - --- Should include a kind field -DROP FUNCTION IF EXISTS _cs_encrypted_check_k(jsonb); -CREATE FUNCTION _cs_encrypted_check_k(val jsonb) - RETURNS boolean -AS $$ - BEGIN - IF (val->>'k' = ANY('{ct, sv}')) THEN - RETURN true; - END IF; - RAISE 'Invalid kind (%) in Encrypted column. Kind should be one of {ct, sv}', val; - END; -$$ LANGUAGE plpgsql; - - --- --- CT payload should include a c field --- -DROP FUNCTION IF EXISTS _cs_encrypted_check_k_ct(jsonb); -CREATE FUNCTION _cs_encrypted_check_k_ct(val jsonb) - RETURNS boolean -AS $$ - BEGIN - IF (val->>'k' = 'ct') THEN - IF (val ? 'c') THEN - RETURN true; - END IF; - RAISE 'Encrypted column kind (k) of "ct" missing data field (c): %', val; - END IF; - RETURN true; - END; -$$ LANGUAGE plpgsql; - - --- --- SV payload should include an sv field --- -DROP FUNCTION IF EXISTS _cs_encrypted_check_k_sv(jsonb); -CREATE FUNCTION _cs_encrypted_check_k_sv(val jsonb) - RETURNS boolean -AS $$ - BEGIN - IF (val->>'k' = 'sv') THEN - IF (val ? 'sv') THEN - RETURN true; - END IF; - RAISE 'Encrypted column kind (k) of "sv" missing data field (sv): %', val; - END IF; - RETURN true; - END; -$$ LANGUAGE plpgsql; - - --- Plaintext field should never be present in an encrypted column -DROP FUNCTION IF EXISTS _cs_encrypted_check_p(jsonb); -CREATE FUNCTION _cs_encrypted_check_p(val jsonb) - RETURNS boolean -AS $$ - BEGIN - IF NOT val ? 'p' THEN - RETURN true; - END IF; - RAISE 'Encrypted column includes plaintext (p) field: %', val; - END; -$$ LANGUAGE plpgsql; - --- Should include an ident field -DROP FUNCTION IF EXISTS _cs_encrypted_check_i(jsonb); -CREATE FUNCTION _cs_encrypted_check_i(val jsonb) - RETURNS boolean -AS $$ - BEGIN - IF val ? 'i' THEN - RETURN true; - END IF; - RAISE 'Encrypted column missing ident (i) field: %', val; - END; -$$ LANGUAGE plpgsql; - --- Query field should never be present in an encrypted column -DROP FUNCTION IF EXISTS _cs_encrypted_check_q(jsonb); -CREATE FUNCTION _cs_encrypted_check_q(val jsonb) - RETURNS boolean -AS $$ - BEGIN - IF val ? 'q' THEN - RAISE 'Encrypted column includes query (q) field: %', val; - END IF; - RETURN true; - END; -$$ LANGUAGE plpgsql; - --- Ident field should include table and column -DROP FUNCTION IF EXISTS _cs_encrypted_check_i_ct(jsonb); -CREATE FUNCTION _cs_encrypted_check_i_ct(val jsonb) - RETURNS boolean -AS $$ - BEGIN - IF (val->'i' ?& array['t', 'c']) THEN - RETURN true; - END IF; - RAISE 'Encrypted column ident (i) missing table (t) or column (c) fields: %', val; - END; -$$ LANGUAGE plpgsql; - --- Should include a version field -DROP FUNCTION IF EXISTS _cs_encrypted_check_v(jsonb); -CREATE FUNCTION _cs_encrypted_check_v(val jsonb) - RETURNS boolean -AS $$ - BEGIN - IF (val ? 'v') THEN - RETURN true; - END IF; - RAISE 'Encrypted column missing version (v) field: %', val; - END; -$$ LANGUAGE plpgsql; - - -DROP FUNCTION IF EXISTS cs_check_encrypted_v1(val jsonb); - -CREATE FUNCTION cs_check_encrypted_v1(val jsonb) - RETURNS BOOLEAN -LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE -BEGIN ATOMIC - RETURN ( - _cs_encrypted_check_v(val) AND - _cs_encrypted_check_i(val) AND - _cs_encrypted_check_k(val) AND - _cs_encrypted_check_k_ct(val) AND - _cs_encrypted_check_k_sv(val) AND - _cs_encrypted_check_q(val) AND - _cs_encrypted_check_p(val) - ); -END; - - -ALTER DOMAIN cs_encrypted_v1 DROP CONSTRAINT IF EXISTS cs_encrypted_v1_check; - -ALTER DOMAIN cs_encrypted_v1 - ADD CONSTRAINT cs_encrypted_v1_check CHECK ( - cs_check_encrypted_v1(VALUE) -); - - DROP FUNCTION IF EXISTS cs_ciphertext_v1_v0_0(val jsonb); CREATE FUNCTION cs_ciphertext_v1_v0_0(val jsonb) @@ -256,7 +95,7 @@ BEGIN ATOMIC END; -DROP FUNCTION IF EXISTS cs_unique_v1(val jsonb); +DROP FUNCTION IF EXISTS cs_unique_v1(val jsonb); CREATE FUNCTION cs_unique_v1(val jsonb) RETURNS cs_unique_index_v1 diff --git a/sql/015-operators-unique.sql b/sql/015-operators-unique.sql new file mode 100644 index 00000000..6bb9720e --- /dev/null +++ b/sql/015-operators-unique.sql @@ -0,0 +1,151 @@ +-- Operators for unique comparisons of cs_encrypted_v1 types +-- +-- Support for the following comparisons: +-- +-- cs_encrypted_v1 = cs_encrypted_v1 +-- cs_encrypted_v1 <> cs_encrypted_v1 +-- cs_encrypted_v1 = jsonb +-- cs_encrypted_v1 <> jsonb +-- cs_encrypted_v1 = text +-- cs_encrypted_v1 <> text +-- + +DROP OPERATOR IF EXISTS = (cs_encrypted_v1, cs_encrypted_v1); +DROP FUNCTION IF EXISTS cs_encrypted_eq_v1(a cs_encrypted_v1, b cs_encrypted_v1); + +CREATE FUNCTION cs_encrypted_eq_v1(a cs_encrypted_v1, b cs_encrypted_v1) +RETURNS boolean AS $$ + SELECT cs_unique_v1(a) = cs_unique_v1(b); +$$ LANGUAGE SQL; + +CREATE OPERATOR = ( + PROCEDURE="cs_encrypted_eq_v1", + LEFTARG=cs_encrypted_v1, + RIGHTARG=cs_encrypted_v1, + NEGATOR = <>, + RESTRICT = eqsel, + JOIN = eqjoinsel, + HASHES, + MERGES +); + + +DROP OPERATOR IF EXISTS = (cs_encrypted_v1, cs_unique_index_v1); +DROP FUNCTION IF EXISTS cs_encrypted_eq_v1(a cs_encrypted_v1, b cs_unique_index_v1); + +CREATE FUNCTION cs_encrypted_eq_v1(a cs_encrypted_v1, b cs_unique_index_v1) +RETURNS boolean AS $$ + SELECT cs_unique_v1(a) = b; +$$ LANGUAGE SQL; + +CREATE OPERATOR = ( + PROCEDURE="cs_encrypted_eq_v1", + LEFTARG=cs_encrypted_v1, + RIGHTARG=cs_unique_index_v1, + NEGATOR = <>, + RESTRICT = eqsel, + JOIN = eqjoinsel, + HASHES, + MERGES +); + +DROP OPERATOR IF EXISTS = (cs_unique_index_v1, cs_encrypted_v1); +DROP FUNCTION IF EXISTS cs_encrypted_eq_v1(a cs_unique_index_v1, b cs_encrypted_v1); + +CREATE FUNCTION cs_encrypted_eq_v1(a cs_unique_index_v1, b cs_encrypted_v1) +RETURNS boolean AS $$ + SELECT a = cs_unique_v1(b); +$$ LANGUAGE SQL; + +CREATE OPERATOR =( + PROCEDURE="cs_encrypted_eq_v1", + LEFTARG=cs_unique_index_v1, + RIGHTARG=cs_encrypted_v1, + NEGATOR = <>, + RESTRICT = eqsel, + JOIN = eqjoinsel, + HASHES, + MERGES +); + + + +--- ------------------------------------------------------------ + +DROP OPERATOR IF EXISTS <> (cs_encrypted_v1, cs_encrypted_v1); +DROP FUNCTION IF EXISTS cs_encrypted_neq_v1(a cs_encrypted_v1, b cs_encrypted_v1); + +CREATE FUNCTION cs_encrypted_neq_v1(a cs_encrypted_v1, b cs_encrypted_v1) +RETURNS boolean AS $$ + SELECT cs_unique_v1(a) <> cs_unique_v1(b); +$$ LANGUAGE SQL; + +CREATE OPERATOR <> ( + PROCEDURE="cs_encrypted_neq_v1", + LEFTARG=cs_encrypted_v1, + RIGHTARG=cs_encrypted_v1, + NEGATOR = =, + RESTRICT = eqsel, + JOIN = eqjoinsel, + HASHES, + MERGES +); + + +-- DROP OPERATOR IF EXISTS <> (cs_encrypted_v1, jsonb); +-- DROP FUNCTION IF EXISTS cs_encrypted_neq_v1(a cs_encrypted_v1, b jsonb); + +-- CREATE FUNCTION cs_encrypted_neq_v1(a cs_encrypted_v1, b jsonb) +-- RETURNS boolean AS $$ +-- SELECT cs_unique_v1(a) <> cs_unique_v1(b); +-- $$ LANGUAGE SQL; + +-- CREATE OPERATOR <> ( +-- PROCEDURE="cs_encrypted_neq_v1", +-- LEFTARG=cs_encrypted_v1, +-- RIGHTARG=jsonb, +-- NEGATOR = =, +-- RESTRICT = eqsel, +-- JOIN = eqjoinsel, +-- HASHES, +-- MERGES +-- ); + +DROP OPERATOR IF EXISTS <> (cs_encrypted_v1, cs_unique_index_v1); +DROP FUNCTION IF EXISTS cs_encrypted_neq_v1(a cs_encrypted_v1, b cs_unique_index_v1); + +CREATE FUNCTION cs_encrypted_neq_v1(a cs_encrypted_v1, b cs_unique_index_v1) +RETURNS boolean AS $$ + SELECT cs_unique_v1(a) <> b; +$$ LANGUAGE SQL; + +CREATE OPERATOR <> ( + PROCEDURE="cs_encrypted_neq_v1", + LEFTARG=cs_encrypted_v1, + RIGHTARG=cs_unique_index_v1, + NEGATOR = =, + RESTRICT = eqsel, + JOIN = eqjoinsel, + HASHES, + MERGES +); + + +DROP OPERATOR IF EXISTS <> (cs_unique_index_v1, cs_encrypted_v1); +DROP FUNCTION IF EXISTS cs_encrypted_neq_v1(a cs_unique_index_v1, b cs_encrypted_v1); + +CREATE FUNCTION cs_encrypted_neq_v1(a cs_unique_index_v1, b cs_encrypted_v1) +RETURNS boolean AS $$ + SELECT a <> cs_unique_v1(b); +$$ LANGUAGE SQL; + +CREATE OPERATOR <> ( + PROCEDURE="cs_encrypted_neq_v1", + LEFTARG=cs_unique_index_v1, + RIGHTARG=cs_encrypted_v1, + NEGATOR = =, + RESTRICT = eqsel, + JOIN = eqjoinsel, + HASHES, + MERGES +); diff --git a/sql/016-operators-match.sql b/sql/016-operators-match.sql new file mode 100644 index 00000000..717aadef --- /dev/null +++ b/sql/016-operators-match.sql @@ -0,0 +1,105 @@ +-- Operators for match comparisons of cs_encrypted_v1 types +-- +-- Support for the following comparisons: +-- +-- cs_encrypted_v1 @> cs_encrypted_v1 +-- cs_encrypted_v1 @> jsonb +-- cs_encrypted_v1 @> cs_match_index_v1 +-- + +DROP OPERATOR IF EXISTS @> (cs_encrypted_v1, cs_encrypted_v1); +DROP FUNCTION IF EXISTS cs_encrypted_contains_v1(a cs_encrypted_v1, b cs_encrypted_v1); + +CREATE FUNCTION cs_encrypted_contains_v1(a cs_encrypted_v1, b cs_encrypted_v1) +RETURNS boolean AS $$ + SELECT cs_match_v1(a) @> cs_match_v1(b); +$$ LANGUAGE SQL; + +CREATE OPERATOR @>( + PROCEDURE="cs_encrypted_contains_v1", + LEFTARG=cs_encrypted_v1, + RIGHTARG=cs_encrypted_v1, + RESTRICT = eqsel, + JOIN = eqjoinsel, + HASHES, + MERGES +); + + +DROP OPERATOR IF EXISTS @> (cs_encrypted_v1, cs_match_index_v1); +DROP FUNCTION IF EXISTS cs_encrypted_contains_v1(a cs_encrypted_v1, b cs_match_index_v1); + +CREATE FUNCTION cs_encrypted_contains_v1(a cs_encrypted_v1, b cs_match_index_v1) +RETURNS boolean AS $$ + SELECT cs_match_v1(a) @> b; +$$ LANGUAGE SQL; + +CREATE OPERATOR @>( + PROCEDURE="cs_encrypted_contains_v1", + LEFTARG=cs_encrypted_v1, + RIGHTARG=cs_match_index_v1, + RESTRICT = eqsel, + JOIN = eqjoinsel, + HASHES, + MERGES +); + + +-- ------------------------------------------------------------------------------------ + + +DROP OPERATOR IF EXISTS <@ (cs_encrypted_v1, cs_encrypted_v1); +DROP FUNCTION IF EXISTS cs_encrypted_contained_v1(a cs_encrypted_v1, b cs_encrypted_v1); + +CREATE FUNCTION cs_encrypted_contained_v1(a cs_encrypted_v1, b cs_encrypted_v1) +RETURNS boolean AS $$ + SELECT cs_match_v1(a) <@ cs_match_v1(b); +$$ LANGUAGE SQL; + +CREATE OPERATOR <@( + PROCEDURE="cs_encrypted_contained_v1", + LEFTARG=cs_encrypted_v1, + RIGHTARG=cs_encrypted_v1, + RESTRICT = eqsel, + JOIN = eqjoinsel, + HASHES, + MERGES +); + + +DROP OPERATOR IF EXISTS <@ (cs_match_index_v1, cs_encrypted_v1); +DROP FUNCTION IF EXISTS cs_encrypted_contained_v1(a cs_match_index_v1, b cs_encrypted_v1); + +CREATE FUNCTION cs_encrypted_contained_v1(a cs_match_index_v1, b cs_encrypted_v1) +RETURNS boolean AS $$ + SELECT a <@ cs_match_v1(b); +$$ LANGUAGE SQL; + +CREATE OPERATOR <@ ( + PROCEDURE="cs_encrypted_contained_v1", + LEFTARG=cs_match_index_v1, + RIGHTARG=cs_encrypted_v1, + RESTRICT = eqsel, + JOIN = eqjoinsel, + HASHES, + MERGES +); + + +DROP OPERATOR IF EXISTS <@ (cs_encrypted_v1, cs_match_index_v1); +DROP FUNCTION IF EXISTS cs_encrypted_contained_v1(a cs_encrypted_v1, b cs_match_index_v1); + +CREATE FUNCTION cs_encrypted_contained_v1(a cs_match_index_v1, b cs_encrypted_v1) +RETURNS boolean AS $$ + SELECT cs_match_v1(a) <@ b; +$$ LANGUAGE SQL; + +CREATE OPERATOR <@ ( + PROCEDURE="cs_encrypted_contained_v1", + LEFTARG=cs_encrypted_v1, + RIGHTARG=cs_match_index_v1, + RESTRICT = eqsel, + JOIN = eqjoinsel, + HASHES, + MERGES +); diff --git a/tasks/test.sh b/tasks/test.sh index 4b9673e2..8c56a431 100755 --- a/tasks/test.sh +++ b/tasks/test.sh @@ -8,8 +8,10 @@ connection_url=postgresql://${CS_DATABASE__USERNAME:-$USER}:@localhost:$CS_DATABASE__PORT/$CS_DATABASE__NAME # tests + PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/operators.sql + # PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/core.sql - PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/core-functions.sql + # PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/core-functions.sql # PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/config.sql # PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/encryptindex.sql diff --git a/tests/core.sql b/tests/core.sql index 6349a4d1..ec42e738 100644 --- a/tests/core.sql +++ b/tests/core.sql @@ -12,7 +12,7 @@ CREATE TABLE users TRUNCATE TABLE users; --- no version + INSERT INTO users (name_encrypted) VALUES ( '{ "v": 1, diff --git a/tests/operators.sql b/tests/operators.sql new file mode 100644 index 00000000..3b7b189c --- /dev/null +++ b/tests/operators.sql @@ -0,0 +1,184 @@ +\set ON_ERROR_STOP on + + +-- Create a table with a plaintext column +DROP TABLE IF EXISTS users; +CREATE TABLE users +( + id bigint GENERATED ALWAYS AS IDENTITY, + name_encrypted cs_encrypted_v1, + PRIMARY KEY(id) +); + +TRUNCATE TABLE users; + + +INSERT INTO users (name_encrypted) VALUES ( + '{ + "v": 1, + "k": "ct", + "c": "ciphertext", + "i": { + "t": "users", + "c": "name" + }, + "m": [1, 2, 3], + "u": "unique-text", + "o": ["a"] + }'::jsonb +); + + +-- SELECT id FROM users WHERE ARRAY[1,2]::cs_match_index_v1 <@ name_encrypted; +-- SELECT ARRAY[1,2,3,4]::cs_match_index_v1 <@ ARRAY[1,2,3]::cs_match_index_v1; + + +-- SELECT id FROM users WHERE name_encrypted @> '{"m":[1,2]}'::jsonb; +-- SELECT id FROM users WHERE name_encrypted @> ARRAY[1,2]::smallint[]; +-- SELECT id FROM users WHERE name_encrypted @> ARRAY[1,2]::cs_match_index_v1; + +-- SELECT id FROM users WHERE name_encrypted @> '{ +-- "v": 1, +-- "k": "ct", +-- "c": "ciphertext", +-- "i": { +-- "t": "users", +-- "c": "name" +-- }, +-- "m": [1, 2] +-- }'::cs_encrypted_v1; + + + +-- MATCH @> OPERATORS +DO $$ + BEGIN + -- SANITY CHECK FOR UNIQUE payloads + ASSERT (SELECT EXISTS (SELECT id FROM users WHERE cs_match_v1(name_encrypted) @> cs_match_v1('{"m":[1,2]}'))); + + -- cs_encrypted_v1 = jsonb + ASSERT (SELECT EXISTS (SELECT id FROM users WHERE name_encrypted @> '{"m":[1,2]}'::jsonb)); + + -- cs_encrypted_v1 = text + ASSERT (SELECT EXISTS (SELECT id FROM users WHERE name_encrypted @> ARRAY[1,2]::smallint[])); + ASSERT (SELECT EXISTS (SELECT id FROM users WHERE name_encrypted @> ARRAY[1,2]::cs_match_index_v1)); + + -- cs_encrypted_v1 = cs_encrypted_v1 + ASSERT (SELECT EXISTS (SELECT id FROM users WHERE name_encrypted @> '{ + "v": 1, + "k": "ct", + "c": "ciphertext", + "i": { + "t": "users", + "c": "name" + }, + "m": [1, 2] + }'::cs_encrypted_v1)); + + END; +$$ LANGUAGE plpgsql; + + + +-- MATCH <@ OPERATORS +DO $$ + BEGIN + -- SANITY CHECK FOR UNIQUE payloads + ASSERT (SELECT EXISTS (SELECT id FROM users WHERE cs_match_v1('{"m":[1,2]}') <@ cs_match_v1(name_encrypted))); + + -- cs_encrypted_v1 = jsonb + ASSERT (SELECT EXISTS (SELECT id FROM users WHERE '{"m":[1,2]}'::jsonb <@ name_encrypted)); + + -- cs_encrypted_v1 = text + ASSERT (SELECT EXISTS (SELECT id FROM users WHERE ARRAY[1,2]::smallint[] <@ name_encrypted)); + ASSERT (SELECT EXISTS (SELECT id FROM users WHERE ARRAY[1,2]::cs_match_index_v1 <@ name_encrypted)); + + -- cs_encrypted_v1 = cs_encrypted_v1 + ASSERT (SELECT EXISTS (SELECT id FROM users WHERE '{ + "v": 1, + "k": "ct", + "c": "ciphertext", + "i": { + "t": "users", + "c": "name" + }, + "m": [1, 2] + }'::cs_encrypted_v1 <@ name_encrypted)); + + END; +$$ LANGUAGE plpgsql; + + +-- UNIQUE eq = OPERATORS +DO $$ + BEGIN + -- SANITY CHECK FOR UNIQUE payloads + ASSERT (SELECT EXISTS (SELECT id FROM users WHERE cs_unique_v1(name_encrypted) = cs_unique_v1('{"u":"unique-text"}'))); + + -- cs_encrypted_v1 = jsonb + ASSERT (SELECT EXISTS (SELECT id FROM users WHERE name_encrypted = '{"u":"unique-text"}'::jsonb)); + + -- cs_encrypted_v1 = text + ASSERT (SELECT EXISTS (SELECT id FROM users WHERE name_encrypted = 'unique-text'::text)); + + -- text = cs_encrypted_v1 + ASSERT (SELECT EXISTS (SELECT id FROM users WHERE 'unique-text'::text = name_encrypted)); + + -- cs_encrypted_v1 = cs_encrypted_v1 + ASSERT (SELECT EXISTS (SELECT id FROM users WHERE name_encrypted = '{ + "v": 1, + "k": "ct", + "c": "ciphertext", + "i": { + "t": "users", + "c": "name" + }, + "u": "unique-text" + }'::cs_encrypted_v1)); + + END; +$$ LANGUAGE plpgsql; + +-- UNIQUE inequality <> OPERATORS +DO $$ + BEGIN + -- SANITY CHECK FOR UNIQUE payloads + ASSERT (SELECT EXISTS (SELECT id FROM users WHERE cs_unique_v1(name_encrypted) != cs_unique_v1('{"u":"random-text"}'))); + ASSERT (SELECT EXISTS (SELECT id FROM users WHERE cs_unique_v1(name_encrypted) <> cs_unique_v1('{"u":"random-text"}'))); + + -- cs_encrypted_v1 = jsonb + ASSERT (SELECT EXISTS (SELECT id FROM users WHERE name_encrypted != '{"u":"random-text"}'::jsonb)); + ASSERT (SELECT EXISTS (SELECT id FROM users WHERE name_encrypted <> '{"u":"random-text"}'::jsonb)); + + -- cs_encrypted_v1 = text + ASSERT (SELECT EXISTS (SELECT id FROM users WHERE name_encrypted != 'random-text'::text)); + ASSERT (SELECT EXISTS (SELECT id FROM users WHERE name_encrypted <> 'random-text'::text)); + + -- cs_encrypted_v1 = cs_encrypted_v1 + ASSERT (SELECT EXISTS (SELECT id FROM users WHERE name_encrypted != '{ + "v": 1, + "k": "ct", + "c": "ciphertext", + "i": { + "t": "users", + "c": "name" + }, + "u": "random-text" + }'::cs_encrypted_v1)); + ASSERT (SELECT EXISTS (SELECT id FROM users WHERE name_encrypted <> '{ + "v": 1, + "k": "ct", + "c": "ciphertext", + "i": { + "t": "users", + "c": "name" + }, + "u": "random-text" + }'::cs_encrypted_v1)); + + + + END; +$$ LANGUAGE plpgsql; + + From 6a1b3555657922256d3f9d2279f16eabb224a504 Mon Sep 17 00:00:00 2001 From: Toby Hede Date: Mon, 6 Jan 2025 10:33:52 +1100 Subject: [PATCH 04/32] Tests --- .github/workflows/test-eql.yml | 13 +++++++++---- mise.toml | 10 +++++----- sql/015-operators-unique.sql | 20 -------------------- tasks/test.sh | 11 +++++------ tests/operators.sql | 21 --------------------- 5 files changed, 19 insertions(+), 56 deletions(-) diff --git a/.github/workflows/test-eql.yml b/.github/workflows/test-eql.yml index 34faf19c..769c9e12 100644 --- a/.github/workflows/test-eql.yml +++ b/.github/workflows/test-eql.yml @@ -31,14 +31,19 @@ jobs: postgres-version: [17, 16, 15, 14] env: - CS_DATABASE__PASSWORD: - CS_DATABASE__PORT: 5432 CS_DATABASE__NAME: test + CS_DATABASE__PASSWORD: + CS_DATABASE__PORT: 5532 + steps: - uses: actions/checkout@v4 - - uses: extractions/setup-just@v1 + - uses: jdx/mise-action@v2 + with: + version: 2024.12.11 # [default: latest] mise version to install + install: true # [default: true] run `mise install` + cache: true # [default: true] cache mise using GitHub's cache - uses: ankane/setup-postgres@v1 with: @@ -47,5 +52,5 @@ jobs: - name: Test EQL run: | - just build test + mise run test diff --git a/mise.toml b/mise.toml index efda929d..3ed4cf59 100644 --- a/mise.toml +++ b/mise.toml @@ -12,8 +12,8 @@ includes = ["tasks"] [env] # Default configuration for running cipherstash-proxy out of the box -CS_DATABASE__NAME = "cipherstash" -CS_DATABASE__USERNAME = "cipherstash" -CS_DATABASE__PASSWORD = "password" -CS_DATABASE__HOST = "localhost" -CS_DATABASE__PORT = "5532" +# CS_DATABASE__NAME = "cipherstash" +# CS_DATABASE__USERNAME = "cipherstash" +# CS_DATABASE__PASSWORD = "password" +# CS_DATABASE__HOST = "localhost" +# CS_DATABASE__PORT = "5532" diff --git a/sql/015-operators-unique.sql b/sql/015-operators-unique.sql index 6bb9720e..0d51eed3 100644 --- a/sql/015-operators-unique.sql +++ b/sql/015-operators-unique.sql @@ -91,26 +91,6 @@ CREATE OPERATOR <> ( MERGES ); - --- DROP OPERATOR IF EXISTS <> (cs_encrypted_v1, jsonb); --- DROP FUNCTION IF EXISTS cs_encrypted_neq_v1(a cs_encrypted_v1, b jsonb); - --- CREATE FUNCTION cs_encrypted_neq_v1(a cs_encrypted_v1, b jsonb) --- RETURNS boolean AS $$ --- SELECT cs_unique_v1(a) <> cs_unique_v1(b); --- $$ LANGUAGE SQL; - --- CREATE OPERATOR <> ( --- PROCEDURE="cs_encrypted_neq_v1", --- LEFTARG=cs_encrypted_v1, --- RIGHTARG=jsonb, --- NEGATOR = =, --- RESTRICT = eqsel, --- JOIN = eqjoinsel, --- HASHES, --- MERGES --- ); - DROP OPERATOR IF EXISTS <> (cs_encrypted_v1, cs_unique_index_v1); DROP FUNCTION IF EXISTS cs_encrypted_neq_v1(a cs_encrypted_v1, b cs_unique_index_v1); diff --git a/tasks/test.sh b/tasks/test.sh index 8c56a431..6a14273b 100755 --- a/tasks/test.sh +++ b/tasks/test.sh @@ -8,13 +8,12 @@ connection_url=postgresql://${CS_DATABASE__USERNAME:-$USER}:@localhost:$CS_DATABASE__PORT/$CS_DATABASE__NAME # tests + PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/core.sql + PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/core-functions.sql + PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/config.sql + PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/encryptindex.sql PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/operators.sql - # PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/core.sql - # PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/core-functions.sql - # PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/config.sql - # PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/encryptindex.sql - # Uninstall - # PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f release/cipherstash-encrypt-uninstall.sql + PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f release/cipherstash-encrypt-uninstall.sql diff --git a/tests/operators.sql b/tests/operators.sql index 3b7b189c..1d1d7041 100644 --- a/tests/operators.sql +++ b/tests/operators.sql @@ -12,7 +12,6 @@ CREATE TABLE users TRUNCATE TABLE users; - INSERT INTO users (name_encrypted) VALUES ( '{ "v": 1, @@ -29,26 +28,6 @@ INSERT INTO users (name_encrypted) VALUES ( ); --- SELECT id FROM users WHERE ARRAY[1,2]::cs_match_index_v1 <@ name_encrypted; --- SELECT ARRAY[1,2,3,4]::cs_match_index_v1 <@ ARRAY[1,2,3]::cs_match_index_v1; - - --- SELECT id FROM users WHERE name_encrypted @> '{"m":[1,2]}'::jsonb; --- SELECT id FROM users WHERE name_encrypted @> ARRAY[1,2]::smallint[]; --- SELECT id FROM users WHERE name_encrypted @> ARRAY[1,2]::cs_match_index_v1; - --- SELECT id FROM users WHERE name_encrypted @> '{ --- "v": 1, --- "k": "ct", --- "c": "ciphertext", --- "i": { --- "t": "users", --- "c": "name" --- }, --- "m": [1, 2] --- }'::cs_encrypted_v1; - - -- MATCH @> OPERATORS DO $$ From 0d1f086cdb56d7e405db9ac79d3e4984225afaba Mon Sep 17 00:00:00 2001 From: Toby Hede Date: Thu, 9 Jan 2025 16:26:55 +1100 Subject: [PATCH 05/32] update mise version and add bash shebang to tasks --- .github/workflows/test-eql.yml | 4 +++- tasks/build.sh | 3 +++ tasks/reset.sh | 6 +++--- tasks/test.sh | 3 +++ 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-eql.yml b/.github/workflows/test-eql.yml index 769c9e12..43b36227 100644 --- a/.github/workflows/test-eql.yml +++ b/.github/workflows/test-eql.yml @@ -41,7 +41,7 @@ jobs: - uses: jdx/mise-action@v2 with: - version: 2024.12.11 # [default: latest] mise version to install + version: 2025.1.0 # [default: latest] mise version to install install: true # [default: true] run `mise install` cache: true # [default: true] cache mise using GitHub's cache @@ -54,3 +54,5 @@ jobs: run: | mise run test + + diff --git a/tasks/build.sh b/tasks/build.sh index ac4e8397..b8c38073 100755 --- a/tasks/build.sh +++ b/tasks/build.sh @@ -1,5 +1,8 @@ #!/usr/bin/env bash #MISE description="Build SQL into single release file" + +#!/bin/bash + set -euxo pipefail mkdir -p release diff --git a/tasks/reset.sh b/tasks/reset.sh index c9e95bca..df02eb39 100755 --- a/tasks/reset.sh +++ b/tasks/reset.sh @@ -1,9 +1,9 @@ #!/usr/bin/env bash #MISE description="Clean install of EQL" - set -euxo pipefail -# PGPASSWORD=$CS_DATABASE__PASSWORD dropdb --force --if-exists --username ${CS_DATABASE__USERNAME:-$USER} --port $CS_DATABASE__PORT $CS_DATABASE__NAME -# PGPASSWORD=$CS_DATABASE__PASSWORD createdb --username ${CS_DATABASE__USERNAME:-$USER} --port $CS_DATABASE__PORT $CS_DATABASE__NAME + #!/bin/bash + + set -euxo pipefail connection_url=postgresql://${CS_DATABASE__USERNAME:-$USER}:@localhost:$CS_DATABASE__PORT/$CS_DATABASE__NAME diff --git a/tasks/test.sh b/tasks/test.sh index 6a14273b..7cc0a6bf 100755 --- a/tasks/test.sh +++ b/tasks/test.sh @@ -1,5 +1,8 @@ #!/usr/bin/env bash #MISE description="Build, reset and run test" + + #!/bin/bash + set -euxo pipefail mise run build From 578d82bd681421c1196031a7a8ae7488938d0af8 Mon Sep 17 00:00:00 2001 From: Toby Hede Date: Thu, 9 Jan 2025 16:56:12 +1100 Subject: [PATCH 06/32] Allow operators for jsonb on one side of encrypt --- sql/015-operators-unique.sql | 79 ++++++++++++++++++++++++++++++++++++ tasks/reset.sh | 3 ++ tasks/test.sh | 12 +++--- tests/config.sql | 2 +- tests/operators.sql | 74 +++++++++++++++++++++------------ 5 files changed, 136 insertions(+), 34 deletions(-) diff --git a/sql/015-operators-unique.sql b/sql/015-operators-unique.sql index 0d51eed3..1111692e 100644 --- a/sql/015-operators-unique.sql +++ b/sql/015-operators-unique.sql @@ -29,6 +29,44 @@ CREATE OPERATOR = ( MERGES ); +DROP OPERATOR IF EXISTS = (cs_encrypted_v1, jsonb); +DROP FUNCTION IF EXISTS cs_encrypted_eq_v1(a cs_encrypted_v1, b jsonb); + +CREATE FUNCTION cs_encrypted_eq_v1(a cs_encrypted_v1, b jsonb) +RETURNS boolean AS $$ + SELECT cs_unique_v1(a) = cs_unique_v1(b); +$$ LANGUAGE SQL; + +CREATE OPERATOR = ( + PROCEDURE="cs_encrypted_eq_v1", + LEFTARG=cs_encrypted_v1, + RIGHTARG=jsonb, + NEGATOR = <>, + RESTRICT = eqsel, + JOIN = eqjoinsel, + HASHES, + MERGES +); + +DROP OPERATOR IF EXISTS = (jsonb, cs_encrypted_v1); +DROP FUNCTION IF EXISTS cs_encrypted_eq_v1(a jsonb, b cs_encrypted_v1); + +CREATE FUNCTION cs_encrypted_eq_v1(a jsonb, b cs_encrypted_v1) +RETURNS boolean AS $$ + SELECT cs_unique_v1(a) = cs_unique_v1(b); +$$ LANGUAGE SQL; + +CREATE OPERATOR = ( + PROCEDURE="cs_encrypted_eq_v1", + LEFTARG=jsonb, + RIGHTARG=cs_encrypted_v1, + NEGATOR = <>, + RESTRICT = eqsel, + JOIN = eqjoinsel, + HASHES, + MERGES +); + DROP OPERATOR IF EXISTS = (cs_encrypted_v1, cs_unique_index_v1); DROP FUNCTION IF EXISTS cs_encrypted_eq_v1(a cs_encrypted_v1, b cs_unique_index_v1); @@ -91,6 +129,47 @@ CREATE OPERATOR <> ( MERGES ); + +DROP OPERATOR IF EXISTS <> (cs_encrypted_v1, jsonb); +DROP FUNCTION IF EXISTS cs_encrypted_neq_v1(a cs_encrypted_v1, b jsonb); + +CREATE FUNCTION cs_encrypted_neq_v1(a cs_encrypted_v1, b jsonb) +RETURNS boolean AS $$ + SELECT cs_unique_v1(a) <> cs_unique_v1(b); +$$ LANGUAGE SQL; + +CREATE OPERATOR <> ( + PROCEDURE="cs_encrypted_neq_v1", + LEFTARG=cs_encrypted_v1, + RIGHTARG=jsonb, + NEGATOR = =, + RESTRICT = eqsel, + JOIN = eqjoinsel, + HASHES, + MERGES +); + + +DROP OPERATOR IF EXISTS <> (jsonb, cs_encrypted_v1); +DROP FUNCTION IF EXISTS cs_encrypted_neq_v1(a jsonb, b cs_encrypted_v1); + +CREATE FUNCTION cs_encrypted_neq_v1(a jsonb, b cs_encrypted_v1) +RETURNS boolean AS $$ + SELECT cs_unique_v1(a) <> cs_unique_v1(b); +$$ LANGUAGE SQL; + +CREATE OPERATOR <> ( + PROCEDURE="cs_encrypted_neq_v1", + LEFTARG=jsonb, + RIGHTARG=cs_encrypted_v1, + NEGATOR = =, + RESTRICT = eqsel, + JOIN = eqjoinsel, + HASHES, + MERGES +); + + DROP OPERATOR IF EXISTS <> (cs_encrypted_v1, cs_unique_index_v1); DROP FUNCTION IF EXISTS cs_encrypted_neq_v1(a cs_encrypted_v1, b cs_unique_index_v1); diff --git a/tasks/reset.sh b/tasks/reset.sh index df02eb39..6e014fa6 100755 --- a/tasks/reset.sh +++ b/tasks/reset.sh @@ -7,4 +7,7 @@ connection_url=postgresql://${CS_DATABASE__USERNAME:-$USER}:@localhost:$CS_DATABASE__PORT/$CS_DATABASE__NAME + # Uninstall + PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f release/cipherstash-encrypt-uninstall.sql + PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f release/cipherstash-encrypt.sql \ No newline at end of file diff --git a/tasks/test.sh b/tasks/test.sh index 7cc0a6bf..a55c11d8 100755 --- a/tasks/test.sh +++ b/tasks/test.sh @@ -10,13 +10,11 @@ connection_url=postgresql://${CS_DATABASE__USERNAME:-$USER}:@localhost:$CS_DATABASE__PORT/$CS_DATABASE__NAME - # tests - PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/core.sql - PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/core-functions.sql - PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/config.sql - PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/encryptindex.sql + # # tests + # PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/core.sql + # PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/core-functions.sql + # PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/config.sql + # PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/encryptindex.sql PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/operators.sql - # Uninstall - PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f release/cipherstash-encrypt-uninstall.sql diff --git a/tests/config.sql b/tests/config.sql index 8274935a..e165c76b 100644 --- a/tests/config.sql +++ b/tests/config.sql @@ -6,7 +6,7 @@ -- -- Helper function for assertions -- -DROP FUNCTION IF EXISTS _index_exists(text, text, text); +DROP FUNCTION IF EXISTS _index_exists(text, text, text, text); CREATE FUNCTION _index_exists(table_name text, column_name text, index_name text, state text DEFAULT 'pending') RETURNS boolean LANGUAGE sql STRICT PARALLEL SAFE diff --git a/tests/operators.sql b/tests/operators.sql index 1d1d7041..45bc51f1 100644 --- a/tests/operators.sql +++ b/tests/operators.sql @@ -88,17 +88,39 @@ DO $$ $$ LANGUAGE plpgsql; + -- UNIQUE eq = OPERATORS DO $$ BEGIN -- SANITY CHECK FOR UNIQUE payloads ASSERT (SELECT EXISTS (SELECT id FROM users WHERE cs_unique_v1(name_encrypted) = cs_unique_v1('{"u":"unique-text"}'))); + ASSERT (SELECT EXISTS ( + SELECT id FROM users WHERE name_encrypted = '{ + "v": 1, + "k": "ct", + "c": "ciphertext", + "i": { + "t": "users", + "c": "name" + }, + "u": "unique-text" + }'::jsonb + )); + -- cs_encrypted_v1 = jsonb - ASSERT (SELECT EXISTS (SELECT id FROM users WHERE name_encrypted = '{"u":"unique-text"}'::jsonb)); + ASSERT (SELECT EXISTS ( + SELECT id FROM users WHERE name_encrypted = '{"u": "unique-text"}'::jsonb + )); + + -- jsonb = cs_encrypted_v1 + ASSERT (SELECT EXISTS ( + SELECT id FROM users WHERE '{"u": "unique-text"}'::jsonb = name_encrypted + )); -- cs_encrypted_v1 = text ASSERT (SELECT EXISTS (SELECT id FROM users WHERE name_encrypted = 'unique-text'::text)); + ASSERT (SELECT EXISTS (SELECT id FROM users WHERE name_encrypted = 'unique-text'::cs_unique_index_v1)); -- text = cs_encrypted_v1 ASSERT (SELECT EXISTS (SELECT id FROM users WHERE 'unique-text'::text = name_encrypted)); @@ -129,31 +151,31 @@ DO $$ ASSERT (SELECT EXISTS (SELECT id FROM users WHERE name_encrypted != '{"u":"random-text"}'::jsonb)); ASSERT (SELECT EXISTS (SELECT id FROM users WHERE name_encrypted <> '{"u":"random-text"}'::jsonb)); - -- cs_encrypted_v1 = text - ASSERT (SELECT EXISTS (SELECT id FROM users WHERE name_encrypted != 'random-text'::text)); - ASSERT (SELECT EXISTS (SELECT id FROM users WHERE name_encrypted <> 'random-text'::text)); - - -- cs_encrypted_v1 = cs_encrypted_v1 - ASSERT (SELECT EXISTS (SELECT id FROM users WHERE name_encrypted != '{ - "v": 1, - "k": "ct", - "c": "ciphertext", - "i": { - "t": "users", - "c": "name" - }, - "u": "random-text" - }'::cs_encrypted_v1)); - ASSERT (SELECT EXISTS (SELECT id FROM users WHERE name_encrypted <> '{ - "v": 1, - "k": "ct", - "c": "ciphertext", - "i": { - "t": "users", - "c": "name" - }, - "u": "random-text" - }'::cs_encrypted_v1)); + -- -- cs_encrypted_v1 = text + -- ASSERT (SELECT EXISTS (SELECT id FROM users WHERE name_encrypted != 'random-text'::text)); + -- ASSERT (SELECT EXISTS (SELECT id FROM users WHERE name_encrypted <> 'random-text'::text)); + + -- -- cs_encrypted_v1 = cs_encrypted_v1 + -- ASSERT (SELECT EXISTS (SELECT id FROM users WHERE name_encrypted != '{ + -- "v": 1, + -- "k": "ct", + -- "c": "ciphertext", + -- "i": { + -- "t": "users", + -- "c": "name" + -- }, + -- "u": "random-text" + -- }'::cs_encrypted_v1)); + -- ASSERT (SELECT EXISTS (SELECT id FROM users WHERE name_encrypted <> '{ + -- "v": 1, + -- "k": "ct", + -- "c": "ciphertext", + -- "i": { + -- "t": "users", + -- "c": "name" + -- }, + -- "u": "random-text" + -- }'::cs_encrypted_v1)); From 64ab437e59d7ee3b7373c696916c56e0e5a6ca70 Mon Sep 17 00:00:00 2001 From: Toby Hede Date: Thu, 9 Jan 2025 17:14:51 +1100 Subject: [PATCH 07/32] Maybe? --- .github/workflows/test-eql.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-eql.yml b/.github/workflows/test-eql.yml index 43b36227..79b4f611 100644 --- a/.github/workflows/test-eql.yml +++ b/.github/workflows/test-eql.yml @@ -23,7 +23,7 @@ defaults: jobs: test: name: "Test EQL SQL components" - runs-on: ubuntu-24.04 + runs-on: buildjet-16vcpu-ubuntu-2204 strategy: fail-fast: false From aa79ca14e83bb0d76d9707a61cebab905c6fbf8d Mon Sep 17 00:00:00 2001 From: Lindsay Holmwood Date: Thu, 9 Jan 2025 17:41:53 +1100 Subject: [PATCH 08/32] Use an available runner --- .github/workflows/test-eql.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-eql.yml b/.github/workflows/test-eql.yml index 79b4f611..9d354d97 100644 --- a/.github/workflows/test-eql.yml +++ b/.github/workflows/test-eql.yml @@ -23,7 +23,7 @@ defaults: jobs: test: name: "Test EQL SQL components" - runs-on: buildjet-16vcpu-ubuntu-2204 + runs-on: ubuntu-latest-m strategy: fail-fast: false From 354b62804dfadadc83a355f01f5ac2686980c79f Mon Sep 17 00:00:00 2001 From: Lindsay Holmwood Date: Fri, 10 Jan 2025 12:18:43 +1100 Subject: [PATCH 09/32] Remove leading whitespace that was causing shebang to fail --- tasks/reset.sh | 16 +++++++--------- tasks/test.sh | 28 +++++++++++++--------------- 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/tasks/reset.sh b/tasks/reset.sh index 6e014fa6..8f6d142b 100755 --- a/tasks/reset.sh +++ b/tasks/reset.sh @@ -1,13 +1,11 @@ - #!/usr/bin/env bash - #MISE description="Clean install of EQL" +#!/bin/bash +#MISE description="Clean install of EQL" - #!/bin/bash +set -euxo pipefail - set -euxo pipefail +connection_url=postgresql://${CS_DATABASE__USERNAME:-$USER}:@localhost:$CS_DATABASE__PORT/$CS_DATABASE__NAME - connection_url=postgresql://${CS_DATABASE__USERNAME:-$USER}:@localhost:$CS_DATABASE__PORT/$CS_DATABASE__NAME +# Uninstall +PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f release/cipherstash-encrypt-uninstall.sql - # Uninstall - PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f release/cipherstash-encrypt-uninstall.sql - - PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f release/cipherstash-encrypt.sql \ No newline at end of file +PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f release/cipherstash-encrypt.sql diff --git a/tasks/test.sh b/tasks/test.sh index a55c11d8..c20a3e80 100755 --- a/tasks/test.sh +++ b/tasks/test.sh @@ -1,20 +1,18 @@ - #!/usr/bin/env bash - #MISE description="Build, reset and run test" +#!/usr/bin/env bash +#MISE description="Build, reset and run test" - #!/bin/bash +#!/bin/bash - set -euxo pipefail +set -euxo pipefail - mise run build - mise run reset - - connection_url=postgresql://${CS_DATABASE__USERNAME:-$USER}:@localhost:$CS_DATABASE__PORT/$CS_DATABASE__NAME - - # # tests - # PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/core.sql - # PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/core-functions.sql - # PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/config.sql - # PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/encryptindex.sql - PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/operators.sql +mise run build +mise run reset +connection_url=postgresql://${CS_DATABASE__USERNAME:-$USER}:@localhost:$CS_DATABASE__PORT/$CS_DATABASE__NAME +# # tests +# PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/core.sql +# PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/core-functions.sql +# PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/config.sql +# PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/encryptindex.sql +PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/operators.sql From b5e3367e88ba9aa1888d4fef9a7d4c705298e0dc Mon Sep 17 00:00:00 2001 From: Toby Hede Date: Fri, 10 Jan 2025 12:29:38 +1100 Subject: [PATCH 10/32] DB credentials --- .github/workflows/test-eql.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-eql.yml b/.github/workflows/test-eql.yml index 9d354d97..ec610453 100644 --- a/.github/workflows/test-eql.yml +++ b/.github/workflows/test-eql.yml @@ -31,9 +31,10 @@ jobs: postgres-version: [17, 16, 15, 14] env: + CS_DATABASE__USERNAME: ${USER} CS_DATABASE__NAME: test CS_DATABASE__PASSWORD: - CS_DATABASE__PORT: 5532 + CS_DATABASE__PORT: 5432 steps: @@ -50,6 +51,7 @@ jobs: postgres-version: ${{ matrix.postgres-version }} database: ${{ env.CS_DATABASE__NAME }} + - name: Test EQL run: | mise run test From f951c459db80d6fc655069a358a5789fbd643ed4 Mon Sep 17 00:00:00 2001 From: Lindsay Holmwood Date: Fri, 10 Jan 2025 17:00:45 +1100 Subject: [PATCH 11/32] Only build if the sources haven't changed --- tasks/build.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tasks/build.sh b/tasks/build.sh index b8c38073..62a9ded1 100755 --- a/tasks/build.sh +++ b/tasks/build.sh @@ -1,5 +1,7 @@ #!/usr/bin/env bash #MISE description="Build SQL into single release file" +#MISE sources=["sql/*.sql"] +#MISE outputs=["release/cipherstash-encrypt.sql","release/cipherstash-encrypt-uninstall.sql"] #!/bin/bash From 70a8b9585364deb8afdaa2fe64c28b235d5d132f Mon Sep 17 00:00:00 2001 From: Lindsay Holmwood Date: Fri, 10 Jan 2025 17:06:08 +1100 Subject: [PATCH 12/32] Set the password in the connection string To keep it all in one place --- tasks/reset.sh | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tasks/reset.sh b/tasks/reset.sh index 8f6d142b..690ad1b9 100755 --- a/tasks/reset.sh +++ b/tasks/reset.sh @@ -1,11 +1,12 @@ #!/bin/bash -#MISE description="Clean install of EQL" +#MISE description="Uninstall and install EQL to local postgres" set -euxo pipefail -connection_url=postgresql://${CS_DATABASE__USERNAME:-$USER}:@localhost:$CS_DATABASE__PORT/$CS_DATABASE__NAME +connection_url=postgresql://${CS_DATABASE__USERNAME:-$USER}:${CS_DATABASE__PASSWORD}@localhost:$CS_DATABASE__PORT/$CS_DATABASE__NAME # Uninstall -PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f release/cipherstash-encrypt-uninstall.sql +psql ${connection_url} -f release/cipherstash-encrypt-uninstall.sql -PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f release/cipherstash-encrypt.sql +# Install +psql ${connection_url} -f release/cipherstash-encrypt.sql From 1cb965e33e222e03b16c3c7e0b3e2f990ea2dc51 Mon Sep 17 00:00:00 2001 From: Lindsay Holmwood Date: Mon, 13 Jan 2025 16:38:27 +1100 Subject: [PATCH 13/32] Add docker compose to run different postgres instances --- tests/docker-compose.yml | 50 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 tests/docker-compose.yml diff --git a/tests/docker-compose.yml b/tests/docker-compose.yml new file mode 100644 index 00000000..8cfb1372 --- /dev/null +++ b/tests/docker-compose.yml @@ -0,0 +1,50 @@ +services: + postgres: &postgres + container_name: postgres + image: postgres + ports: + - 5532:5532 + environment: + - PGPORT=5532 + - PGUSER=${POSTGRES_USERNAME} + - POSTGRES_DB=${POSTGRES_DB} + - POSTGRES_USER=${POSTGRES_USER} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + networks: + - postgres + deploy: + resources: + limits: + cpus: "${CPU_LIMIT:-2}" + memory: 2048mb + restart: always + healthcheck: + test: ["CMD-SHELL", "pg_isready"] + interval: 1s + timeout: 5s + retries: 10 + + postgres-17: + <<: *postgres + image: postgres:17 + container_name: postgres-17 + + postgres-16: + <<: *postgres + image: postgres:16 + container_name: postgres-16 + + postgres-15: + <<: *postgres + image: postgres:15 + container_name: postgres-15 + + postgres-14: + <<: *postgres + image: postgres:14 + container_name: postgres-14 + +networks: + postgres: + driver: bridge + From 6e6c733297035c9fa0f46f34401eeac30faa0ca4 Mon Sep 17 00:00:00 2001 From: Lindsay Holmwood Date: Mon, 13 Jan 2025 15:21:50 +1100 Subject: [PATCH 14/32] Add tasks for starting and stopping Postgres containers --- mise.toml | 6 ++++-- tasks/postgres.toml | 12 ++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 tasks/postgres.toml diff --git a/mise.toml b/mise.toml index 3ed4cf59..35ab591b 100644 --- a/mise.toml +++ b/mise.toml @@ -7,8 +7,10 @@ # "./tests/mise.tls.toml", # ] [task_config] -includes = ["tasks"] - +includes = [ + "tasks", + "tasks/postgres.toml" +] [env] # Default configuration for running cipherstash-proxy out of the box diff --git a/tasks/postgres.toml b/tasks/postgres.toml new file mode 100644 index 00000000..5df6d898 --- /dev/null +++ b/tasks/postgres.toml @@ -0,0 +1,12 @@ +["postgres:down"] +description = "Tear down Postgres containers" +dir = "{{config_root}}/tests" +run = "docker compose down" + +["postgres:up"] +description = "Run Postgres instances with docker compose" +dir = "{{config_root}}/tests" +run = """ +{% set default_service = "postgres-" ~ get_env(name="POSTGRES_VERSION",default="17") %} +echo docker compose up {{arg(name="service",default=default_service)}} {{option(name="extra-args",default="")}} | bash +""" From 32049f8894e614e46e26d6b58728374721f2a27f Mon Sep 17 00:00:00 2001 From: Lindsay Holmwood Date: Mon, 13 Jan 2025 16:32:53 +1100 Subject: [PATCH 15/32] Run psql inside containers And use idiomatic postgres environment variable names --- mise.toml | 11 +++++------ tasks/reset.sh | 7 ++++--- tasks/test.sh | 5 +++-- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/mise.toml b/mise.toml index 35ab591b..54398e9f 100644 --- a/mise.toml +++ b/mise.toml @@ -13,9 +13,8 @@ includes = [ ] [env] -# Default configuration for running cipherstash-proxy out of the box -# CS_DATABASE__NAME = "cipherstash" -# CS_DATABASE__USERNAME = "cipherstash" -# CS_DATABASE__PASSWORD = "password" -# CS_DATABASE__HOST = "localhost" -# CS_DATABASE__PORT = "5532" +POSTGRES_DB = "cipherstash" +POSTGRES_USER = "cipherstash" +POSTGRES_PASSWORD = "password" +POSTGRES_HOST = "localhost" +POSTGRES_PORT = "5532" diff --git a/tasks/reset.sh b/tasks/reset.sh index 690ad1b9..6dba2042 100755 --- a/tasks/reset.sh +++ b/tasks/reset.sh @@ -3,10 +3,11 @@ set -euxo pipefail -connection_url=postgresql://${CS_DATABASE__USERNAME:-$USER}:${CS_DATABASE__PASSWORD}@localhost:$CS_DATABASE__PORT/$CS_DATABASE__NAME +connection_url=postgresql://${POSTGRES_USER:-$USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} +container_name=postgres-${POSTGRES_VERSION} # Uninstall -psql ${connection_url} -f release/cipherstash-encrypt-uninstall.sql +cat release/cipherstash-encrypt-uninstall.sql | docker exec -i ${container_name} psql ${connection_url} -f- # Install -psql ${connection_url} -f release/cipherstash-encrypt.sql +cat release/cipherstash-encrypt.sql | docker exec -i ${container_name} psql ${connection_url} -f- diff --git a/tasks/test.sh b/tasks/test.sh index c20a3e80..6bf55c8a 100755 --- a/tasks/test.sh +++ b/tasks/test.sh @@ -8,11 +8,12 @@ set -euxo pipefail mise run build mise run reset -connection_url=postgresql://${CS_DATABASE__USERNAME:-$USER}:@localhost:$CS_DATABASE__PORT/$CS_DATABASE__NAME +connection_url=postgresql://${POSTGRES_USER:-$USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} +container_name=postgres-${POSTGRES_VERSION} # # tests # PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/core.sql # PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/core-functions.sql # PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/config.sql # PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/encryptindex.sql -PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/operators.sql +cat tests/operators.sql | docker exec -i ${container_name} psql ${connection_url} -f- From cb5b3cceaf60e3078e9620e63b9764bcfc3f31d0 Mon Sep 17 00:00:00 2001 From: Lindsay Holmwood Date: Mon, 13 Jan 2025 16:35:54 +1100 Subject: [PATCH 16/32] Run postgres in containers in each job Make it possible to run the tests locally --- .github/workflows/test-eql.yml | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/.github/workflows/test-eql.yml b/.github/workflows/test-eql.yml index ec610453..1c0c04b7 100644 --- a/.github/workflows/test-eql.yml +++ b/.github/workflows/test-eql.yml @@ -31,11 +31,7 @@ jobs: postgres-version: [17, 16, 15, 14] env: - CS_DATABASE__USERNAME: ${USER} - CS_DATABASE__NAME: test - CS_DATABASE__PASSWORD: - CS_DATABASE__PORT: 5432 - + POSTGRES_VERSION: ${{ matrix.postgres-version }} steps: - uses: actions/checkout@v4 @@ -46,15 +42,10 @@ jobs: install: true # [default: true] run `mise install` cache: true # [default: true] cache mise using GitHub's cache - - uses: ankane/setup-postgres@v1 - with: - postgres-version: ${{ matrix.postgres-version }} - database: ${{ env.CS_DATABASE__NAME }} - + - name: Setup database + run: | + mise run postgres:up postgres-${POSTGRES_VERSION} --extra-args "--detach --wait" - name: Test EQL run: | - mise run test - - - + mise run --output prefix test From 3bce22e70aca4f4fdc966aab96a7f4867cf31c08 Mon Sep 17 00:00:00 2001 From: Lindsay Holmwood Date: Mon, 13 Jan 2025 16:36:33 +1100 Subject: [PATCH 17/32] Check if config is correct before running --- tasks/test.sh | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tasks/test.sh b/tasks/test.sh index 6bf55c8a..920862f0 100755 --- a/tasks/test.sh +++ b/tasks/test.sh @@ -3,7 +3,18 @@ #!/bin/bash -set -euxo pipefail +set -eo pipefail + +if [ -z "${POSTGRES_VERSION}" ]; then + echo "error: POSTGRES_VERSION not set" + echo "Please re-run with a version set:" + echo + echo "POSTGRES_VERSION=16 mise run test" + echo + exit 1 +fi + +set -ux mise run build mise run reset From b60e2125ebd25611e4300f42e06d827e67179685 Mon Sep 17 00:00:00 2001 From: Lindsay Holmwood Date: Mon, 13 Jan 2025 17:03:34 +1100 Subject: [PATCH 18/32] Use the correct variable name --- tests/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/docker-compose.yml b/tests/docker-compose.yml index 8cfb1372..b3783bab 100644 --- a/tests/docker-compose.yml +++ b/tests/docker-compose.yml @@ -6,7 +6,7 @@ services: - 5532:5532 environment: - PGPORT=5532 - - PGUSER=${POSTGRES_USERNAME} + - PGUSER=${POSTGRES_USER} - POSTGRES_DB=${POSTGRES_DB} - POSTGRES_USER=${POSTGRES_USER} - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} From 949ffd465b74f52830bb9aacd2fdb60565de5e2f Mon Sep 17 00:00:00 2001 From: Lindsay Holmwood Date: Mon, 13 Jan 2025 17:11:55 +1100 Subject: [PATCH 19/32] Run all tests through a harness --- tasks/test.sh | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/tasks/test.sh b/tasks/test.sh index 920862f0..e9687d43 100755 --- a/tasks/test.sh +++ b/tasks/test.sh @@ -14,7 +14,7 @@ if [ -z "${POSTGRES_VERSION}" ]; then exit 1 fi -set -ux +set -u mise run build mise run reset @@ -22,9 +22,18 @@ mise run reset connection_url=postgresql://${POSTGRES_USER:-$USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} container_name=postgres-${POSTGRES_VERSION} -# # tests -# PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/core.sql -# PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/core-functions.sql -# PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/config.sql -# PGPASSWORD=$CS_DATABASE__PASSWORD psql $connection_url -f tests/encryptindex.sql -cat tests/operators.sql | docker exec -i ${container_name} psql ${connection_url} -f- +run_test () { + echo + echo '###############################################' + echo "# ${1}" + echo '###############################################' + echo + cat $1 | docker exec -i ${container_name} psql $connection_url -f- +} + +# tests +run_test tests/core.sql +run_test tests/core-functions.sql +run_test tests/config.sql +run_test tests/encryptindex.sql +run_test tests/operators.sql From 567afa5b2416663dcc43092e8ec0286b04eec78f Mon Sep 17 00:00:00 2001 From: Lindsay Holmwood Date: Mon, 13 Jan 2025 22:33:01 +1100 Subject: [PATCH 20/32] Document how to run the tests locally --- README.md | 38 ++++++++++++++++++++++++++++++++++++++ tests/docker-compose.yml | 2 ++ 2 files changed, 40 insertions(+) diff --git a/README.md b/README.md index c5660687..b61f6002 100644 --- a/README.md +++ b/README.md @@ -392,3 +392,41 @@ To cut a [release](https://github.com/cipherstash/encrypt-query-language/release 1. Click `Publish release`. This will trigger the [Release EQL](https://github.com/cipherstash/encrypt-query-language/actions/workflows/release-eql.yml) workflow, which will build and attach artifacts to [the release](https://github.com/cipherstash/encrypt-query-language/releases/). + +## Testing + +> [!IMPORTANT] +> **Before you run the tests** you need to have this software installed: +> - [mise](https://mise.jdx.dev/) — see the [installing mise](#installing-mise) instructions +> - [Docker](https://www.docker.com/) — see Docker's [documentation for installing](https://docs.docker.com/get-started/get-docker/) + +To run tests locally: + +``` shell +# Clone the repo +git clone https://github.com/cipherstash/encrypt-query-language +cd encrypt-query-language + +# Install dependencies +mise trust --yes + +# Start a postgres instance +mise run postgres:up postgres-17 --extra-args "--detach --wait" + +# Run the tests +mise run test --postgres 17 + +# Stop and remove all containers and networks +mise run postgres:down +``` + +You can run the same tasks for Postgres 14, 15, 16, and 17. + +Limitations: + +- **Volumes for Postgres containers are not persistent.** + If you need to look at data in the container, uncomment a volume in + `tests/docker-compose.yml` +- **You can't run multiple Postgres containers at the same time.** + All the containers bind to the same port. If you want to run multiple + containers at the same time, you'll have to change the ports. diff --git a/tests/docker-compose.yml b/tests/docker-compose.yml index b3783bab..651c5fa2 100644 --- a/tests/docker-compose.yml +++ b/tests/docker-compose.yml @@ -28,6 +28,8 @@ services: <<: *postgres image: postgres:17 container_name: postgres-17 + #volumes: # uncomment if you need to inspect the container contents + #- ./pg/data-17:/var/lib/postgresql/data postgres-16: <<: *postgres From 1099ad565d7f5d8a228eda339f6758e29f95aaa3 Mon Sep 17 00:00:00 2001 From: Lindsay Holmwood Date: Mon, 13 Jan 2025 22:45:17 +1100 Subject: [PATCH 21/32] Run workflow on changes to tests and tasks --- .github/workflows/test-eql.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/test-eql.yml b/.github/workflows/test-eql.yml index 1c0c04b7..096f89a8 100644 --- a/.github/workflows/test-eql.yml +++ b/.github/workflows/test-eql.yml @@ -6,6 +6,8 @@ on: paths: - ".github/workflows/test-eql.yml" - "sql/*.sql" + - "tests/**/*" + - "tasks/**/*" pull_request: branches: @@ -13,6 +15,8 @@ on: paths: - ".github/workflows/test-eql.yml" - "sql/*.sql" + - "tests/**/*" + - "tasks/**/*" workflow_dispatch: From d3c8faedf5056c51635087112402171889f6bbf7 Mon Sep 17 00:00:00 2001 From: Lindsay Holmwood Date: Mon, 13 Jan 2025 22:49:29 +1100 Subject: [PATCH 22/32] Add preamble: what the tests are and where they run --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index b61f6002..f1e9993e 100644 --- a/README.md +++ b/README.md @@ -395,6 +395,12 @@ This will trigger the [Release EQL](https://github.com/cipherstash/encrypt-query ## Testing +There are tests for EQL for PostgreSQL versions 14–17. + +They easiest way to run them is in [GitHub Actions](https://github.com/cipherstash/encrypt-query-language/actions/workflows/test-eql.yml). + +### Running tests locally + > [!IMPORTANT] > **Before you run the tests** you need to have this software installed: > - [mise](https://mise.jdx.dev/) — see the [installing mise](#installing-mise) instructions From 866eae3f2e76efe7d357b03562774a80c4258732 Mon Sep 17 00:00:00 2001 From: Lindsay Holmwood Date: Mon, 13 Jan 2025 23:20:25 +1100 Subject: [PATCH 23/32] Specify the postgres version as a flag instead of env var To be more idiomatically mise --- tasks/reset.sh | 5 +++++ tasks/test.sh | 20 +++++++------------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/tasks/reset.sh b/tasks/reset.sh index 6dba2042..446c2e07 100755 --- a/tasks/reset.sh +++ b/tasks/reset.sh @@ -1,8 +1,13 @@ #!/bin/bash #MISE description="Uninstall and install EQL to local postgres" +#USAGE flag "--postgres " help="Run tests for specified Postgres version" default="17" { +#USAGE choices "14" "15" "16" "17" +#USAGE } set -euxo pipefail +POSTGRES_VERSION=${usage_postgres} + connection_url=postgresql://${POSTGRES_USER:-$USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} container_name=postgres-${POSTGRES_VERSION} diff --git a/tasks/test.sh b/tasks/test.sh index e9687d43..c081dad5 100755 --- a/tasks/test.sh +++ b/tasks/test.sh @@ -1,23 +1,17 @@ #!/usr/bin/env bash -#MISE description="Build, reset and run test" +#MISE description="Build, reset and run tests" +#USAGE flag "--postgres " help="Run tests for specified Postgres version" default="17" { +#USAGE choices "14" "15" "16" "17" +#USAGE } #!/bin/bash -set -eo pipefail +set -euo pipefail -if [ -z "${POSTGRES_VERSION}" ]; then - echo "error: POSTGRES_VERSION not set" - echo "Please re-run with a version set:" - echo - echo "POSTGRES_VERSION=16 mise run test" - echo - exit 1 -fi - -set -u +POSTGRES_VERSION=${usage_postgres} mise run build -mise run reset +mise run reset --postgres ${POSTGRES_VERSION} connection_url=postgresql://${POSTGRES_USER:-$USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} container_name=postgres-${POSTGRES_VERSION} From a3c9c538264bfab9216e65df75aa8a0657d382de Mon Sep 17 00:00:00 2001 From: Lindsay Holmwood Date: Mon, 13 Jan 2025 23:21:09 +1100 Subject: [PATCH 24/32] Output when the tests have passed successfully --- tasks/test.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tasks/test.sh b/tasks/test.sh index c081dad5..19dc1993 100755 --- a/tasks/test.sh +++ b/tasks/test.sh @@ -31,3 +31,9 @@ run_test tests/core-functions.sql run_test tests/config.sql run_test tests/encryptindex.sql run_test tests/operators.sql + +echo +echo '###############################################' +echo "# ✅ALL TESTS PASSED " +echo '###############################################' +echo From 0c05921864290f50967baf956dc32ff9c9778df9 Mon Sep 17 00:00:00 2001 From: Lindsay Holmwood Date: Mon, 13 Jan 2025 23:23:30 +1100 Subject: [PATCH 25/32] Say what version of Postgres the step is for --- .github/workflows/test-eql.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-eql.yml b/.github/workflows/test-eql.yml index 096f89a8..ff292835 100644 --- a/.github/workflows/test-eql.yml +++ b/.github/workflows/test-eql.yml @@ -46,10 +46,10 @@ jobs: install: true # [default: true] run `mise install` cache: true # [default: true] cache mise using GitHub's cache - - name: Setup database + - name: Setup database (Postgres ${{ matrix.postgres-version }}) run: | mise run postgres:up postgres-${POSTGRES_VERSION} --extra-args "--detach --wait" - - name: Test EQL + - name: Test EQL for Postgres ${{ matrix.postgres-version }} run: | mise run --output prefix test From 58df4888b7b04babe50d4a5c7b498e70b80f73a2 Mon Sep 17 00:00:00 2001 From: Lindsay Holmwood Date: Mon, 13 Jan 2025 23:23:51 +1100 Subject: [PATCH 26/32] Specify what version of Postgres to run the tests for --- .github/workflows/test-eql.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-eql.yml b/.github/workflows/test-eql.yml index ff292835..6ff39bb9 100644 --- a/.github/workflows/test-eql.yml +++ b/.github/workflows/test-eql.yml @@ -52,4 +52,4 @@ jobs: - name: Test EQL for Postgres ${{ matrix.postgres-version }} run: | - mise run --output prefix test + mise run --output prefix test --postgres ${POSTGRES_VERSION} From 67d60a05c7256cd1b9e8f824d62df5c4552b6772 Mon Sep 17 00:00:00 2001 From: Lindsay Holmwood Date: Mon, 13 Jan 2025 23:24:01 +1100 Subject: [PATCH 27/32] Use the latest mise --- .github/workflows/test-eql.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-eql.yml b/.github/workflows/test-eql.yml index 6ff39bb9..050e9dd9 100644 --- a/.github/workflows/test-eql.yml +++ b/.github/workflows/test-eql.yml @@ -42,7 +42,7 @@ jobs: - uses: jdx/mise-action@v2 with: - version: 2025.1.0 # [default: latest] mise version to install + version: 2025.1.6 # [default: latest] mise version to install install: true # [default: true] run `mise install` cache: true # [default: true] cache mise using GitHub's cache From 69eab3a2d9a313883beff7d1d8179b49995c7415 Mon Sep 17 00:00:00 2001 From: Lindsay Holmwood Date: Mon, 13 Jan 2025 23:36:29 +1100 Subject: [PATCH 28/32] Ensure postgres is running before running SQL --- tasks/reset.sh | 12 ++++++++++++ tasks/test.sh | 17 ++++++++++++++--- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/tasks/reset.sh b/tasks/reset.sh index 446c2e07..c617ea10 100755 --- a/tasks/reset.sh +++ b/tasks/reset.sh @@ -6,11 +6,23 @@ set -euxo pipefail +fail_if_postgres_not_running () { + containers=$(docker ps --filter "name=${container_name}" --quiet) + if [ -z "${containers}" ]; then + echo "error: Docker container for PostgreSQL is not running" + echo "error: Try running 'mise run postgres:up ${container_name}' to start the container" + exit 65 + fi +} + POSTGRES_VERSION=${usage_postgres} connection_url=postgresql://${POSTGRES_USER:-$USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} container_name=postgres-${POSTGRES_VERSION} +# Setup +fail_if_postgres_not_running + # Uninstall cat release/cipherstash-encrypt-uninstall.sql | docker exec -i ${container_name} psql ${connection_url} -f- diff --git a/tasks/test.sh b/tasks/test.sh index 19dc1993..e9f270db 100755 --- a/tasks/test.sh +++ b/tasks/test.sh @@ -10,12 +10,18 @@ set -euo pipefail POSTGRES_VERSION=${usage_postgres} -mise run build -mise run reset --postgres ${POSTGRES_VERSION} - connection_url=postgresql://${POSTGRES_USER:-$USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} container_name=postgres-${POSTGRES_VERSION} +fail_if_postgres_not_running () { + containers=$(docker ps --filter "name=${container_name}" --quiet) + if [ -z "${containers}" ]; then + echo "error: Docker container for PostgreSQL is not running" + echo "error: Try running 'mise run postgres:up ${container_name}' to start the container" + exit 65 + fi +} + run_test () { echo echo '###############################################' @@ -25,6 +31,11 @@ run_test () { cat $1 | docker exec -i ${container_name} psql $connection_url -f- } +# setup +fail_if_postgres_not_running +mise run build +mise run reset --postgres ${POSTGRES_VERSION} + # tests run_test tests/core.sql run_test tests/core-functions.sql From 0050fba9d8a715b01bcfaa697484ab4cfc0502b1 Mon Sep 17 00:00:00 2001 From: Lindsay Holmwood Date: Tue, 14 Jan 2025 00:02:11 +1100 Subject: [PATCH 29/32] Ensure postgres is running on a unique port --- mise.toml | 2 +- tests/docker-compose.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mise.toml b/mise.toml index 54398e9f..ccfd9ef0 100644 --- a/mise.toml +++ b/mise.toml @@ -17,4 +17,4 @@ POSTGRES_DB = "cipherstash" POSTGRES_USER = "cipherstash" POSTGRES_PASSWORD = "password" POSTGRES_HOST = "localhost" -POSTGRES_PORT = "5532" +POSTGRES_PORT = "7432" diff --git a/tests/docker-compose.yml b/tests/docker-compose.yml index 651c5fa2..13c28d49 100644 --- a/tests/docker-compose.yml +++ b/tests/docker-compose.yml @@ -3,9 +3,9 @@ services: container_name: postgres image: postgres ports: - - 5532:5532 + - 7432:7432 environment: - - PGPORT=5532 + - PGPORT=${POSTGRES_PORT} - PGUSER=${POSTGRES_USER} - POSTGRES_DB=${POSTGRES_DB} - POSTGRES_USER=${POSTGRES_USER} From 264b272528bf85a9b627add178cba24561540c5a Mon Sep 17 00:00:00 2001 From: Lindsay Holmwood Date: Tue, 14 Jan 2025 00:02:48 +1100 Subject: [PATCH 30/32] Make tests less chatty --- tasks/reset.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks/reset.sh b/tasks/reset.sh index c617ea10..7d5088fe 100755 --- a/tasks/reset.sh +++ b/tasks/reset.sh @@ -4,7 +4,7 @@ #USAGE choices "14" "15" "16" "17" #USAGE } -set -euxo pipefail +set -euo pipefail fail_if_postgres_not_running () { containers=$(docker ps --filter "name=${container_name}" --quiet) From efc8a75d90d72d2f4b7e5e21414f29a0cb4ed367 Mon Sep 17 00:00:00 2001 From: Lindsay Holmwood Date: Tue, 14 Jan 2025 00:03:38 +1100 Subject: [PATCH 31/32] Filter on exact container name --- tasks/reset.sh | 2 +- tasks/test.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tasks/reset.sh b/tasks/reset.sh index 7d5088fe..e128801c 100755 --- a/tasks/reset.sh +++ b/tasks/reset.sh @@ -7,7 +7,7 @@ set -euo pipefail fail_if_postgres_not_running () { - containers=$(docker ps --filter "name=${container_name}" --quiet) + containers=$(docker ps --filter "name=^${container_name}$" --quiet) if [ -z "${containers}" ]; then echo "error: Docker container for PostgreSQL is not running" echo "error: Try running 'mise run postgres:up ${container_name}' to start the container" diff --git a/tasks/test.sh b/tasks/test.sh index e9f270db..0db783c2 100755 --- a/tasks/test.sh +++ b/tasks/test.sh @@ -14,7 +14,7 @@ connection_url=postgresql://${POSTGRES_USER:-$USER}:${POSTGRES_PASSWORD}@${POSTG container_name=postgres-${POSTGRES_VERSION} fail_if_postgres_not_running () { - containers=$(docker ps --filter "name=${container_name}" --quiet) + containers=$(docker ps --filter "name=^${container_name}$" --quiet) if [ -z "${containers}" ]; then echo "error: Docker container for PostgreSQL is not running" echo "error: Try running 'mise run postgres:up ${container_name}' to start the container" From 06dd962a855a8635e3b19bc2bac3181a717275a1 Mon Sep 17 00:00:00 2001 From: Lindsay Holmwood Date: Tue, 14 Jan 2025 00:10:02 +1100 Subject: [PATCH 32/32] Explain more about how the containers work --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f1e9993e..476241d3 100644 --- a/README.md +++ b/README.md @@ -428,11 +428,13 @@ mise run postgres:down You can run the same tasks for Postgres 14, 15, 16, and 17. +The configuration for the Postgres containers in `tests/docker-compose.yml`. + Limitations: - **Volumes for Postgres containers are not persistent.** If you need to look at data in the container, uncomment a volume in `tests/docker-compose.yml` - **You can't run multiple Postgres containers at the same time.** - All the containers bind to the same port. If you want to run multiple - containers at the same time, you'll have to change the ports. + All the containers bind to the same port (`7543`). If you want to run + multiple containers at the same time, you'll have to change the ports.