Skip to content

Commit c2a19aa

Browse files
committed
docs: update README with accurate metrics and prominent swift-records integration
- Update test count to 467 (from 280+) - Remove coverage percentage claims - Change Swift requirement to 6.1+ (from 6.0+) - Make swift-records integration more prominent with feature list - Remove CLAUDE.md reference from documentation section - Update license to MIT (from Apache/AGPL dual-license) - Remove 'production-ready' claims docs: fix trait configuration syntax in README - Traits are package-level, not target swiftSettings - CasePaths and SQLValidation traits enabled by default - Consumers just add dependency normally docs: improve README examples - remove unnecessary @column annotation, update window function examples to match tests, showcase elegant trigger DSL
1 parent fd43e1b commit c2a19aa

File tree

1 file changed

+90
-68
lines changed

1 file changed

+90
-68
lines changed

README.md

Lines changed: 90 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,10 @@ Type-safe PostgreSQL query builder for Swift. Build complex SQL queries with com
1111
## Key Features
1212

1313
- 🔒 **Type-safe query building** with compile-time validation
14-
- 🎯 **85% PostgreSQL Chapter 9 coverage** (Functions & Operators)
1514
- 🚀 **PostgreSQL-native features**: JSONB, triggers, window functions, CTEs, full-text search
16-
- **Production-ready**: Swift 6.0+ with strict concurrency
17-
- 🧪 **280+ tests** with SQL snapshot testing
18-
- 🔌 **Seamless integration** with [swift-records](https://github.com/coenttb/swift-records)
15+
- 🔌 **Built for [swift-records](https://github.com/coenttb/swift-records)**: High-level database operations with connection pooling, transactions, and migrations
16+
- **Swift 6.1+** with strict concurrency
17+
- 🧪 **467 tests** with SQL snapshot testing
1918

2019
## Quick Start
2120

@@ -63,18 +62,17 @@ Debug builds have Swift 6.x compiler linker issues. This is a known Swift compil
6362

6463
### Optional Features
6564

66-
Enable optional traits in your `Package.swift`:
65+
The following traits are enabled by default:
66+
- **StructuredQueriesPostgresCasePaths**: Enum table support via swift-case-paths
67+
- **StructuredQueriesPostgresSQLValidation**: SQL validation using PostgresNIO (heavy dependency)
68+
69+
These traits are configured at the package level and automatically available when you add the dependency:
6770

6871
```swift
6972
.target(
7073
name: "YourTarget",
7174
dependencies: [
7275
.product(name: "StructuredQueriesPostgres", package: "swift-structured-queries-postgres")
73-
],
74-
swiftSettings: [
75-
.enableExperimentalFeature("Trait"),
76-
.enableTrait("StructuredQueriesPostgresCasePaths"), // Enum table support
77-
.enableTrait("StructuredQueriesPostgresSQLValidation") // SQL validation (heavy dependency)
7876
]
7977
)
8078
```
@@ -143,8 +141,6 @@ User
143141
struct User {
144142
let id: Int
145143
var name: String
146-
147-
@Column(as: Data.self)
148144
var settings: Data
149145
}
150146

@@ -180,16 +176,14 @@ struct Employee {
180176
var salary: Double
181177
}
182178

183-
Employee
184-
.select {
185-
let department = $0.department
186-
let salary = $0.salary
187-
return (
188-
$0.name,
189-
salary,
179+
Employee.all
180+
.select { employee in
181+
(
182+
employee.name,
183+
employee.salary,
190184
rank().over {
191-
$0.partition(by: department)
192-
.order(by: salary.desc())
185+
$0.partition(by: employee.department)
186+
.order(by: employee.salary.desc())
193187
}
194188
)
195189
}
@@ -200,7 +194,7 @@ Employee
200194
**Named windows (WINDOW clause):**
201195

202196
```swift
203-
Employee
197+
Employee.all
204198
.window("dept_salary") {
205199
$0.partition(by: $1.department)
206200
.order(by: $1.salary.desc())
@@ -220,7 +214,7 @@ Employee
220214

221215
### Triggers
222216

223-
**BEFORE UPDATE trigger with automatic timestamps:**
217+
**Auto-update timestamp on changes:**
224218

225219
```swift
226220
@Table
@@ -232,29 +226,38 @@ struct Product {
232226
var updatedAt: Date?
233227
}
234228

229+
// Elegant DSL - auto-generates trigger name and function
235230
Product.createTrigger(
236-
name: "update_timestamp",
237231
timing: .before,
238232
event: .update,
239-
function: .plpgsql(
240-
"set_updated_at",
241-
"""
242-
NEW."updatedAt" = CURRENT_TIMESTAMP;
243-
RETURN NEW;
244-
"""
245-
)
233+
function: .updateTimestamp(column: \.updatedAt)
246234
)
247-
// SQL: CREATE TRIGGER "update_timestamp" BEFORE UPDATE ON "products"
248-
// FOR EACH ROW EXECUTE FUNCTION "set_updated_at"()
235+
// SQL: CREATE TRIGGER "products_before_update_update_updatedAt"
236+
// BEFORE UPDATE ON "products"
237+
// FOR EACH ROW EXECUTE FUNCTION "update_updatedAt_products"()
249238
```
250239

251-
**Conditional trigger with WHEN clause:**
240+
**Set creation timestamp:**
241+
242+
```swift
243+
Product.createTrigger(
244+
timing: .before,
245+
event: .insert,
246+
function: .createdAt(column: \.createdAt)
247+
)
248+
// Automatically generates descriptive names: "products_before_insert_set_createdAt"
249+
```
250+
251+
**Conditional trigger with UPDATE OF and WHEN:**
252252

253253
```swift
254254
Product.createTrigger(
255255
name: "low_stock_alert",
256256
timing: .after,
257-
event: .update(of: { $0.stock }, when: { new in new.stock < 10 }),
257+
event: .update(
258+
of: { $0.stock },
259+
when: { new in new.stock < 10 }
260+
),
258261
function: .plpgsql(
259262
"notify_low_stock",
260263
"""
@@ -263,27 +266,47 @@ Product.createTrigger(
263266
"""
264267
)
265268
)
266-
// SQL: CREATE TRIGGER "low_stock_alert" AFTER UPDATE OF "stock" ON "products"
267-
// FOR EACH ROW WHEN (NEW."stock" < 10) EXECUTE FUNCTION "notify_low_stock"()
269+
// SQL: CREATE TRIGGER "low_stock_alert"
270+
// AFTER UPDATE OF "stock" ON "products"
271+
// FOR EACH ROW WHEN (NEW."stock" < 10)
272+
// EXECUTE FUNCTION "notify_low_stock"()
268273
```
269274

270-
**Access NEW and OLD records:**
275+
**Audit logging with type-safe DSL:**
271276

272277
```swift
278+
@Table
279+
struct ProductAudit: AuditTable {
280+
let id: Int
281+
var tableName: String
282+
var operation: String
283+
var oldData: String?
284+
var newData: String?
285+
var changedAt: Date
286+
var changedBy: String
287+
}
288+
289+
// Elegant audit function - tracks all changes automatically
273290
Product.createTrigger(
274291
timing: .after,
275-
event: .update,
276-
function: .plpgsql(
277-
"log_price_change",
278-
"""
279-
IF NEW.price != OLD.price THEN
280-
INSERT INTO price_history (product_id, old_price, new_price, changed_at)
281-
VALUES (NEW.id, OLD.price, NEW.price, CURRENT_TIMESTAMP);
282-
END IF;
283-
RETURN NEW;
284-
"""
292+
event: .insert, .update(), .delete(),
293+
function: .audit(to: ProductAudit.self)
294+
)
295+
// Captures OLD and NEW data, operation type, and timestamp
296+
```
297+
298+
**Soft delete implementation:**
299+
300+
```swift
301+
Product.createTrigger(
302+
timing: .before,
303+
event: .delete,
304+
function: .softDelete(
305+
deletedAtColumn: \.deletedAt,
306+
identifiedBy: \.id
285307
)
286308
)
309+
// Intercepts DELETE and converts to UPDATE with timestamp
287310
```
288311

289312
### Full-Text Search
@@ -316,8 +339,6 @@ struct Article: FullTextSearchable {
316339
let id: Int
317340
var title: String
318341
var searchVector: String // Pre-computed tsvector column
319-
320-
static var searchVectorColumn: String { "searchVector" }
321342
}
322343

323344
// Search with ranking
@@ -363,8 +384,6 @@ User.insert { User(id: nil, name: "Alice") }
363384

364385
## PostgreSQL Feature Coverage
365386

366-
### Comprehensive Support (85% of Chapter 9)
367-
368387
**Window Functions:**
369388
- ✅ ROW_NUMBER, RANK, DENSE_RANK, NTILE
370389
- ✅ FIRST_VALUE, LAST_VALUE, NTH_VALUE
@@ -422,25 +441,21 @@ User.insert { User(id: nil, name: "Alice") }
422441

423442
## Documentation
424443

425-
### Quick Reference
426-
📘 [**CLAUDE.md**](CLAUDE.md) - LLM-optimized quick reference (build commands, troubleshooting, key differences)
427-
428-
### Detailed Guides
429444
- 📖 [**ARCHITECTURE.md**](ARCHITECTURE.md) - Design decisions, PostgreSQL features, module architecture
430445
- 🧪 [**TESTING.md**](TESTING.md) - Test patterns, snapshot testing, SQL validation
431446
- 📜 [**HISTORY.md**](HISTORY.md) - Evolution timeline, decisions, learnings
432447

433448
## Requirements
434449

435-
- **Swift**: 6.0 or later
450+
- **Swift**: 6.1 or later
436451
- **Platforms**: macOS 13+, iOS 13+, Linux
437452
- **Build**: Use `swift build -c release` (debug mode has linker issues)
438453
- **PostgreSQL**: Designed for PostgreSQL 12+
439454

440455
### CI Status
441456

442457
Tested on:
443-
- ✅ Swift 6.0, 6.1, 6.2
458+
- ✅ Swift 6.1, 6.2
444459
- ✅ macOS (latest)
445460
- ✅ Linux (Ubuntu)
446461

@@ -462,13 +477,25 @@ Key differences from Point-Free's swift-structured-queries (SQLite):
462477

463478
## Integration with swift-records
464479

465-
This package provides **query building only**. For database operations (connection pooling, execution, migrations), use [swift-records](https://github.com/coenttb/swift-records):
480+
This package provides **query building only**. For complete database functionality, use it with [**swift-records**](https://github.com/coenttb/swift-records):
481+
482+
### What swift-records Provides
483+
484+
- 🔄 **Connection pooling** with automatic lifecycle management
485+
- 💾 **Transaction support** with isolation levels and savepoints
486+
- 🔄 **Migration management** with version tracking
487+
- 🧪 **Test utilities** with schema isolation for parallel testing
488+
489+
### Usage
466490

467491
```swift
468-
// Query building (this package)
469-
let statement = User.where { $0.isActive }
492+
// 1. Build queries (this package)
493+
let statement = User
494+
.where { $0.isActive }
495+
.order(by: \.name)
496+
.limit(10)
470497

471-
// Execution (swift-records)
498+
// 2. Execute with swift-records
472499
let users = try await statement.fetchAll(db)
473500
let user = try await statement.fetchOne(db)
474501
try await statement.execute(db)
@@ -482,12 +509,7 @@ To learn about the core concepts behind structured query building in Swift, chec
482509

483510
## License
484511

485-
Dual-licensed:
486-
487-
- **Apache License 2.0**: Core query building infrastructure (inherited from Point-Free's swift-structured-queries)
488-
- **AGPL 3.0**: PostgreSQL-specific additions and features
489-
490-
See [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-AGPL](LICENSE-AGPL) for details.
512+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
491513

492514
## Related Projects
493515

0 commit comments

Comments
 (0)