Skip to content

Commit b0e3485

Browse files
auxesistobyhede
andcommitted
docs(dev): rework for clarity and readability
Co-authored-by: Toby Hede <toby@cipherstash.com>
1 parent aee6f11 commit b0e3485

File tree

1 file changed

+95
-78
lines changed

1 file changed

+95
-78
lines changed

DEVELOPMENT.md

Lines changed: 95 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,17 @@
88
- [Testing](#testing)
99
- [Running tests locally](#running-tests-locally)
1010
- [Releasing](#releasing)
11+
- [Building](#building)
12+
- [Dependencies](#dependencies)
13+
- [Building a release locally](#building-a-release-locally)
14+
- [Structure](#structure)
15+
- [Schema](#schema)
16+
- [Types](#types)
17+
- [Encrypted column type](#encrypted-column-type)
18+
- [Encrypted index term types](#encrypted-index-term-types)
19+
- [Operators](#operators)
20+
- [Working without operators](#working-without-operators)
21+
- [Configuration table](#configuration-table)
1122

1223
### How this project is organised
1324

@@ -25,16 +36,43 @@ These are the important files in the repo:
2536
.
2637
├── mise.toml <-- the main config file for mise
2738
├── tasks/ <-- mise tasks
28-
├── sql/ <-- The individual SQL components that make up EQL
39+
├── src/ <-- The individual SQL components that make up EQL
40+
│ ├── encrypted/ <-- Encrypted column type
41+
│ ├── operators/ <-- Operators for the encrypted column type
42+
│ ├── match/ <-- match index term type
43+
│ ├── unique/ <-- unique index term type
44+
│ ├── ore/ <-- ore index term type
45+
│ ├── ore-cllw/ <-- ore-cllw index term type
46+
│ ├── config/ <-- Configuration management for encrypted columns
47+
│ ├── schema.sql <-- Defines the PostgreSQL schema for namespacing EQL
48+
│ ├── crypto.sql <-- Installs pg_crypto extension, required by ORE
49+
│ ├── common.sql <-- Shared helper functions
50+
│ └── version.sql <-- Defines function to query current EQL version - automatically generated on build
2951
├── docs/ <-- Tutorial, reference, and concept documentation
3052
├── tests/ <-- Unit and integration tests
3153
│ ├── docker-compose.yml <-- Docker configuration for running PostgreSQL instances
32-
│ └── *.sql <-- Individual unit and integration tests
54+
│ └── *.sql <-- Helpers and test data loaded during test runs
3355
├── release/ <-- Build artifacts produced by the `build` task
3456
├── examples/ <-- Example uses of EQL in different languages
3557
└── playground/ <-- Playground enviroment for experimenting with EQL and CipherStash Proxy
3658
```
3759

60+
Tests live alongside the individual SQL files, with a filename ending with `_test.sql`
61+
62+
We break SQL into small modules named after what they do.
63+
64+
In general, operator functions are thin wrappers around larger functions that do the actual work.
65+
Put the wrapper functions in `operators.sql` and the larger functions in `functions.sql`.
66+
67+
Dependencies between SQL in `src/` are declared in a comment at the top of each file.
68+
All SQL files should `REQUIRE` the source file of any other object they reference.
69+
70+
All files must have at least one declaration, and the default is to reference the schema:
71+
72+
```
73+
-- REQUIRE: src/schema.sql
74+
```
75+
3876
## Set up a local development environment
3977

4078
> [!IMPORTANT]
@@ -123,7 +161,7 @@ stateDiagram-v2
123161
state "🧍 Human makes changes to EQL sources" as changes
124162
state sources_fork <<fork>>
125163
state sources_join <<join>>
126-
state "sql/*.sql" as source_sql
164+
state "src/*.sql" as source_sql
127165
state "tasks/**/*" as source_tasks
128166
state "tests/**/*" as source_tests
129167
state sources_changed <<choice>>
@@ -235,118 +273,97 @@ To cut a [release](https://github.com/cipherstash/encrypt-query-language/release
235273

236274
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/).
237275

276+
## Building
238277

239-
====
240-
241-
242-
###
243-
244-
EQL is installed into the `eql_v1` schema.
245-
246-
247-
## Types
248-
249-
### `public.eql_v1_encrypted`
250-
251-
Core column type, defined as PostgreSQL composite type.
252-
In public schema as once used in customer tables it cannot be dropped without dropping data.
253-
254-
### Index terms
255-
256-
Each type of encrypted indexing has an associated type and functions
257-
258-
- `eql_v1.unique_index`
259-
- `eql_v1.match`
260-
- `eql_v1.ore_64_8_v1`
261-
- `eql_v1.ore_64_8_v1_term`
278+
### Dependencies
262279

280+
SQL sources are split into smaller files in `src/`.
281+
Dependencies are resolved at build time to construct a single SQL file with the correct ordering.
263282

264-
## Operators
283+
### Building a release locally
265284

266-
Operators are provided for the `eql_v1_encrypted` column type and `jsonb`.
285+
To build a release locally, run:
267286

268-
```
269-
eql_v1_encrypted - eql_v1_encrypted
270-
jsonb - eql_v1_encrypted
271-
eql_v1_encrypted - jsonb
287+
```bash
288+
mise run build
272289
```
273290

274-
The index types and functions are internal implementation details and should not need to be exposed as operators on the `eql_v1_encrypted` type.
291+
This produces two SQL files in `releases/`:
275292

293+
- An installer (`cipherstash-encrypt.sql`), and
294+
- An uninstaller (`cipherstash-encrypt-uninstall.sql`)
276295

277-
-- eql_v1_encrypted = eql_v1_encrypted
278-
-- eql_v1_encrypted = jsonb
279-
-- jsonb = eql_v1_encrypted
280-
-- ore_64_8_v1 = ore_64_8_v1
296+
## Structure
281297

282-
The jsonb comparison is handy as it automates casting.
283-
Comparing ore_64_8_v1 index values requires that sides are functionalated:
284-
eql_v1.ore_64_8_v1(...) = eql_v1.ore_64_8_v1(...)
285-
In the spirit of aggressive simplification, however, I am not going to add operators to compare eql_v1_encrypted with the ore_64_8_v1 type.
286-
In an operator world, the index types and functions are internal implementation details.
287-
Customers should never need to think about the internals.
288-
I can't think of a reason to need it that isn't a version of "holding it wrong". (edited)
298+
### Schema
289299

300+
EQL is installed into the `eql_v1` PostgreSQL schema.
290301

302+
### Types
291303

304+
#### Encrypted column type
292305

293-
## Working without operators
306+
`public.eql_v1_encrypted` is EQL's encrypted column type, defined as PostgreSQL composite type.
294307

308+
This column type is used for storing the encrypted value and any associated indexes for searching.
309+
The associated indexes are described in the [index term types](#index-term-types) section.
295310

296-
### Equality
311+
`public.eql_v1_encrypted` is in the public schema, because once it's used by a user in one of their tables, encrypted column types cannot be dropped without dropping data.
297312

298-
```sql
299-
eql_v1.eq(a eql_v1_encrypted, b eql_v1_encrypted);
300-
```
313+
#### Encrypted index term types
301314

315+
Each type of encrypted index (`unique`, `match`, `ore`) has an associated type, functions, and operators.
302316

317+
These are transient runtime types, used internally by EQL functions and operators:
303318

319+
- `eql_v1.unique_index`
320+
- `eql_v1.match`
321+
- `eql_v1.ore_64_8_v1`
322+
- `eql_v1.ore_64_8_v1_term`
304323

324+
The data in the column is converted into these types, when any operations are being performed on that encrypted data.
305325

306-
## Organisation
326+
### Operators
307327

308-
Break SQL into small modules, aligned with the core domains and types where possible
328+
Searchable encryption functionality is driven by operators on two types:
309329

310-
- types.sql
311-
- casts.sql
312-
- constraints.sql
313-
- functions.sql
314-
- operators.sql
330+
- EQL's `eql_v1_encrypted` column type
331+
- PostgreSQL's `jsonb` column type
315332

316-
Operators are also functions, so some judgement is required.
317-
The intent is to reduce file size and cognitive load.
333+
For convenience, operators allow comparisons between `eql_v1_encrypted` and `jsonb` column types.
318334

319-
In general, operator functions should be thin wrappers around a larger function that does the work.
320-
Put the wrapper functions in `operators.sql` and the "heavy lifting" functions in `functions.sql`.
335+
Operators allow comparisons between:
321336

322-
Tests should follow a similar pattern.
337+
- `eql_v1_encrypted` and `eql_v1_encrypted`
338+
- `jsonb` and `eql_v1_encrypted`
339+
- `eql_v1_encrypted` and `jsonb`
323340

341+
The index types and functions are internal implementation details and should not be exposed as operators on the `eql_v1_encrypted` type.
342+
For example, `eql_v1_encrypted` should not have an operator with the `ore_64_8_v1` type.
343+
Users should never need to think about or interact with EQL internals.
324344

345+
#### Working without operators
325346

326-
### Dependencies
347+
There are scenarios where users are unable to install EQL operators in your database.
348+
Users will experience this in more restrictive environments like Supabase.
327349

328-
SQL sources are split into smaller files.
329-
Dependencies are resolved at build time to construct a single SQL file with the correct ordering.
330-
331-
Dependencies between files are declared in a comment at the top of the file.
332-
All SQL files should `REQUIRE` the source file of any other object they reference.
350+
EQL can still be used, but requires the use of functions instead of operators.
333351

334-
All files must have at least one declaration, and the default is to reference the schema
352+
For example, to perform an equality query:
335353

336-
```
337-
-- REQUIRE: src/schema.sql
354+
```sql
355+
SELECT email FROM users WHERE eql_v1.eq(email, $1);
338356
```
339357

358+
### Configuration table
340359

360+
EQL uses a table for tracking configuration state in the database, called `public.eql_v1_configuration`.
341361

342-
### Tables
343-
344-
### Configuration
345-
346-
347-
`public.eql_v1_configuration`
348-
362+
This table should never be dropped, except by a user explicitly uninstalling EQL.
349363

364+
<!--
365+
TODO(toby): probably move to README
366+
TODO(toby): include examples of how to get data out of the table
350367
351368
EQL Design Note
352369
Experimenting with using a Composite type instead of a Domain type for the encrypted column.
@@ -363,4 +380,4 @@ Already built cast helpers so syntax is something like
363380
INSERT INTO encrypted (e) VALUES (
364381
'{}'::jsonb::eql_v1_encrypted
365382
);
366-
383+
-->

0 commit comments

Comments
 (0)