litedbmodel 1.0.0 → 1.1.0

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 (92) hide show
  1. package/README.md +18 -14
  2. package/dist/DBModel.d.ts +23 -20
  3. package/dist/DBModel.d.ts.map +1 -1
  4. package/dist/DBModel.js +64 -31
  5. package/dist/DBModel.js.map +1 -1
  6. package/dist/LazyRelation.d.ts.map +1 -1
  7. package/dist/LazyRelation.js +16 -10
  8. package/dist/LazyRelation.js.map +1 -1
  9. package/dist/SqlFragment.js +1 -1
  10. package/dist/SqlFragment.js.map +1 -1
  11. package/dist/decorators.d.ts +6 -7
  12. package/dist/decorators.d.ts.map +1 -1
  13. package/dist/decorators.js +42 -23
  14. package/dist/decorators.js.map +1 -1
  15. package/dist/drivers/PostgresHelper.d.ts.map +1 -1
  16. package/dist/drivers/PostgresHelper.js +4 -0
  17. package/dist/drivers/PostgresHelper.js.map +1 -1
  18. package/dist/drivers/postgres.d.ts.map +1 -1
  19. package/dist/drivers/postgres.js +57 -10
  20. package/dist/drivers/postgres.js.map +1 -1
  21. package/dist/types.d.ts +0 -9
  22. package/dist/types.d.ts.map +1 -1
  23. package/docs/BENCHMARK.md +52 -52
  24. package/docs/COMPARISON.md +9 -7
  25. package/docs/api/README.md +61 -9
  26. package/docs/api/classes/Conditions.md +75 -8
  27. package/docs/api/classes/DBHandler.md +3 -3
  28. package/docs/api/classes/DBModel.md +260 -106
  29. package/docs/api/classes/LimitExceededError.md +1 -1
  30. package/docs/api/classes/Middleware.md +1 -1
  31. package/docs/api/classes/SqlRaw.md +53 -0
  32. package/docs/api/classes/SqlRef.md +59 -0
  33. package/docs/api/classes/Values.md +6 -6
  34. package/docs/api/classes/WriteInReadOnlyContextError.md +1 -1
  35. package/docs/api/classes/WriteOutsideTransactionError.md +1 -1
  36. package/docs/api/functions/belongsTo.md +2 -2
  37. package/docs/api/functions/closeAllPools.md +1 -1
  38. package/docs/api/functions/condsToRecord.md +23 -0
  39. package/docs/api/functions/createMiddleware.md +68 -0
  40. package/docs/api/functions/createPostgresDriver.md +2 -2
  41. package/docs/api/functions/createSqliteDriver.md +1 -1
  42. package/docs/api/functions/dbCast.md +1 -1
  43. package/docs/api/functions/dbCastIn.md +1 -1
  44. package/docs/api/functions/dbDynamic.md +1 -1
  45. package/docs/api/functions/dbFalse.md +1 -1
  46. package/docs/api/functions/dbImmediate.md +1 -1
  47. package/docs/api/functions/dbIn.md +1 -1
  48. package/docs/api/functions/dbNotNull.md +1 -1
  49. package/docs/api/functions/dbNow.md +1 -1
  50. package/docs/api/functions/dbNull.md +1 -1
  51. package/docs/api/functions/dbRaw.md +1 -1
  52. package/docs/api/functions/dbTrue.md +1 -1
  53. package/docs/api/functions/dbUuid.md +1 -1
  54. package/docs/api/functions/dbUuidIn.md +1 -1
  55. package/docs/api/functions/getDBConfig.md +1 -1
  56. package/docs/api/functions/getDBHandler.md +1 -1
  57. package/docs/api/functions/getTransactionConnection.md +2 -2
  58. package/docs/api/functions/getTransactionContext.md +2 -2
  59. package/docs/api/functions/hasMany.md +2 -2
  60. package/docs/api/functions/hasOne.md +2 -2
  61. package/docs/api/functions/initDBHandler.md +1 -1
  62. package/docs/api/functions/isAnySqlFragment.md +25 -0
  63. package/docs/api/functions/isSqlCondition.md +23 -0
  64. package/docs/api/functions/isSqlFragment.md +23 -0
  65. package/docs/api/functions/isSqlRaw.md +23 -0
  66. package/docs/api/functions/isSqlRef.md +23 -0
  67. package/docs/api/functions/isSqlTypedFragment.md +23 -0
  68. package/docs/api/functions/model.md +4 -4
  69. package/docs/api/functions/parentRef.md +1 -1
  70. package/docs/api/functions/sql.md +182 -0
  71. package/docs/api/globals.md +71 -19
  72. package/docs/api/interfaces/Column.md +31 -31
  73. package/docs/api/interfaces/ColumnOptions.md +4 -4
  74. package/docs/api/interfaces/CreatedMiddlewareClass.md +110 -0
  75. package/docs/api/interfaces/DBConfig.md +2 -2
  76. package/docs/api/interfaces/DBConfigOptions.md +1 -1
  77. package/docs/api/interfaces/DeleteOptions.md +1 -1
  78. package/docs/api/interfaces/InsertOptions.md +2 -2
  79. package/docs/api/interfaces/LimitConfig.md +1 -1
  80. package/docs/api/interfaces/MiddlewareConfig.md +40 -0
  81. package/docs/api/interfaces/ModelOptions.md +1 -1
  82. package/docs/api/interfaces/PkeyResult.md +1 -1
  83. package/docs/api/interfaces/SelectOptions.md +1 -1
  84. package/docs/api/interfaces/SqlCondition.md +27 -0
  85. package/docs/api/interfaces/SqlFragment.md +20 -0
  86. package/docs/api/interfaces/SqlTypedFragment.md +29 -0
  87. package/docs/api/interfaces/TransactionOptions.md +1 -1
  88. package/docs/api/interfaces/UpdateManyOptions.md +1 -1
  89. package/docs/api/interfaces/UpdateOptions.md +1 -1
  90. package/docs/benchmark-chart.svg +163 -163
  91. package/docs/sql-tagged-template.md +5 -5
  92. package/package.json +1 -1
