pgterra 0.1.0 → 0.1.2

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.
Files changed (3) hide show
  1. package/README.md +64 -499
  2. package/dist/index.js +100670 -49
  3. package/package.json +47 -35
package/README.md CHANGED
@@ -1,544 +1,109 @@
1
- # PGTerra
1
+ # pgterra
2
2
 
3
- A declarative Infrastructure as Code tool for PostgreSQL databases, similar to Terraform but specifically designed for database schema management.
3
+ Declarative schema management for Postgres. Define your desired schema, pgterra handles the migrations.
4
4
 
5
- ## 🔍 Overview
5
+ ## Quick Start
6
6
 
7
- PGTerra follows a declarative approach where you define your **desired database state** in a `schema.sql` file, and the tool automatically figures out what changes are needed to bring your database to that state. No more writing manual migration scripts - just describe what you want and let PGTerra handle the rest!
8
-
9
- ### Key Concepts
10
-
11
- - **Declarative, Not Imperative**: You describe WHAT you want, not HOW to get there
12
- - **State-Based Management**: Compare current vs desired state and generate minimal changes
13
- - **Safety First**: Preview changes with `plan` before applying them
14
- - **Idempotent Operations**: Running the same schema multiple times is safe
15
-
16
- ## 🏗️ How It Works
17
-
18
- ```mermaid
19
- graph TD
20
- A["schema.sql<br/>(Desired State)"] --> B["Schema Parser"]
21
- C["PostgreSQL Database<br/>(Current State)"] --> D["Database Inspector"]
22
-
23
- B --> E["Desired Schema<br/>(Parsed Objects)"]
24
- D --> F["Current Schema<br/>(Queried Objects)"]
25
-
26
- E --> G["Schema Differ"]
27
- F --> G
28
-
29
- G --> H["Migration Plan<br/>(SQL Statements)"]
30
-
31
- H --> I{"Command Type"}
32
- I -->|plan| J["Display Changes"]
33
- I -->|apply| K["Execute Changes"]
34
-
35
- K --> L["Updated Database"]
36
- ```
37
-
38
- ### Architecture Components
39
-
40
- #### 1. **Schema Parser**
41
-
42
- - Converts your `schema.sql` file into structured objects
43
- - Uses `sql-parser-cst` for robust SQL parsing
44
- - Handles CREATE TABLE statements with columns, constraints, and data types
45
-
46
- #### 2. **Database Inspector**
47
-
48
- - Queries the current database structure using PostgreSQL's `information_schema`
49
- - Extracts table definitions, column details, constraints, and metadata
50
- - Normalizes database state into the same format as parsed schema
51
-
52
- #### 3. **Schema Differ**
53
-
54
- - Compares desired vs current state
55
- - Generates optimized migration plan with proper operation ordering
56
- - Handles complex scenarios like type conversions, constraint changes, and data preservation
57
-
58
- #### 4. **Migration Planner & Executor**
59
-
60
- - Orchestrates the diffing process safely
61
- - Executes SQL statements with proper error handling
62
- - Provides detailed feedback on changes applied
63
-
64
- ## 🔄 Step-by-Step Process
65
-
66
- ```mermaid
67
- sequenceDiagram
68
- participant CLI as CLI Command<br/>(plan/apply)
69
- participant SS as Schema Service
70
- participant SP as Schema Parser
71
- participant DI as Database Inspector
72
- participant SD as Schema Differ
73
- participant ME as Migration Executor
74
- participant DB as PostgreSQL Database
75
-
76
- CLI->>SS: plan("schema.sql")
77
- SS->>SP: parseSchemaFile("schema.sql")
78
- SP->>SP: Parse CREATE TABLE statements
79
- SP-->>SS: Desired Schema (Table[])
80
-
81
- SS->>DB: Connect
82
- SS->>DI: getCurrentSchema(client)
83
- DI->>DB: Query information_schema
84
- DB-->>DI: Table metadata
85
- DI-->>SS: Current Schema (Table[])
86
-
87
- SS->>SD: generateMigrationPlan(desired, current)
88
- SD->>SD: Compare schemas
89
- SD->>SD: Generate SQL statements
90
- SD-->>SS: Migration Plan
91
-
92
- alt Command is "plan"
93
- SS->>CLI: Display changes
94
- else Command is "apply"
95
- SS->>ME: executePlan(client, plan)
96
- ME->>DB: Execute SQL statements
97
- DB-->>ME: Success/Failure
98
- ME-->>SS: Results
99
- end
100
-
101
- SS->>DB: Disconnect
7
+ ```bash
8
+ npm install -g pgterra
102
9
  ```
103
10
 
104
- ## 📝 Simple Example
105
-
106
- Let's walk through a simple example to see how PGTerra works:
11
+ ## How it works
107
12
 
108
- ### Starting Point: Empty Database
109
-
110
- ```sql
111
- -- Database has no tables
112
- ```
113
-
114
- ### Define Desired State: `schema.sql`
13
+ **1. Start with a schema:**
115
14
 
116
15
  ```sql
