Skip to content

Commit 29ff0ff

Browse files
committed
fix: config functions remove process
1 parent 597db56 commit 29ff0ff

File tree

6 files changed

+109
-43
lines changed

6 files changed

+109
-43
lines changed

README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,37 @@ These frameworks use EQL to enable searchable encryption functionality in Postgr
8484
| Protect.php | [Protect.php](https://github.com/cipherstash/protectphp) |
8585
| CipherStash Proxy | [CipherStash Proxy](https://github.com/cipherstash/proxy) |
8686

87+
## Versioning
88+
89+
You can find the version of EQL installed in your database by running the following query:
90+
91+
```sql
92+
SELECT eql_v2.version();
93+
```
94+
95+
### Upgrading
96+
97+
To upgrade to the latest version of EQL, you can simply run the install script again.
98+
99+
1. Download the latest EQL install script:
100+
101+
```sh
102+
curl -sLo cipherstash-encrypt.sql https://github.com/cipherstash/encrypt-query-language/releases/latest/download/cipherstash-encrypt.sql
103+
```
104+
105+
2. Run this command to install the custom types and functions:
106+
107+
```sh
108+
psql -f cipherstash-encrypt.sql
109+
```
110+
111+
> [!NOTE]
112+
> The install script will not remove any existing configurations, so you can safely run it multiple times.
113+
114+
#### Using dbdev?
115+
116+
Follow the instructions in the [dbdev documentation](https://database.dev/cipherstash/eql) to upgrade the extension to your desired version.
117+
87118
## Developing
88119

89120
See the [development guide](./DEVELOPMENT.md).

dbdev/eql.control

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
default_version = 2.1.2
1+
default_version = 2.1.3
22
comment = 'Index and search encrypted data in PostgreSQL with SQL'
33
relocatable = true

docs/tutorials/proxy-configuration.md

Lines changed: 49 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,17 @@
33
Initialize the column using the `eql_v2.add_column` function to enable encryption and decryption via CipherStash Proxy.
44

55
```sql
6-
SELECT eql_v2.add_column('users', 'encrypted_email'); -- where users is the table name and encrypted_email is the column name of type eql_v2_encrypted
6+
SELECT eql_v2.add_column('users', 'encrypted_email', 'text'); -- where users is the table name and encrypted_email is the column name of type eql_v2_encrypted
7+
```
8+
9+
**Full signature:**
10+
```sql
11+
SELECT eql_v2.add_column(
12+
'table_name', -- Name of the table
13+
'column_name', -- Name of the column (must be of type eql_v2_encrypted)
14+
'cast_as', -- PostgreSQL type to cast decrypted data [optional, defaults to 'text']
15+
migrating -- If true, stages changes without immediate activation [optional, defaults to false]
16+
);
717
```
818

919
**Note:** This function allows you to encrypt and decrypt data but does not enable searchable encryption. See [Searching data with EQL](#searching-data-with-eql) for enabling searchable encryption.
@@ -97,8 +107,9 @@ SELECT eql_v2.add_search_config(
97107
'table_name', -- Name of the table
98108
'column_name', -- Name of the column
99109
'index_name', -- Index kind ('unique', 'match', 'ore', 'ste_vec')
100-
'cast_as', -- PostgreSQL type to cast decrypted data ('text', 'int', etc.)
101-
'opts' -- Index options as JSONB (optional)
110+
'cast_as', -- PostgreSQL type to cast decrypted data ('text', 'int', etc.) [optional, defaults to 'text']
111+
'opts', -- Index options as JSONB [optional, defaults to '{}']
112+
migrating -- If true, stages changes without immediate activation [optional, defaults to false]
102113
);
103114
```
104115

@@ -115,7 +126,20 @@ SELECT eql_v2.add_search_config(
115126
);
116127
```
117128

118-
Configuration changes are automatically migrated and activated.
129+
**Example (With custom options and staging):**
130+
131+
```sql
132+
SELECT eql_v2.add_search_config(
133+
'users',
134+
'encrypted_name',
135+
'match',
136+
'text',
137+
'{"k": 6, "bf": 4096}',
138+
true -- Stage changes without immediate activation
139+
);
140+
```
141+
142+
Configuration changes are automatically migrated and activated unless the `migrating` parameter is set to `true`.
119143

120144
## Searching data with EQL
121145

@@ -322,9 +346,24 @@ EQL supports the following index types:
322346

323347
Use these functions to manage your EQL configurations:
324348

325-
- `eql_v2.add_column()` - Add a new encrypted column
326-
- `eql_v2.remove_column()` - Remove an encrypted column
327-
- `eql_v2.add_search_config()` - Add a search index
328-
- `eql_v2.remove_search_config()` - Remove a search index
329-
- `eql_v2.modify_search_config()` - Modify an existing search index
330-
- `eql_v2.config()` - View current configuration in tabular format
349+
**Column Management:**
350+
- `eql_v2.add_column(table_name, column_name, cast_as DEFAULT 'text', migrating DEFAULT false)` - Add a new encrypted column
351+
- `eql_v2.remove_column(table_name, column_name, migrating DEFAULT false)` - Remove an encrypted column completely
352+
353+
**Index Management:**
354+
- `eql_v2.add_search_config(table_name, column_name, index_name, cast_as DEFAULT 'text', opts DEFAULT '{}', migrating DEFAULT false)` - Add a search index to a column
355+
- `eql_v2.remove_search_config(table_name, column_name, index_name, migrating DEFAULT false)` - Remove a specific search index (preserves column configuration)
356+
- `eql_v2.modify_search_config(table_name, column_name, index_name, cast_as DEFAULT 'text', opts DEFAULT '{}', migrating DEFAULT false)` - Modify an existing search index
357+
358+
**Configuration Management:**
359+
- `eql_v2.migrate_config()` - Manually migrate pending configuration to encrypting state
360+
- `eql_v2.activate_config()` - Manually activate encrypting configuration
361+
- `eql_v2.discard()` - Discard pending configuration changes
362+
- `eql_v2.config()` - View current configuration in tabular format (returns a table with columns: state, relation, col_name, decrypts_as, indexes)
363+
364+
**Note:** All configuration functions automatically migrate and activate changes unless `migrating` is set to `true`. When `migrating` is `true`, changes are staged but not immediately applied, allowing for batch configuration updates.
365+
366+
**Important Behavior Differences:**
367+
- `remove_search_config()` removes only the specified index but preserves the column configuration (including `cast_as` setting)
368+
- `remove_column()` removes the entire column configuration including all its indexes
369+
- Empty configurations (no tables/columns) are automatically maintained as active to reflect the current state

src/config/constraints.sql

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,16 @@ CREATE FUNCTION eql_v2.config_check_cast(val jsonb)
3939
RETURNS BOOLEAN
4040
AS $$
4141
BEGIN
42-
IF EXISTS (SELECT jsonb_array_elements_text(jsonb_path_query_array(val, '$.tables.*.*.cast_as')) = ANY('{text, int, small_int, big_int, real, double, boolean, date, jsonb}')) THEN
43-
RETURN true;
42+
-- If there are cast_as fields, validate them
43+
IF EXISTS (SELECT jsonb_array_elements_text(jsonb_path_query_array(val, '$.tables.*.*.cast_as'))) THEN
44+
IF (SELECT bool_and(cast_as = ANY('{text, int, small_int, big_int, real, double, boolean, date, jsonb}'))
45+
FROM (SELECT jsonb_array_elements_text(jsonb_path_query_array(val, '$.tables.*.*.cast_as')) AS cast_as) casts) THEN
46+
RETURN true;
47+
END IF;
48+
RAISE 'Configuration has an invalid cast_as (%). Cast should be one of {text, int, small_int, big_int, real, double, boolean, date, jsonb}', val;
4449
END IF;
45-
RAISE 'Configuration has an invalid cast_as (%). Cast should be one of {text, int, small_int, big_int, real, double, boolean, date, jsonb}', val;
50+
-- If no cast_as fields exist (empty config), that's valid
51+
RETURN true;
4652
END;
4753
$$ LANGUAGE plpgsql;
4854

@@ -53,7 +59,7 @@ CREATE FUNCTION eql_v2.config_check_tables(val jsonb)
5359
RETURNS boolean
5460
AS $$
5561
BEGIN
56-
IF (val ? 'tables') AND (val->'tables' <> '{}'::jsonb) THEN
62+
IF (val ? 'tables') THEN
5763
RETURN true;
5864
END IF;
5965
RAISE 'Configuration missing tables (tables) field: %', val;

src/config/functions.sql

Lines changed: 11 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ $$ LANGUAGE plpgsql;
7070

7171

7272

73-
CREATE FUNCTION eql_v2.remove_search_config(table_name text, column_name text, index_name text)
73+
CREATE FUNCTION eql_v2.remove_search_config(table_name text, column_name text, index_name text, migrating boolean DEFAULT false)
7474
RETURNS jsonb
7575
AS $$
7676
DECLARE
@@ -105,22 +105,12 @@ AS $$
105105
-- remove the index
106106
SELECT _config #- array['tables', table_name, column_name, 'indexes', index_name] INTO _config;
107107

108-
-- if column is now empty, remove the column
109-
IF _config #> array['tables', table_name, column_name, 'indexes'] = '{}' THEN
110-
SELECT _config #- array['tables', table_name, column_name] INTO _config;
111-
END IF;
112-
113-
-- if table is now empty, remove the table
114-
IF _config #> array['tables', table_name] = '{}' THEN
115-
SELECT _config #- array['tables', table_name] INTO _config;
116-
END IF;
117-
118-
-- if config empty delete
119-
-- or update the config
120-
IF _config #> array['tables'] = '{}' THEN
121-
DELETE FROM public.eql_v2_configuration WHERE state = 'pending';
122-
ELSE
123-
UPDATE public.eql_v2_configuration SET data = _config WHERE state = 'pending';
108+
-- update the config and migrate (even if empty)
109+
UPDATE public.eql_v2_configuration SET data = _config WHERE state = 'pending';
110+
111+
IF NOT migrating THEN
112+
PERFORM eql_v2.migrate_config();
113+
PERFORM eql_v2.activate_config();
124114
END IF;
125115

126116
-- exeunt
@@ -134,7 +124,7 @@ CREATE FUNCTION eql_v2.modify_search_config(table_name text, column_name text, i
134124
RETURNS jsonb
135125
AS $$
136126
BEGIN
137-
PERFORM eql_v2.remove_search_config(table_name, column_name, index_name);
127+
PERFORM eql_v2.remove_search_config(table_name, column_name, index_name, migrating);
138128
RETURN eql_v2.add_search_config(table_name, column_name, index_name, cast_as, opts, migrating);
139129
END;
140130
$$ LANGUAGE plpgsql;
@@ -293,16 +283,11 @@ AS $$
293283
SELECT _config #- array['tables', table_name] INTO _config;
294284
END IF;
295285

296-
-- if config empty delete
297-
-- or update the config
298-
IF _config #> array['tables'] = '{}' THEN
299-
DELETE FROM public.eql_v2_configuration WHERE state = 'pending';
300-
ELSE
301-
UPDATE public.eql_v2_configuration SET data = _config WHERE state = 'pending';
302-
END IF;
303-
304286
PERFORM eql_v2.remove_encrypted_constraint(table_name, column_name);
305287

288+
-- update the config and migrate (even if empty)
289+
UPDATE public.eql_v2_configuration SET data = _config WHERE state = 'pending';
290+
306291
IF NOT migrating THEN
307292
PERFORM eql_v2.migrate_config();
308293
PERFORM eql_v2.activate_config();

tasks/postgres.toml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ mise run postgres:up --extra-args "--detach --wait"
2121
["postgres:psql"]
2222
description = "Run psql"
2323
run = """
24-
{% set default_service = "postgres-" ~ get_env(name="POSTGRES_VERSION",default="17") %}
25-
psql -U {{arg(name="user",default="cipherstash")}} -d {{arg(name="db",default="cipherstash")}} -h localhost -p {{arg(name="port",default="7432")}} --service {{arg(name="service",default=default_service)}}
24+
psql -U {{arg(name="user",default="cipherstash")}} -d {{arg(name="db",default="cipherstash")}} -h localhost -p {{arg(name="port",default="7432")}}
25+
"""
26+
27+
["eql:install"]
28+
description = "Install EQL to local postgres"
29+
run = """
30+
psql -U {{arg(name="user",default="cipherstash")}} -d {{arg(name="db",default="cipherstash")}} -h localhost -p {{arg(name="port",default="7432")}} -f {{arg(name="file",default="release/cipherstash-encrypt.sql")}}
2631
"""

0 commit comments

Comments
 (0)