Skip to content
Closed
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
3bda78f
Add support for incrementing version number
na-ka-na May 13, 2025
011e3a3
Update README
na-ka-na Jun 11, 2025
9b88963
feat: WIP
mscottx88 Jun 18, 2025
77237a5
feat: WIP
Jun 24, 2025
710731a
feat: WIP
Jun 24, 2025
aa2a651
feat: WIP
Jun 24, 2025
83b9aea
feat: wip
Jun 24, 2025
627e1c4
feat: fixes
Jun 26, 2025
e5d8e41
Update versioning section in the README with actual subscriptions tab…
na-ka-na Jun 26, 2025
da0dafa
feat: tests overhaul WIP
Jun 27, 2025
1705e31
feat: formatting
Jun 27, 2025
43ebcd9
feat: WIP
Jun 27, 2025
a5b09f4
feat: wip
Jun 27, 2025
06956b4
feat: WIP
Jun 27, 2025
9225573
feat: wip
Jun 27, 2025
195e63b
feat: actions
Jun 27, 2025
99d7b57
feat: fix test runner
Jun 30, 2025
5eecb59
feat: fix scripts
Jun 30, 2025
0611a2b
feat: keep it DRY
Jun 30, 2025
db684ee
feat: server versions
Jun 30, 2025
9fe51d4
feat: server versions
Jun 30, 2025
ffc0548
Add tests for increment version
na-ka-na Jun 30, 2025
b432ba8
feat: wip
Jun 30, 2025
c5d844e
feat: wip
Jun 30, 2025
3ff101b
feat: wip
Jun 30, 2025
7ff495e
feat: docs
Jun 30, 2025
fe3f532
Merge commit '08fff5de0f2f2618917f00e05b105dd0ffe5d6d9' into feature/…
Jul 1, 2025
81d95bd
feat: docs
Jul 1, 2025
452d7ed
feat: lint
Jul 1, 2025
03631bc
feat: comparison reports
Jul 2, 2025
b3e3300
feat: wip
Jul 2, 2025
0a880c1
feat: tests
Jul 2, 2025
6cd52ce
Merge branch 'master' of https://github.com/na-ka-na/temporal_tables
Jul 2, 2025
6eda315
chore: revert
Jul 2, 2025
e659a8a
chore: revert
Jul 2, 2025
b085f74
chore: cleanup
Jul 2, 2025
73f990a
feat: docs
Jul 2, 2025
f8b2163
chore: revert
Jul 2, 2025
3152d31
Merge branch 'master' of https://github.com/nearform/temporal_tables
Jul 10, 2025
3786584
Merge branch 'master' into feature/static-trigger-generator
Jul 10, 2025
bd346ae
feat: wip
Jul 11, 2025
489ff3c
feat: wip
Jul 11, 2025
f8e0a22
feat: wip
Jul 12, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .commitlintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": [
"@commitlint/config-conventional"
]
}
6 changes: 6 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
indent_style = space
indent_size = 2
1 change: 1 addition & 0 deletions .eslintcache
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"C:\\Users\\michael\\nearform\\llm-chunk\\dist\\chunker.js":"1","C:\\Users\\michael\\nearform\\llm-chunk\\eslint.config.js":"2","C:\\Users\\michael\\nearform\\llm-chunk\\dist\\types.js":"3","C:\\Users\\michael\\nearform\\llm-chunk\\dist\\utils.js":"4"},{"size":3858,"mtime":1749766311905,"results":"5","hashOfConfig":"6"},{"size":339,"mtime":1749665160374,"results":"7","hashOfConfig":"6"},{"size":44,"mtime":1749766311878},{"size":5373,"mtime":1749766311895},{"filePath":"8","messages":"9","suppressedMessages":"10","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"10mj8a1",{"filePath":"11","messages":"12","suppressedMessages":"13","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"C:\\Users\\michael\\nearform\\llm-chunk\\dist\\chunker.js",[],[],"C:\\Users\\michael\\nearform\\llm-chunk\\eslint.config.js",[],[]]
8 changes: 8 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,11 @@ jobs:
- name: Run tests no check
run: |
make run_test_nochecks

