@mlagie/sql-connector 3.0.3 → 3.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.
package/docs/QUERY.md ADDED
@@ -0,0 +1,281 @@
1
+ # Query and condition reference
2
+
3
+ ## Table of contents
4
+
5
+ 1. [Structured filters](#structured-filters)
6
+ 2. [Values](#values)
7
+ 3. [Comparison operators](#comparison-operators)
8
+ 4. [IN and NOT IN](#in-and-not-in)
9
+ 5. [BETWEEN and NOT BETWEEN](#between-and-not-between)
10
+ 6. [NULL](#null)
11
+ 7. [AND, OR and NOT](#and-or-and-not)
12
+ 8. [Select](#select)
13
+ 9. [Group by](#group-by)
14
+ 10. [Having](#having)
15
+ 11. [Order by](#order-by)
16
+ 12. [Limit and offset](#limit-and-offset)
17
+ 13. [Joins](#joins)
18
+
19
+ ## Structured filters
20
+
21
+ `where` and `having` accept structured objects. Raw SQL strings are rejected by the query builder.
22
+
23
+ ```js
24
+ {
25
+ status: 'active',
26
+ age: { '>=': 18 },
27
+ }
28
+ ```
29
+
30
+ Fields at the same object level are combined with `AND`.
31
+
32
+ ## Values
33
+
34
+ A scalar value means equality:
35
+
36
+ ```js
37
+ { status: 'active' }
38
+ ```
39
+
40
+ Values are bound through driver parameters.
41
+
42
+ `null` is special and becomes `IS NULL`:
43
+
44
+ ```js
45
+ { deleted_at: null }
46
+ ```
47
+
48
+ Objects that do not contain a recognized operator are treated as values and passed to the driver.
49
+
50
+ ## Comparison operators
51
+
52
+ Supported operators:
53
+
54
+ ```text
55
+ =
56
+ !=
57
+ >
58
+ <
59
+ >=
60
+ <=
61
+ LIKE
62
+ NOT LIKE
63
+ ILIKE
64
+ NOT ILIKE
65
+ IN
66
+ NOT IN
67
+ BETWEEN
68
+ NOT BETWEEN
69
+ ```
70
+
71
+ Examples:
72
+
73
+ ```js
74
+ { age: { '>=': 18 } }
75
+ { email: { LIKE: '%@example.com' } }
76
+ { email: { 'NOT LIKE': '%@example.com' } }
77
+ ```
78
+
79
+ ## IN and NOT IN
80
+
81
+ ```js
82
+ { status: { IN: ['active', 'pending'] } }
83
+ { status: { 'NOT IN': ['deleted', 'blocked'] } }
84
+ ```
85
+
86
+ Each item receives its own parameter placeholder.
87
+
88
+ ## BETWEEN and NOT BETWEEN
89
+
90
+ ```js
91
+ { age: { BETWEEN: [18, 65] } }
92
+ { age: { 'NOT BETWEEN': [18, 65] } }
93
+ ```
94
+
95
+ The value is a two-element array `[min, max]`.
96
+
97
+ ## NULL
98
+
99
+ ```js
100
+ { deleted_at: null }
101
+ { deleted_at: { '=': null } }
102
+ { deleted_at: { '!=': null } }
103
+ ```
104
+
105
+ These compile to `IS NULL` / `IS NOT NULL`.
106
+
107
+ ## AND, OR and NOT
108
+
109
+ ```js
110
+ {
111
+ AND: [
112
+ { active: true },
113
+ {
114
+ OR: [
115
+ { role: 'admin' },
116
+ { role: 'moderator' },
117
+ ],
118
+ },
119
+ ],
120
+ }
121
+ ```
122
+
123
+ Negation:
124
+
125
+ ```js
126
+ {
127
+ NOT: {
128
+ status: { IN: ['deleted', 'blocked'] },
129
+ },
130
+ }
131
+ ```
132
+
133
+ Empty logical arrays and invalid `NOT` values are rejected.
134
+
135
+ ## Select
136
+
137
+ Supported structured select entries include:
138
+
139
+ | Entry | Meaning |
140
+ |---|---|
141
+ | `'email'` | Column |
142
+ | `'*'` | All columns |
143
+ | `{ col: 'email', as: 'address' }` | Column + alias |
144
+ | `{ sum: 'amount', as: 'total' }` | `SUM` |
145
+ | `{ count: 'id', as: 'total' }` | `COUNT` |
146
+ | `{ count: ['team', 'source'] }` | Multi-column count expression |
147
+ | `{ count: { status: 'active' } }` | Conditional count |
148
+ | `{ distinct: 'status' }` | `DISTINCT` |
149
+ | `{ dateFormat: ['created_at', '%Y-%m'] }` | Date formatting |
150
+
151
+ Example:
152
+
153
+ ```js
154
+ const stats = await User.find({
155
+ select: [
156
+ 'status',
157
+ { count: 'id', as: 'total' },
158
+ { sum: 'amount', as: 'revenue' },
159
+ ],
160
+ where: { active: true },
161
+ });
162
+ ```
163
+
164
+ ## Group by
165
+
166
+ `groupBy` is passed as an option to `find()`:
167
+
168
+ ```js
169
+ const stats = await User.find({
170
+ select: [
171
+ 'status',
172
+ { count: 'id', as: 'total' },
173
+ ],
174
+ groupBy: ['status'],
175
+ });
176
+ ```
177
+
178
+ Structured group items can use `col` and `dateFormat` (and the builder accepts `as`).
179
+
180
+ ## Having
181
+
182
+ `having` filters grouped results and uses the same structured condition format as `where`:
183
+
184
+ ```js
185
+ const stats = await User.find({
186
+ select: [
187
+ 'status',
188
+ { count: 'id', as: 'total' },
189
+ ],
190
+ groupBy: ['status'],
191
+ having: {
192
+ total: { '>': 10 },
193
+ },
194
+ });
195
+ ```
196
+
197
+ The condition is applied after `GROUP BY`. Raw `HAVING` strings are rejected.
198
+
199
+ ## Order by
200
+
201
+ `orderBy` is also part of the `find()` options and can sort by a selected column or alias:
202
+
203
+ ```js
204
+ const stats = await User.find({
205
+ select: [
206
+ 'status',
207
+ { count: 'id', as: 'total' },
208
+ ],
209
+ groupBy: ['status'],
210
+ having: {
211
+ total: { '>': 10 },
212
+ },
213
+ orderBy: [
214
+ { field: 'total', direction: 'DESC' },
215
+ { field: 'status', direction: 'ASC' },
216
+ ],
217
+ });
218
+ ```
219
+
220
+ Only `ASC` and `DESC` are accepted.
221
+
222
+ ## Complete example
223
+
224
+ The following example combines `SELECT`, aggregation, `WHERE`, `GROUP BY`, `HAVING` and `ORDER BY` in one query:
225
+
226
+ ```js
227
+ const stats = await User.find({
228
+ select: [
229
+ 'status',
230
+ { count: 'id', as: 'total' },
231
+ { sum: 'amount', as: 'revenue' },
232
+ ],
233
+ where: {
234
+ active: true,
235
+ },
236
+ groupBy: ['status'],
237
+ having: {
238
+ total: { '>': 10 },
239
+ },
240
+ orderBy: [
241
+ { field: 'total', direction: 'DESC' },
242
+ ],
243
+ });
244
+ ```
245
+
246
+ Conceptually, this produces the following SQL shape:
247
+
248
+ ```sql
249
+ SELECT status, COUNT(id) AS total, SUM(amount) AS revenue
250
+ FROM users
251
+ WHERE active = ?
252
+ GROUP BY status
253
+ HAVING total > ?
254
+ ORDER BY total DESC
255
+ ```
256
+
257
+ `WHERE` and `HAVING` values remain parameterized by the Connector.
258
+
259
+ ## Limit and offset
260
+
261
+ ```js
262
+ {
263
+ limit: 50,
264
+ offset: 100,
265
+ }
266
+ ```
267
+
268
+ Both values must be non-negative integers. PostgreSQL uses numbered placeholders for these values.
269
+
270
+ ## Joins
271
+
272
+ `Model.find()` currently emits an `INNER JOIN` from `join.table` and `join.on`:
273
+
274
+ ```js
275
+ join: {
276
+ table: 'profiles',
277
+ on: 'users.id = profiles.user_id',
278
+ }
279
+ ```
280
+
281
+ The `on` expression is SQL text and must therefore be trusted.
package/docs/README.md ADDED
@@ -0,0 +1,20 @@
1
+ # sql-connector documentation
2
+
3
+ This directory contains the detailed reference documentation for `@mlagie/sql-connector`.
4
+
5
+ ## Contents
6
+
7
+ | Document | Purpose |
8
+ |---|---|
9
+ | [Schema](./SCHEMA.md) | Types, fields, defaults, constraints and foreign keys. |
10
+ | [Queries and conditions](./QUERY.md) | `WHERE`, `HAVING`, operators, values, aggregations, grouping, ordering and joins. |
11
+ | [Migrations](./MIGRATIONS.md) | Planning, schema diffing, approvals, destructive changes, checksums, encryption and backups. |
12
+ | [API](./API.md) | Public exports and method reference. |
13
+ | [Dialects](./DIALECTS.md) | MySQL/PostgreSQL behavior and SQL generation differences. |
14
+ | [French documentation](./fr/README_FR.md) | Documentation française. |
15
+
16
+ ## Documentation principles
17
+
18
+ The documentation describes the behavior implemented by the current package rather than every SQL feature that exists in the SQL standard or in an individual database engine.
19
+
20
+ Where an API is intentionally narrower than raw SQL, the supported structured representation is documented explicitly.
package/docs/SCHEMA.md ADDED
@@ -0,0 +1,167 @@
1
+ # Schema reference
2
+
3
+ ## Table of contents
4
+
5
+ 1. [Schema object](#schema-object)
6
+ 2. [Field definition](#field-definition)
7
+ 3. [Types](#types)
8
+ 4. [Length, precision and scale](#length-precision-and-scale)
9
+ 5. [Defaults](#defaults)
10
+ 6. [Constraints](#constraints)
11
+ 7. [Enums](#enums)
12
+ 8. [Foreign keys](#foreign-keys)
13
+ 9. [Column renames](#column-renames)
14
+ 10. [Custom SQL](#custom-sql)
15
+
16
+ ## Schema object
17
+
18
+ ```js
19
+ const { Schema } = require('@mlagie/sql-connector');
20
+
21
+ const userSchema = new Schema({
22
+ id: { type: Number, primary_key: true, auto_increment: true },
23
+ email: { type: String, required: true, unique: true, length: 320 },
24
+ });
25
+ ```
26
+
27
+ `Schema` stores the desired field definitions in `schemaDict`.
28
+
29
+ ## Field definition
30
+
31
+ ```js
32
+ {
33
+ type: String,
34
+ length: 255,
35
+ precision: 12,
36
+ scale: 2,
37
+ required: true,
38
+ default: 'active',
39
+ unique: true,
40
+ primary_key: false,
41
+ auto_increment: false,
42
+ enum: ['active', 'inactive'],
43
+ foreignKey: 'users(id)',
44
+ oldname: 'legacy_email',
45
+ customize: 'CHECK (...)'
46
+ }
47
+ ```
48
+
49
+ ## Types
50
+
51
+ The built-in map is:
52
+
53
+ ```text
54
+ String -> VARCHAR
55
+ Char -> CHAR
56
+ Number -> INT
57
+ SmallInt -> SMALLINT
58
+ BigInt -> BIGINT
59
+ Decimal -> DECIMAL
60
+ Boolean -> BOOLEAN
61
+ Date -> DATETIME
62
+ Object -> JSON
63
+ Array -> VARCHAR
64
+ Now -> NOW()
65
+ Float -> FLOAT
66
+ Double -> DOUBLE
67
+ Text -> TEXT
68
+ Blob -> BLOB
69
+ Binary -> VARBINARY
70
+ Uuid -> CHAR
71
+ DateTime -> DATETIME
72
+ Timestamp -> TIMESTAMP
73
+ CurrentTimestamp -> CURRENT_TIMESTAMP
74
+ ```
75
+
76
+ JavaScript constructors `String`, `Number`, `Boolean`, `Date`, `Object` and `Array` are also accepted by the schema API.
77
+
78
+ ## Length, precision and scale
79
+
80
+ Use `length` with `VARCHAR`, `CHAR`, `BINARY` and `VARBINARY`.
81
+
82
+ Use `precision` and `scale` with `DECIMAL`:
83
+
84
+ ```js
85
+ amount: {
86
+ type: 'DECIMAL',
87
+ precision: 12,
88
+ scale: 2,
89
+ }
90
+ ```
91
+
92
+ ## Defaults
93
+
94
+ Defaults may be `null`, strings, numbers, booleans, dates, objects, temporal SQL expressions or functions returning supported values.
95
+
96
+ ```js
97
+ created_at: {
98
+ type: Date,
99
+ default: 'CURRENT_TIMESTAMP',
100
+ }
101
+ ```
102
+
103
+ `sqlTypeMap.CurrentTimestamp` is available as a reusable constant.
104
+
105
+ ## Constraints
106
+
107
+ Supported field-level constraints:
108
+
109
+ - `required` → `NOT NULL`;
110
+ - `unique` → `UNIQUE`;
111
+ - `primary_key` → `PRIMARY KEY`;
112
+ - `auto_increment` → auto-increment / identity behavior;
113
+ - `foreignKey` → `FOREIGN KEY`;
114
+ - `default` → `DEFAULT`;
115
+ - `enum` → `ENUM` where supported by the generated dialect SQL;
116
+ - `customize` → additional SQL definition.
117
+
118
+ Do not set `primary_key` and `unique` together.
119
+
120
+ ## Enums
121
+
122
+ ```js
123
+ status: {
124
+ type: String,
125
+ enum: ['pending', 'paid', 'cancelled'],
126
+ default: 'pending',
127
+ }
128
+ ```
129
+
130
+ Enum values are SQL-escaped when the column definition is generated.
131
+
132
+ ## Foreign keys
133
+
134
+ The connector accepts both forms:
135
+
136
+ ```js
137
+ user_id: { type: Number, foreignKey: 'users(id)' }
138
+ user_id: { type: Number, foreignKey: 'users.id' }
139
+ ```
140
+
141
+ `Model.syncAllTables()` orders table creation according to model dependencies and rejects cycles.
142
+
143
+ ## Column renames
144
+
145
+ Migrations use `oldname` for explicit renames:
146
+
147
+ ```js
148
+ email_address: {
149
+ type: String,
150
+ oldname: 'email',
151
+ }
152
+ ```
153
+
154
+ The differ never guesses a rename from similar names.
155
+
156
+ ## Custom SQL
157
+
158
+ ```js
159
+ amount: {
160
+ type: 'DECIMAL',
161
+ precision: 12,
162
+ scale: 2,
163
+ customize: 'CHECK (amount >= 0)',
164
+ }
165
+ ```
166
+
167
+ `customize` is appended to the generated column definition. Treat it as trusted SQL.