dzql 0.4.3 โ 0.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -6
- package/docs/README.md +5 -23
- package/docs/compiler/README.md +2 -8
- package/docs/examples/README.md +38 -0
- package/docs/examples/blog.sql +160 -0
- package/docs/examples/venue-detail-simple.sql +8 -0
- package/docs/examples/venue-detail-subscribable.sql +45 -0
- package/docs/for-ai/claude-guide.md +32 -0
- package/docs/guides/interpreter-vs-compiler.md +237 -0
- package/docs/guides/many-to-many.md +17 -0
- package/docs/guides/subscriptions.md +2 -2
- package/package.json +3 -2
- package/docs/compiler/OVERNIGHT_BUILD.md +0 -474
- package/docs/compiler/SESSION_SUMMARY.md +0 -266
- package/docs/compiler/SUMMARY.md +0 -528
- package/docs/compiler/dzql-compiler-m2m-change-request 2.md +0 -562
- package/docs/compiler/dzql-compiler-m2m-change-request.md +0 -375
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ PostgreSQL-powered framework with automatic CRUD operations, live query subscrip
|
|
|
7
7
|
- **[Documentation Hub](docs/)** - Complete documentation index
|
|
8
8
|
- **[Getting Started Tutorial](docs/getting-started/tutorial.md)** - Complete tutorial with working todo app
|
|
9
9
|
- **[API Reference](docs/reference/api.md)** - Complete API documentation
|
|
10
|
-
- **[Live Query Subscriptions](docs/getting-started/subscriptions-quick-start.md)** - Real-time denormalized documents
|
|
10
|
+
- **[Live Query Subscriptions](docs/getting-started/subscriptions-quick-start.md)** - Real-time denormalized documents
|
|
11
11
|
- **[Compiler Documentation](docs/compiler/)** - Entity compilation guide and coding standards
|
|
12
12
|
- **[Claude Guide](docs/for-ai/claude-guide.md)** - Development guide for AI assistants
|
|
13
13
|
- **[Venues Example](../venues/)** - Full working application
|
|
@@ -32,7 +32,7 @@ await ws.connect();
|
|
|
32
32
|
const user = await ws.api.save.users({ name: 'Alice' });
|
|
33
33
|
const results = await ws.api.search.users({ filters: { name: 'alice' } });
|
|
34
34
|
|
|
35
|
-
//
|
|
35
|
+
// Live query subscriptions
|
|
36
36
|
const { data, unsubscribe } = await ws.api.subscribe_venue_detail(
|
|
37
37
|
{ venue_id: 123 },
|
|
38
38
|
(updated) => console.log('Venue changed!', updated)
|
|
@@ -59,17 +59,20 @@ See **[Compiler Documentation](docs/compiler/)** for complete usage guide, codin
|
|
|
59
59
|
## Testing
|
|
60
60
|
|
|
61
61
|
```bash
|
|
62
|
-
#
|
|
63
|
-
|
|
62
|
+
# From repository root - start test database
|
|
63
|
+
docker compose up -d
|
|
64
|
+
|
|
65
|
+
# Initialize test database
|
|
66
|
+
bun run test:init
|
|
64
67
|
|
|
65
68
|
# Run tests
|
|
66
69
|
bun test
|
|
67
70
|
|
|
68
71
|
# Stop database
|
|
69
|
-
|
|
72
|
+
docker compose down
|
|
70
73
|
```
|
|
71
74
|
|
|
72
|
-
All tests use `bun:test` framework
|
|
75
|
+
All tests use `bun:test` framework. See **[tests/README.md](../../tests/README.md)** for details.
|
|
73
76
|
|
|
74
77
|
## License
|
|
75
78
|
|
package/docs/README.md
CHANGED
|
@@ -7,6 +7,7 @@ Complete documentation for the DZQL PostgreSQL-powered framework.
|
|
|
7
7
|
New to DZQL? Start here:
|
|
8
8
|
|
|
9
9
|
- **[Tutorial](getting-started/tutorial.md)** - Complete step-by-step guide with a working todo app
|
|
10
|
+
- **[Interpreter vs Compiler](guides/interpreter-vs-compiler.md)** - Understand the two execution modes
|
|
10
11
|
- **[Subscriptions Quick Start](getting-started/subscriptions-quick-start.md)** - Get real-time subscriptions working in 5 minutes
|
|
11
12
|
|
|
12
13
|
## ๐ Guides
|
|
@@ -14,6 +15,9 @@ New to DZQL? Start here:
|
|
|
14
15
|
Feature-specific guides and how-tos:
|
|
15
16
|
|
|
16
17
|
- **[Live Query Subscriptions](guides/subscriptions.md)** - Real-time denormalized documents
|
|
18
|
+
- **[Many-to-Many Relationships](guides/many-to-many.md)** - Junction table management
|
|
19
|
+
- **[Field Defaults](guides/field-defaults.md)** - Auto-populate fields on create
|
|
20
|
+
- **[Custom Functions](guides/custom-functions.md)** - Extend with PostgreSQL or Bun functions
|
|
17
21
|
- **[Client Stores](guides/client-stores.md)** - Pinia store patterns for Vue.js
|
|
18
22
|
|
|
19
23
|
## ๐ Reference
|
|
@@ -29,7 +33,7 @@ Complete API documentation:
|
|
|
29
33
|
- [Quickstart](compiler/QUICKSTART.md) - Get started with the DZQL compiler
|
|
30
34
|
- [Advanced Filters](compiler/ADVANCED_FILTERS.md) - Complex search operators
|
|
31
35
|
- [Coding Standards](compiler/CODING_STANDARDS.md) - Best practices for DZQL code
|
|
32
|
-
- [Comparison](compiler/COMPARISON.md) -
|
|
36
|
+
- [Comparison](compiler/COMPARISON.md) - Runtime vs compiled side-by-side
|
|
33
37
|
|
|
34
38
|
## ๐ค For AI Assistants
|
|
35
39
|
|
|
@@ -40,28 +44,6 @@ Complete API documentation:
|
|
|
40
44
|
- [npm Package](https://www.npmjs.com/package/dzql)
|
|
41
45
|
- [GitHub Repository](https://github.com/blueshed/dzql)
|
|
42
46
|
- [Issue Tracker](https://github.com/blueshed/dzql/issues)
|
|
43
|
-
- [Changelog](../../../CHANGELOG.md)
|
|
44
|
-
- [Contributing](../../../CONTRIBUTING.md)
|
|
45
|
-
|
|
46
|
-
## ๐๏ธ Architecture
|
|
47
|
-
|
|
48
|
-
Looking for architecture and design docs? See the [repository docs](../../../docs/):
|
|
49
|
-
|
|
50
|
-
- [Permissions System](../../../docs/architecture/PERMISSIONS.md)
|
|
51
|
-
- [Project Roadmap](../../../docs/architecture/ROADMAP.md)
|
|
52
|
-
- [Subscription Architecture](../../../docs/architecture/SUBSCRIPTIONS_STRATEGY.md)
|
|
53
|
-
|
|
54
|
-
## ๐งช Development
|
|
55
|
-
|
|
56
|
-
Contributing to DZQL? See development documentation:
|
|
57
|
-
|
|
58
|
-
- [TDD Workflow](../../../docs/development/TDD_WORKFLOW.md)
|
|
59
|
-
- [WebSocket Testing](../../../docs/development/WEBSOCKET_TESTING.md)
|
|
60
|
-
- [Claude Web Setup](../../../docs/development/CLAUDE-WEB.md)
|
|
61
|
-
|
|
62
|
-
## ๐ฆ Package Contents
|
|
63
|
-
|
|
64
|
-
This documentation is published with the npm package. For repository-wide documentation (contributors, development workflow, architecture), see [`/docs/`](../../../docs/) in the repository root.
|
|
65
47
|
|
|
66
48
|
## Need Help?
|
|
67
49
|
|
package/docs/compiler/README.md
CHANGED
|
@@ -6,17 +6,11 @@ The DZQL Compiler transforms declarative entity definitions into optimized Postg
|
|
|
6
6
|
|
|
7
7
|
- **[Quickstart Guide](QUICKSTART.md)** - Get started with the compiler in 5 minutes
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Reference
|
|
10
10
|
|
|
11
11
|
- **[Advanced Filters](ADVANCED_FILTERS.md)** - Complex search operators and patterns
|
|
12
12
|
- **[Coding Standards](CODING_STANDARDS.md)** - Best practices for DZQL code
|
|
13
|
-
|
|
14
|
-
## Reference
|
|
15
|
-
|
|
16
|
-
- **[Comparison](COMPARISON.md)** - How DZQL compares to other approaches
|
|
17
|
-
- **[Session Summary](SESSION_SUMMARY.md)** - Development session documentation
|
|
18
|
-
- **[Summary](SUMMARY.md)** - Compiler overview and architecture
|
|
19
|
-
- **[Overnight Build](OVERNIGHT_BUILD.md)** - Batch compilation process
|
|
13
|
+
- **[Comparison](COMPARISON.md)** - Runtime vs compiled side-by-side
|
|
20
14
|
|
|
21
15
|
## Using the Compiler
|
|
22
16
|
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# DZQL Examples
|
|
2
|
+
|
|
3
|
+
SQL examples demonstrating DZQL entity registration patterns.
|
|
4
|
+
|
|
5
|
+
## Files
|
|
6
|
+
|
|
7
|
+
### blog.sql
|
|
8
|
+
A complete blog application with:
|
|
9
|
+
- Multiple entities (users, posts, comments, tags)
|
|
10
|
+
- Many-to-many relationships (posts โ tags)
|
|
11
|
+
- Soft delete
|
|
12
|
+
- FK includes
|
|
13
|
+
- Permission paths
|
|
14
|
+
- Notification paths
|
|
15
|
+
|
|
16
|
+
### venue-detail-simple.sql
|
|
17
|
+
Basic subscribable definition for venue data.
|
|
18
|
+
|
|
19
|
+
### venue-detail-subscribable.sql
|
|
20
|
+
Full subscribable with relations and permission paths. Demonstrates:
|
|
21
|
+
- Root entity with FK includes
|
|
22
|
+
- Child entity filtering
|
|
23
|
+
- Permission path syntax
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
These files are meant to be run after DZQL core migrations. See the [Tutorial](../getting-started/tutorial.md) for complete setup instructions.
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# After setting up your database with DZQL migrations
|
|
31
|
+
psql $DATABASE_URL < examples/blog.sql
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## See Also
|
|
35
|
+
|
|
36
|
+
- [API Reference](../reference/api.md) - Entity registration parameters
|
|
37
|
+
- [Many-to-Many Guide](../guides/many-to-many.md) - M2M configuration
|
|
38
|
+
- [Subscriptions Guide](../guides/subscriptions.md) - Subscribable patterns
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
-- ============================================================================
|
|
2
|
+
-- Blog Application Example
|
|
3
|
+
-- ============================================================================
|
|
4
|
+
--
|
|
5
|
+
-- This example demonstrates:
|
|
6
|
+
-- - Multiple related entities (users, posts, comments, tags)
|
|
7
|
+
-- - Many-to-many relationships (posts <-> tags via post_tags junction)
|
|
8
|
+
-- - Soft delete (posts.deleted_at)
|
|
9
|
+
-- - FK includes (dereferencing author, post)
|
|
10
|
+
-- - Permission paths (author can edit/delete own content)
|
|
11
|
+
-- - Notification paths (notify post author when comments added)
|
|
12
|
+
--
|
|
13
|
+
-- To use this example:
|
|
14
|
+
-- 1. Create tables first (see CREATE TABLE statements below)
|
|
15
|
+
-- 2. Run the dzql.register_entity() calls to enable CRUD operations
|
|
16
|
+
-- 3. Use the generated API: save_posts, get_posts, search_posts, etc.
|
|
17
|
+
--
|
|
18
|
+
-- For a complete working example with Docker, tests, and frontend:
|
|
19
|
+
-- See packages/blog/ in the DZQL repository
|
|
20
|
+
--
|
|
21
|
+
-- ============================================================================
|
|
22
|
+
|
|
23
|
+
-- Create tables
|
|
24
|
+
CREATE TABLE IF NOT EXISTS users (
|
|
25
|
+
id SERIAL PRIMARY KEY,
|
|
26
|
+
name VARCHAR(255) NOT NULL,
|
|
27
|
+
email VARCHAR(255) UNIQUE NOT NULL,
|
|
28
|
+
password_hash TEXT NOT NULL,
|
|
29
|
+
created_at TIMESTAMP DEFAULT NOW()
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
CREATE TABLE IF NOT EXISTS posts (
|
|
33
|
+
id SERIAL PRIMARY KEY,
|
|
34
|
+
title VARCHAR(500) NOT NULL,
|
|
35
|
+
content TEXT NOT NULL,
|
|
36
|
+
summary TEXT,
|
|
37
|
+
author_id INT REFERENCES users(id),
|
|
38
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
39
|
+
deleted_at TIMESTAMP
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
CREATE TABLE IF NOT EXISTS comments (
|
|
43
|
+
id SERIAL PRIMARY KEY,
|
|
44
|
+
content TEXT NOT NULL,
|
|
45
|
+
post_id INT REFERENCES posts(id),
|
|
46
|
+
author_id INT REFERENCES users(id),
|
|
47
|
+
created_at TIMESTAMP DEFAULT NOW()
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
-- Tags table for categorizing posts
|
|
51
|
+
CREATE TABLE IF NOT EXISTS tags (
|
|
52
|
+
id SERIAL PRIMARY KEY,
|
|
53
|
+
name VARCHAR(50) UNIQUE NOT NULL,
|
|
54
|
+
color VARCHAR(7) DEFAULT '#3788d8'
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
-- Junction table for post-tag many-to-many relationship
|
|
58
|
+
CREATE TABLE IF NOT EXISTS post_tags (
|
|
59
|
+
post_id INT NOT NULL REFERENCES posts(id) ON DELETE CASCADE,
|
|
60
|
+
tag_id INT NOT NULL REFERENCES tags(id) ON DELETE CASCADE,
|
|
61
|
+
PRIMARY KEY (post_id, tag_id)
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
CREATE INDEX IF NOT EXISTS idx_post_tags_post_id ON post_tags(post_id);
|
|
65
|
+
CREATE INDEX IF NOT EXISTS idx_post_tags_tag_id ON post_tags(tag_id);
|
|
66
|
+
|
|
67
|
+
-- ============================================================================
|
|
68
|
+
-- DZQL Entity Registrations
|
|
69
|
+
-- ============================================================================
|
|
70
|
+
|
|
71
|
+
-- Users entity - blog authors
|
|
72
|
+
select dzql.register_entity(
|
|
73
|
+
'users',
|
|
74
|
+
'name',
|
|
75
|
+
array['name', 'email'],
|
|
76
|
+
'{}', -- no FK includes
|
|
77
|
+
false, -- hard delete
|
|
78
|
+
'{}', -- no reverse FK
|
|
79
|
+
'{}', -- no notifications
|
|
80
|
+
jsonb_build_object(
|
|
81
|
+
'view', array[]::text[], -- Anyone can view users
|
|
82
|
+
'create', array[]::text[], -- Anyone can register
|
|
83
|
+
'update', array['@id'], -- Only update own profile
|
|
84
|
+
'delete', array['@id'] -- Only delete own account
|
|
85
|
+
)
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
-- Register tags entity first
|
|
89
|
+
select dzql.register_entity(
|
|
90
|
+
'tags',
|
|
91
|
+
'name',
|
|
92
|
+
array['name'],
|
|
93
|
+
'{}', -- no FK includes
|
|
94
|
+
false, -- hard delete
|
|
95
|
+
'{}', -- no temporal
|
|
96
|
+
'{}', -- no notifications
|
|
97
|
+
jsonb_build_object(
|
|
98
|
+
'view', array[]::text[], -- Anyone can view tags
|
|
99
|
+
'create', array[]::text[], -- Anyone can create tags
|
|
100
|
+
'update', array[]::text[], -- Anyone can update tags
|
|
101
|
+
'delete', array[]::text[] -- Anyone can delete tags
|
|
102
|
+
)
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
-- Posts entity - blog posts with M2M tags
|
|
106
|
+
select dzql.register_entity(
|
|
107
|
+
'posts',
|
|
108
|
+
'title',
|
|
109
|
+
array['title', 'content', 'summary'],
|
|
110
|
+
jsonb_build_object(
|
|
111
|
+
'author', 'users' -- FK to users
|
|
112
|
+
),
|
|
113
|
+
true, -- soft delete (deleted_at)
|
|
114
|
+
'{}', -- no temporal
|
|
115
|
+
'{}', -- no notification paths
|
|
116
|
+
jsonb_build_object(
|
|
117
|
+
'view', array[]::text[], -- Anyone can view posts
|
|
118
|
+
'create', array[]::text[], -- Anyone can create posts
|
|
119
|
+
'update', array['@author_id'], -- Only author can update
|
|
120
|
+
'delete', array['@author_id'] -- Only author can delete
|
|
121
|
+
),
|
|
122
|
+
jsonb_build_object(
|
|
123
|
+
'many_to_many', jsonb_build_object(
|
|
124
|
+
'tags', jsonb_build_object(
|
|
125
|
+
'junction_table', 'post_tags',
|
|
126
|
+
'local_key', 'post_id',
|
|
127
|
+
'foreign_key', 'tag_id',
|
|
128
|
+
'target_entity', 'tags',
|
|
129
|
+
'id_field', 'tag_ids',
|
|
130
|
+
'expand', true -- Include full tag objects in response
|
|
131
|
+
)
|
|
132
|
+
)
|
|
133
|
+
) -- graph_rules with M2M
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
-- Comments entity - blog comments
|
|
137
|
+
select dzql.register_entity(
|
|
138
|
+
'comments',
|
|
139
|
+
'content',
|
|
140
|
+
array['content'],
|
|
141
|
+
jsonb_build_object(
|
|
142
|
+
'post', 'posts',
|
|
143
|
+
'author', 'users'
|
|
144
|
+
),
|
|
145
|
+
false, -- hard delete
|
|
146
|
+
'{}', -- no reverse FK
|
|
147
|
+
jsonb_build_object(
|
|
148
|
+
'post_author', array['@post_id->posts.author_id'],
|
|
149
|
+
'commenters', array['@post_id']
|
|
150
|
+
),
|
|
151
|
+
jsonb_build_object(
|
|
152
|
+
'view', array[]::text[], -- Anyone can view comments
|
|
153
|
+
'create', array[]::text[], -- Anyone can comment
|
|
154
|
+
'update', array['@author_id'], -- Only author can update
|
|
155
|
+
'delete', array[
|
|
156
|
+
'@author_id', -- Author can delete
|
|
157
|
+
'@post_id->posts.author_id' -- Post author can delete
|
|
158
|
+
]
|
|
159
|
+
)
|
|
160
|
+
);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
-- Simplified test subscribable for initial testing
|
|
2
|
+
SELECT dzql.register_subscribable(
|
|
3
|
+
'venue_detail',
|
|
4
|
+
'{"subscribe": ["@org_id->acts_for[org_id=$]{active}.user_id"]}'::jsonb,
|
|
5
|
+
'{"venue_id": "int"}'::jsonb,
|
|
6
|
+
'venues',
|
|
7
|
+
'{"org": "organisations", "sites": {"entity": "sites", "filter": "venue_id=$venue_id"}}'::jsonb
|
|
8
|
+
);
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
-- Example: Venue Detail Subscribable
|
|
2
|
+
-- This subscribable builds a denormalized document containing:
|
|
3
|
+
-- - The venue record
|
|
4
|
+
-- - The organization (FK expanded)
|
|
5
|
+
-- - All sites belonging to the venue
|
|
6
|
+
-- - All packages with their allocations
|
|
7
|
+
|
|
8
|
+
SELECT dzql.register_subscribable(
|
|
9
|
+
'venue_detail',
|
|
10
|
+
|
|
11
|
+
-- Permission: who can subscribe?
|
|
12
|
+
-- Users who act_for the venue's organization (active roles only)
|
|
13
|
+
jsonb_build_object(
|
|
14
|
+
'subscribe', ARRAY['@org_id->acts_for[org_id=$]{active}.user_id']
|
|
15
|
+
),
|
|
16
|
+
|
|
17
|
+
-- Parameters: subscription key
|
|
18
|
+
jsonb_build_object(
|
|
19
|
+
'venue_id', 'int'
|
|
20
|
+
),
|
|
21
|
+
|
|
22
|
+
-- Root entity
|
|
23
|
+
'venues',
|
|
24
|
+
|
|
25
|
+
-- Relations to include in document
|
|
26
|
+
jsonb_build_object(
|
|
27
|
+
-- FK expansion: organisation
|
|
28
|
+
'org', 'organisations',
|
|
29
|
+
|
|
30
|
+
-- Child collection: sites
|
|
31
|
+
'sites', jsonb_build_object(
|
|
32
|
+
'entity', 'sites',
|
|
33
|
+
'filter', 'venue_id=$venue_id'
|
|
34
|
+
),
|
|
35
|
+
|
|
36
|
+
-- Child collection: packages with nested allocations
|
|
37
|
+
'packages', jsonb_build_object(
|
|
38
|
+
'entity', 'packages',
|
|
39
|
+
'filter', 'venue_id=$venue_id',
|
|
40
|
+
'include', jsonb_build_object(
|
|
41
|
+
'allocations', 'allocations'
|
|
42
|
+
)
|
|
43
|
+
)
|
|
44
|
+
)
|
|
45
|
+
);
|
|
@@ -2,6 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
4
|
|
|
5
|
+
## Quick Reference Card
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
DZQL QUICK REFERENCE
|
|
9
|
+
====================
|
|
10
|
+
|
|
11
|
+
5 Operations: get, save, delete, lookup, search
|
|
12
|
+
2 Modes: Interpreter (runtime) | Compiler (static SQL)
|
|
13
|
+
Client API: ws.api.{operation}.{entity}(params)
|
|
14
|
+
Server API: db.api.{operation}.{entity}(params, userId)
|
|
15
|
+
|
|
16
|
+
Entity Registration:
|
|
17
|
+
dzql.register_entity(
|
|
18
|
+
table_name, -- 'todos'
|
|
19
|
+
label_field, -- 'title' (for lookups)
|
|
20
|
+
searchable_fields, -- ARRAY['title', 'description']
|
|
21
|
+
fk_includes, -- '{"org": "organisations"}'
|
|
22
|
+
soft_delete, -- false
|
|
23
|
+
temporal_fields, -- '{}'
|
|
24
|
+
notification_paths, -- '{"ownership": ["@org_id->acts_for..."]}'
|
|
25
|
+
permission_paths, -- '{"view": [], "create": [...]}'
|
|
26
|
+
graph_rules, -- '{"on_create": {...}, "many_to_many": {...}}'
|
|
27
|
+
field_defaults -- '{"owner_id": "@user_id"}'
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
M2M id_field naming: tag_ids (singular + _ids), NOT tags_ids
|
|
31
|
+
Permission [] = public, omitted = denied
|
|
32
|
+
Path syntax: @field->table[filter]{temporal}.target_field
|
|
33
|
+
|
|
34
|
+
Compile: dzql compile entities.sql -o compiled/
|
|
35
|
+
```
|
|
36
|
+
|
|
5
37
|
## Project Overview
|
|
6
38
|
|
|
7
39
|
DZQL is a PostgreSQL-powered framework that eliminates CRUD boilerplate by providing automatic database operations, real-time WebSocket synchronization, and graph-based relationship management. The core concept: register an entity in PostgreSQL and instantly get 5 standard operations (get, save, delete, lookup, search) plus real-time notifications with zero code.
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
# Interpreter vs Compiler Mode
|
|
2
|
+
|
|
3
|
+
DZQL offers two execution modes for your entities. Understanding when to use each is fundamental to getting the best out of the framework.
|
|
4
|
+
|
|
5
|
+
## Quick Summary
|
|
6
|
+
|
|
7
|
+
| Aspect | Interpreter | Compiler |
|
|
8
|
+
|--------|-------------|----------|
|
|
9
|
+
| **Setup** | Register entity, use immediately | Register entity, compile, deploy SQL |
|
|
10
|
+
| **Performance** | ~8-12ms per operation | ~2-4ms per operation |
|
|
11
|
+
| **Debugging** | Opaque (dynamic SQL) | Transparent (static SQL) |
|
|
12
|
+
| **Best For** | Development, prototyping | Production, performance-critical |
|
|
13
|
+
|
|
14
|
+
## How It Works
|
|
15
|
+
|
|
16
|
+
### Interpreter Mode (Runtime)
|
|
17
|
+
|
|
18
|
+
Entity configuration is stored as JSON in `dzql.entities` table and parsed at runtime:
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
Client Request โ generic_exec() โ Parse JSON config โ Build SQL โ Execute
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**Characteristics:**
|
|
25
|
+
- Zero build step - changes take effect immediately
|
|
26
|
+
- JSON config parsed on every request
|
|
27
|
+
- Dynamic SQL generated at runtime
|
|
28
|
+
- Generic query plans (harder to optimize)
|
|
29
|
+
|
|
30
|
+
**Usage:**
|
|
31
|
+
```sql
|
|
32
|
+
-- Register entity
|
|
33
|
+
SELECT dzql.register_entity('todos', 'title', ARRAY['title'], ...);
|
|
34
|
+
|
|
35
|
+
-- Use immediately via generic executor
|
|
36
|
+
SELECT dzql.generic_exec('save', 'todos', '{"title": "Buy milk"}'::jsonb, 1);
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Compiler Mode (Static)
|
|
40
|
+
|
|
41
|
+
Entity configuration is compiled into dedicated PostgreSQL functions:
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
Entity Definition โ dzql compile โ Static SQL Functions โ Deploy โ Execute
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Characteristics:**
|
|
48
|
+
- Build step required
|
|
49
|
+
- No JSON parsing at runtime
|
|
50
|
+
- Static SQL with specific query plans
|
|
51
|
+
- PostgreSQL can optimize and cache plans
|
|
52
|
+
|
|
53
|
+
**Usage:**
|
|
54
|
+
```bash
|
|
55
|
+
# Compile entities to SQL
|
|
56
|
+
dzql compile entities.sql -o compiled/
|
|
57
|
+
|
|
58
|
+
# Deploy to database
|
|
59
|
+
psql < compiled/entities.sql
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
```sql
|
|
63
|
+
-- Use compiled functions directly
|
|
64
|
+
SELECT save_todos('{"title": "Buy milk"}'::jsonb, 1);
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## The Server Automatically Chooses
|
|
68
|
+
|
|
69
|
+
The DZQL server (`db.js`) automatically tries compiled functions first:
|
|
70
|
+
|
|
71
|
+
```javascript
|
|
72
|
+
// In callDZQLOperation()
|
|
73
|
+
try {
|
|
74
|
+
// Try compiled function: save_todos()
|
|
75
|
+
const result = await sql.unsafe(`SELECT save_todos($1, $2)`, [data, userId]);
|
|
76
|
+
return result[0].result;
|
|
77
|
+
} catch (error) {
|
|
78
|
+
// If compiled function doesn't exist, fall back to interpreter
|
|
79
|
+
if (error.message.includes('save_todos') && error.code === '42883') {
|
|
80
|
+
return await sql`SELECT dzql.generic_exec('save', 'todos', ${data}, ${userId})`;
|
|
81
|
+
}
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
This means you can:
|
|
87
|
+
1. Start with interpreter mode during development
|
|
88
|
+
2. Compile and deploy when ready for production
|
|
89
|
+
3. Mix and match - some entities compiled, others interpreted
|
|
90
|
+
|
|
91
|
+
## Performance Comparison
|
|
92
|
+
|
|
93
|
+
### Interpreter (Runtime Parsing)
|
|
94
|
+
|
|
95
|
+
```sql
|
|
96
|
+
SELECT dzql.generic_exec('save', 'venues', '{"name": "MSG"}'::jsonb, 42);
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Execution steps:**
|
|
100
|
+
1. Fetch entity config from `dzql.entities` (table lookup)
|
|
101
|
+
2. Parse `permission_paths` JSONB
|
|
102
|
+
3. Build permission query dynamically
|
|
103
|
+
4. Parse `graph_rules` JSONB
|
|
104
|
+
5. Execute rules via dynamic SQL
|
|
105
|
+
6. Parse `notification_paths` JSONB
|
|
106
|
+
7. Resolve paths dynamically
|
|
107
|
+
8. Execute the actual save
|
|
108
|
+
|
|
109
|
+
**Cost:** ~8-12ms, 3-5 JSONB parses, unpredictable query plans
|
|
110
|
+
|
|
111
|
+
### Compiler (Pre-built Functions)
|
|
112
|
+
|
|
113
|
+
```sql
|
|
114
|
+
SELECT save_venues('{"name": "MSG"}'::jsonb, 42);
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**Execution steps:**
|
|
118
|
+
1. Call `can_update_venues()` - pre-compiled permission check
|
|
119
|
+
2. Execute INSERT/UPDATE - direct SQL
|
|
120
|
+
3. Call `graph_venues_on_create()` - pre-compiled graph rules
|
|
121
|
+
4. Call `resolve_notification_paths_venues()` - pre-compiled
|
|
122
|
+
5. Done
|
|
123
|
+
|
|
124
|
+
**Cost:** ~2-4ms, 0 JSONB parses, optimized query plans
|
|
125
|
+
|
|
126
|
+
## When to Use Each
|
|
127
|
+
|
|
128
|
+
### Use Interpreter When:
|
|
129
|
+
- Rapid prototyping and development
|
|
130
|
+
- Schema changes frequently
|
|
131
|
+
- Learning DZQL concepts
|
|
132
|
+
- Small applications with low traffic
|
|
133
|
+
- Need maximum flexibility
|
|
134
|
+
|
|
135
|
+
### Use Compiler When:
|
|
136
|
+
- Production deployments
|
|
137
|
+
- Performance is critical
|
|
138
|
+
- Need predictable query performance
|
|
139
|
+
- Want reviewable/auditable SQL
|
|
140
|
+
- Large teams (generated SQL is easy to review)
|
|
141
|
+
- Complex permission or graph rules
|
|
142
|
+
|
|
143
|
+
### Recommended Workflow:
|
|
144
|
+
1. **Development:** Use interpreter for fast iteration
|
|
145
|
+
2. **Staging:** Compile and test performance
|
|
146
|
+
3. **Production:** Deploy compiled functions
|
|
147
|
+
|
|
148
|
+
## Compiling Entities
|
|
149
|
+
|
|
150
|
+
### Via CLI
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
# Single file
|
|
154
|
+
dzql compile database/entities.sql -o compiled/
|
|
155
|
+
|
|
156
|
+
# Multiple files
|
|
157
|
+
dzql compile database/*.sql -o compiled/
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Programmatically
|
|
161
|
+
|
|
162
|
+
```javascript
|
|
163
|
+
import { DZQLCompiler } from 'dzql/compiler';
|
|
164
|
+
|
|
165
|
+
const compiler = new DZQLCompiler();
|
|
166
|
+
const result = compiler.compileFromSQL(sqlContent);
|
|
167
|
+
|
|
168
|
+
console.log(result.sql); // Generated PostgreSQL functions
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### What Gets Generated
|
|
172
|
+
|
|
173
|
+
For each entity, the compiler generates:
|
|
174
|
+
|
|
175
|
+
| Function | Purpose |
|
|
176
|
+
|----------|---------|
|
|
177
|
+
| `get_{entity}(id, user_id)` | Retrieve single record |
|
|
178
|
+
| `save_{entity}(data, user_id)` | Create or update |
|
|
179
|
+
| `delete_{entity}(id, user_id)` | Delete record |
|
|
180
|
+
| `lookup_{entity}(term, user_id)` | Autocomplete search |
|
|
181
|
+
| `search_{entity}(filters, user_id)` | Paginated search |
|
|
182
|
+
| `can_view_{entity}(user_id, record)` | Permission check |
|
|
183
|
+
| `can_create_{entity}(user_id, record)` | Permission check |
|
|
184
|
+
| `can_update_{entity}(user_id, record)` | Permission check |
|
|
185
|
+
| `can_delete_{entity}(user_id, record)` | Permission check |
|
|
186
|
+
|
|
187
|
+
## Debugging
|
|
188
|
+
|
|
189
|
+
### Interpreter Mode
|
|
190
|
+
|
|
191
|
+
Debugging is harder because SQL is generated dynamically:
|
|
192
|
+
|
|
193
|
+
```sql
|
|
194
|
+
-- You see this
|
|
195
|
+
EXPLAIN ANALYZE SELECT dzql.generic_exec('save', 'venues', '...');
|
|
196
|
+
|
|
197
|
+
-- But the actual query is hidden inside
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Compiler Mode
|
|
201
|
+
|
|
202
|
+
Standard PostgreSQL tools work:
|
|
203
|
+
|
|
204
|
+
```sql
|
|
205
|
+
-- See the actual function
|
|
206
|
+
\sf save_venues
|
|
207
|
+
|
|
208
|
+
-- Analyze performance
|
|
209
|
+
EXPLAIN ANALYZE SELECT save_venues('{"name": "MSG"}'::jsonb, 42);
|
|
210
|
+
|
|
211
|
+
-- Check slow queries
|
|
212
|
+
SELECT * FROM pg_stat_statements WHERE query LIKE '%save_venues%';
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Feature Parity
|
|
216
|
+
|
|
217
|
+
Both modes support the same features:
|
|
218
|
+
|
|
219
|
+
| Feature | Interpreter | Compiler |
|
|
220
|
+
|---------|-------------|----------|
|
|
221
|
+
| CRUD operations | โ
| โ
|
|
|
222
|
+
| Permission paths | โ
| โ
|
|
|
223
|
+
| Graph rules | โ
| โ
|
|
|
224
|
+
| Notification paths | โ
| โ
|
|
|
225
|
+
| FK includes | โ
| โ
|
|
|
226
|
+
| Many-to-many | โ
| โ
|
|
|
227
|
+
| Field defaults | โ
| โ
|
|
|
228
|
+
| Soft delete | โ
| โ
|
|
|
229
|
+
| Temporal fields | โ
| โ
|
|
|
230
|
+
|
|
231
|
+
The difference is purely in execution speed and debuggability, not functionality.
|
|
232
|
+
|
|
233
|
+
## See Also
|
|
234
|
+
|
|
235
|
+
- [Compiler Quickstart](../compiler/QUICKSTART.md) - Get started with compilation
|
|
236
|
+
- [Compiler Comparison](../compiler/COMPARISON.md) - Detailed side-by-side analysis
|
|
237
|
+
- [API Reference](../reference/api.md) - The 5 operations
|
|
@@ -161,6 +161,23 @@ SELECT dzql.register_entity(
|
|
|
161
161
|
| `id_field` | Yes | Field name for ID array in API | `"tag_ids"` |
|
|
162
162
|
| `expand` | No | Include full objects (default: false) | `false` or `true` |
|
|
163
163
|
|
|
164
|
+
### Naming Convention for `id_field`
|
|
165
|
+
|
|
166
|
+
**Important:** The `id_field` should use the **singular** form of the target entity, not plural:
|
|
167
|
+
|
|
168
|
+
| Target Entity | Correct `id_field` | Wrong |
|
|
169
|
+
|---------------|-------------------|-------|
|
|
170
|
+
| `tags` | `tag_ids` | `tags_ids` |
|
|
171
|
+
| `roles` | `role_ids` | `roles_ids` |
|
|
172
|
+
| `categories` | `category_ids` | `categories_ids` |
|
|
173
|
+
| `users` | `user_ids` | `users_ids` |
|
|
174
|
+
|
|
175
|
+
This convention matches common ORM patterns and is more readable:
|
|
176
|
+
- `tag_ids: [1, 2, 3]` reads as "tag IDs"
|
|
177
|
+
- `tags_ids: [1, 2, 3]` reads awkwardly as "tags IDs"
|
|
178
|
+
|
|
179
|
+
Using the wrong naming will cause the M2M sync to fail because the generated code looks for a field name that doesn't match what clients send.
|
|
180
|
+
|
|
164
181
|
### The `expand` Flag
|
|
165
182
|
|
|
166
183
|
Controls whether full related objects are included in responses:
|
|
@@ -28,7 +28,7 @@ Live Query Subscriptions (Pattern 1 from vision.md) enable clients to subscribe
|
|
|
28
28
|
Create a SQL file with your subscribable definition:
|
|
29
29
|
|
|
30
30
|
```sql
|
|
31
|
-
-- examples/
|
|
31
|
+
-- examples/venue-detail-subscribable.sql
|
|
32
32
|
SELECT dzql.register_subscribable(
|
|
33
33
|
'venue_detail',
|
|
34
34
|
'{"subscribe": ["@org_id->acts_for[org_id=$]{active}.user_id"]}'::jsonb,
|
|
@@ -56,7 +56,7 @@ SELECT dzql.register_subscribable(
|
|
|
56
56
|
```bash
|
|
57
57
|
# Compile subscribable to SQL functions
|
|
58
58
|
bun packages/dzql/src/compiler/cli/compile-subscribable.js \
|
|
59
|
-
examples/
|
|
59
|
+
examples/venue-detail-subscribable.sql \
|
|
60
60
|
> /tmp/venue_detail.sql
|
|
61
61
|
|
|
62
62
|
# Deploy to database
|