rado 1.0.10 → 1.0.11
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 +260 -131
- package/dist/core/Dialect.d.ts +3 -1
- package/dist/core/Dialect.js +5 -3
- package/dist/core/Driver.d.ts +1 -0
- package/dist/core/Emitter.d.ts +5 -4
- package/dist/core/Emitter.js +15 -2
- package/dist/core/query/Insert.d.ts +8 -4
- package/dist/core/query/Insert.js +15 -6
- package/dist/driver/better-sqlite3.js +1 -0
- package/dist/driver/bun-sqlite.js +1 -0
- package/dist/driver/d1.d.ts +1 -0
- package/dist/driver/d1.js +7 -9
- package/dist/driver/mysql2.d.ts +1 -0
- package/dist/driver/mysql2.js +1 -0
- package/dist/driver/pg.d.ts +1 -0
- package/dist/driver/pg.js +1 -0
- package/dist/driver/pglite.d.ts +1 -0
- package/dist/driver/pglite.js +1 -0
- package/dist/driver/sql.js.js +1 -0
- package/dist/mysql/dialect.js +1 -0
- package/dist/postgres/dialect.js +1 -0
- package/dist/sqlite/dialect.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,200 +5,329 @@
|
|
|
5
5
|
|
|
6
6
|
Fully typed, lightweight TypeScript query builder.
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- Fully typed queries using TypeScript
|
|
11
|
+
- Composable and reusable query structures
|
|
12
|
+
- First-class support for JSON columns
|
|
13
|
+
- No code generation step required
|
|
14
|
+
- Zero dependencies
|
|
15
|
+
- Universal query support for multiple database engines
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
13
18
|
|
|
14
19
|
<pre>npm install <a href="https://www.npmjs.com/package/rado">rado</a></pre>
|
|
15
20
|
|
|
16
|
-
##
|
|
21
|
+
## Quick Start
|
|
17
22
|
|
|
18
|
-
|
|
23
|
+
```typescript
|
|
24
|
+
import {table} from 'rado'
|
|
25
|
+
import {text, integer} from 'rado/postgres'
|
|
26
|
+
import {connect} from 'rado/driver/pg'
|
|
27
|
+
import {Pool} from 'pg'
|
|
28
|
+
|
|
29
|
+
// Define your schema
|
|
30
|
+
const User = table({
|
|
31
|
+
id: integer().primaryKey(),
|
|
32
|
+
name: text().notNull(),
|
|
33
|
+
email: text().unique()
|
|
34
|
+
})
|
|
19
35
|
|
|
20
|
-
|
|
36
|
+
// Connect to the database
|
|
37
|
+
const db = connect(new Pool({
|
|
38
|
+
host: 'localhost',
|
|
39
|
+
database: 'my_database'
|
|
40
|
+
}))
|
|
21
41
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
eq(User.id, Post.userId) // Compare to fields on other tables
|
|
42
|
+
// Perform a query
|
|
43
|
+
const users = await db.select().from(User)
|
|
44
|
+
console.log(users)
|
|
26
45
|
```
|
|
27
46
|
|
|
28
|
-
|
|
47
|
+
## Supported Databases
|
|
29
48
|
|
|
30
|
-
|
|
31
|
-
import {eq, or, gt, lt} from 'rado'
|
|
32
|
-
db.select().from(User).where(eq(User.id, 1))
|
|
33
|
-
// Conditions can be as complex as you want
|
|
34
|
-
db.select().from(User).where(or(gt(User.id, 1), lt(User.id, 5)))
|
|
35
|
-
```
|
|
49
|
+
Currently supported drivers:
|
|
36
50
|
|
|
37
|
-
|
|
51
|
+
| `PostgreSQL ` | `import ` |
|
|
52
|
+
| -------------------------- | ------------------------------ |
|
|
53
|
+
| `pg` | `'rado/driver/pg'` |
|
|
54
|
+
| `@electric-sql/pglite` | `'rado/driver/pglite'` |
|
|
55
|
+
| `@neondatabase/serverless` | `'rado/driver/pg'` |
|
|
56
|
+
| `@vercel/postgres` | `'rado/driver/pg'` |
|
|
38
57
|
|
|
39
|
-
|
|
58
|
+
| `SQLite ` | `import ` |
|
|
59
|
+
| -------------------------- | ------------------------------ |
|
|
60
|
+
| `better-sqlite3` | `'rado/driver/better-sqlite3'` |
|
|
61
|
+
| `bun:sqlite` | `'rado/driver/bun-sqlite'` |
|
|
62
|
+
| `sql.js` | `'rado/driver/sql.js'` |
|
|
63
|
+
| `Cloudflare D1` | `'rado/driver/d1'` |
|
|
40
64
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
65
|
+
| `MySQL ` | `import ` |
|
|
66
|
+
| -------------------------- | ------------------------------ |
|
|
67
|
+
| `mysql2` | `'rado/driver/mysql2'` |
|
|
44
68
|
|
|
45
|
-
|
|
69
|
+
Pass an instance of the database to the `connect` function to get started:
|
|
46
70
|
|
|
47
71
|
```ts
|
|
48
|
-
import
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
// Aggregate posts of this user
|
|
53
|
-
posts: include(db.select().from(Post).where(eq(Post.userId, User.id)))
|
|
54
|
-
}).from(User)
|
|
72
|
+
import Database from 'better-sqlite3'
|
|
73
|
+
import {connect} from 'rado/driver/better-sqlite3'
|
|
74
|
+
|
|
75
|
+
const db = connect(new Database('foobar.db'))
|
|
55
76
|
```
|
|
56
77
|
|
|
57
|
-
|
|
78
|
+
## Querying
|
|
58
79
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
80
|
+
### Select
|
|
81
|
+
|
|
82
|
+
Select operations allow you to retrieve data from your database.
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
// Basic select
|
|
86
|
+
const allUsers = await db.select().from(User)
|
|
87
|
+
|
|
88
|
+
// Select specific columns
|
|
89
|
+
const userNames = await db.select({id: User.id, name: User.name}).from(User)
|
|
68
90
|
```
|
|
69
91
|
|
|
70
|
-
|
|
92
|
+
### Where conditions
|
|
71
93
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
94
|
+
Where conditions help you filter your query results.
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
import {eq, and, or, gt, isNull} from 'rado'
|
|
98
|
+
|
|
99
|
+
// Simple equality
|
|
100
|
+
const john = await db.select().from(User).where(eq(User.name, 'John'))
|
|
101
|
+
|
|
102
|
+
// Complex conditions
|
|
103
|
+
const users = await db.select().from(User)
|
|
104
|
+
.where(
|
|
105
|
+
gt(User.age, 18),
|
|
106
|
+
or(
|
|
107
|
+
eq(User.name, 'Alice'),
|
|
108
|
+
isNull(User.email)
|
|
109
|
+
)
|
|
110
|
+
)
|
|
78
111
|
```
|
|
79
112
|
|
|
80
|
-
|
|
113
|
+
### Joins
|
|
81
114
|
|
|
82
|
-
|
|
115
|
+
Joins allow you to combine data from multiple tables.
|
|
83
116
|
|
|
84
|
-
```
|
|
85
|
-
db.
|
|
117
|
+
```typescript
|
|
118
|
+
const usersWithPosts = await db.select({
|
|
119
|
+
userName: User.name,
|
|
120
|
+
postTitle: Post.title,
|
|
121
|
+
})
|
|
122
|
+
.from(User)
|
|
123
|
+
.leftJoin(Post, eq(User.id, Post.authorId))
|
|
86
124
|
```
|
|
87
125
|
|
|
88
|
-
|
|
126
|
+
### Ordering and Grouping
|
|
89
127
|
|
|
90
|
-
|
|
91
|
-
|
|
128
|
+
You can order and group your query results:
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
import {desc, asc, count} from 'rado'
|
|
132
|
+
|
|
133
|
+
const orderedUsers = await db.select()
|
|
134
|
+
.from(User)
|
|
135
|
+
.orderBy(asc(User.name))
|
|
136
|
+
|
|
137
|
+
const userPostCounts = await db.select({
|
|
138
|
+
userName: User.name,
|
|
139
|
+
postCount: count(Post.id)
|
|
140
|
+
})
|
|
141
|
+
.from(User)
|
|
142
|
+
.leftJoin(Post, eq(User.id, Post.authorId))
|
|
143
|
+
.groupBy(User.name)
|
|
92
144
|
```
|
|
93
145
|
|
|
94
|
-
|
|
146
|
+
### Pagination
|
|
95
147
|
|
|
96
|
-
|
|
148
|
+
Pagination helps you manage large datasets by retrieving results in smaller chunks:
|
|
97
149
|
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
150
|
+
```typescript
|
|
151
|
+
const pageSize = 10
|
|
152
|
+
const page = 2
|
|
153
|
+
|
|
154
|
+
const paginatedUsers = await db.select()
|
|
155
|
+
.from(User)
|
|
156
|
+
.limit(pageSize)
|
|
157
|
+
.offset((page - 1) * pageSize)
|
|
102
158
|
```
|
|
103
159
|
|
|
104
|
-
|
|
160
|
+
### JSON columns
|
|
105
161
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
162
|
+
Rado provides first-class support for JSON columns:
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
import {pgTable, serial, text, jsonb} from 'rado/postgres'
|
|
166
|
+
|
|
167
|
+
const User = pgTable({
|
|
168
|
+
id: serial().primaryKey(),
|
|
169
|
+
name: text(),
|
|
170
|
+
metadata: jsonb<{subscribed: boolean}>()
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
const subscribedUsers = await db.select()
|
|
174
|
+
.from(User)
|
|
175
|
+
.where(eq(User.metadata.subscribed, true))
|
|
111
176
|
```
|
|
112
177
|
|
|
113
|
-
|
|
178
|
+
### Subqueries
|
|
114
179
|
|
|
115
|
-
|
|
180
|
+
```typescript
|
|
181
|
+
const subquery = db.select({authorId: Post.authorId})
|
|
182
|
+
.from(Post)
|
|
183
|
+
.groupBy(Post.authorId)
|
|
184
|
+
.having(gt(count(Post.id), 5))
|
|
185
|
+
.as('authorIds')
|
|
116
186
|
|
|
117
|
-
|
|
118
|
-
|
|
187
|
+
const prolificAuthors = await db.select()
|
|
188
|
+
.from(User)
|
|
189
|
+
.where(inArray(User.id, subquery))
|
|
119
190
|
```
|
|
120
191
|
|
|
121
|
-
|
|
192
|
+
### Include
|
|
122
193
|
|
|
123
|
-
|
|
124
|
-
import * as sqlite from 'rado/sqlite'
|
|
125
|
-
import {sqliteTable as table} from 'rado/sqlite'
|
|
194
|
+
Aggregate rows using the `include` function:
|
|
126
195
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
userName: sqlite.text('user') // Column names are optional
|
|
130
|
-
})
|
|
196
|
+
```typescript
|
|
197
|
+
import {include} from 'rado'
|
|
131
198
|
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
199
|
+
const usersWithPosts = await db.select({
|
|
200
|
+
...User,
|
|
201
|
+
posts: include(
|
|
202
|
+
db.select().from(Post).where(eq(Post.authorId, User.id))
|
|
203
|
+
)
|
|
204
|
+
}).from(User)
|
|
137
205
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
206
|
+
// Use include.one for a single related record
|
|
207
|
+
const usersWithLatestPost = await db.select({
|
|
208
|
+
...User,
|
|
209
|
+
latestPost: include.one(
|
|
210
|
+
db.select()
|
|
211
|
+
.from(Post)
|
|
212
|
+
.where(eq(Post.authorId, User.id))
|
|
213
|
+
.orderBy(desc(Post.createdAt))
|
|
214
|
+
.limit(1)
|
|
215
|
+
)
|
|
216
|
+
}).from(User)
|
|
217
|
+
```
|
|
142
218
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
219
|
+
### SQL Operator
|
|
220
|
+
|
|
221
|
+
The `sql` operator allows you to write raw SQL and interpolate values safely:
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
import {sql} from 'rado'
|
|
225
|
+
|
|
226
|
+
const minAge = 18
|
|
227
|
+
const adultUsers = await db.select()
|
|
228
|
+
.from(User)
|
|
229
|
+
.where(sql`${User.age} >= ${minAge}`)
|
|
147
230
|
```
|
|
148
231
|
|
|
149
|
-
##
|
|
232
|
+
## Modifying Data
|
|
150
233
|
|
|
151
|
-
|
|
234
|
+
### Insert
|
|
152
235
|
|
|
153
|
-
|
|
154
|
-
| -------------------------- | ------------------------------ |
|
|
155
|
-
| `pg` | `'rado/driver/pg'` |
|
|
156
|
-
| `@electric-sql/pglite` | `'rado/driver/pglite'` |
|
|
157
|
-
| `@neondatabase/serverless` | `'rado/driver/pg'` |
|
|
158
|
-
| `@vercel/postgres` | `'rado/driver/pg'` |
|
|
236
|
+
Insert operations allow you to add new records to your database:
|
|
159
237
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
| `Cloudflare D1` | `'rado/driver/d1'` |
|
|
238
|
+
```typescript
|
|
239
|
+
// Single insert
|
|
240
|
+
const newUser = await db.insert(User)
|
|
241
|
+
.values({name: 'Alice', email: 'alice@example.com'})
|
|
242
|
+
.returning()
|
|
166
243
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
244
|
+
// Bulk insert
|
|
245
|
+
const newUsers = await db.insert(User)
|
|
246
|
+
.values([
|
|
247
|
+
{name: 'Bob', email: 'bob@example.com'},
|
|
248
|
+
{name: 'Charlie', email: 'charlie@example.com'},
|
|
249
|
+
])
|
|
250
|
+
.returning()
|
|
251
|
+
```
|
|
170
252
|
|
|
171
|
-
|
|
253
|
+
### Update
|
|
172
254
|
|
|
173
|
-
|
|
174
|
-
import Database from 'better-sqlite3'
|
|
175
|
-
import {connect} from 'rado/driver/better-sqlite3'
|
|
255
|
+
Update operations modify existing records:
|
|
176
256
|
|
|
177
|
-
|
|
257
|
+
```typescript
|
|
258
|
+
const updatedCount = await db.update(User)
|
|
259
|
+
.set({name: 'Johnny'})
|
|
260
|
+
.where(eq(User.name, 'John'))
|
|
178
261
|
```
|
|
179
262
|
|
|
263
|
+
### Delete
|
|
180
264
|
|
|
181
|
-
|
|
265
|
+
Delete operations remove records from your database:
|
|
182
266
|
|
|
183
|
-
|
|
267
|
+
```typescript
|
|
268
|
+
await db.delete(User).where(eq(User.name, 'John'))
|
|
269
|
+
```
|
|
184
270
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
271
|
+
### Transactions
|
|
272
|
+
|
|
273
|
+
Transactions allow you to group multiple operations into a single atomic unit.
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
276
|
+
const result = await db.transaction(async (tx) => {
|
|
277
|
+
const user = await tx.insert(User)
|
|
278
|
+
.values({ name: 'Alice', email: 'alice@example.com' })
|
|
279
|
+
.returning()
|
|
280
|
+
const post = await tx.insert(Post)
|
|
281
|
+
.values({ title: 'My First Post', authorId: user.id })
|
|
282
|
+
.returning()
|
|
283
|
+
return {user, post}
|
|
284
|
+
})
|
|
190
285
|
```
|
|
191
286
|
|
|
192
|
-
|
|
287
|
+
## Universal Queries
|
|
193
288
|
|
|
194
|
-
|
|
195
|
-
|
|
289
|
+
Rado provides a universal query builder that works across different database
|
|
290
|
+
engines, whether they run synchronously or asynchronously. This is useful for
|
|
291
|
+
writing database-agnostic code.
|
|
196
292
|
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
293
|
+
```typescript
|
|
294
|
+
import {table} from 'rado'
|
|
295
|
+
import {id, text} from 'rado/universal'
|
|
296
|
+
|
|
297
|
+
const User = table('user', {
|
|
298
|
+
id: id(),
|
|
299
|
+
name: text()
|
|
203
300
|
})
|
|
301
|
+
|
|
302
|
+
const db = process.env.SQLITE ? sqliteDb : postgresDb
|
|
303
|
+
|
|
304
|
+
const userNames = await db.select(User.name).from(User)
|
|
204
305
|
```
|
|
306
|
+
|
|
307
|
+
## Custom Column Types
|
|
308
|
+
|
|
309
|
+
You can define custom column types, such as a boolean column that is stored as a
|
|
310
|
+
tinyint in the database:
|
|
311
|
+
|
|
312
|
+
```typescript
|
|
313
|
+
import {Column, column, table, sql} from 'rado'
|
|
314
|
+
|
|
315
|
+
export function bool(name?: string): Column<boolean | null> {
|
|
316
|
+
return column({
|
|
317
|
+
name,
|
|
318
|
+
type: sql`tinyint(1)`,
|
|
319
|
+
mapFromDriverValue(value: number): boolean {
|
|
320
|
+
return value === 1
|
|
321
|
+
},
|
|
322
|
+
mapToDriverValue(value: boolean): number {
|
|
323
|
+
return value ? 1 : 0
|
|
324
|
+
}
|
|
325
|
+
})
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Usage
|
|
329
|
+
const User = table('user', {
|
|
330
|
+
// ...
|
|
331
|
+
isActive: bool()
|
|
332
|
+
})
|
|
333
|
+
```
|
package/dist/core/Dialect.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { Emitter } from './Emitter.js';
|
|
2
2
|
import { type HasQuery, type HasSql } from './Internal.js';
|
|
3
|
+
import type { Runtime } from './MetaData.js';
|
|
3
4
|
export declare class Dialect {
|
|
4
5
|
#private;
|
|
5
|
-
|
|
6
|
+
runtime: Runtime;
|
|
7
|
+
constructor(runtime: Runtime, createEmitter: new (runtime: Runtime) => Emitter);
|
|
6
8
|
emit: (input: HasSql | HasQuery) => Emitter;
|
|
7
9
|
inline: (input: HasSql | HasQuery) => string;
|
|
8
10
|
}
|
package/dist/core/Dialect.js
CHANGED
|
@@ -5,19 +5,21 @@ import {
|
|
|
5
5
|
hasQuery
|
|
6
6
|
} from "./Internal.js";
|
|
7
7
|
var Dialect = class {
|
|
8
|
+
runtime;
|
|
8
9
|
#createEmitter;
|
|
9
|
-
constructor(createEmitter) {
|
|
10
|
+
constructor(runtime, createEmitter) {
|
|
11
|
+
this.runtime = runtime;
|
|
10
12
|
this.#createEmitter = createEmitter;
|
|
11
13
|
}
|
|
12
14
|
emit = (input) => {
|
|
13
15
|
const sql = hasQuery(input) ? getQuery(input) : getSql(input);
|
|
14
|
-
const emitter = new this.#createEmitter();
|
|
16
|
+
const emitter = new this.#createEmitter(this.runtime);
|
|
15
17
|
sql.emitTo(emitter);
|
|
16
18
|
return emitter;
|
|
17
19
|
};
|
|
18
20
|
inline = (input) => {
|
|
19
21
|
const sql = hasQuery(input) ? getQuery(input) : getSql(input);
|
|
20
|
-
const emitter = new this.#createEmitter();
|
|
22
|
+
const emitter = new this.#createEmitter(this.runtime);
|
|
21
23
|
sql.inlineValues().emitTo(emitter);
|
|
22
24
|
return emitter.sql;
|
|
23
25
|
};
|
package/dist/core/Driver.d.ts
CHANGED
package/dist/core/Emitter.d.ts
CHANGED
|
@@ -13,17 +13,18 @@ import type { SelectData } from './query/Select.js';
|
|
|
13
13
|
import type { UnionData } from './query/Union.js';
|
|
14
14
|
import type { Update } from './query/Update.js';
|
|
15
15
|
export declare abstract class Emitter {
|
|
16
|
+
#private;
|
|
16
17
|
sql: string;
|
|
17
18
|
protected params: Array<Param>;
|
|
18
|
-
get hasParams(): boolean;
|
|
19
|
-
bind(inputs?: Record<string, unknown>): Array<unknown>;
|
|
20
|
-
processValue(value: unknown): unknown;
|
|
21
|
-
abstract runtime: Runtime;
|
|
22
19
|
abstract emitIdentifier(value: string): void;
|
|
23
20
|
abstract emitValue(value: unknown): void;
|
|
24
21
|
abstract emitInline(value: unknown): void;
|
|
25
22
|
abstract emitJsonPath(path: JsonPath): void;
|
|
26
23
|
abstract emitPlaceholder(value: string): void;
|
|
24
|
+
constructor(runtime: Runtime);
|
|
25
|
+
get hasParams(): boolean;
|
|
26
|
+
bind(inputs?: Record<string, unknown>): Array<unknown>;
|
|
27
|
+
processValue(value: unknown): unknown;
|
|
27
28
|
emitIdentifierOrSelf(value: string): void;
|
|
28
29
|
selfName?: string;
|
|
29
30
|
emitSelf({ name, inner }: {
|
package/dist/core/Emitter.js
CHANGED
|
@@ -10,8 +10,12 @@ import { Sql, sql } from "./Sql.js";
|
|
|
10
10
|
import { callFunction } from "./expr/Functions.js";
|
|
11
11
|
import { jsonAggregateArray, jsonArray } from "./expr/Json.js";
|
|
12
12
|
var Emitter = class {
|
|
13
|
+
#runtime;
|
|
13
14
|
sql = "";
|
|
14
15
|
params = [];
|
|
16
|
+
constructor(runtime) {
|
|
17
|
+
this.#runtime = runtime;
|
|
18
|
+
}
|
|
15
19
|
get hasParams() {
|
|
16
20
|
return this.params.length > 0;
|
|
17
21
|
}
|
|
@@ -85,7 +89,15 @@ var Emitter = class {
|
|
|
85
89
|
}).emitTo(this);
|
|
86
90
|
}
|
|
87
91
|
emitInsert(insert) {
|
|
88
|
-
const {
|
|
92
|
+
const {
|
|
93
|
+
cte,
|
|
94
|
+
into,
|
|
95
|
+
values,
|
|
96
|
+
select,
|
|
97
|
+
onConflict,
|
|
98
|
+
onDuplicateKeyUpdate,
|
|
99
|
+
returning
|
|
100
|
+
} = getData(insert);
|
|
89
101
|
if (cte) this.emitWith(cte);
|
|
90
102
|
const table = getTable(into);
|
|
91
103
|
const tableName = sql.identifier(table.name);
|
|
@@ -93,6 +105,7 @@ var Emitter = class {
|
|
|
93
105
|
insertInto: sql`${tableName}(${table.listColumns()})`,
|
|
94
106
|
...values ? { values } : { "": select },
|
|
95
107
|
onConflict,
|
|
108
|
+
onDuplicateKeyUpdate,
|
|
96
109
|
returning
|
|
97
110
|
}).inlineFields(false).emitTo(this);
|
|
98
111
|
}
|
|
@@ -160,7 +173,7 @@ var Emitter = class {
|
|
|
160
173
|
sql`(select ${data.first ? subject : jsonAggregateArray(subject)} from (${inner}) as _)`.emitTo(this);
|
|
161
174
|
}
|
|
162
175
|
emitUniversal(runtimes) {
|
|
163
|
-
const sql2 = runtimes[this
|
|
176
|
+
const sql2 = runtimes[this.#runtime] ?? runtimes.default;
|
|
164
177
|
if (!sql2) throw new Error("Unsupported runtime");
|
|
165
178
|
sql2.emitTo(this);
|
|
166
179
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type HasSql, type HasTable, internalData, internalQuery, internalSelection } from '../Internal.js';
|
|
2
|
-
import type { IsPostgres, IsSqlite, QueryMeta } from '../MetaData.js';
|
|
2
|
+
import type { IsMysql, IsPostgres, IsSqlite, QueryMeta } from '../MetaData.js';
|
|
3
3
|
import { Query, type QueryData } from '../Query.js';
|
|
4
4
|
import { type Selection, type SelectionInput, type SelectionRow } from '../Selection.js';
|
|
5
5
|
import { type Sql } from '../Sql.js';
|
|
@@ -12,6 +12,7 @@ interface InsertIntoData<Meta extends QueryMeta> extends QueryData<Meta> {
|
|
|
12
12
|
export interface InsertData<Meta extends QueryMeta> extends InsertIntoData<Meta> {
|
|
13
13
|
returning?: Selection;
|
|
14
14
|
onConflict?: HasSql;
|
|
15
|
+
onDuplicateKeyUpdate?: HasSql;
|
|
15
16
|
}
|
|
16
17
|
export declare class Insert<Result, Meta extends QueryMeta = QueryMeta> extends Query<Result, Meta> {
|
|
17
18
|
readonly [internalData]: InsertData<Meta>;
|
|
@@ -27,14 +28,17 @@ export interface OnConflict {
|
|
|
27
28
|
target: HasSql | Array<HasSql>;
|
|
28
29
|
targetWhere?: HasSql<boolean>;
|
|
29
30
|
}
|
|
30
|
-
export interface
|
|
31
|
+
export interface OnConflictSet<Definition extends TableDefinition> {
|
|
31
32
|
set: TableUpdate<Definition>;
|
|
33
|
+
}
|
|
34
|
+
export interface OnConflictUpdate<Definition extends TableDefinition> extends OnConflict, OnConflictSet<Definition> {
|
|
32
35
|
setWhere?: HasSql<boolean>;
|
|
33
36
|
}
|
|
34
37
|
declare class InsertCanConflict<Definition extends TableDefinition, Meta extends QueryMeta> extends InsertCanReturn<Definition, Meta> {
|
|
35
38
|
#private;
|
|
36
|
-
onConflictDoNothing(this: InsertCanConflict<Definition, IsPostgres>, onConflict?: OnConflict): InsertCanReturn<Definition, Meta>;
|
|
37
|
-
onConflictDoUpdate(this: InsertCanConflict<Definition, IsPostgres>, onConflict: OnConflictUpdate<Definition>): InsertCanReturn<Definition, Meta>;
|
|
39
|
+
onConflictDoNothing(this: InsertCanConflict<Definition, IsPostgres | IsSqlite>, onConflict?: OnConflict): InsertCanReturn<Definition, Meta>;
|
|
40
|
+
onConflictDoUpdate(this: InsertCanConflict<Definition, IsPostgres | IsSqlite>, onConflict: OnConflictUpdate<Definition>): InsertCanReturn<Definition, Meta>;
|
|
41
|
+
onDuplicateKeyUpdate(this: InsertCanConflict<Definition, IsMysql>, update: OnConflictSet<Definition>): InsertCanReturn<Definition, Meta>;
|
|
38
42
|
}
|
|
39
43
|
export declare class InsertInto<Definition extends TableDefinition, Meta extends QueryMeta> {
|
|
40
44
|
[internalData]: InsertData<Meta>;
|
|
@@ -40,18 +40,27 @@ var InsertCanConflict = class extends InsertCanReturn {
|
|
|
40
40
|
onConflictDoUpdate(onConflict) {
|
|
41
41
|
return this.#onConflict(onConflict);
|
|
42
42
|
}
|
|
43
|
+
onDuplicateKeyUpdate(update) {
|
|
44
|
+
return new InsertCanReturn({
|
|
45
|
+
...getData(this),
|
|
46
|
+
onDuplicateKeyUpdate: this.#updateFields(update.set)
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
#updateFields(update) {
|
|
50
|
+
return sql.join(
|
|
51
|
+
Object.entries(update).map(
|
|
52
|
+
([key, value]) => sql`${sql.identifier(key)} = ${input(value)}`
|
|
53
|
+
),
|
|
54
|
+
sql`, `
|
|
55
|
+
);
|
|
56
|
+
}
|
|
43
57
|
#onConflict({
|
|
44
58
|
target,
|
|
45
59
|
targetWhere,
|
|
46
60
|
set,
|
|
47
61
|
setWhere
|
|
48
62
|
}) {
|
|
49
|
-
const update = set &&
|
|
50
|
-
Object.entries(set).map(
|
|
51
|
-
([key, value]) => sql`${sql.identifier(key)} = ${input(value)}`
|
|
52
|
-
),
|
|
53
|
-
sql`, `
|
|
54
|
-
);
|
|
63
|
+
const update = set && this.#updateFields(set);
|
|
55
64
|
return new InsertCanReturn({
|
|
56
65
|
...getData(this),
|
|
57
66
|
onConflict: sql.join([
|
package/dist/driver/d1.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ declare class PreparedStatement implements AsyncStatement {
|
|
|
14
14
|
export declare class D1Driver implements AsyncDriver {
|
|
15
15
|
private client;
|
|
16
16
|
parsesJson: boolean;
|
|
17
|
+
supportsTransactions: boolean;
|
|
17
18
|
constructor(client: Client);
|
|
18
19
|
exec(query: string): Promise<void>;
|
|
19
20
|
prepare(sql: string, options: PrepareOptions): PreparedStatement;
|
package/dist/driver/d1.js
CHANGED
|
@@ -17,11 +17,7 @@ var PreparedStatement = class {
|
|
|
17
17
|
return this.stmt.bind(...params).first();
|
|
18
18
|
}
|
|
19
19
|
async values(params) {
|
|
20
|
-
if (this.isSelection)
|
|
21
|
-
const results = await this.stmt.bind(...params).raw();
|
|
22
|
-
console.log(results);
|
|
23
|
-
return results;
|
|
24
|
-
}
|
|
20
|
+
if (this.isSelection) return this.stmt.bind(...params).raw();
|
|
25
21
|
await this.stmt.bind(...params).run();
|
|
26
22
|
return [];
|
|
27
23
|
}
|
|
@@ -33,6 +29,7 @@ var D1Driver = class {
|
|
|
33
29
|
this.client = client;
|
|
34
30
|
}
|
|
35
31
|
parsesJson = false;
|
|
32
|
+
supportsTransactions = false;
|
|
36
33
|
async exec(query) {
|
|
37
34
|
await this.client.exec(query);
|
|
38
35
|
}
|
|
@@ -42,10 +39,11 @@ var D1Driver = class {
|
|
|
42
39
|
async close() {
|
|
43
40
|
}
|
|
44
41
|
async batch(queries) {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
42
|
+
const stmts = queries.map(
|
|
43
|
+
({ sql, params }) => this.client.prepare(sql).bind(...params)
|
|
44
|
+
);
|
|
45
|
+
const rows = await this.client.batch(stmts);
|
|
46
|
+
return rows.map((row) => row.results);
|
|
49
47
|
}
|
|
50
48
|
async transaction() {
|
|
51
49
|
throw new Error("Transactions are not supported in D1");
|
package/dist/driver/mysql2.d.ts
CHANGED
|
@@ -19,6 +19,7 @@ export declare class Mysql2Driver implements AsyncDriver {
|
|
|
19
19
|
private client;
|
|
20
20
|
private depth;
|
|
21
21
|
parsesJson: boolean;
|
|
22
|
+
supportsTransactions: boolean;
|
|
22
23
|
constructor(client: Queryable, depth?: number);
|
|
23
24
|
exec(query: string): Promise<void>;
|
|
24
25
|
prepare(sql: string, options?: PrepareOptions): PreparedStatement;
|
package/dist/driver/mysql2.js
CHANGED
package/dist/driver/pg.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export declare class PgDriver implements AsyncDriver {
|
|
|
17
17
|
private client;
|
|
18
18
|
private depth;
|
|
19
19
|
parsesJson: boolean;
|
|
20
|
+
supportsTransactions: boolean;
|
|
20
21
|
constructor(client: Queryable, depth?: number);
|
|
21
22
|
exec(query: string): Promise<void>;
|
|
22
23
|
prepare(sql: string, options?: PrepareOptions): PreparedStatement;
|
package/dist/driver/pg.js
CHANGED
package/dist/driver/pglite.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export declare class PGliteDriver implements AsyncDriver {
|
|
|
16
16
|
private client;
|
|
17
17
|
private depth;
|
|
18
18
|
parsesJson: boolean;
|
|
19
|
+
supportsTransactions: boolean;
|
|
19
20
|
constructor(client: Queryable, depth?: number);
|
|
20
21
|
exec(query: string): Promise<void>;
|
|
21
22
|
close(): Promise<void>;
|
package/dist/driver/pglite.js
CHANGED
package/dist/driver/sql.js.js
CHANGED
package/dist/mysql/dialect.js
CHANGED
package/dist/postgres/dialect.js
CHANGED
package/dist/sqlite/dialect.js
CHANGED
|
@@ -9,8 +9,8 @@ var SINGLE_QUOTE = "'";
|
|
|
9
9
|
var ESCAPE_SINGLE_QUOTE = "''";
|
|
10
10
|
var MATCH_SINGLE_QUOTE = /'/g;
|
|
11
11
|
var sqliteDialect = new Dialect(
|
|
12
|
+
"sqlite",
|
|
12
13
|
class extends Emitter {
|
|
13
|
-
runtime = "sqlite";
|
|
14
14
|
processValue(value) {
|
|
15
15
|
return typeof value === "boolean" ? value ? 1 : 0 : value;
|
|
16
16
|
}
|