- name: Run Node.js e2e Tests
uses: actions/setup-node@v4
with:
node-version: '20.x'
- run: npm ci
- run: npm run build --if-present
- run: npm test:e2e:runner
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ test/result
test/remote_expected
test/remote_sql
test/remote_result
node_modules
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lts/*
6 changes: 6 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"semi": false,
"singleQuote": true,
"arrowParens": "avoid",
"trailingComma": "none"
}
24 changes: 24 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Test File w/ ts-node",
"protocol": "inspector",
"runtimeArgs": ["--loader", "ts-node/esm/transpile-only", "--test"],
"args": ["${relativeFile}"],
"outputCapture": "std",
"internalConsoleOptions": "openOnSessionStart",
"envFile": "${workspaceRoot}/.env",
"skipFiles": [
"${workspaceRoot}/../../node_modules/**/*",
"<node_internals>/**/*"
],
"windows": {
"skipFiles": ["C:\\**\\node_modules\\**\\*", "<node_internals>/**/*"]
},
"disableOptimisticBPs": true
}
]
}
148 changes: 148 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,60 @@ If the column doesn't accept null values you'll need to modify it to allow for n

## Test

### End-to-End Tests (New)

We've added comprehensive end-to-end tests written in modern TypeScript using Node.js built-in test runner and node-postgres. These tests provide extensive coverage of all temporal table features:

#### Test Coverage
- **Static Generator Tests**: Full coverage of the static trigger generator functionality
- **Legacy Function Tests**: Backward compatibility testing with the original versioning function
- **Event Trigger Tests**: Automatic trigger re-rendering on schema changes
- **Integration Tests**: Real-world scenarios including e-commerce workflows, schema evolution, and performance testing
- **Error Handling**: Edge cases, transaction rollbacks, and concurrent modifications

#### Running E2E Tests

1. **Prerequisites**: Ensure you have PostgreSQL running (Docker or local installation)

2. **Install dependencies**:
```bash
npm install
```

3. **Start database** (if using Docker):
```bash
npm run db:start
```

4. **Run all E2E tests**:
```bash
npm run test:e2e
```

5. **Run specific test suites**:
```bash
# Static generator tests
npm run test:e2e:static

# Legacy function tests
npm run test:e2e:legacy

# Integration tests
npm run test:e2e:integration
```

#### Features of E2E Tests
- **Type-safe**: Written in TypeScript with proper type definitions
- **Modern**: Uses Node.js built-in test runner (no external dependencies like Jest)
- **Comprehensive**: Tests all features including advanced options and edge cases
- **Isolated**: Each test starts with a clean database state
- **Real-world scenarios**: Includes practical examples like e-commerce order processing
- **Performance testing**: Bulk operations and concurrent access patterns

See [test/e2e/README.md](test/e2e/README.md) for detailed documentation.

### Traditional Tests

Ensure you have a postgres database available. A database container can be started by running:

```sh
Expand Down Expand Up @@ -504,3 +558,97 @@ Licensed under [MIT](./LICENSE).
The test scenarios in test/sql and test/expected have been copied over from the original temporal_tables extension, whose license is [BSD 2-clause](https://github.com/arkhipov/temporal_tables/blob/master/LICENSE)

[![banner](https://raw.githubusercontent.com/nearform/.github/refs/heads/master/assets/os-banner-green.svg)](https://www.nearform.com/contact/?utm_source=open-source&utm_medium=banner&utm_campaign=os-project-pages)

# Static Trigger Code Generation and Event Trigger Usage

## Static Trigger Code Generation

This project now supports generating fully static versioning triggers for your tables. This is useful for environments where you want the trigger logic to be hardcoded for the current table structure, with no dynamic lookups at runtime.

### How to Generate a Static Trigger

1. **Install the code generator function:**

```sh
psql temporal_test < generate_static_versioning_trigger.sql
```

2. **Generate the static trigger code for your table:**

```sql
SELECT generate_static_versioning_trigger('subscriptions', 'subscriptions_history', 'sys_period', true, true) AS sql_code \gset
\echo :sql_code | psql temporal_test
```
This will output and apply the static trigger function and trigger for the `subscriptions` table, using the current schema.

- The arguments are:
- `p_table_name`: The table to version (e.g. 'subscriptions')
- `p_history_table`: The history table (e.g. 'subscriptions_history')
- `p_sys_period`: The system period column (e.g. 'sys_period')
- `p_ignore_unchanged_values`: Only version on actual changes (true/false)
- `p_include_current_version_in_history`: Include current version in history (true/false)

3. **Example: Full workflow**

```sql
CREATE TABLE subscriptions (
name text NOT NULL,
state text NOT NULL
);
ALTER TABLE subscriptions ADD COLUMN sys_period tstzrange NOT NULL DEFAULT tstzrange(current_timestamp, null);
CREATE TABLE subscriptions_history (LIKE subscriptions);
-- Now generate and apply the static trigger:
SELECT generate_static_versioning_trigger('subscriptions', 'subscriptions_history', 'sys_period', true, true) AS sql_code \gset
\echo :sql_code | psql temporal_test
```

4. **After schema changes:**
- If you change the schema of your table or history table, you must re-run the generator to update the static trigger.

## Event Trigger for Automatic Re-rendering

You can set up an event trigger to automatically re-render the static versioning trigger whenever you run an `ALTER TABLE` on your versioned tables.

1. **Install the event trigger function:**

```sh
psql temporal_test < event_trigger_versioning.sql
```

2. **How it works:**
- The event trigger listens for `ALTER TABLE` DDL commands.
- When a table is altered, it automatically calls `generate_static_versioning_trigger` for that table (using a naming convention or metadata lookup for the history table and sys_period column).
- The static trigger is dropped and recreated for the new schema.

3. **Example:**
- Suppose you add a column:
```sql
ALTER TABLE subscriptions ADD COLUMN plan text;
-- The event trigger will automatically re-render the static versioning trigger for 'subscriptions'.
```

4. **Customizing the event trigger:**
- By default, the event trigger assumes the history table is named `<table>_history` and the system period column is `sys_period`.
- You can modify the event trigger function to use your own conventions or a metadata table.

## Advanced Usage

- You can generate and review the static SQL before applying it:
```sql
SELECT generate_static_versioning_trigger('subscriptions', 'subscriptions_history', 'sys_period', true, true);
-- Review the output, then run it manually if desired.
```
- You can use this approach for any table, just adjust the arguments.
- If you use migrations, always re-run the generator after schema changes.

## Troubleshooting

- If you see errors about missing columns or mismatched types, ensure your history table matches the structure of your main table (except for columns you intentionally omit).
- If you change the name of the system period column or history table, update the arguments accordingly.

## See Also
- [versioning_function.sql](./versioning_function.sql) for the original dynamic trigger logic.
- [event_trigger_versioning.sql](./event_trigger_versioning.sql) for the event trigger implementation.
- [generate_static_versioning_trigger.sql](./generate_static_versioning_trigger.sql) for the code generator.

---
17 changes: 17 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import globals from 'globals'
import js from '@eslint/js'
import prettierRecommended from 'eslint-plugin-prettier/recommended'

export default [
js.configs.recommended,
prettierRecommended,
{
languageOptions: {
globals: {
...globals.node
},
ecmaVersion: 'latest',
sourceType: 'module'
}
}
]
50 changes: 50 additions & 0 deletions event_trigger_versioning.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
-- event_trigger_versioning.sql
-- Event trigger to re-render static versioning trigger on ALTER TABLE

CREATE OR REPLACE FUNCTION rerender_versioning_trigger()
RETURNS event_trigger AS $$
DECLARE
obj record;
config record;
sql text;
source_schema text;
source_table text;
history_table text;
sys_period text;
BEGIN
FOR obj IN SELECT * FROM pg_event_trigger_ddl_commands() LOOP
CONTINUE WHEN obj.command_tag <> 'ALTER TABLE';
source_schema := SPLIT_PART(obj.object_identity, '.', 1);
source_table := SPLIT_PART(obj.object_identity, '.', 2);
-- when the source is history, invert to the actual source table
IF source_table ~ '_history$' THEN
source_table := SUBSTRING(source_table, 1, LENGTH(source_table) - 8);
END IF;
-- when a versioned table is altered, we need to re-render the trigger
SELECT *
INTO config
FROM versioning_tables_metadata
WHERE table_name = source_table
AND table_schema = source_schema;

IF FOUND THEN
CALL render_versioning_trigger(
FORMAT('%I.%I', source_schema, source_table),
FORMAT('%I.%I', config.history_table_schema, config.history_table),
config.sys_period,
config.ignore_unchanged_values,
config.include_current_version_in_history,
config.mitigate_update_conflicts,
config.enable_migration_mode
);
END IF;
END LOOP;
END;
$$ LANGUAGE plpgsql;

DROP EVENT TRIGGER IF EXISTS rerender_versioning_on_alter;

CREATE EVENT TRIGGER rerender_versioning_on_alter
ON ddl_command_end
WHEN TAG IN ('ALTER TABLE')
EXECUTE FUNCTION rerender_versioning_trigger();
Loading
Loading