16
+ -- schema.sql
117
17
  CREATE TABLE users (
118
- id SERIAL PRIMARY KEY,
119
- email VARCHAR(255) NOT NULL,
120
- name VARCHAR(100) NOT NULL,
121
- created_at TIMESTAMP DEFAULT NOW()
18
+ id SERIAL PRIMARY KEY,
19
+ email VARCHAR(255) NOT NULL UNIQUE,
20
+ created_at TIMESTAMP DEFAULT NOW()
122
21
  );
123
22
  ```
124
23
 
125
- ### Run Plan Command
126
-
127
24
  ```bash
128
- pgterra plan
25
+ pgterra apply # Creates the table
129
26
  ```
130
27
 
131
- **Output:**
132
-
133
- ```
134
- 📋 Analyzing schema changes...
135
- 📝 Found 1 change(s) to apply:
136
-
137
- 1. CREATE TABLE users (id SERIAL PRIMARY KEY, email VARCHAR(255) NOT NULL, name VARCHAR(100) NOT NULL, created_at TIMESTAMP DEFAULT NOW());
138
- ```
139
-
140
- ### Apply Changes
141
-
142
- ```bash
143
- pgterra apply
144
- ```
145
-
146
- **Result:** The `users` table is created in your database.
147
-
148
- ## 🔄 Schema Evolution Example
149
-
150
- Now let's modify the existing table:
151
-
152
- ### Current Database State:
28
+ **2. Update your schema declaratively:**
153
29
 
154
30
  ```sql
31
+ -- schema.sql
155
32
  CREATE TABLE users (
156
- id SERIAL PRIMARY KEY,
157
- email VARCHAR(255) NOT NULL,
158
- name VARCHAR(100) NOT NULL,
159
- created_at TIMESTAMP DEFAULT NOW()
33
+ id SERIAL PRIMARY KEY,
34
+ email VARCHAR(255) NOT NULL UNIQUE,
35
+ full_name VARCHAR(200) NOT NULL, -- new column
36
+ is_active BOOLEAN DEFAULT true, -- another new column
37
+ created_at TIMESTAMP DEFAULT NOW()
160
38
  );