package/docs/BENCHMARK.md CHANGED
@@ -27,52 +27,52 @@ Reference: [Kysely performance comparison article](https://izanami.dev/post/1e3f
27
27
  <!--@embedoc:benchmark_table-->
28
28
  | Operation | litedbmodel | Kysely | Drizzle | TypeORM | Prisma |
29
29
  | --- | --- | --- | --- | --- | --- |
30
- | Find all (limit 100) | 0.65ms | **0.61ms** 🏆 | **0.61ms** 🏆 | 0.69ms | 1.28ms |
31
- | Filter, paginate & sort | **0.55ms** 🏆 | 0.57ms | 0.61ms | 0.64ms | 0.95ms |
32
- | Nested find all (include posts) | 3.57ms | 4.59ms | **3.32ms** 🏆 | 7.63ms | 12.37ms |
33
- | Find first | 0.35ms | **0.31ms** 🏆 | 0.32ms | 0.33ms | 0.59ms |
34
- | Nested find first (include posts) | 0.57ms | 0.53ms | 0.58ms | **0.46ms** 🏆 | 0.98ms |
35
- | Find unique (by email) | 0.29ms | **0.27ms** 🏆 | 0.29ms | 0.32ms | 0.54ms |
36
- | Nested find unique (include posts) | 0.56ms | **0.50ms** 🏆 | 0.57ms | 0.94ms | 0.88ms |
37
- | Create | **0.62ms** 🏆 | 0.67ms | 0.69ms | 0.74ms | 0.97ms |
38
- | Nested create (with post) | 0.91ms | **0.89ms** 🏆 | 0.95ms | 1.15ms | 1.64ms |
39
- | Update | **0.65ms** 🏆 | 0.66ms | **0.65ms** 🏆 | **0.65ms** 🏆 | 1.11ms |
40
- | Nested update (update user + post) | **0.86ms** 🏆 | 0.87ms | 0.92ms | 0.95ms | 2.12ms |
41
- | Upsert | 0.65ms | **0.63ms** 🏆 | 0.67ms | 0.75ms | 1.77ms |
42
- | Nested upsert (user + post) | **0.87ms** 🏆 | 0.90ms | 0.95ms | 0.98ms | 1.92ms |
43
- | Delete | **0.85ms** 🏆 | 0.88ms | 0.91ms | 0.96ms | 1.44ms |
44
- | Create Many (10 records) | 0.68ms | **0.67ms** 🏆 | 0.85ms | 0.91ms | 1.17ms |
45
- | Upsert Many (10 records) | 0.81ms | **0.80ms** 🏆 | 0.98ms | 1.02ms | 11.34ms |
46
- | Update Many (10 different values) | **0.75ms** 🏆 | 2.71ms | 2.79ms | 3.05ms | 5.24ms |
47
- | Nested relations (100→1000→10000) | 26.84ms | 23.74ms | **22.31ms** 🏆 | 75.25ms | 83.91ms |
48
- | Nested relations (composite key, 5 tenants) | 23.38ms | N/A | **15.75ms** 🏆 | 37.72ms | 214.51ms |
30
+ | Find all (limit 100) | 0.70ms | 0.65ms | **0.64ms** 🏆 | 0.72ms | 1.35ms |
31
+ | Filter, paginate & sort | **0.58ms** 🏆 | 0.64ms | 0.59ms | 0.66ms | 1.10ms |
32
+ | Nested find all (include posts) | **3.64ms** 🏆 | 4.60ms | 4.66ms | 8.34ms | 12.06ms |
33
+ | Find first | 0.37ms | **0.32ms** 🏆 | 0.36ms | 0.40ms | 0.59ms |
34
+ | Nested find first (include posts) | 0.65ms | 0.61ms | 0.71ms | **0.52ms** 🏆 | 1.06ms |
35
+ | Find unique (by email) | **0.29ms** 🏆 | 0.31ms | 0.30ms | 0.32ms | 0.53ms |
36
+ | Nested find unique (include posts) | **0.57ms** 🏆 | 0.58ms | 0.59ms | 1.05ms | 0.94ms |
37
+ | Create | 0.69ms | **0.68ms** 🏆 | 0.76ms | 0.79ms | 1.01ms |
38
+ | Nested create (with post) | **0.90ms** 🏆 | 0.96ms | 1.03ms | 1.22ms | 1.81ms |
39
+ | Update | 0.63ms | **0.61ms** 🏆 | 0.66ms | 0.67ms | 1.12ms |
40
+ | Nested update (update user + post) | 0.94ms | 1.00ms | **0.92ms** 🏆 | 1.02ms | 2.28ms |
41
+ | Upsert | 0.71ms | 0.74ms | **0.66ms** 🏆 | 0.75ms | 2.07ms |
42
+ | Nested upsert (user + post) | **1.00ms** 🏆 | **1.00ms** 🏆 | **1.00ms** 🏆 | 1.14ms | 2.27ms |
43
+ | Delete | **0.97ms** 🏆 | 1.00ms | 0.98ms | 1.10ms | 1.72ms |
44
+ | Create Many (10 records) | 0.77ms | **0.72ms** 🏆 | 0.89ms | 1.00ms | 1.20ms |
45
+ | Upsert Many (10 records) | **0.88ms** 🏆 | 0.90ms | 1.03ms | 1.06ms | 11.89ms |
46
+ | Update Many (10 different values) | **0.70ms** 🏆 | 2.83ms | 2.75ms | 3.02ms | 5.31ms |
47
+ | Nested relations (100→1000→10000) | 25.82ms | 24.65ms | **24.25ms** 🏆 | 80.56ms | 84.10ms |
48
+ | Nested relations (composite key, 5 tenants) | 22.41ms | N/A | **16.26ms** 🏆 | 39.25ms | 219.00ms |
49
49
  <!--@embedoc:end-->
50
50
 
51
51
  *All write operations wrapped in transactions for fair comparison across all ORMs.*
52
52
 
53
53
  ### Analysis
54
54
 
55
- 1. **litedbmodel** - **Fastest in write operations** 🏆
56
- - **#1 in Filter/paginate, Create, Update (tie), Nested update, Nested upsert, Delete, Update Many**
57
- - Update Many with different values: **3.6x - 7.0x faster** than other ORMs
55
+ 1. **litedbmodel** - **Fastest in read/write operations** 🏆
56
+ - **#1 in Filter/paginate, Nested find all, Find unique, Nested find unique, Nested create, Nested upsert (tie), Delete, Upsert Many, Update Many**
57
+ - Update Many with different values: **3.9x - 7.6x faster** than other ORMs
58
58
  - Excellent balance of read and write performance
59
59
 
60
60
  2. **Kysely** - **Strong simple query performance**
61
- - **#1 in Find all (tie), Find first, Find unique, Nested find unique, Nested create, Upsert, Create Many, Upsert Many**
61
+ - **#1 in Find first, Create, Update, Create Many**
62
62
  - Minimal abstraction overhead
63
63
  - **Limitation:** Cannot batch load composite FK
64
64
 
65
65
  3. **Drizzle** - **Fastest in nested relations** 🏆
66
- - **#1 in Find all (tie), Nested find all, Update (tie), Nested relations single key (22ms), Nested composite key (16ms)** using LATERAL JOIN
66
+ - **#1 in Find all, Nested update, Upsert, Nested upsert (tie), Nested relations single key (24ms), Nested composite key (16ms)** using LATERAL JOIN
67
67
  - Query API with relations provides excellent performance
68
68
 
69
69
  4. **TypeORM** - Variable performance
70
- - **#1 in Update (tie), Nested find first** (JOIN-based approach)
71
- - Decent composite key support (38ms)
70
+ - **#1 in Nested find first** (JOIN-based approach)
71
+ - Decent composite key support (39ms)
72
72
 
73
73
  5. **Prisma** - Convenience over speed
74
74
  - **Slowest in most operations** (1.5x - 14x slower)
75
- - Composite key nested: 215ms (**14x slower** than Drizzle)
75
+ - Composite key nested: 219ms (**13x slower** than Drizzle)
76
76
  - Trade-off: Rich DX features (Prisma Studio, migrations, etc.)
77
77
 
78
78
  ---
@@ -89,33 +89,33 @@ Reference: [Kysely performance comparison article](https://izanami.dev/post/1e3f
89
89
 
90
90
  | ORM | Median | Ratio | Queries | Notes |
91
91
  |-----|--------|-------|---------|-------|
92
- | **Drizzle** 🏆 | **22.31ms** | **1.00x** | 1 | **LATERAL JOIN** |
93
- | Kysely | 23.74ms | 1.06x | 3 | Separate queries |
94
- | litedbmodel | 26.84ms | 1.20x | 3 | Separate queries + sync cache |
95
- | TypeORM | 75.25ms | 3.37x | 2 | JOIN-based |
96
- | Prisma | 83.91ms | 3.76x | 3 | Parameter explosion |
92
+ | **Drizzle** 🏆 | **24.25ms** | **1.00x** | 1 | **LATERAL JOIN** |
93
+ | Kysely | 24.65ms | 1.02x | 3 | Separate queries |
94
+ | litedbmodel | 25.82ms | 1.06x | 3 | Separate queries + sync cache |
95
+ | TypeORM | 80.56ms | 3.32x | 2 | JOIN-based |
96
+ | Prisma | 84.10ms | 3.47x | 3 | Parameter explosion |
97
97
 
98
98
  ### Composite Key Relations (5 tenants × 100 users)
99
99
 
100
100
  | ORM | Median | Ratio | Queries | Notes |
101
101
  |-----|--------|-------|---------|-------|
102
- | **Drizzle** 🏆 | **15.75ms** | **1.00x** | 1 | **LATERAL JOIN with composite** |
103
- | litedbmodel | 23.38ms | 1.48x | 3 | unnest + JOIN |
104
- | TypeORM | 37.72ms | 2.39x | 2 | Multiple OR conditions |
105
- | Prisma | 214.51ms | 13.62x | 3 | Parameter explosion (2000+ params) |
102
+ | **Drizzle** 🏆 | **16.26ms** | **1.00x** | 1 | **LATERAL JOIN with composite** |
103
+ | litedbmodel | 22.41ms | 1.38x | 3 | unnest + JOIN |
104
+ | TypeORM | 39.25ms | 2.41x | 2 | Multiple OR conditions |
105
+ | Prisma | 219.00ms | 13.47x | 3 | Parameter explosion (2000+ params) |
106
106
  | Kysely | N/A | - | - | Cannot batch composite FK |
107
107
 
108
108
  ### Deep Nested Analysis
109
109
 
110
110
  **Single Key:**
111
- - Drizzle fastest with LATERAL JOIN (22ms)
112
- - Kysely close (24ms) with 3 separate queries
113
- - litedbmodel (27ms, 1.20x) with 3 separate queries
111
+ - Drizzle fastest with LATERAL JOIN (24ms)
112
+ - Kysely close (25ms) with 3 separate queries
113
+ - litedbmodel (26ms, 1.06x) with 3 separate queries
114
114
 
115
115
  **Composite Key (multi-tenant):**
116
116
  - **Drizzle fastest (16ms)** with LATERAL JOIN query API
117
- - litedbmodel 1.48x (23ms) with `unnest + JOIN` approach
118
- - Prisma 14x slower with 2000+ parameters
117
+ - litedbmodel 1.38x (22ms) with `unnest + JOIN` approach
118
+ - Prisma 13x slower with 2000+ parameters
119
119
  - **Kysely cannot batch load composite FK** (would require manual tuple matching)
120
120
 
121
121
  > **Drizzle advantage:** LATERAL JOIN provides fastest nested relation loading for both single and composite keys when using the query API with relations.
@@ -538,11 +538,11 @@ WHERE (tenant_id, user_id) IN (($1,$2),($3,$4),...,($1999,$2000))
538
538
 
539
539
  | Scenario | Best Fit | Notes |
540
540
  |----------|----------|-------|
541
- | Write operations (transactions) | **litedbmodel** | Fastest in Create, Upsert, Delete, etc. |
542
- | Bulk Update (diff values) | **litedbmodel** | **3.8x - 7.3x faster** than others (single query) |
543
- | Nested find all | **litedbmodel** | Fastest at 3.59ms |
544
- | Nested single key (speed) | Drizzle | 1 query via LATERAL JOIN (22ms) |
545
- | Nested composite key (speed) | **litedbmodel** | **23ms** (7x faster than Drizzle, 9x faster than Prisma) |
541
+ | Write operations (transactions) | **litedbmodel** | Fastest in Nested create, Delete, Upsert Many, etc. |
542
+ | Bulk Update (diff values) | **litedbmodel** | **3.9x - 7.6x faster** than others (single query) |
543
+ | Nested find all | **litedbmodel** | Fastest at 3.64ms |
544
+ | Nested single key (speed) | Drizzle | 1 query via LATERAL JOIN (24ms) |
545
+ | Nested composite key (speed) | Drizzle | 16ms via LATERAL JOIN |
546
546
  | Nested relation SQL quality | **litedbmodel** | Readable + debuggable; stable query patterns (PG) |
547
547
  | Log Analysis / Monitoring | **litedbmodel** | Consistent query patterns |
548
548
  | SQL Fingerprint Stability (PG) | **litedbmodel** | Most predictable in prepared-statement setups |
@@ -550,14 +550,14 @@ WHERE (tenant_id, user_id) IN (($1,$2),($3,$4),...,($1999,$2000))
550
550
  | Data Integrity | **litedbmodel** | Explicit transactions required for writes |
551
551
 
552
552
  **litedbmodel excels at:**
553
- - **Write operations** (Create, Nested create, Upsert, Delete - all fastest when using transactions)
554
- - **Bulk update with different values** (3.8x - 7.3x faster)
555
- - **Nested find all** (fastest at 3.40ms)
553
+ - **Read operations** (Find unique, Nested find unique, Nested find all - all fastest)
554
+ - **Write operations** (Nested create, Delete, Upsert Many, Update Many - fastest)
555
+ - **Bulk update with different values** (3.9x - 7.6x faster)
556
556
  - **Consistent performance** across all operations
557
557
  - **Data integrity** via required transactions
558
558
 
559
- **vs Prisma:** litedbmodel is **1.6x - 14.4x faster** across operations
560
- **vs Query Builders:** litedbmodel is **fastest for write operations** when all ORMs use transactions
559
+ **vs Prisma:** litedbmodel is **1.5x - 13.5x faster** across operations
560
+ **vs Query Builders:** litedbmodel matches or beats Kysely/Drizzle in most operations
561
561
 
562
562
  > **litedbmodel provides best-in-class performance with safety guarantees:**
563
563
  > - Type-safe column symbols (IDE refactoring)
@@ -569,6 +569,6 @@ WHERE (tenant_id, user_id) IN (($1,$2),($3,$4),...,($1999,$2000))
569
569
 
570
570
  ---
571
571
 
572
- *Last updated: January 2026*
572
+ *Last updated: March 2026*
573
573
  *Benchmark methodology: Based on [Prisma orm-benchmarks](https://github.com/prisma/orm-benchmarks)*
574
574
 
@@ -290,8 +290,8 @@ SKIP also works in `createMany` and `updateMany`:
290
290
  | Operation | SKIP Behavior |
291
291
  |-----------|---------------|
292
292
  | `create` / `update` | Column excluded from INSERT/UPDATE |
293
- | `createMany` | Column excluded → DB DEFAULT applied |
294
- | `updateMany` | Column excluded → existing value retained |
293
+ | `createMany` | Column excluded → DB DEFAULT applied (see note below) |
294
+ | `updateMany` | Column excluded → existing value retained (single query) |
295
295
 
296
296
  ```typescript
297
297
  // createMany - records grouped by SKIP pattern for efficient batch INSERT
@@ -307,6 +307,8 @@ await User.updateMany([
307
307
  ], { keyColumns: User.id });
308
308
  ```
309
309
 
310
+ > **Note on `createMany` with SKIP:** `createMany` groups records by their SKIP pattern and issues a separate INSERT per group, because SQL does not allow mixing `DEFAULT` with array-based bulk insert (`UNNEST`). `updateMany` does not have this limitation — it handles SKIP in a single query using boolean flags. For best performance with `createMany`, prefer providing explicit values for all columns instead of using SKIP.
311
+
310
312
  ---
311
313
 
312
314
  ## 4. SQL-Friendly Design
@@ -1077,13 +1079,13 @@ For detailed benchmark results comparing litedbmodel with Prisma, Kysely, Drizzl
1077
1079
 
1078
1080
  | Operation Type | litedbmodel Performance |
1079
1081
  |----------------|------------------------|
1080
- | Standard CRUD | **Fastest in 7/14 operations** (Find all, Filter/sort, Create, Upsert, Delete) |
1081
- | Nested relations | Competitive (within 10% of fastest) |
1082
- | Deep nested (10K) | 2.9x faster than Prisma |
1082
+ | Standard CRUD | **Fastest in 9/19 operations** (Filter/sort, Nested find all, Find unique, Nested find unique, Nested create, Delete, Upsert Many, Update Many, etc.) |
1083
+ | Nested relations | Competitive (within 6% of fastest) |
1084
+ | Deep nested (10K) | 3.3x faster than Prisma |
1083
1085
  | SQL quality | ⭐⭐⭐⭐⭐ (readable, fixed params on PG) |
1084
1086
 
1085
- **vs Prisma:** litedbmodel is **1.6x - 5.9x faster** across all operations
1086
- **vs Query Builders:** litedbmodel is competitive (within 10%), with better DX
1087
+ **vs Prisma:** litedbmodel is **1.5x - 13.5x faster** across all operations
1088
+ **vs Query Builders:** litedbmodel matches or beats Kysely/Drizzle in most operations
1087
1089
 
1088
1090
  ---
1089
1091
 
@@ -1,4 +1,4 @@
1
- **litedbmodel v0.20.1**
1
+ **litedbmodel v1.0.1**
2
2
 
3
3
  ***
4
4
 
@@ -407,8 +407,8 @@ class LoggingMiddleware extends Middleware {
407
407
  }
408
408
  }
409
409
 
410
- // Register middleware
411
- User.use(LoggingMiddleware);
410
+ // Register middleware (applies to ALL models)
411
+ DBModel.use(LoggingMiddleware);
412
412
  ```
413
413
 
414
414
  See: [Middleware](classes/Middleware.md)
@@ -463,13 +463,65 @@ await User.updateMany([
463
463
  ], { keyColumns: User.id });
464
464
  ```
465
465
 
466
- ### Batch SQL Strategy by Database
466
+ ### SQL Generation Strategy (Complete Matrix)
467
+
468
+ | DB | Operation | SKIP | SQL Strategy | SQL Example | Test |
469
+ |----|-----------|------|--------------|-------------|------|
470
+ | PostgreSQL | create | No | VALUES | `INSERT INTO t (name, arr, json) VALUES ($1, '{1,2}', '{"a":1}')` | ✅ |
471
+ | PostgreSQL | create | Yes | VALUES (column excluded) | `INSERT INTO t (name, json) VALUES ($1, '{"a":1}')` - arr excluded | ✅ |
472
+ | PostgreSQL | createMany | No | UNNEST | `INSERT INTO t SELECT v.name, (jsonb->arr), v.json::jsonb FROM UNNEST($1::text[], $2::text[], $3::text[]) AS v` | ✅ |
473
+ | PostgreSQL | createMany | Yes | Group by SKIP pattern -> UNNEST per group | Same as above, SKIP columns excluded from INSERT -> DB DEFAULT | ✅ |
474
+ | PostgreSQL | update | No | SET | `UPDATE t SET name=$1, arr='{1,2}', json='{"a":1}' WHERE id=$2` | ✅ |
475
+ | PostgreSQL | update | Yes | SET (column excluded) | `UPDATE t SET name=$1, json='{"a":1}' WHERE id=$2` - arr excluded | ✅ |
476
+ | PostgreSQL | updateMany | No | UNNEST | `UPDATE t SET arr=(jsonb->arr), json=v.json::jsonb FROM UNNEST(...) AS v WHERE t.id=v.id` | ✅ |
477
+ | PostgreSQL | updateMany | Yes | UNNEST + skip_flag column | `SET arr=CASE WHEN v._skip_arr THEN t.arr ELSE (jsonb->arr) END` | ✅ |
478
+ | MySQL | create | No | VALUES | `INSERT INTO t (name, status) VALUES (?, ?)` | ✅ |
479
+ | MySQL | create | Yes | VALUES (column excluded) | `INSERT INTO t (name) VALUES (?)` - status excluded -> DB DEFAULT | ✅ |
480
+ | MySQL | createMany | No | VALUES ROW | `INSERT INTO t (name, status) VALUES ROW(?,?), ROW(?,?)` | ✅ |
481
+ | MySQL | createMany | Yes | Group by SKIP pattern -> VALUES per group | Same as above, SKIP columns excluded from INSERT -> DB DEFAULT | ✅ |
482
+ | MySQL | update | No | SET | `UPDATE t SET name=?, status=? WHERE id=?` | ✅ |
483
+ | MySQL | update | Yes | SET (column excluded) | `UPDATE t SET name=? WHERE id=?` - status excluded | ✅ |
484
+ | MySQL | updateMany | No | JOIN VALUES ROW | `UPDATE t JOIN (VALUES ROW(?,?)) AS v ON t.id=v.id SET t.col=v.col` | ✅ |
485
+ | MySQL | updateMany | Yes | JOIN VALUES + IF(skip_flag) | `SET t.status=IF(v._skip_status, t.status, v.status)` | ✅ |
486
+ | SQLite | create | No | VALUES | `INSERT INTO t (name, status) VALUES (?, ?)` | ✅ |
487
+ | SQLite | create | Yes | VALUES (column excluded) | `INSERT INTO t (name) VALUES (?)` - status excluded -> DB DEFAULT | ✅ |
488
+ | SQLite | createMany | No | VALUES | `INSERT INTO t (name, status) VALUES (?,?), (?,?)` | ✅ |
489
+ | SQLite | createMany | Yes | Group by SKIP pattern -> VALUES per group | Same as above, SKIP columns excluded from INSERT -> DB DEFAULT | ✅ |
490
+ | SQLite | update | No | SET | `UPDATE t SET name=?, status=? WHERE id=?` | ✅ |
491
+ | SQLite | update | Yes | SET (column excluded) | `UPDATE t SET name=? WHERE id=?` - status excluded | ✅ |
492
+ | SQLite | updateMany | No | CASE WHEN | `UPDATE t SET status=CASE WHEN id=? THEN ? WHEN id=? THEN ? END WHERE id IN (?,?)` | ✅ |
493
+ | SQLite | updateMany | Yes | CASE WHEN + column ref | `SET status=CASE WHEN id=1 THEN ? WHEN id=2 THEN t.status END` - SKIP uses column ref | ✅ |
494
+
495
+ **Notes:**
496
+ - PostgreSQL array types: In createMany/updateMany, `JSON.stringify()` -> `text[]` -> `jsonb_array_elements_text()` to deserialize
497
+ - PostgreSQL JSON types: In createMany/updateMany, `JSON.stringify()` -> `text[]` -> `::jsonb` cast
498
+ - MySQL/SQLite: Use `@column.json<T[]>()` for array-like data (stored as JSON text)
499
+ - `(jsonb->arr)` = `COALESCE((SELECT array_agg(elem::int) FROM jsonb_array_elements_text(v.col::jsonb) AS elem), ARRAY[]::int[])`
500
+
501
+ ### Architecture: Driver-Specific SQL Building
502
+
503
+ litedbmodel uses a `SqlBuilder` architecture to generate optimized SQL for each database:
504
+
505
+ | Driver | Interface | INSERT Strategy | UPDATE Strategy |
506
+ |--------|-----------|-----------------|-----------------|
507
+ | PostgreSQL | `PostgresSqlBuilder` | VALUES for single, UNNEST for batch (2+) | UNNEST with skip flags |
508
+ | MySQL | `MysqlSqlBuilder` | VALUES ROW | JOIN VALUES + IF |
509
+ | SQLite | `SqliteSqlBuilder` | VALUES clause | CASE WHEN |
510
+
511
+ Each driver also provides `DriverTypeCast` for serialization/deserialization:
467
512
 
468
- | Database | createMany | updateMany |
469
- |----------|------------|------------|
470
- | PostgreSQL | Grouped `UNNEST` INSERT | `UNNEST` + `CASE WHEN skip_flag` |
471
- | MySQL | Grouped `VALUES ROW` INSERT | `JOIN VALUES ROW` + `IF(skip_flag)` |
472
- | SQLite | Grouped `VALUES` INSERT | `CASE WHEN key=? THEN col ELSE ?` |
513
+ ```typescript
514
+ interface DriverTypeCast {
515
+ serializeArray<T>(val: T[]): unknown; // JS array -> DB format
516
+ deserializeArray<T>(val: unknown): T[]; // DB value -> JS array
517
+ serializeJson(val: unknown): unknown; // JS object -> DB format
518
+ deserializeJson<T>(val: unknown): T; // DB value -> JS object
519
+ }
520
+ ```
521
+
522
+ - **PostgreSQL**: Native array format `{1,2,3}`, native JSONB
523
+ - **MySQL**: JSON column type with automatic serialization
524
+ - **SQLite**: TEXT columns with JSON.stringify/JSON.parse
473
525
 
474
526
  See: [Conditions](classes/Conditions.md), [Values](classes/Values.md), [SKIP](variables/SKIP.md)
475
527
 
@@ -1,4 +1,4 @@
1
- [**litedbmodel v0.20.1**](../README.md)
1
+ [**litedbmodel v1.0.1**](../README.md)
2
2
 
3
3
  ***
4
4
 
@@ -6,7 +6,7 @@
6
6
 
7
7
  # Class: Conditions\<Model\>
8
8
 
9
- Defined in: Column.ts:627
9
+ Defined in: Column.ts:628
10
10
 
11
11
  Type-safe builder for query conditions.
12
12
  Use array literals for static conditions, builder for dynamic construction.
@@ -47,7 +47,7 @@ if (query.active) where.add(User.is_active, true);
47
47
  new Conditions<Model>(initial?: Conds): Conditions<Model>;
48
48
  ```
49
49
 
50
- Defined in: Column.ts:633
50
+ Defined in: Column.ts:634
51
51
 
52
52
  Create a Conditions builder, optionally with initial conditions.
53
53
 
@@ -71,7 +71,7 @@ Create a Conditions builder, optionally with initial conditions.
71
71
  get length(): number;
72
72
  ```
73
73
 
74
- Defined in: Column.ts:677
74
+ Defined in: Column.ts:700
75
75
 
76
76
  Get the number of conditions.
77
77
 
@@ -87,7 +87,7 @@ Get the number of conditions.
87
87
  add<V>(column: Column<V, Model>, value: V | null | undefined): this;
88
88
  ```
89
89
 
90
- Defined in: Column.ts:642
90
+ Defined in: Column.ts:643
91
91
 
92
92
  Add a type-safe column equality condition.
93
93
 
@@ -116,7 +116,7 @@ Add a type-safe column equality condition.
116
116
  addRaw(condition: string, value?: unknown): this;
117
117
  ```
118
118
 
119
- Defined in: Column.ts:650
119
+ Defined in: Column.ts:651
120
120
 
121
121
  Add a raw condition with template literal (e.g., `${User.age} > ?`).
122
122
 
@@ -133,13 +133,80 @@ Add a raw condition with template literal (e.g., `${User.age} > ?`).
133
133
 
134
134
  ***
135
135
 
136
+ ### addSql()
137
+
138
+ #### Call Signature
139
+
140
+ ```ts
141
+ addSql<V>(fragment: SqlTypedFragment<V, Model>, value: V): this;
142
+ ```
143
+
144
+ Defined in: Column.ts:670
145
+
146
+ Add a type-safe sql-tagged condition.
147
+
148
+ ##### Type Parameters
149
+
150
+ | Type Parameter |
151
+ | ------ |
152
+ | `V` |
153
+
154
+ ##### Parameters
155
+
156
+ | Parameter | Type |
157
+ | ------ | ------ |
158
+ | `fragment` | [`SqlTypedFragment`](../interfaces/SqlTypedFragment.md)\<`V`, `Model`\> |
159
+ | `value` | `V` |
160
+
161
+ ##### Returns
162
+
163
+ `this`
164
+
165
+ ##### Example
166
+
167
+ ```typescript
168
+ const where = new Conditions<User>();
169
+ where.addSql(sql`${User.age} > ?`, 18);
170
+ where.addSql(sql`${User.deleted_at} IS NULL`);
171
+ ```
172
+
173
+ #### Call Signature
174
+
175
+ ```ts
176
+ addSql(fragment: SqlTypedFragment<any, Model>): this;
177
+ ```
178
+
179
+ Defined in: Column.ts:671
180
+
181
+ Add a type-safe sql-tagged condition.
182
+
183
+ ##### Parameters
184
+
185
+ | Parameter | Type |
186
+ | ------ | ------ |
187
+ | `fragment` | [`SqlTypedFragment`](../interfaces/SqlTypedFragment.md)\<`any`, `Model`\> |
188
+
189
+ ##### Returns
190
+
191
+ `this`
192
+
193
+ ##### Example
194
+
195
+ ```typescript
196
+ const where = new Conditions<User>();
197
+ where.addSql(sql`${User.age} > ?`, 18);
198
+ where.addSql(sql`${User.deleted_at} IS NULL`);
199
+ ```
200
+
201
+ ***
202
+
136
203
  ### or()
137
204
 
138
205
  ```ts
139
206
  or(...condGroups: readonly Conds[]): this;
140
207
  ```
141
208
 
142
- Defined in: Column.ts:662
209
+ Defined in: Column.ts:685
143
210
 
144
211
  Add an OR condition group.
145
212
 
@@ -161,7 +228,7 @@ Add an OR condition group.
161
228
  build(): Conds;
162
229
  ```
163
230
 
164
- Defined in: Column.ts:670
231
+ Defined in: Column.ts:693
165
232
 
166
233
  Build the final array for use with find/count/delete.
167
234
 
@@ -1,4 +1,4 @@
1
- [**litedbmodel v0.20.1**](../README.md)
1
+ [**litedbmodel v1.0.1**](../README.md)
2
2
 
3
3
  ***
4
4
 
@@ -48,7 +48,7 @@ Defined in: DBHandler.ts:180
48
48
  ### getDriverType()
49
49
 
50
50
  ```ts
51
- getDriverType(): "postgres" | "sqlite" | "mysql";
51
+ getDriverType(): "postgres" | "mysql" | "sqlite";
52
52
  ```
53
53
 
54
54
  Defined in: DBHandler.ts:198
@@ -57,7 +57,7 @@ Get the driver type
57
57
 
58
58
  #### Returns
59
59
 
60
- `"postgres"` \| `"sqlite"` \| `"mysql"`
60
+ `"postgres"` \| `"mysql"` \| `"sqlite"`
61
61
 
62
62
  ***
63
63