161
- ```
162
-
163
- ### Update Desired State: `schema.sql`
164
39
 
165
- ```sql
166
- CREATE TABLE users (
167
- id SERIAL PRIMARY KEY,
168
- email VARCHAR(255) NOT NULL,
169
- full_name VARCHAR(200) NOT NULL, -- renamed and expanded
170
- created_at TIMESTAMP DEFAULT NOW(),
171
- is_active BOOLEAN DEFAULT true -- new column
40
+ CREATE TABLE posts ( -- new table
41
+ id SERIAL PRIMARY KEY,
42
+ title VARCHAR(255) NOT NULL,
43
+ user_id INTEGER REFERENCES users(id),
44
+ created_at TIMESTAMP DEFAULT NOW()
172
45
  );
173
46
  ```
174
47
 
175
- ### Generated Migration Plan:
176
-
177
- ```sql
178
- ALTER TABLE users ADD COLUMN full_name VARCHAR(200) NOT NULL;
179
- ALTER TABLE users ADD COLUMN is_active BOOLEAN DEFAULT true;
180
- ALTER TABLE users DROP COLUMN name;
181
- ```
182
-
183
- **PGTerra automatically:**
184
-
185
- - Detected the new `full_name` column
186
- - Added the new `is_active` column with default
187
- - Removed the old `name` column
188
- - Preserved all existing data
189
-
190
- ## 🛠️ Advanced Features
191
-
192
- ### Smart Type Conversions
193
-
194
- When changing column types, PGTerra automatically handles complex conversions:
195
-
196
- ```sql
197
- -- Before: age VARCHAR(10)
198
- -- After: age INTEGER
199
-
200
- -- Generated migration:
201
- ALTER TABLE users ALTER COLUMN age TYPE INTEGER USING age::INTEGER;
202
- ```
203
-
204
- ### Constraint Management
205
-
206
- PGTerra intelligently handles constraint changes:
207
-
208
- ```mermaid
209
- flowchart TD
210
- A["Column Modification"] --> B["Drop Conflicting Defaults"]
211
- B --> C["Change Data Type"]
212
- C --> D["Set New Default"]
213
- D --> E["Add/Drop NOT NULL"]
214
-
215
- F["Complex Example:"] --> G["VARCHAR → INTEGER<br/>with new DEFAULT"]
216
- G --> H["1. DROP DEFAULT"]
217
- H --> I["2. ALTER TYPE USING"]
218
- I --> J["3. SET DEFAULT"]
219
- J --> K["4. SET NOT NULL"]
220
- ```
221
-
222
- ### Operation Ordering
223
-
224
- The differ carefully orders operations to avoid conflicts:
225
-
226
- 1. Drop conflicting defaults
227
- 2. Change data types (with USING clauses when needed)
228
- 3. Set new defaults
229
- 4. Modify NULL/NOT NULL constraints
230
-
231
- ## 🚀 Installation & Usage
232
-
233
- ### Prerequisites
234
-
235
- - Node.js 18+ with Bun package manager
236
- - PostgreSQL database
237
- - Database connection configured
238
-
239
- ### Installation
48
+ **3. pgterra calculates the migration:**
240
49
 
241
50
  ```bash
242
- bun install
243
- ```
244
-
245
- ### Commands
246
-
247
- #### Plan Changes
51
+ $ pgterra plan
52
+ 📋 Analyzing schema changes...
248
53
 
249
- Preview what changes would be made without applying them:
54
+ Planned changes:
55
+ 1. ALTER TABLE users ADD COLUMN full_name VARCHAR(200) NOT NULL
56
+ 2. ALTER TABLE users ADD COLUMN is_active BOOLEAN DEFAULT true
57
+ 3. CREATE TABLE posts (id SERIAL PRIMARY KEY, title VARCHAR(255) NOT NULL, user_id INTEGER REFERENCES users(id), created_at TIMESTAMP DEFAULT NOW())
250
58
 
251
- ```bash
252
- bun run cli plan
59
+ $ pgterra apply # Applies the changes safely
253
60
  ```
254
61
 
255
- #### Apply Changes
256
-
257
- Execute the planned changes:
62
+ **That's it.** No migration files, no manual ALTER statements, no dependency ordering. Just define what you want.
258
63
 
259
- ```bash
260
- bun run cli apply
261
- ```
64
+ ## Configuration
262
65
 
263
- #### Use Custom Schema File
66
+ Set your database connection:
264
67
 
265
68
  ```bash
266
- bun run cli plan --file custom-schema.sql
267
- bun run cli apply --file custom-schema.sql
69
+ export DB_HOST=localhost
70
+ export DB_PORT=5432
71
+ export DB_NAME=mydb
72
+ export DB_USER=postgres
73
+ export DB_PASSWORD=password
268
74
  ```
269
75
 
270
- ### Configuration
271
-
272
- Create a configuration file (e.g., `pgterra.config.json`):
273
-
274
- ```json
275
- {
276
- "database": {
277
- "host": "localhost",
278
- "port": 5432,
279
- "database": "myapp",
280
- "username": "postgres",
281
- "password": "password"
282
- }
283
- }
284
- ```
285
-
286
- ## 📁 Project Structure
287
-
288
- ```
289
- pgterra/
290
- ├── src/
291
- │ ├── cli/
292
- │ │ └── commands/ # CLI command handlers
293
- │ ├── core/
294
- │ │ ├── database/ # Database connection & client
295
- │ │ ├── migration/ # Migration planning & execution
296
- │ │ └── schema/ # Schema parsing, inspection, diffing
297
- │ ├── types/ # TypeScript type definitions
298
- │ └── utils/ # Shared utilities
299
- ├── schema.sql # Your database schema definition
300
- └── README.md
301
- ```
302
-
303
- ## 🔧 Technical Deep Dive
304
-
305
- ### Schema Differ Logic
76
+ ## Features
306
77
 
307
- The `SchemaDiffer` contains the most sophisticated logic:
78
+ - Tables, columns, and data types
79
+ - ✅ Primary keys, foreign keys, check constraints, unique constraints
80
+ - ✅ Indexes (btree, gin, gist, partial, expression-based)
81
+ - ✅ ENUM types
82
+ - ✅ Dependency resolution for complex schemas
83
+ - ✅ Data-safe migrations with validation
84
+ - ✅ Destructive operation protection
308
85
 
309
- ```mermaid
310
- flowchart TD
311
- A["Compare Schemas"] --> B["Check Tables"]
86
+ ## Why declarative?
312
87
 
313
- B --> C["New Tables"]
314
- B --> D["Existing Tables"]
315
- B --> E["Dropped Tables"]
88
+ Like Terraform for infrastructure, pgterra lets you define *what* you want, not *how* to get there:
316
89
 
317
- C --> F["Generate CREATE TABLE"]
318
- E --> G["Generate DROP TABLE"]
90
+ - **Version control your complete schema** - not scattered migration files
91
+ - **No migration ordering issues** - pgterra handles dependencies
92
+ - **Easier code reviews** - see the full schema state, not just changes
93
+ - **Safe schema changes** - preview before applying, with rollback support
319
94
 
320
- D --> H["Compare Columns"]
95
+ ## Development
321
96
 
322
- H --> I["New Columns"]
323
- H --> J["Modified Columns"]
324
- H --> K["Dropped Columns"]
325
-
326
- I --> L["Generate ADD COLUMN"]
327
- K --> M["Generate DROP COLUMN"]
328
-
329
- J --> N["Check What Changed"]
330
- N --> O["Data Type"]
331
- N --> P["Default Value"]
332
- N --> Q["Nullable"]
333
-
334
- O --> R["ALTER COLUMN TYPE<br/>(with USING if needed)"]
335
- P --> S["SET/DROP DEFAULT"]
336
- Q --> T["SET/DROP NOT NULL"]
337
-
338
- R --> U["Order Operations<br/>Carefully"]
339
- S --> U
340
- T --> U
341
-
342
- U --> V["Final SQL Statements"]
343
- ```
344
-
345
- ### Data Structure
346
-
347
- Tables and columns are represented as TypeScript interfaces:
348
-
349
- ```typescript
350
- interface Column {
351
- name: string;
352
- type: string;
353
- nullable: boolean;
354
- default?: string;
355
- primary?: boolean;
356
- }
357
-
358
- interface Table {
359
- name: string;
360
- columns: Column[];
361
- }
362
- ```
363
-
364
- ## 🎯 Design Principles
365
-
366
- ```mermaid
367
- graph LR
368
- A["Declarative<br/>Approach"] --> B["You describe<br/>WHAT you want"]
369
- B --> C["Tool figures out<br/>HOW to get there"]
97
+ ```bash
98
+ git clone https://github.com/elitan/pgterra.git
99
+ cd pgterra
100
+ bun install
370
101
 
371
- D["State-Based<br/>Management"] --> E["Compare current vs desired"]
372
- E --> F["Generate minimal<br/>change set"]
102
+ # Set up test database connection
103
+ export DATABASE_URL="postgres://user:password@localhost:5432/test_db"
373
104
 
374
- G["Safety First"] --> H["Preview changes<br/>with 'plan'"]
375
- H --> I["Explicit 'apply'<br/>to execute"]
105
+ # Run tests
106
+ bun test
376
107
  ```
377
108
 
378
- ## 🚦 Current Status & Roadmap
379
-
380
- ### ✅ **Implemented Features**
381
-
382
- #### **Core Table Operations**
383
-
384
- - ✅ **Table Creation** - Create new tables from schema definitions
385
- - ✅ **Table Dropping** - Remove tables not in desired schema
386
- - ✅ **Mixed Table Operations** - Add, keep, and remove tables in single migration
387
-
388
- #### **Column Management**
389
-
390
- - ✅ **Column Addition** - Add new columns with various data types
391
- - ✅ **Column Removal** - Remove columns while preserving data
392
- - ✅ **Column Type Changes** - Convert between compatible data types
393
- - ✅ **Default Values** - Handle columns with default values and constraints
394
- - ✅ **Nullable Constraints** - Manage NOT NULL/NULL constraints
395
-
396
- #### **Advanced Type Conversions**
397
-
398
- - ✅ **String Type Conversions** - VARCHAR ↔ TEXT with length handling
399
- - ✅ **Numeric Type Conversions** - INTEGER ↔ BIGINT, DECIMAL precision changes
400
- - ✅ **Boolean Type Conversions** - All PostgreSQL boolean representations
401
- - ✅ **Smart USING Clauses** - Automatic type conversion logic
402
- - ✅ **Unicode Support** - Full Unicode, emoji, and multi-byte character handling
403
-
404
- #### **Data Integrity & Safety**
405
-
406
- - ✅ **Data Preservation** - All migrations preserve existing data
407
- - ✅ **Operation Ordering** - Smart ordering to avoid constraint conflicts
408
- - ✅ **Boundary Value Testing** - Edge cases for all data types
409
- - ✅ **Large Dataset Support** - Performance-tested with large tables
410
-
411
- #### **Performance & Reliability**
412
-
413
- - ✅ **Performance Monitoring** - Benchmark tracking and regression detection
414
- - ✅ **Concurrent Operations** - Lock management and concurrent access
415
- - ✅ **Memory Efficiency** - Optimized for large datasets
416
- - ✅ **Error Handling** - Graceful handling of edge cases and failures
417
-
418
- #### **CLI & Tooling**
419
-
420
- - ✅ **Plan Command** - Preview changes before applying
421
- - ✅ **Apply Command** - Execute migrations safely
422
- - ✅ **Schema File Support** - Custom schema file paths
423
- - ✅ **Database Configuration** - Connection management
424
- - ✅ **Comprehensive Testing** - 20+ test suites covering edge cases
425
-
426
- ### 🔄 **In Progress**
427
-
428
- #### **Primary Key Support**
429
-
430
- - 🔄 **Primary Key Detection** - Currently handles SERIAL PRIMARY KEY
431
- - 🔄 **Composite Primary Keys** - Multi-column primary keys
432
- - 🔄 **Primary Key Changes** - Adding/removing/modifying primary keys
433
-
434
- ### 📋 **Planned Features**
435
-
436
- #### **Core Schema Objects**
437
-
438
- - [ ] **Indexes**
439
- - B-tree, GIN, GiST, BRIN, Hash indexes
440
- - Unique, partial, expression indexes
441
- - Concurrent creation and REINDEX operations
442
- - [ ] **Advanced Constraints**
443
- - Foreign Keys with CASCADE/RESTRICT/SET NULL actions
444
- - Unique Constraints (multi-column)
445
- - Check Constraints with custom expressions
446
- - DEFERRABLE constraints
447
-
448
- #### **Advanced PostgreSQL Features**
449
-
450
- - [ ] **Sequences**
451
- - Custom sequences with start/increment/min/max
452
- - Sequence ownership and dependencies
453
- - [ ] **Views & Materialized Views**
454
- - Standard view creation and OR REPLACE
455
- - Materialized view management and refresh
456
- - [ ] **Custom Types**
457
- - ENUMs with value management
458
- - Composite Types for complex data structures
459
- - Domain types with constraints
460
-
461
- #### **Functions & Triggers**
462
-
463
- - [ ] **Stored Functions/Procedures**
464
- - PL/pgSQL and SQL functions
465
- - Parameter and return type management
466
- - Function versioning (OR REPLACE)
467
- - [ ] **Triggers**
468
- - BEFORE/AFTER/INSTEAD OF triggers
469
- - Row-level and statement-level triggers
470
- - Trigger enabling/disabling
471
-
472
- #### **Database Administration**
473
-
474
- - [ ] **Extensions**
475
- - Enable/disable PostgreSQL extensions
476
- - Extension version management
477
- - [ ] **Roles & Security**
478
- - User and role management
479
- - Permission grants and revokes
480
- - Role membership hierarchy
481
- - [ ] **Schema Namespaces**
482
- - Multi-schema support
483
- - Schema ownership and search paths
484
- - [ ] **Tablespaces**
485
- - Custom tablespace management
486
- - Table and index tablespace assignment
487
-
488
- #### **Enhanced Features**
489
-
490
- - [ ] **Comments & Documentation**
491
- - Object-level comments and descriptions
492
- - Schema documentation generation
493
- - [ ] **Advanced Rules**
494
- - Query rewrite rules
495
- - Rule creation and management
496
- - [ ] **Event Triggers**
497
- - DDL event triggers
498
- - Database-level event handling
499
-
500
- #### **Tooling & DevEx Improvements**
501
-
502
- - [ ] **Configuration Management**
503
- - Multiple environment support
504
- - Configuration file formats (JSON/YAML)
505
- - [ ] **Migration History**
506
- - Track applied migrations
507
- - Rollback capabilities
508
- - [ ] **Schema Validation**
509
- - Pre-migration validation
510
- - Dependency checking
511
- - [ ] **Import/Export**
512
- - Import from existing databases
513
- - Export current schema to files
514
-
515
- ### 📊 **Current Test Coverage**
516
-
517
- The project has comprehensive test coverage with **20+ test suites** covering:
518
-
519
- - **Table Operations**: 6 core scenarios
520
- - **Column Operations**: 15+ test suites
521
- - **Type Conversions**: String, Numeric, Boolean edge cases
522
- - **Performance Testing**: Large datasets, concurrent operations
523
- - **Unicode Support**: Emoji, multi-byte characters, escape sequences
524
- - **Data Integrity**: Boundary values, NULL handling, constraint management
525
-
526
- ### 🎯 **Next Milestones**
527
-
528
- **v0.2.0 - Primary Key & Index Support**
529
-
530
- - Complete primary key constraint management
531
- - Basic index creation and management
532
- - Performance improvements for large schemas
533
-
534
- **v0.3.0 - Foreign Keys & Advanced Constraints**
535
-
536
- - Foreign key relationships with actions
537
- - Unique and check constraints
538
- - Constraint dependency resolution
539
-
540
- **v0.4.0 - Views & Functions**
541
-
542
- - Standard and materialized views
543
- - Basic stored function support
544
- - Enhanced schema validation
109
+ **Note:** Tests require a `DATABASE_URL` environment variable pointing to a PostgreSQL database. The tests will create and drop tables as needed, so use a dedicated test database.