prisma-ts-select 0.0.34 → 0.1.3
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/LICENSE +21 -0
- package/README.md +1343 -362
- package/assets/groupBy.gif +0 -0
- package/assets/joinUnsafeIgnoreType.gif +0 -0
- package/assets/joinUnsafeTypeEnforced.gif +0 -0
- package/assets/typesafe-join.gif +0 -0
- package/assets/typesafe-join.png +0 -0
- package/assets/{whereisNull.gif → whereIsNull.gif} +0 -0
- package/assets/whereNotNull.gif +0 -0
- package/dist/bin.cjs +1 -1
- package/dist/bin.js +1 -1
- package/dist/chunk-47KZVQLD.js +283 -0
- package/dist/chunk-54D2J5AR.cjs +291 -0
- package/dist/extend/dialects/index.d.ts +13 -0
- package/dist/extend/dialects/index.js +194 -0
- package/dist/extend/dialects/mysql-v6.d.ts +103 -0
- package/dist/extend/dialects/mysql-v6.js +157 -0
- package/dist/extend/dialects/mysql-v7.d.ts +6 -0
- package/dist/extend/dialects/mysql-v7.js +143 -0
- package/dist/extend/dialects/mysql.d.ts +93 -0
- package/dist/extend/dialects/mysql.js +161 -0
- package/dist/extend/dialects/postgresql-v6.d.ts +101 -0
- package/dist/extend/dialects/postgresql-v6.js +147 -0
- package/dist/extend/dialects/postgresql-v7.d.ts +101 -0
- package/dist/extend/dialects/postgresql-v7.js +147 -0
- package/dist/extend/dialects/postgresql.d.ts +92 -0
- package/dist/extend/dialects/postgresql.js +158 -0
- package/dist/extend/dialects/shared.d.ts +10 -0
- package/dist/extend/dialects/shared.js +14 -0
- package/dist/extend/dialects/sqlite.d.ts +68 -0
- package/dist/extend/dialects/sqlite.js +146 -0
- package/dist/extend/dialects/types.d.ts +13 -0
- package/dist/extend/dialects/types.js +4 -0
- package/dist/extend/extend.d.ts +292 -46
- package/dist/extend/extend.js +769 -162
- package/dist/extend/sql-expr-BaKWzJ-r.d.ts +10 -0
- package/dist/extend/types-B0F8m0ok.d.ts +8 -0
- package/dist/generator.cjs +1 -1
- package/dist/generator.js +1 -1
- package/package.json +44 -42
- package/built/extend.cjs +0 -680
- package/built/extend.d.cts +0 -520
- package/built/extend.d.ts +0 -520
- package/built/extend.js +0 -678
- package/dist/chunk-TBO3MX7Q.cjs +0 -195
- package/dist/chunk-X3N5N5KQ.js +0 -187
- package/dist/extend/extend.cjs +0 -357
- package/dist/extend/extend.d.cts +0 -264
package/README.md
CHANGED
|
@@ -4,10 +4,21 @@
|
|
|
4
4
|

|
|
5
5
|

|
|
6
6
|
|
|
7
|
+
## Test Matrix
|
|
8
|
+
|
|
9
|
+
| | SQLite | MySQL | PostgreSQL |
|
|
10
|
+
|----------------|--------|-------|------------|
|
|
11
|
+
| **Prisma v6** |  |  |  |
|
|
12
|
+
| **Prisma v7** |  |  |  |
|
|
13
|
+
|
|
7
14
|
<!-- toc -->
|
|
8
15
|
|
|
9
16
|
* [Summary](#summary)
|
|
10
17
|
* [Installation](#installation)
|
|
18
|
+
* [Setup](#setup)
|
|
19
|
+
+ [Schema](#schema)
|
|
20
|
+
+ [Client — Prisma v6](#client--prisma-v6)
|
|
21
|
+
+ [Client — Prisma v7](#client--prisma-v7)
|
|
11
22
|
* [Supported DBs](#supported-dbs)
|
|
12
23
|
* [Usage](#usage)
|
|
13
24
|
+ [Generator](#generator)
|
|
@@ -18,6 +29,13 @@
|
|
|
18
29
|
* [SQL](#sql)
|
|
19
30
|
- [Example - Inline Alias Syntax](#example---inline-alias-syntax)
|
|
20
31
|
* [SQL](#sql-1)
|
|
32
|
+
+ [`.$with`](#with)
|
|
33
|
+
- [Example — CTE as joined table](#example--cte-as-joined-table)
|
|
34
|
+
* [SQL](#sql-2)
|
|
35
|
+
- [Example — CTE as base table](#example--cte-as-base-table)
|
|
36
|
+
* [SQL](#sql-3)
|
|
37
|
+
- [Example — Multiple CTEs](#example--multiple-ctes)
|
|
38
|
+
* [SQL](#sql-4)
|
|
21
39
|
+ [Table Aliases](#table-aliases)
|
|
22
40
|
- [Table Alias Syntax Options](#table-alias-syntax-options)
|
|
23
41
|
- [Basic Table Alias](#basic-table-alias)
|
|
@@ -32,6 +50,8 @@
|
|
|
32
50
|
* [SQL](#sql-5)
|
|
33
51
|
* [SQL](#sql-6)
|
|
34
52
|
+ [Joins](#joins)
|
|
53
|
+
- [Dialect Support](#dialect-support)
|
|
54
|
+
- [Nullability Semantics](#nullability-semantics)
|
|
35
55
|
- [`.join`](#join)
|
|
36
56
|
* [Example](#example-1)
|
|
37
57
|
* [SQL](#sql-7)
|
|
@@ -44,6 +64,15 @@
|
|
|
44
64
|
* [Example](#example-3)
|
|
45
65
|
* [SQL](#sql-9)
|
|
46
66
|
* [Parameters](#parameters-2)
|
|
67
|
+
- [`.innerJoin`](#innerjoin)
|
|
68
|
+
- [`.leftJoin`](#leftjoin)
|
|
69
|
+
- [`.crossJoin`](#crossjoin)
|
|
70
|
+
- [`.rightJoin`](#rightjoin) *(MySQL / PostgreSQL)*
|
|
71
|
+
- [`.fullJoin`](#fulljoin) *(PostgreSQL only)*
|
|
72
|
+
- [`.manyToManyJoin`](#manytomanyjoin)
|
|
73
|
+
* [Example](#example-4)
|
|
74
|
+
* [SQL](#sql-10)
|
|
75
|
+
* [Parameters](#parameters-3)
|
|
47
76
|
+ [Where](#where)
|
|
48
77
|
- [`.where`](#where)
|
|
49
78
|
* [TypeSyntax](#typesyntax)
|
|
@@ -75,6 +104,7 @@
|
|
|
75
104
|
* [SQL](#sql-15)
|
|
76
105
|
- [Example - Join table](#example---join-table)
|
|
77
106
|
* [SQL](#sql-16)
|
|
107
|
+
- [`.selectAllOmit`](#selectallomit)
|
|
78
108
|
- [`.select`](#select)
|
|
79
109
|
- [Example - `*`](#example---)
|
|
80
110
|
* [SQL](#sql-17)
|
|
@@ -102,15 +132,14 @@
|
|
|
102
132
|
+ [Offset](#offset)
|
|
103
133
|
- [Example](#example-12)
|
|
104
134
|
* [SQL](#sql-27)
|
|
135
|
+
* [Select Functions](#select-functions)
|
|
136
|
+
+ [Shared (all dialects)](#shared-all-dialects)
|
|
137
|
+
+ [MySQL-specific](#mysql-specific)
|
|
138
|
+
+ [PostgreSQL-specific](#postgresql-specific)
|
|
139
|
+
+ [SQLite-specific](#sqlite-specific)
|
|
105
140
|
* [Future updates](#future-updates)
|
|
106
141
|
* [Changelog / Versioning](#changelog--versioning)
|
|
107
142
|
* [License](#license)
|
|
108
|
-
- [prisma-ts-select](#prisma-ts-select)
|
|
109
|
-
* [Install](#install)
|
|
110
|
-
* [Setup](#setup)
|
|
111
|
-
+ [Extract](#extract)
|
|
112
|
-
* [Usage](#usage-1)
|
|
113
|
-
|
|
114
143
|
<!-- tocstop -->
|
|
115
144
|
|
|
116
145
|
## Summary
|
|
@@ -119,13 +148,9 @@
|
|
|
119
148
|
It simplifies the selection of fields in Prisma queries, ensuring type safety and reducing boilerplate when working with nested fields.
|
|
120
149
|
Ideal for developers seeking an efficient, type-safe way to select data with Prisma in TypeScript.
|
|
121
150
|
|
|
122
|
-
[!NOTE]
|
|
123
|
-
>
|
|
124
|
-
>
|
|
125
|
-
> - HAVING
|
|
126
|
-
> - SQLite
|
|
127
|
-
> - Requires you to have either an aggregate function in the `SELECT` or make use of `GROUP BY`
|
|
128
|
-
> - Can only use columns that are specified in `SELECT` or `GROUP BY`
|
|
151
|
+
> [!NOTE]
|
|
152
|
+
> Fully tested on SQLite, MySQL, and PostgreSQL. Known exceptions:
|
|
153
|
+
> - HAVING on SQLite requires either an aggregate function in `SELECT` or a `GROUP BY` clause, and can only reference columns from `SELECT` or `GROUP BY`.
|
|
129
154
|
|
|
130
155
|
|
|
131
156
|
## Installation
|
|
@@ -137,15 +162,149 @@ npm install prisma-ts-select
|
|
|
137
162
|
pnpm add prisma-ts-select
|
|
138
163
|
```
|
|
139
164
|
|
|
165
|
+
## Setup
|
|
166
|
+
|
|
167
|
+
### Schema
|
|
168
|
+
|
|
169
|
+
Add both generators to `prisma/schema.prisma`. The `output` path is relative to the schema file.
|
|
170
|
+
|
|
171
|
+
```prisma
|
|
172
|
+
generator prisma-ts-select {
|
|
173
|
+
provider = "prisma-ts-select"
|
|
174
|
+
output = "../generated/prisma-ts-select"
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
#### Options
|
|
179
|
+
|
|
180
|
+
| Option | Default | Description |
|
|
181
|
+
|--------|---------|-------------|
|
|
182
|
+
| `output` | *(required)* | Output directory, relative to schema file |
|
|
183
|
+
| `packageName` | `prisma-ts-select-<hash>` | Package name in the generated `package.json`. Defaults to a stable hash of the output path — set this when referencing the generated output as a workspace package. |
|
|
184
|
+
|
|
185
|
+
Example with `packageName`:
|
|
186
|
+
|
|
187
|
+
```prisma
|
|
188
|
+
generator prisma-ts-select {
|
|
189
|
+
provider = "prisma-ts-select"
|
|
190
|
+
output = "../generated/prisma-ts-select"
|
|
191
|
+
packageName = "my-app-prisma-types"
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Then register as a workspace package and import by name:
|
|
196
|
+
|
|
197
|
+
**npm / yarn** — `package.json`:
|
|
198
|
+
```json
|
|
199
|
+
{
|
|
200
|
+
"workspaces": ["generated/prisma-ts-select"]
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
**pnpm** — `pnpm-workspace.yaml`:
|
|
205
|
+
```yaml
|
|
206
|
+
packages:
|
|
207
|
+
- generated/prisma-ts-select
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Then add it as a dependency:
|
|
211
|
+
```shell
|
|
212
|
+
pnpm add my-app-prisma-types
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
import tsSelectExtend from 'my-app-prisma-types/extend-v7.js'
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
The client generator differs between Prisma versions:
|
|
220
|
+
|
|
221
|
+
**Prisma v6**
|
|
222
|
+
```prisma
|
|
223
|
+
generator client {
|
|
224
|
+
provider = "prisma-client-js"
|
|
225
|
+
output = "../generated/prisma"
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
**Prisma v7**
|
|
230
|
+
```prisma
|
|
231
|
+
generator client {
|
|
232
|
+
provider = "prisma-client"
|
|
233
|
+
output = "../generated/prisma"
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Then generate:
|
|
238
|
+
|
|
239
|
+
```shell
|
|
240
|
+
pnpm exec prisma generate
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Client — Prisma v6
|
|
244
|
+
|
|
245
|
+
No driver adapter needed. Import from the generated `extend-v6.js`:
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
import { PrismaClient } from './generated/prisma/index.js'
|
|
249
|
+
import tsSelectExtend from './generated/prisma-ts-select/extend-v6.js'
|
|
250
|
+
|
|
251
|
+
export const prisma = new PrismaClient().$extends(tsSelectExtend)
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Client — Prisma v7
|
|
255
|
+
|
|
256
|
+
Prisma v7 requires a driver adapter. Install the adapter for your database and import from the generated `extend-v7.js`:
|
|
257
|
+
|
|
258
|
+
**SQLite**
|
|
259
|
+
```shell
|
|
260
|
+
pnpm add @prisma/adapter-better-sqlite3
|
|
261
|
+
```
|
|
262
|
+
```typescript
|
|
263
|
+
import { PrismaClient } from './generated/prisma/client.ts'
|
|
264
|
+
import tsSelectExtend from './generated/prisma-ts-select/extend-v7.js'
|
|
265
|
+
import { PrismaBetterSqlite3 } from '@prisma/adapter-better-sqlite3'
|
|
266
|
+
|
|
267
|
+
const adapter = new PrismaBetterSqlite3({ url: process.env.DATABASE_URL })
|
|
268
|
+
export const prisma = new PrismaClient({ adapter }).$extends(tsSelectExtend)
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
**MySQL**
|
|
272
|
+
```shell
|
|
273
|
+
pnpm add @prisma/adapter-mariadb
|
|
274
|
+
```
|
|
275
|
+
```typescript
|
|
276
|
+
import { PrismaClient } from './generated/prisma/client.ts'
|
|
277
|
+
import tsSelectExtend from './generated/prisma-ts-select/extend-v7.js'
|
|
278
|
+
import { PrismaMariaDb } from '@prisma/adapter-mariadb'
|
|
279
|
+
|
|
280
|
+
const url = new URL(process.env.DATABASE_URL!)
|
|
281
|
+
const adapter = new PrismaMariaDb({
|
|
282
|
+
host: url.hostname, port: +url.port,
|
|
283
|
+
user: url.username, password: url.password,
|
|
284
|
+
database: url.pathname.slice(1),
|
|
285
|
+
})
|
|
286
|
+
export const prisma = new PrismaClient({ adapter }).$extends(tsSelectExtend)
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
**PostgreSQL**
|
|
290
|
+
```shell
|
|
291
|
+
pnpm add @prisma/adapter-pg
|
|
292
|
+
```
|
|
293
|
+
```typescript
|
|
294
|
+
import { PrismaClient } from './generated/prisma/client.ts'
|
|
295
|
+
import tsSelectExtend from './generated/prisma-ts-select/extend-v7.js'
|
|
296
|
+
import { PrismaPg } from '@prisma/adapter-pg'
|
|
297
|
+
|
|
298
|
+
const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL })
|
|
299
|
+
export const prisma = new PrismaClient({ adapter }).$extends(tsSelectExtend)
|
|
300
|
+
```
|
|
301
|
+
|
|
140
302
|
## Supported DBs
|
|
141
303
|
|
|
142
|
-
|
|
304
|
+
Fully tested on:
|
|
143
305
|
|
|
144
306
|
- SQLite
|
|
145
307
|
- MySQL
|
|
146
|
-
|
|
147
|
-
Most items should also work for
|
|
148
|
-
|
|
149
308
|
- PostgreSQL
|
|
150
309
|
|
|
151
310
|
Other DBs will be added when I have chance.
|
|
@@ -207,31 +366,101 @@ The way the methods are chained, are heavily inspired by [Dr Milan Milanović](h
|
|
|
207
366
|
This takes the `base` table to work from.
|
|
208
367
|
|
|
209
368
|
#### Example
|
|
210
|
-
```typescript
|
|
211
|
-
prisma.$from("User")
|
|
369
|
+
```typescript file=../usage-sqlite-v7/tests/readme/from-basic.ts region=example-$from
|
|
370
|
+
prisma.$from("User");
|
|
212
371
|
```
|
|
213
372
|
|
|
214
373
|
#### Example - With Table Alias
|
|
215
|
-
```typescript
|
|
216
|
-
prisma.$from("User
|
|
374
|
+
```typescript file=../usage-sqlite-v7/tests/readme/from-inline-alias.ts region=example-$from
|
|
375
|
+
prisma.$from("User u");
|
|
376
|
+
```
|
|
377
|
+
##### SQL
|
|
378
|
+
```sql file=../usage-sqlite-v7/tests/readme/from-inline-alias.ts region=inline-alias-sql
|
|
379
|
+
FROM User AS `u`;
|
|
380
|
+
```
|
|
381
|
+
**Note:** Alias can be inline (space-separated) or as second parameter.
|
|
382
|
+
**Note:** Table aliases are particularly useful for self-joins where you need to join a table to itself with different aliases.
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
### `.$with`
|
|
386
|
+
|
|
387
|
+
Defines one or more Common Table Expressions (CTEs) that can be referenced in `.join()` calls or used directly as the base table via `.from('cteName')`.
|
|
388
|
+
|
|
389
|
+
| Param | Description |
|
|
390
|
+
|-------|-------------|
|
|
391
|
+
| `name` | CTE name — used to reference the CTE in `join()` or `from()` |
|
|
392
|
+
| `query` | Any query built with `.$from()` |
|
|
393
|
+
|
|
394
|
+
Chain `.with(name, query)` before `.from()` to define additional CTEs.
|
|
395
|
+
|
|
396
|
+
#### Example — CTE as joined table
|
|
397
|
+
|
|
398
|
+
```typescript file=../../shared-tests/readme/with-cte.ts region=join
|
|
399
|
+
const posts = prisma.$from("Post").select("id").select("authorId").select("title");
|
|
400
|
+
|
|
401
|
+
prisma.$with("pp", posts)
|
|
402
|
+
.from("User")
|
|
403
|
+
.join("pp", "authorId", "User.id")
|
|
217
404
|
```
|
|
218
405
|
|
|
219
406
|
##### SQL
|
|
220
|
-
|
|
221
|
-
|
|
407
|
+
|
|
408
|
+
```sql file=../../shared-tests/readme/with-cte.ts region=join-sql
|
|
409
|
+
WITH pp AS (
|
|
410
|
+
SELECT id, authorId, title
|
|
411
|
+
FROM Post)
|
|
412
|
+
FROM User
|
|
413
|
+
JOIN pp ON pp.authorId = User.id;
|
|
222
414
|
```
|
|
223
415
|
|
|
224
|
-
|
|
416
|
+
#### Example — CTE as base table
|
|
225
417
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
418
|
+
Use `.from('cteName')` to query a CTE directly, without a real table as the base.
|
|
419
|
+
|
|
420
|
+
```typescript file=../../shared-tests/readme/with-cte.ts region=cte-base
|
|
421
|
+
const posts = prisma.$from("Post").select("id").select("title");
|
|
422
|
+
|
|
423
|
+
prisma.$with("pp", posts)
|
|
424
|
+
.from("pp")
|
|
425
|
+
.select("pp.id")
|
|
426
|
+
.select("pp.title")
|
|
229
427
|
```
|
|
428
|
+
|
|
230
429
|
##### SQL
|
|
231
|
-
|
|
232
|
-
|
|
430
|
+
|
|
431
|
+
```sql file=../../shared-tests/readme/with-cte.ts region=cte-base-sql
|
|
432
|
+
WITH pp AS (
|
|
433
|
+
SELECT id, title
|
|
434
|
+
FROM Post)
|
|
435
|
+
SELECT pp.id AS `pp.id`, pp.title AS `pp.title`
|
|
436
|
+
FROM pp;
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
**Type safety:** only CTEs declared in `.$with()` / `.with()` are accepted by `.from()`. Unknown CTE names are rejected at compile time.
|
|
440
|
+
|
|
441
|
+
#### Example — Multiple CTEs
|
|
442
|
+
|
|
443
|
+
```typescript file=../../shared-tests/readme/with-cte.ts region=multi-cte
|
|
444
|
+
const posts = prisma.$from("Post").select("id").select("authorId").select("title");
|
|
445
|
+
const users = prisma.$from("User").select("id").select("name");
|
|
446
|
+
|
|
447
|
+
prisma.$with("pp", posts)
|
|
448
|
+
.with("uu", users)
|
|
449
|
+
.from("User")
|
|
450
|
+
.join("pp", "authorId", "User.id")
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
##### SQL
|
|
454
|
+
|
|
455
|
+
```sql file=../../shared-tests/readme/with-cte.ts region=multi-cte-sql
|
|
456
|
+
WITH pp AS (
|
|
457
|
+
SELECT id, authorId, title
|
|
458
|
+
FROM Post), uu AS (
|
|
459
|
+
SELECT id, name
|
|
460
|
+
FROM User)
|
|
461
|
+
FROM User
|
|
462
|
+
JOIN pp ON pp.authorId = User.id;
|
|
233
463
|
```
|
|
234
|
-
**Note:** Alias can be inline (space-separated) or as second parameter.
|
|
235
464
|
|
|
236
465
|
### Table Aliases
|
|
237
466
|
|
|
@@ -243,49 +472,33 @@ Table aliases allow you to give tables shorter or more meaningful names in your
|
|
|
243
472
|
#### Table Alias Syntax Options
|
|
244
473
|
|
|
245
474
|
Multiple syntaxes supported:
|
|
246
|
-
- **Inline in .$from()**: `prisma.$from("User u")
|
|
475
|
+
- **Inline in .$from()**: `prisma.$from("User u")` - Note: Second parameter syntax `.$from("User", "u")` is NOT supported
|
|
247
476
|
- **Inline in .join()**: `.join("Post p", "authorId", "User.id")`
|
|
248
477
|
- **Object syntax**: `.join({table: "Post", src: "authorId", on: "User.id", alias: "p"})`
|
|
249
478
|
|
|
250
|
-
#### Basic Table Alias
|
|
251
|
-
|
|
252
|
-
```typescript
|
|
253
|
-
prisma.$from("User", "u")
|
|
254
|
-
.select("u.name")
|
|
255
|
-
.select("u.email")
|
|
256
|
-
.run();
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
##### SQL
|
|
260
|
-
```sql
|
|
261
|
-
SELECT name, email FROM User AS u;
|
|
262
|
-
```
|
|
263
|
-
|
|
264
479
|
#### Table Aliases with Joins
|
|
265
480
|
|
|
266
481
|
##### Inline Alias Syntax
|
|
267
|
-
```typescript
|
|
482
|
+
```typescript file=../usage-sqlite-v7/tests/readme/table-alias.ts region=inline-join
|
|
268
483
|
prisma.$from("User u")
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
.run();
|
|
484
|
+
.join("Post p", "authorId", "u.id")
|
|
485
|
+
.select("u.name")
|
|
486
|
+
.select("p.title");
|
|
273
487
|
```
|
|
274
488
|
|
|
275
489
|
##### Object Syntax
|
|
276
|
-
```typescript
|
|
277
|
-
prisma.$from("User
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
.run();
|
|
490
|
+
```typescript file=../usage-sqlite-v7/tests/readme/table-alias.ts region=object-join-1
|
|
491
|
+
prisma.$from("User u")
|
|
492
|
+
.join({table: "Post", src: "authorId", on: "u.id", alias: "p"})
|
|
493
|
+
.select("u.name")
|
|
494
|
+
.select("p.title");
|
|
282
495
|
```
|
|
283
496
|
|
|
284
497
|
##### SQL
|
|
285
|
-
```sql
|
|
286
|
-
SELECT name, title
|
|
287
|
-
FROM User AS u
|
|
288
|
-
JOIN Post AS p ON authorId = u.id;
|
|
498
|
+
```sql file=../usage-sqlite-v7/tests/readme/table-alias.ts region=inline-join-sql
|
|
499
|
+
SELECT name, title
|
|
500
|
+
FROM User AS `u`
|
|
501
|
+
JOIN Post AS `p` ON p.authorId = u.id;
|
|
289
502
|
```
|
|
290
503
|
|
|
291
504
|
**Note:** The object syntax provides a foundation for future enhancements like multiple join conditions and complex WHERE-style conditions in joins.
|
|
@@ -294,60 +507,77 @@ JOIN Post AS p ON authorId = u.id;
|
|
|
294
507
|
|
|
295
508
|
Self-joins require aliases to distinguish between the different "instances" of the same table:
|
|
296
509
|
|
|
297
|
-
```typescript
|
|
298
|
-
prisma.$from("User
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
.run();
|
|
510
|
+
```typescript file=../usage-sqlite-v7/tests/readme/table-alias.ts region=self-join
|
|
511
|
+
prisma.$from("User u1")
|
|
512
|
+
.joinUnsafeTypeEnforced("User u2", "id", "u1.id")
|
|
513
|
+
.select("u1.name", "user1Name")
|
|
514
|
+
.select("u2.name", "user2Name");
|
|
303
515
|
```
|
|
304
516
|
|
|
305
517
|
##### SQL
|
|
306
|
-
```sql
|
|
307
|
-
SELECT u1.name AS `user1Name`, u2.name AS `user2Name`
|
|
308
|
-
FROM User AS u1
|
|
309
|
-
JOIN User AS u2 ON
|
|
518
|
+
```sql file=../usage-sqlite-v7/tests/readme/table-alias.ts region=self-join-sql
|
|
519
|
+
SELECT u1.name AS `user1Name`, u2.name AS `user2Name`
|
|
520
|
+
FROM User AS `u1`
|
|
521
|
+
JOIN User AS `u2` ON u2.id = u1.id;
|
|
310
522
|
```
|
|
311
523
|
|
|
312
524
|
#### Table.* with Aliases
|
|
313
525
|
|
|
314
526
|
You can use the `alias.*` syntax to select all columns from an aliased table:
|
|
315
527
|
|
|
316
|
-
```typescript
|
|
317
|
-
prisma.$from("User
|
|
318
|
-
|
|
319
|
-
.run();
|
|
528
|
+
```typescript file=../usage-sqlite-v7/tests/readme/table-alias.ts region=star-single
|
|
529
|
+
prisma.$from("User u")
|
|
530
|
+
.select("u.*");
|
|
320
531
|
```
|
|
321
532
|
|
|
322
533
|
##### SQL
|
|
323
|
-
```sql
|
|
324
|
-
SELECT
|
|
534
|
+
```sql file=../usage-sqlite-v7/tests/readme/table-alias.ts region=star-single-sql
|
|
535
|
+
SELECT id, email, name, age
|
|
536
|
+
FROM User AS `u`;
|
|
325
537
|
```
|
|
326
538
|
|
|
327
539
|
With joins:
|
|
328
|
-
```typescript
|
|
329
|
-
prisma.$from("User
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
.run();
|
|
540
|
+
```typescript file=../usage-sqlite-v7/tests/readme/table-alias.ts region=star-join
|
|
541
|
+
prisma.$from("User u")
|
|
542
|
+
.join("Post p", "authorId", "u.id")
|
|
543
|
+
.select("u.*")
|
|
544
|
+
.select("p.*");
|
|
334
545
|
```
|
|
335
546
|
|
|
336
547
|
##### SQL
|
|
337
|
-
```sql
|
|
338
|
-
SELECT u.id AS `u.id`, u.email AS `u.email`, u.name AS `u.name`,
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
JOIN Post AS p ON authorId = u.id;
|
|
548
|
+
```sql file=../usage-sqlite-v7/tests/readme/table-alias.ts region=star-join-sql
|
|
549
|
+
SELECT u.id AS `u.id`, u.email AS `u.email`, u.name AS `u.name`, u.age AS `u.age`, p.id AS `p.id`, p.title AS `p.title`, p.content AS `p.content`, p.published AS `p.published`, p.createdAt AS `p.createdAt`, p.authorId AS `p.authorId`, p.lastModifiedById AS `p.lastModifiedById`, p.metadata AS `p.metadata`
|
|
550
|
+
FROM User AS `u`
|
|
551
|
+
JOIN Post AS `p` ON p.authorId = u.id;
|
|
342
552
|
```
|
|
343
553
|
|
|
344
554
|
### Joins
|
|
555
|
+
|
|
556
|
+
#### Dialect Support
|
|
557
|
+
|
|
558
|
+
| Method | SQLite | MySQL | PostgreSQL |
|
|
559
|
+
|--------|--------|-------|------------|
|
|
560
|
+
| `join` / `innerJoin` / `crossJoin` / `leftJoin` | ✓ | ✓ | ✓ |
|
|
561
|
+
| `rightJoin` | ✗ | ✓ | ✓ |
|
|
562
|
+
| `fullJoin` | ✗ | ✗ | ✓ |
|
|
563
|
+
|
|
564
|
+
Each method has `*UnsafeTypeEnforced` and `*UnsafeIgnoreType` variants with the same dialect restrictions.
|
|
565
|
+
|
|
566
|
+
#### Nullability Semantics
|
|
567
|
+
|
|
568
|
+
| Join type | Effect on result type |
|
|
569
|
+
|-----------|----------------------|
|
|
570
|
+
| `join` / `innerJoin` / `crossJoin` | No nullable change — both sides guaranteed to match |
|
|
571
|
+
| `leftJoin` | Joined table fields become `T \| null` |
|
|
572
|
+
| `rightJoin` | Base table fields become `T \| null` |
|
|
573
|
+
| `fullJoin` | Both sides become `T \| null` |
|
|
574
|
+
|
|
345
575
|
#### `.join`
|
|
346
576
|
|
|
347
577
|
Using the defined links (foreign keys) defined in the schema, provides a type-safe way of joining on tables.
|
|
348
578
|
|
|
349
579
|
##### Example
|
|
350
|
-
```typescript
|
|
580
|
+
```typescript file=../usage-sqlite-v7/tests/readme/join-basic.ts region=example
|
|
351
581
|
prisma.$from("User")
|
|
352
582
|
.join("Post", "authorId", "User.id");
|
|
353
583
|
```
|
|
@@ -358,9 +588,9 @@ prisma.$from("User")
|
|
|
358
588
|
|
|
359
589
|
The resulting SQL will look like:
|
|
360
590
|
|
|
361
|
-
```sql
|
|
362
|
-
FROM User
|
|
363
|
-
JOIN Post ON authorId = User.id;
|
|
591
|
+
```sql file=../usage-sqlite-v7/tests/readme/join-basic.ts region=join-basic-sql
|
|
592
|
+
FROM User
|
|
593
|
+
JOIN Post ON Post.authorId = User.id;
|
|
364
594
|
```
|
|
365
595
|
|
|
366
596
|
##### Parameters
|
|
@@ -369,27 +599,107 @@ JOIN Post ON authorId = User.id;
|
|
|
369
599
|
| `table` | The table to join on (supports inline alias: `"Post p"` or `"Post", "p"`). <br/>TS autocomplete will show tables that can join with previously defined tables on. |
|
|
370
600
|
| `field` | Column on table. <br/>TS autocomplete will show known columns that this table, can join with previously defined tables on. |
|
|
371
601
|
| `reference` | `Table.Column` to a previously defined table (either the base, or another join), with a FK that is defined in the schema definition. |
|
|
602
|
+
| `where` | *(optional)* Criteria added to the `ON` clause (`ON a = b AND ...`). Same syntax as `.where()`. Keys scoped to the joined table only. |
|
|
603
|
+
| `joinType` | *(optional)* Join variant. One of `"INNER"`, `"LEFT"`, `"LEFT OUTER"`, `"RIGHT"`, `"RIGHT OUTER"`, `"FULL"`, `"FULL OUTER"`, `"CROSS"`. Default: plain `JOIN`. |
|
|
372
604
|
|
|
373
605
|
**Alternative Syntaxes:**
|
|
374
606
|
```typescript
|
|
375
607
|
// Inline alias
|
|
376
608
|
.join("Post p", "authorId", "User.id")
|
|
377
|
-
|
|
609
|
+
|
|
378
610
|
// Object syntax
|
|
379
611
|
.join({
|
|
380
612
|
table: "Post",
|
|
381
613
|
src: "authorId",
|
|
382
614
|
on: "User.id",
|
|
383
|
-
alias: "p"
|
|
615
|
+
alias: "p", // optional
|
|
616
|
+
joinType: "LEFT", // optional
|
|
617
|
+
where: { "Post.published": true } // optional
|
|
384
618
|
})
|
|
385
|
-
```
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
##### Join Type
|
|
622
|
+
|
|
623
|
+
Control the SQL join variant via the `joinType` option:
|
|
624
|
+
|
|
625
|
+
```typescript file=../usage-sqlite-v7/tests/readme/join-type.ts region=join-type-left
|
|
626
|
+
prisma.$from("User")
|
|
627
|
+
.join("Post", "authorId", "User.id", { joinType: "LEFT" })
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
```sql file=../usage-sqlite-v7/tests/readme/join-type.ts region=join-type-left-sql
|
|
631
|
+
FROM User
|
|
632
|
+
LEFT JOIN Post ON Post.authorId = User.id;
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
`CROSS JOIN` has no `ON` clause — it is suppressed automatically:
|
|
636
|
+
|
|
637
|
+
```typescript file=../usage-sqlite-v7/tests/readme/join-type.ts region=join-type-cross
|
|
638
|
+
prisma.$from("User")
|
|
639
|
+
.joinUnsafeIgnoreType("Post", "id", "User.id", { joinType: "CROSS" })
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
```sql file=../usage-sqlite-v7/tests/readme/join-type.ts region=join-type-cross-sql
|
|
643
|
+
FROM User
|
|
644
|
+
CROSS JOIN Post;
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
`joinType` and `where` can be combined — `where` is ignored for `CROSS`:
|
|
648
|
+
|
|
649
|
+
```typescript file=../usage-sqlite-v7/tests/readme/join-type.ts region=join-type-with-where
|
|
650
|
+
prisma.$from("User")
|
|
651
|
+
.join("Post", "authorId", "User.id", {
|
|
652
|
+
joinType: "LEFT",
|
|
653
|
+
where: { "Post.published": true }
|
|
654
|
+
})
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
```sql file=../usage-sqlite-v7/tests/readme/join-type.ts region=join-type-with-where-sql
|
|
658
|
+
FROM User
|
|
659
|
+
LEFT JOIN Post ON Post.authorId = User.id AND Post.published = true;
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
##### Join-level WHERE
|
|
663
|
+
|
|
664
|
+
Conditions placed on the `ON` clause instead of the top-level `WHERE`:
|
|
665
|
+
|
|
666
|
+
```typescript file=../usage-sqlite-v7/tests/readme/join-where.ts region=join-where-example
|
|
667
|
+
prisma.$from("User")
|
|
668
|
+
.join("Post", "authorId", "User.id", { where: { "Post.published": true } })
|
|
669
|
+
```
|
|
670
|
+
|
|
671
|
+
```sql file=../usage-sqlite-v7/tests/readme/join-where.ts region=join-where-sql
|
|
672
|
+
FROM User
|
|
673
|
+
JOIN Post ON Post.authorId = User.id AND Post.published = true;
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
Supports the same MongoDB-inspired operators as `.where()` — `$AND`, `$OR`, `$NOT`, `$NOR`:
|
|
677
|
+
|
|
678
|
+
```typescript file=../usage-sqlite-v7/tests/readme/join-where.ts region=join-where-ops-example
|
|
679
|
+
prisma.$from("User")
|
|
680
|
+
.join("Post", "authorId", "User.id", {
|
|
681
|
+
where: {
|
|
682
|
+
$AND: [
|
|
683
|
+
{ "Post.published": true },
|
|
684
|
+
{ "Post.id": { op: ">", value: 0 } }
|
|
685
|
+
]
|
|
686
|
+
}
|
|
687
|
+
})
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
```sql file=../usage-sqlite-v7/tests/readme/join-where.ts region=join-where-ops-sql
|
|
691
|
+
FROM User
|
|
692
|
+
JOIN Post ON Post.authorId = User.id AND (Post.published = true AND Post.id > 0);
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
> **Type safety**: only `"JoinedTable.field"` keys are accepted — other tables' fields are rejected at compile time.
|
|
386
696
|
|
|
387
697
|
#### `.joinUnsafeTypeEnforced`
|
|
388
698
|
|
|
389
699
|
Unlike the `.join` command, this will allow you to join on columns that are not explicitly linked by a FK, but have the same type.
|
|
390
700
|
|
|
391
701
|
##### Example
|
|
392
|
-
```typescript
|
|
702
|
+
```typescript file=../usage-sqlite-v7/tests/readme/join-unsafe.ts region=type-enforced
|
|
393
703
|
prisma.$from("User")
|
|
394
704
|
.joinUnsafeTypeEnforced("Post", "title", "User.name");
|
|
395
705
|
```
|
|
@@ -398,7 +708,7 @@ prisma.$from("User")
|
|
|
398
708
|
##### SQL
|
|
399
709
|
The resulting SQL will look like:
|
|
400
710
|
|
|
401
|
-
```sql
|
|
711
|
+
```sql file=../usage-sqlite-v7/tests/readme/join-unsafe.ts region=type-enforced-sql
|
|
402
712
|
FROM User
|
|
403
713
|
JOIN Post ON Post.title = User.name;
|
|
404
714
|
```
|
|
@@ -409,13 +719,15 @@ JOIN Post ON Post.title = User.name;
|
|
|
409
719
|
| `table` | The table to join on (supports inline alias: `"Post p"` or `"Post", "p"`). <br/>TS autocomplete will show tables that can join with previously defined tables on. |
|
|
410
720
|
| `field` | Column on table. <br/>TS autocomplete will show known columns that this table, can join with previously defined tables on. |
|
|
411
721
|
| `reference` | `Table.Column` to a previously defined table (either the base, or another join), with a column that is of the same type. |
|
|
722
|
+
| `where` | *(optional)* Criteria added to the `ON` clause. See [Join-level WHERE](#join-level-where). |
|
|
723
|
+
| `joinType` | *(optional)* Join variant. See [Join Type](#join-type). |
|
|
412
724
|
|
|
413
725
|
#### `.joinUnsafeIgnoreType`
|
|
414
726
|
|
|
415
727
|
Unlike the `.joinUnsafeIgnoreType` command, this will allow you to join on columns that are not explicitly linked by a FK, and do not have the same type.
|
|
416
728
|
|
|
417
729
|
##### Example
|
|
418
|
-
```typescript
|
|
730
|
+
```typescript file=../usage-sqlite-v7/tests/readme/join-unsafe.ts region=ignore-type
|
|
419
731
|
prisma.$from("User")
|
|
420
732
|
.joinUnsafeIgnoreType("Post", "id", "User.name");
|
|
421
733
|
```
|
|
@@ -424,9 +736,9 @@ prisma.$from("User")
|
|
|
424
736
|
##### SQL
|
|
425
737
|
The resulting SQL will look like:
|
|
426
738
|
|
|
427
|
-
```sql
|
|
739
|
+
```sql file=../usage-sqlite-v7/tests/readme/join-unsafe.ts region=ignore-type-sql
|
|
428
740
|
FROM User
|
|
429
|
-
JOIN Post ON Post.id = User.name
|
|
741
|
+
JOIN Post ON Post.id = User.name;
|
|
430
742
|
```
|
|
431
743
|
|
|
432
744
|
##### Parameters
|
|
@@ -435,198 +747,497 @@ JOIN Post ON Post.id = User.name
|
|
|
435
747
|
| `table` | The table to join on (supports inline alias: `"Post p"` or `"Post", "p"`). <br/>TS autocomplete will show tables that can join with previously defined tables on. |
|
|
436
748
|
| `field` | Column on table. <br/>TS autocomplete will show known columns that this table, can join with previously defined tables on. |
|
|
437
749
|
| `reference` | `Table.Column` to a previously defined table (either the base, or another join). Referencing any column, of any type. |
|
|
750
|
+
| `where` | *(optional)* Criteria added to the `ON` clause. See [Join-level WHERE](#join-level-where). |
|
|
751
|
+
| `joinType` | *(optional)* Join variant. See [Join Type](#join-type). |
|
|
438
752
|
|
|
439
|
-
|
|
753
|
+
#### `.manyToManyJoin`
|
|
440
754
|
|
|
441
|
-
|
|
755
|
+
Joins through Prisma's implicit or explicit many-to-many junction tables. Automatically detects the junction table and join columns from the generated schema.
|
|
442
756
|
|
|
443
|
-
|
|
757
|
+
##### Example
|
|
758
|
+
```typescript file=../usage-sqlite-v7/tests/readme/join-many-to-many.ts region=m2m-basic
|
|
759
|
+
prisma.$from("M2M_Post")
|
|
760
|
+
.manyToManyJoin("M2M_Category");
|
|
761
|
+
```
|
|
444
762
|
|
|
445
|
-
#####
|
|
446
|
-
```
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
"$OR": [WhereClause, ...Array<WhereClause>],
|
|
451
|
-
"$NOT": [WhereClause, ...Array<WhereClause>],
|
|
452
|
-
"$NOR": [WhereClause, ...Array<WhereClause>]
|
|
453
|
-
}
|
|
763
|
+
##### SQL
|
|
764
|
+
```sql file=../usage-sqlite-v7/tests/readme/join-many-to-many.ts region=m2m-basic-sql
|
|
765
|
+
FROM M2M_Post
|
|
766
|
+
JOIN _M2M_CategoryToM2M_Post ON _M2M_CategoryToM2M_Post.B = M2M_Post.id
|
|
767
|
+
JOIN M2M_Category ON M2M_Category.id = _M2M_CategoryToM2M_Post.A;
|
|
454
768
|
```
|
|
455
769
|
|
|
456
|
-
#####
|
|
457
|
-
|
|
|
458
|
-
|
|
459
|
-
|
|
|
460
|
-
|
|
|
461
|
-
|
|
|
462
|
-
| LIKE | | String |
|
|
463
|
-
| NOT LIKE | | String |
|
|
464
|
-
| IS NULL | | * |
|
|
465
|
-
| IS NOT NULL | | * |
|
|
466
|
-
| \> | | Numbers, Date |
|
|
467
|
-
| \>= | | Numbers, Date |
|
|
468
|
-
| < | | Numbers, Date |
|
|
469
|
-
| <= | | Numbers, Date |
|
|
470
|
-
| != | | Numbers, String, Date |
|
|
770
|
+
##### Parameters
|
|
771
|
+
| Param | Type | Description |
|
|
772
|
+
|-------|------|-------------|
|
|
773
|
+
| `targetTable` | `string` | Target table, optionally with alias: `"M2M_Category"` or `"M2M_Category mc"` |
|
|
774
|
+
| `options.refName` | `string?` | Junction ref name — required when multiple M2M relations point to the same target |
|
|
775
|
+
| `options.source` | `string?` | Explicit source as `"alias.column"` — useful when the source table is aliased |
|
|
471
776
|
|
|
777
|
+
##### With Alias
|
|
778
|
+
```typescript file=../usage-sqlite-v7/tests/readme/join-many-to-many.ts region=m2m-alias
|
|
779
|
+
prisma.$from("M2M_Post")
|
|
780
|
+
.manyToManyJoin("M2M_Category mc");
|
|
781
|
+
```
|
|
472
782
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
| $OR | Will join all items with a `OR` | <pre>.where({ <br /> $OR:[<br /> {"User.name": {op: "LIKE", value:"a%"}},<br /> {"User.name": {op: "LIKE", value:"d%"}},<br />]})</pre> | `(User.name LIKE "a%" OR User.name LIKE "d%")` |
|
|
479
|
-
| $NOT | Will wrap statement in a `NOT (/*...*/)` and join any items with a `AND` | <pre>.where({ <br /> $NOT:[<br /> {"User.age": 20 },<br /> {<br /> "User.age": {op: "=", value:60},<br /> "User.name": "Bob",<br /> },<br />]})</pre> | `(NOT (User.age = 20 AND (User.age = 60 AND User.name = "Bob")))` |
|
|
480
|
-
| $NOR | Will wrap statement in a `NOT (/*...*/)` and join any items with a `OR` | <pre>.where({ <br /> $NOR:[<br /> {"User.age": 20 },<br /> {<br /> "User.age": {op: "!=", value:60},<br /> "User.name": "Bob",<br /> },<br />]})</pre> | `(NOT (User.age = 20 OR (User.age != 60 AND User.name = "Bob")))` |
|
|
783
|
+
```sql file=../usage-sqlite-v7/tests/readme/join-many-to-many.ts region=m2m-alias-sql
|
|
784
|
+
FROM M2M_Post
|
|
785
|
+
JOIN _M2M_CategoryToM2M_Post ON _M2M_CategoryToM2M_Post.B = M2M_Post.id
|
|
786
|
+
JOIN M2M_Category AS `mc` ON mc.id = _M2M_CategoryToM2M_Post.A;
|
|
787
|
+
```
|
|
481
788
|
|
|
789
|
+
##### Named Junction (`refName`)
|
|
482
790
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
"User.age": 20,
|
|
489
|
-
"User.name": {op: "LIKE", value: "Stuart%"},
|
|
490
|
-
});
|
|
791
|
+
Use `refName` when a model has multiple M2M relations to the same target:
|
|
792
|
+
|
|
793
|
+
```typescript file=../usage-sqlite-v7/tests/readme/join-many-to-many.ts region=m2m-refname
|
|
794
|
+
prisma.$from("MMM_Post")
|
|
795
|
+
.manyToManyJoin("MMM_Category", { refName: "M2M_NC_M1" });
|
|
491
796
|
```
|
|
492
797
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
.where({
|
|
498
|
-
$AND: [
|
|
499
|
-
{"User.age": {op: ">", value: 20}},
|
|
500
|
-
{"User.age": {op: "<", value: 60}},
|
|
501
|
-
]
|
|
502
|
-
});
|
|
798
|
+
```sql file=../usage-sqlite-v7/tests/readme/join-many-to-many.ts region=m2m-refname-sql
|
|
799
|
+
FROM MMM_Post
|
|
800
|
+
JOIN _M2M_NC_M1 ON _M2M_NC_M1.B = MMM_Post.id
|
|
801
|
+
JOIN MMM_Category ON MMM_Category.id = _M2M_NC_M1.A;
|
|
503
802
|
```
|
|
504
803
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
{"User.name": {op: "LIKE", value: "d%"}},
|
|
513
|
-
]
|
|
514
|
-
});
|
|
804
|
+
##### Explicit Source (`source`)
|
|
805
|
+
|
|
806
|
+
Use `source` to pin the source alias and column when the source table is aliased:
|
|
807
|
+
|
|
808
|
+
```typescript file=../usage-sqlite-v7/tests/readme/join-many-to-many.ts region=m2m-source
|
|
809
|
+
prisma.$from("M2M_Post mp")
|
|
810
|
+
.manyToManyJoin("M2M_Category mc", { source: "mp.id" });
|
|
515
811
|
```
|
|
516
812
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
.where({
|
|
522
|
-
$NOT: [
|
|
523
|
-
{"User.age": 20},
|
|
524
|
-
{
|
|
525
|
-
"User.age": {op: "=", value: 60},
|
|
526
|
-
"User.name": "Bob",
|
|
527
|
-
},
|
|
528
|
-
]
|
|
529
|
-
});
|
|
813
|
+
```sql file=../usage-sqlite-v7/tests/readme/join-many-to-many.ts region=m2m-source-sql
|
|
814
|
+
FROM M2M_Post AS `mp`
|
|
815
|
+
JOIN _M2M_CategoryToM2M_Post ON _M2M_CategoryToM2M_Post.B = mp.id
|
|
816
|
+
JOIN M2M_Category AS `mc` ON mc.id = _M2M_CategoryToM2M_Post.A;
|
|
530
817
|
```
|
|
531
818
|
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
"User.age": {op: "!=", value: 60},
|
|
541
|
-
"User.name": "Bob",
|
|
542
|
-
},
|
|
543
|
-
]
|
|
544
|
-
});
|
|
819
|
+
#### `.innerJoin`
|
|
820
|
+
|
|
821
|
+
Alias for `.join` — explicitly emits `INNER JOIN`. Same type-safe FK constraints.
|
|
822
|
+
|
|
823
|
+
##### Example
|
|
824
|
+
```typescript file=../../shared-tests/readme/join-inner.ts region=example
|
|
825
|
+
prisma.$from("User")
|
|
826
|
+
.innerJoin("Post", "authorId", "User.id")
|
|
545
827
|
```
|
|
546
828
|
|
|
547
|
-
|
|
829
|
+
##### SQL
|
|
830
|
+
```sql file=../../shared-tests/readme/join-inner.ts region=sql
|
|
831
|
+
FROM User
|
|
832
|
+
INNER JOIN Post ON Post.authorId = User.id;
|
|
833
|
+
```
|
|
834
|
+
|
|
835
|
+
##### `.innerJoinUnsafeTypeEnforced`
|
|
836
|
+
|
|
837
|
+
Same-type column join, INNER semantics.
|
|
838
|
+
|
|
839
|
+
```typescript file=../../shared-tests/readme/join-inner.ts region=type-enforced
|
|
840
|
+
prisma.$from("User")
|
|
841
|
+
.innerJoinUnsafeTypeEnforced("Post", "title", "User.name")
|
|
842
|
+
```
|
|
843
|
+
|
|
844
|
+
```sql file=../../shared-tests/readme/join-inner.ts region=type-enforced-sql
|
|
845
|
+
FROM User
|
|
846
|
+
INNER JOIN Post ON Post.title = User.name;
|
|
847
|
+
```
|
|
848
|
+
|
|
849
|
+
##### `.innerJoinUnsafeIgnoreType`
|
|
850
|
+
|
|
851
|
+
Any-column join, INNER semantics.
|
|
852
|
+
|
|
853
|
+
```typescript file=../../shared-tests/readme/join-inner.ts region=ignore-type
|
|
854
|
+
prisma.$from("User")
|
|
855
|
+
.innerJoinUnsafeIgnoreType("Post", "id", "User.name")
|
|
856
|
+
```
|
|
857
|
+
|
|
858
|
+
```sql file=../../shared-tests/readme/join-inner.ts region=ignore-type-sql
|
|
859
|
+
FROM User
|
|
860
|
+
INNER JOIN Post ON Post.id = User.name;
|
|
861
|
+
```
|
|
548
862
|
|
|
549
|
-
|
|
550
|
-
|
|
863
|
+
---
|
|
864
|
+
|
|
865
|
+
#### `.leftJoin`
|
|
866
|
+
|
|
867
|
+
FK-safe LEFT JOIN. Joined table fields become `T | null` in the result type.
|
|
551
868
|
|
|
552
869
|
##### Example
|
|
870
|
+
```typescript file=../../shared-tests/readme/join-left.ts region=example
|
|
871
|
+
prisma.$from("User")
|
|
872
|
+
.leftJoin("Post", "authorId", "User.id")
|
|
873
|
+
```
|
|
874
|
+
|
|
875
|
+
##### SQL
|
|
876
|
+
```sql file=../../shared-tests/readme/join-left.ts region=sql
|
|
877
|
+
FROM User
|
|
878
|
+
LEFT JOIN Post ON Post.authorId = User.id;
|
|
879
|
+
```
|
|
880
|
+
|
|
881
|
+
##### `.leftJoinUnsafeTypeEnforced`
|
|
882
|
+
|
|
883
|
+
Same-type column join, LEFT semantics.
|
|
884
|
+
|
|
885
|
+
```typescript file=../../shared-tests/readme/join-left.ts region=type-enforced
|
|
886
|
+
prisma.$from("User")
|
|
887
|
+
.leftJoinUnsafeTypeEnforced("Post", "title", "User.name")
|
|
888
|
+
```
|
|
889
|
+
|
|
890
|
+
```sql file=../../shared-tests/readme/join-left.ts region=type-enforced-sql
|
|
891
|
+
FROM User
|
|
892
|
+
LEFT JOIN Post ON Post.title = User.name;
|
|
893
|
+
```
|
|
894
|
+
|
|
895
|
+
##### `.leftJoinUnsafeIgnoreType`
|
|
896
|
+
|
|
897
|
+
Any-column join, LEFT semantics.
|
|
898
|
+
|
|
899
|
+
```typescript file=../../shared-tests/readme/join-left.ts region=ignore-type
|
|
900
|
+
prisma.$from("User")
|
|
901
|
+
.leftJoinUnsafeIgnoreType("Post", "id", "User.name")
|
|
902
|
+
```
|
|
903
|
+
|
|
904
|
+
```sql file=../../shared-tests/readme/join-left.ts region=ignore-type-sql
|
|
905
|
+
FROM User
|
|
906
|
+
LEFT JOIN Post ON Post.id = User.name;
|
|
907
|
+
```
|
|
908
|
+
|
|
909
|
+
---
|
|
910
|
+
|
|
911
|
+
#### `.crossJoin`
|
|
912
|
+
|
|
913
|
+
Produces a cartesian product — no `ON` clause. All dialects supported.
|
|
914
|
+
|
|
915
|
+
##### Example
|
|
916
|
+
```typescript file=../../shared-tests/readme/join-cross.ts region=example
|
|
917
|
+
prisma.$from("User")
|
|
918
|
+
.crossJoin("Post")
|
|
919
|
+
```
|
|
920
|
+
|
|
921
|
+
##### SQL
|
|
922
|
+
```sql file=../../shared-tests/readme/join-cross.ts region=sql
|
|
923
|
+
FROM User
|
|
924
|
+
CROSS JOIN Post;
|
|
925
|
+
```
|
|
926
|
+
|
|
927
|
+
##### `.crossJoinUnsafeTypeEnforced` / `.crossJoinUnsafeIgnoreType`
|
|
928
|
+
|
|
929
|
+
Type-permission variants — still emit `CROSS JOIN` with no `ON` clause (takes only a table argument).
|
|
930
|
+
|
|
931
|
+
```typescript file=../../shared-tests/readme/join-cross.ts region=type-enforced
|
|
932
|
+
prisma.$from("User")
|
|
933
|
+
.crossJoinUnsafeTypeEnforced("Post")
|
|
934
|
+
```
|
|
935
|
+
|
|
936
|
+
```sql file=../../shared-tests/readme/join-cross.ts region=type-enforced-sql
|
|
937
|
+
FROM User
|
|
938
|
+
CROSS JOIN Post;
|
|
939
|
+
```
|
|
940
|
+
|
|
941
|
+
---
|
|
942
|
+
|
|
943
|
+
#### `.rightJoin`
|
|
944
|
+
|
|
945
|
+
> **MySQL / PostgreSQL only** — not supported by SQLite.
|
|
946
|
+
|
|
947
|
+
Base table fields become `T | null`. Use when the joined table drives the result set.
|
|
948
|
+
|
|
949
|
+
##### Example
|
|
950
|
+
```typescript
|
|
951
|
+
prisma.$from("Post")
|
|
952
|
+
.rightJoin("User", "id", "Post.authorId")
|
|
953
|
+
```
|
|
954
|
+
|
|
955
|
+
##### SQL
|
|
956
|
+
```sql
|
|
957
|
+
FROM Post RIGHT JOIN User ON User.id = Post.authorId;
|
|
958
|
+
```
|
|
959
|
+
|
|
960
|
+
##### `.rightJoinUnsafeTypeEnforced`
|
|
961
|
+
```typescript
|
|
962
|
+
prisma.$from("Post")
|
|
963
|
+
.rightJoinUnsafeTypeEnforced("User", "name", "Post.title")
|
|
964
|
+
```
|
|
965
|
+
|
|
966
|
+
##### `.rightJoinUnsafeIgnoreType`
|
|
967
|
+
```typescript
|
|
968
|
+
prisma.$from("Post")
|
|
969
|
+
.rightJoinUnsafeIgnoreType("User", "id", "Post.title")
|
|
970
|
+
```
|
|
971
|
+
|
|
972
|
+
---
|
|
973
|
+
|
|
974
|
+
#### `.fullJoin`
|
|
975
|
+
|
|
976
|
+
> **PostgreSQL only** — not supported by SQLite or MySQL.
|
|
977
|
+
|
|
978
|
+
Both sides become `T | null`. Use for outer joins where either side may have no match.
|
|
979
|
+
|
|
980
|
+
##### Example
|
|
981
|
+
```typescript
|
|
982
|
+
prisma.$from("User")
|
|
983
|
+
.fullJoin("Post", "authorId", "User.id")
|
|
984
|
+
```
|
|
985
|
+
|
|
986
|
+
##### SQL
|
|
987
|
+
```sql
|
|
988
|
+
FROM User FULL JOIN Post ON Post.authorId = User.id;
|
|
989
|
+
```
|
|
990
|
+
|
|
991
|
+
##### `.fullJoinUnsafeTypeEnforced`
|
|
992
|
+
```typescript
|
|
993
|
+
prisma.$from("User")
|
|
994
|
+
.fullJoinUnsafeTypeEnforced("Post", "title", "User.name")
|
|
995
|
+
```
|
|
996
|
+
|
|
997
|
+
##### `.fullJoinUnsafeIgnoreType`
|
|
553
998
|
```typescript
|
|
554
999
|
prisma.$from("User")
|
|
555
|
-
|
|
556
|
-
|
|
1000
|
+
.fullJoinUnsafeIgnoreType("Post", "id", "User.name")
|
|
1001
|
+
```
|
|
1002
|
+
|
|
1003
|
+
---
|
|
1004
|
+
|
|
1005
|
+
### Where
|
|
1006
|
+
|
|
1007
|
+
#### `.where`
|
|
1008
|
+
|
|
1009
|
+
The `where` syntax takes inspiration from how mongoDB does queries.
|
|
1010
|
+
|
|
1011
|
+
##### TypeSyntax
|
|
1012
|
+
```TypeScript
|
|
1013
|
+
type WhereClause = {
|
|
1014
|
+
"Table.Column": <value>
|
|
1015
|
+
| [<value>, ...<value>[]] // scalar array → IN
|
|
1016
|
+
| { "op": "<condition>", "value": <value> }
|
|
1017
|
+
| [{ "op": "<condition>", "value": <value> }, ...] // op-array → OR
|
|
1018
|
+
"$AND": [WhereClause, ...Array<WhereClause>],
|
|
1019
|
+
"$OR": [WhereClause, ...Array<WhereClause>],
|
|
1020
|
+
"$NOT": [WhereClause, ...Array<WhereClause>],
|
|
1021
|
+
"$NOR": [WhereClause, ...Array<WhereClause>]
|
|
1022
|
+
}
|
|
1023
|
+
```
|
|
1024
|
+
|
|
1025
|
+
##### Operation types
|
|
1026
|
+
| Op | Description | Supported Types |
|
|
1027
|
+
|-------------|-------------|-----------------------|
|
|
1028
|
+
| IN | | Numbers, String, Date |
|
|
1029
|
+
| NOT IN | | Numbers, String, Date |
|
|
1030
|
+
| BETWEEN | | Numbers, Date |
|
|
1031
|
+
| LIKE | | String |
|
|
1032
|
+
| NOT LIKE | | String |
|
|
1033
|
+
| IS NULL | | * |
|
|
1034
|
+
| IS NOT NULL | | * |
|
|
1035
|
+
| \> | | Numbers, Date |
|
|
1036
|
+
| \>= | | Numbers, Date |
|
|
1037
|
+
| < | | Numbers, Date |
|
|
1038
|
+
| <= | | Numbers, Date |
|
|
1039
|
+
| != | | Numbers, String, Date |
|
|
1040
|
+
| = | | Numbers, String, Date |
|
|
1041
|
+
|
|
1042
|
+
|
|
1043
|
+
##### Examples
|
|
1044
|
+
| Type | Description | Example | SQL |
|
|
1045
|
+
|--------------|--------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------|
|
|
1046
|
+
| Table.Column | A particular Table.Column name | <pre>.where({ <br /> "User.age": 20,<br /> "User.name": {op: "LIKE", value:"Stuart%"},<br />}) | `(User.age = 20 AND User.name LIKE "Stuart%")` |
|
|
1047
|
+
| $AND | Will join all items with a `AND` | <pre>.where({ <br /> $AND:[<br /> {"User.age": {op: ">", value:20}},<br /> {"User.age": {op: "<", value:60}},<br />]})</pre> | `(User.age > 20 AND User.age < 60)` |
|
|
1048
|
+
| $OR | Will join all items with a `OR` | <pre>.where({ <br /> $OR:[<br /> {"User.name": {op: "LIKE", value:"a%"}},<br /> {"User.name": {op: "LIKE", value:"d%"}},<br />]})</pre> | `(User.name LIKE "a%" OR User.name LIKE "d%")` |
|
|
1049
|
+
| $NOT | Will wrap statement in a `NOT (/*...*/)` and join any items with a `AND` | <pre>.where({ <br /> $NOT:[<br /> {"User.age": 20 },<br /> {<br /> "User.age": {op: "=", value:60},<br /> "User.name": "Bob",<br /> },<br />]})</pre> | `(NOT (User.age = 20 AND (User.age = 60 AND User.name = "Bob")))` |
|
|
1050
|
+
| $NOR | Will wrap statement in a `NOT (/*...*/)` and join any items with a `OR` | <pre>.where({ <br /> $NOR:[<br /> {"User.age": 20 },<br /> {<br /> "User.age": {op: "!=", value:60},<br /> "User.name": "Bob",<br /> },<br />]})</pre> | `(NOT (User.age = 20 OR (User.age != 60 AND User.name = "Bob")))` |
|
|
1051
|
+
| `Array (scalar)` | Non-empty array of values → SQL `IN` | `.where({ "User.name": ["Alice", "Bob"] })` | `User.name IN ('Alice', 'Bob')` |
|
|
1052
|
+
| `Array (op-objects)` | Non-empty array of op-objects → `OR` chain | `.where({ "User.name": [{ op: "LIKE", value: "A%" }, { op: "LIKE", value: "B%" }] })` | `(User.name LIKE 'A%' OR User.name LIKE 'B%')` |
|
|
1053
|
+
|
|
1054
|
+
|
|
1055
|
+
###### Columns
|
|
1056
|
+
```typescript file=../usage-sqlite-v7/tests/readme/where.ts region=columns
|
|
1057
|
+
prisma.$from("User")
|
|
1058
|
+
.joinUnsafeIgnoreType("Post", "id", "User.name")
|
|
1059
|
+
.where({
|
|
1060
|
+
"User.age": 20,
|
|
1061
|
+
"User.name": {op: "LIKE", value: "Stuart%"},
|
|
1062
|
+
});
|
|
1063
|
+
```
|
|
1064
|
+
|
|
1065
|
+
###### $AND
|
|
1066
|
+
```typescript file=../usage-sqlite-v7/tests/readme/where.ts region=and
|
|
1067
|
+
prisma.$from("User")
|
|
1068
|
+
.joinUnsafeIgnoreType("Post", "id", "User.name")
|
|
1069
|
+
.where({
|
|
1070
|
+
$AND: [
|
|
1071
|
+
{"User.age": {op: ">", value: 20}},
|
|
1072
|
+
{"User.age": {op: "<", value: 60}},
|
|
1073
|
+
]
|
|
1074
|
+
});
|
|
1075
|
+
```
|
|
1076
|
+
|
|
1077
|
+
###### $OR
|
|
1078
|
+
```typescript file=../usage-sqlite-v7/tests/readme/where.ts region=or
|
|
1079
|
+
prisma.$from("User")
|
|
1080
|
+
.joinUnsafeIgnoreType("Post", "id", "User.name")
|
|
1081
|
+
.where({
|
|
1082
|
+
$OR: [
|
|
1083
|
+
{"User.name": {op: "LIKE", value: "a%"}},
|
|
1084
|
+
{"User.name": {op: "LIKE", value: "d%"}},
|
|
1085
|
+
]
|
|
1086
|
+
});
|
|
1087
|
+
```
|
|
1088
|
+
|
|
1089
|
+
###### $NOT
|
|
1090
|
+
```typescript file=../usage-sqlite-v7/tests/readme/where.ts region=not
|
|
1091
|
+
prisma.$from("User")
|
|
1092
|
+
.joinUnsafeIgnoreType("Post", "id", "User.name")
|
|
1093
|
+
.where({
|
|
1094
|
+
$NOT: [
|
|
1095
|
+
{"User.age": 20},
|
|
1096
|
+
{
|
|
1097
|
+
"User.age": {op: "=", value: 60},
|
|
1098
|
+
"User.name": "Bob",
|
|
1099
|
+
},
|
|
1100
|
+
]
|
|
1101
|
+
});
|
|
1102
|
+
```
|
|
1103
|
+
|
|
1104
|
+
###### $NOR
|
|
1105
|
+
```typescript file=../usage-sqlite-v7/tests/readme/where.ts region=nor
|
|
1106
|
+
prisma.$from("User")
|
|
1107
|
+
.joinUnsafeIgnoreType("Post", "id", "User.name")
|
|
1108
|
+
.where({
|
|
1109
|
+
$NOR: [
|
|
1110
|
+
{"User.age": 20},
|
|
1111
|
+
{
|
|
1112
|
+
"User.age": {op: "!=", value: 60},
|
|
1113
|
+
"User.name": "Bob",
|
|
1114
|
+
},
|
|
1115
|
+
]
|
|
1116
|
+
});
|
|
1117
|
+
```
|
|
1118
|
+
|
|
1119
|
+
###### Array (Scalar → IN)
|
|
1120
|
+
```typescript file=../usage-sqlite-v7/tests/readme/where.ts region=array-scalar
|
|
1121
|
+
prisma.$from("User")
|
|
1122
|
+
.joinUnsafeIgnoreType("Post", "id", "User.name")
|
|
1123
|
+
.where({
|
|
1124
|
+
"User.name": ["Alice", "Bob"],
|
|
1125
|
+
});
|
|
1126
|
+
```
|
|
1127
|
+
|
|
1128
|
+
###### Array (Op-Object → OR)
|
|
1129
|
+
```typescript file=../usage-sqlite-v7/tests/readme/where.ts region=array-op
|
|
1130
|
+
prisma.$from("User")
|
|
1131
|
+
.joinUnsafeIgnoreType("Post", "id", "User.name")
|
|
1132
|
+
.where({
|
|
1133
|
+
"User.name": [
|
|
1134
|
+
{ op: "LIKE", value: "A%" },
|
|
1135
|
+
{ op: "LIKE", value: "B%" },
|
|
1136
|
+
],
|
|
1137
|
+
});
|
|
1138
|
+
```
|
|
1139
|
+
|
|
1140
|
+
#### `.whereNotNull`
|
|
1141
|
+
|
|
1142
|
+
Removes `null` from the column's type union and adds an `IS NOT NULL` condition to the WHERE clause.
|
|
1143
|
+
Type narrowing is reflected in all downstream `.select()` calls.
|
|
1144
|
+
|
|
1145
|
+
##### Example
|
|
1146
|
+
```typescript file=../usage-sqlite-v7/tests/readme/whereNotNull.ts region=whereNotNull
|
|
1147
|
+
prisma.$from("User")
|
|
1148
|
+
.join("Post", "authorId", "User.id")
|
|
1149
|
+
.whereNotNull("User.name")
|
|
557
1150
|
```
|
|
558
1151
|

|
|
559
1152
|
|
|
560
1153
|
##### SQL
|
|
561
1154
|
The resulting SQL will look like:
|
|
562
1155
|
|
|
563
|
-
```sql
|
|
1156
|
+
```sql file=../usage-sqlite-v7/tests/readme/whereNotNull.ts region=whereNotNull-sql
|
|
564
1157
|
FROM User
|
|
565
|
-
JOIN Post ON authorId = User.id
|
|
566
|
-
WHERE User.name IS NOT NULL;
|
|
1158
|
+
JOIN Post ON Post.authorId = User.id
|
|
1159
|
+
WHERE (User.name IS NOT NULL);
|
|
567
1160
|
```
|
|
568
1161
|
|
|
569
1162
|
#### `.whereIsNull`
|
|
570
1163
|
|
|
571
|
-
|
|
572
|
-
To use `.whereIsNull`, you need to add it before a `.where`.
|
|
1164
|
+
Narrows the column's type to exactly `null` and adds an `IS NULL` condition to the WHERE clause.
|
|
573
1165
|
|
|
574
1166
|
##### Example
|
|
575
|
-
```typescript
|
|
1167
|
+
```typescript file=../usage-sqlite-v7/tests/readme/whereNotNull.ts region=whereIsNull
|
|
576
1168
|
prisma.$from("User")
|
|
577
|
-
|
|
578
|
-
|
|
1169
|
+
.join("Post", "authorId", "User.id")
|
|
1170
|
+
.whereIsNull("Post.content")
|
|
579
1171
|
```
|
|
580
1172
|

|
|
581
1173
|
|
|
582
1174
|
##### SQL
|
|
583
1175
|
The resulting SQL will look like:
|
|
584
1176
|
|
|
585
|
-
```sql
|
|
1177
|
+
```sql file=../usage-sqlite-v7/tests/readme/whereNotNull.ts region=whereIsNull-sql
|
|
1178
|
+
FROM User
|
|
1179
|
+
JOIN Post ON Post.authorId = User.id
|
|
1180
|
+
WHERE (Post.content IS NULL);
|
|
1181
|
+
```
|
|
1182
|
+
|
|
1183
|
+
#### `.where` — fn overload (SQL expressions)
|
|
1184
|
+
|
|
1185
|
+
Pass a callback instead of a criteria object to apply SQL functions as conditions. The callback receives the same select-fn context as `.select()`, giving access to `upper`, `lower`, `length`, `count`, `avg`, etc.
|
|
1186
|
+
|
|
1187
|
+
```typescript file=../../shared-tests/readme/where.ts region=fn-upper-like
|
|
1188
|
+
prisma.$from("User")
|
|
1189
|
+
.where(({ upper }) => [[upper('name'), { op: 'LIKE', value: 'John%' }]])
|
|
1190
|
+
.select("name")
|
|
1191
|
+
```
|
|
1192
|
+
|
|
1193
|
+
```sql file=../../shared-tests/readme/where.ts region=fn-upper-like-sql
|
|
1194
|
+
SELECT name
|
|
586
1195
|
FROM User
|
|
587
|
-
|
|
588
|
-
WHERE Post.content IS NULL;
|
|
1196
|
+
WHERE UPPER(name) LIKE 'John%';
|
|
589
1197
|
```
|
|
590
1198
|
|
|
1199
|
+
Each array element is an `[SQLExpr<T>, condition]` pair — multiple pairs are AND-ed. The condition type is inferred from `SQLExpr<T>`: string expressions accept LIKE/NOT LIKE, numeric expressions accept `>`, `<`, BETWEEN, etc.
|
|
1200
|
+
|
|
591
1201
|
#### `.whereRaw`
|
|
592
1202
|
|
|
593
|
-
When you want to write a complex `where`, or you just don't want the TypeSafety offered by the other methods, you can use `.whereRaw`.
|
|
1203
|
+
When you want to write a complex `where`, or you just don't want the TypeSafety offered by the other methods, you can use `.whereRaw`.
|
|
594
1204
|
|
|
595
1205
|
##### Example
|
|
596
|
-
```typescript
|
|
1206
|
+
```typescript file=../usage-sqlite-v7/tests/readme/where.ts region=raw
|
|
597
1207
|
prisma.$from("User")
|
|
598
|
-
|
|
599
|
-
|
|
1208
|
+
.join("Post", "authorId", "User.id")
|
|
1209
|
+
.whereRaw("this is a raw where statement");
|
|
600
1210
|
```
|
|
601
1211
|
|
|
602
1212
|
##### SQL
|
|
603
1213
|
The resulting SQL will look like:
|
|
604
1214
|
|
|
605
|
-
```sql
|
|
1215
|
+
```sql file=../usage-sqlite-v7/tests/readme/where.ts region=raw-sql
|
|
606
1216
|
FROM User
|
|
607
|
-
JOIN Post ON authorId = User.id
|
|
608
|
-
WHERE this is a raw
|
|
1217
|
+
JOIN Post ON Post.authorId = User.id
|
|
1218
|
+
WHERE this is a raw
|
|
1219
|
+
where statement;
|
|
609
1220
|
```
|
|
610
1221
|
|
|
611
1222
|
|
|
612
1223
|
### Group By
|
|
613
1224
|
|
|
614
|
-
Will allow you to pass a list of columns, that haven been specified from the `.$from` and any `.join` methods.
|
|
1225
|
+
Will allow you to pass a list of columns, that haven been specified from the `.$from` and any `.join` methods.
|
|
615
1226
|
|
|
616
1227
|
#### Example
|
|
617
|
-
```typescript
|
|
1228
|
+
```typescript file=../usage-sqlite-v7/tests/readme/groupby.ts region=basic
|
|
618
1229
|
prisma.$from("User")
|
|
619
|
-
|
|
620
|
-
|
|
1230
|
+
.join("Post", "authorId", "User.id")
|
|
1231
|
+
.groupBy(["name", "Post.content"]);
|
|
621
1232
|
```
|
|
622
1233
|

|
|
623
1234
|
|
|
624
1235
|
#### SQL
|
|
625
1236
|
The resulting SQL will look like:
|
|
626
1237
|
|
|
627
|
-
```sql
|
|
1238
|
+
```sql file=../usage-sqlite-v7/tests/readme/groupby.ts region=basic-sql
|
|
628
1239
|
FROM User
|
|
629
|
-
JOIN Post ON authorId = User.id
|
|
1240
|
+
JOIN Post ON Post.authorId = User.id
|
|
630
1241
|
GROUP BY name, Post.content;
|
|
631
1242
|
```
|
|
632
1243
|
|
|
@@ -637,16 +1248,17 @@ GROUP BY name, Post.content;
|
|
|
637
1248
|
Will add the keyword `DISTINCT` after the select.
|
|
638
1249
|
|
|
639
1250
|
#### Example
|
|
640
|
-
```typescript
|
|
1251
|
+
```typescript file=../usage-sqlite-v7/tests/readme/select-advanced.ts region=distinct
|
|
641
1252
|
prisma.$from("User")
|
|
642
|
-
|
|
1253
|
+
.selectDistinct()
|
|
1254
|
+
.select("User.name");
|
|
643
1255
|
```
|
|
644
1256
|
|
|
645
1257
|
#### SQL
|
|
646
1258
|
The resulting SQL will look like:
|
|
647
1259
|
|
|
648
|
-
```sql
|
|
649
|
-
SELECT DISTINCT
|
|
1260
|
+
```sql file=../usage-sqlite-v7/tests/readme/select-advanced.ts region=distinct-sql
|
|
1261
|
+
SELECT DISTINCT name
|
|
650
1262
|
FROM User;
|
|
651
1263
|
```
|
|
652
1264
|
|
|
@@ -657,71 +1269,100 @@ This method will explicitly list all the tables from the `$from` and `.join`. So
|
|
|
657
1269
|
|
|
658
1270
|
|
|
659
1271
|
#### Example - Single Table
|
|
660
|
-
```typescript
|
|
1272
|
+
```typescript file=../usage-sqlite-v7/tests/readme/select-advanced.ts region=all-single
|
|
661
1273
|
prisma.$from("User")
|
|
662
|
-
|
|
1274
|
+
.selectAll();
|
|
663
1275
|
```
|
|
664
1276
|
|
|
665
1277
|
##### SQL
|
|
666
1278
|
The resulting SQL will look like:
|
|
667
1279
|
|
|
668
|
-
```sql
|
|
669
|
-
SELECT id, email, name
|
|
670
|
-
FROM User
|
|
1280
|
+
```sql file=../usage-sqlite-v7/tests/readme/select-advanced.ts region=all-single-sql
|
|
1281
|
+
SELECT id, email, name, age
|
|
1282
|
+
FROM User;
|
|
671
1283
|
```
|
|
672
1284
|
|
|
673
1285
|
#### Example - Join table
|
|
674
|
-
```typescript
|
|
1286
|
+
```typescript file=../usage-sqlite-v7/tests/readme/select-advanced.ts region=all-join
|
|
675
1287
|
prisma.$from("User")
|
|
676
|
-
|
|
677
|
-
|
|
1288
|
+
.join("Post", "authorId", "User.id")
|
|
1289
|
+
.selectAll();
|
|
678
1290
|
```
|
|
679
1291
|
|
|
680
1292
|
##### SQL
|
|
681
1293
|
The resulting SQL will look like:
|
|
682
1294
|
|
|
683
|
-
```sql
|
|
684
|
-
SELECT User.id
|
|
1295
|
+
```sql file=../usage-sqlite-v7/tests/readme/select-advanced.ts region=all-join-sql
|
|
1296
|
+
SELECT User.id AS `User.id`, User.email AS `User.email`, User.name AS `User.name`, User.age AS `User.age`, Post.id AS `Post.id`, Post.title AS `Post.title`, Post.content AS `Post.content`, Post.published AS `Post.published`, Post.createdAt AS `Post.createdAt`, Post.authorId AS `Post.authorId`, Post.lastModifiedById AS `Post.lastModifiedById`, Post.metadata AS `Post.metadata`
|
|
685
1297
|
FROM User
|
|
686
|
-
JOIN Post ON authorId = User.id
|
|
1298
|
+
JOIN Post ON Post.authorId = User.id;
|
|
1299
|
+
```
|
|
1300
|
+
|
|
1301
|
+
#### `.selectAllOmit`
|
|
1302
|
+
|
|
1303
|
+
Like `.selectAll`, but excludes specific columns. Accepts `Table.column` or bare `column` references.
|
|
1304
|
+
|
|
1305
|
+
#### Example - Single Table
|
|
1306
|
+
```typescript file=../usage-sqlite-v7/tests/readme/select-all-omit.ts region=single-omit
|
|
1307
|
+
prisma.$from("User")
|
|
1308
|
+
.selectAllOmit(["User.email"]);
|
|
1309
|
+
```
|
|
1310
|
+
|
|
1311
|
+
##### SQL
|
|
1312
|
+
```sql file=../usage-sqlite-v7/tests/readme/select-all-omit.ts region=single-omit-sql
|
|
1313
|
+
SELECT `id`, `name`, `age`
|
|
1314
|
+
FROM `User`;
|
|
687
1315
|
```
|
|
688
1316
|
|
|
689
|
-
|
|
1317
|
+
#### Example - Multiple Columns
|
|
1318
|
+
```typescript file=../usage-sqlite-v7/tests/readme/select-all-omit.ts region=multi-omit
|
|
1319
|
+
prisma.$from("User")
|
|
1320
|
+
.selectAllOmit(["User.email", "User.age"]);
|
|
1321
|
+
```
|
|
1322
|
+
|
|
1323
|
+
#### Example - With Join
|
|
1324
|
+
```typescript file=../usage-sqlite-v7/tests/readme/select-all-omit.ts region=join-omit
|
|
1325
|
+
prisma.$from("User")
|
|
1326
|
+
.join("Post", "authorId", "User.id")
|
|
1327
|
+
.selectAllOmit(["User.email", "Post.content"]);
|
|
1328
|
+
```
|
|
1329
|
+
|
|
1330
|
+
> **Note:** `*` and `Table.*` are not valid arguments — use `Table.column` or bare `column` references.
|
|
690
1331
|
|
|
691
1332
|
#### `.select`
|
|
692
1333
|
|
|
693
1334
|
You can supply either; `*`, `Table.*` OR `table.field` and then chain them together.
|
|
694
1335
|
|
|
695
1336
|
#### Example - `*`
|
|
696
|
-
```typescript
|
|
1337
|
+
```typescript file=../usage-sqlite-v7/tests/readme/select-star.ts region=example
|
|
697
1338
|
prisma.$from("User")
|
|
698
|
-
|
|
1339
|
+
.select("*");
|
|
699
1340
|
```
|
|
700
1341
|
|
|
701
1342
|
##### SQL
|
|
702
1343
|
The resulting SQL will look like:
|
|
703
1344
|
|
|
704
|
-
```sql
|
|
705
|
-
SELECT *
|
|
1345
|
+
```sql file=../usage-sqlite-v7/tests/readme/select-star.ts region=example-sql
|
|
1346
|
+
SELECT *
|
|
706
1347
|
FROM User;
|
|
707
1348
|
```
|
|
708
1349
|
|
|
709
1350
|
#### Example - `Table.*` (Single Table)
|
|
710
|
-
```typescript
|
|
1351
|
+
```typescript file=../usage-sqlite-v7/tests/readme/select-advanced.ts region=table-star-single
|
|
711
1352
|
prisma.$from("User")
|
|
712
|
-
|
|
1353
|
+
.select("User.*");
|
|
713
1354
|
```
|
|
714
1355
|
|
|
715
1356
|
##### SQL
|
|
716
1357
|
The resulting SQL will look like:
|
|
717
1358
|
|
|
718
|
-
```sql
|
|
719
|
-
SELECT
|
|
1359
|
+
```sql file=../usage-sqlite-v7/tests/readme/select-advanced.ts region=table-star-single-sql
|
|
1360
|
+
SELECT id, email, name, age
|
|
720
1361
|
FROM User;
|
|
721
1362
|
```
|
|
722
1363
|
|
|
723
1364
|
#### Example - `Table.*` (With Join)
|
|
724
|
-
```typescript
|
|
1365
|
+
```typescript file=../usage-sqlite-v7/tests/readme/select-advanced.ts region=table-star-join
|
|
725
1366
|
prisma.$from("User")
|
|
726
1367
|
.join("Post", "authorId", "User.id")
|
|
727
1368
|
.select("User.*")
|
|
@@ -731,38 +1372,32 @@ prisma.$from("User")
|
|
|
731
1372
|
##### SQL
|
|
732
1373
|
The resulting SQL will look like:
|
|
733
1374
|
|
|
734
|
-
```sql
|
|
735
|
-
SELECT User.id AS `User.id`,
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
Post.id AS `Post.id`,
|
|
739
|
-
Post.title AS `Post.title`,
|
|
740
|
-
Post.content AS `Post.content`,
|
|
741
|
-
Post.published AS `Post.published`
|
|
742
|
-
FROM User
|
|
743
|
-
JOIN Post ON authorId = User.id;
|
|
1375
|
+
```sql file=../usage-sqlite-v7/tests/readme/select-advanced.ts region=table-star-join-sql
|
|
1376
|
+
SELECT User.id AS `User.id`, User.email AS `User.email`, User.name AS `User.name`, User.age AS `User.age`, Post.id AS `Post.id`, Post.title AS `Post.title`, Post.content AS `Post.content`, Post.published AS `Post.published`, Post.createdAt AS `Post.createdAt`, Post.authorId AS `Post.authorId`, Post.lastModifiedById AS `Post.lastModifiedById`, Post.metadata AS `Post.metadata`
|
|
1377
|
+
FROM User
|
|
1378
|
+
JOIN Post ON Post.authorId = User.id;
|
|
744
1379
|
```
|
|
745
1380
|
|
|
746
|
-
[!NOTE]
|
|
1381
|
+
> [!NOTE]
|
|
747
1382
|
> When using `Table.*` with joins, all columns are automatically aliased with the table name prefix to avoid column name conflicts.
|
|
748
1383
|
|
|
749
1384
|
#### Example - Chained
|
|
750
|
-
```typescript
|
|
1385
|
+
```typescript file=../usage-sqlite-v7/tests/readme/select-chained.ts region=example
|
|
751
1386
|
prisma.$from("User")
|
|
752
|
-
|
|
753
|
-
|
|
1387
|
+
.select("name")
|
|
1388
|
+
.select("email");
|
|
754
1389
|
```
|
|
755
1390
|
|
|
756
1391
|
##### SQL
|
|
757
1392
|
The resulting SQL will look like:
|
|
758
1393
|
|
|
759
|
-
```sql
|
|
760
|
-
SELECT name, email
|
|
1394
|
+
```sql file=../usage-sqlite-v7/tests/readme/select-chained.ts region=example-sql
|
|
1395
|
+
SELECT name, email
|
|
761
1396
|
FROM User;
|
|
762
1397
|
```
|
|
763
1398
|
|
|
764
1399
|
#### Example - Join + Chained
|
|
765
|
-
```typescript
|
|
1400
|
+
```typescript file=../usage-sqlite-v7/tests/readme/select-advanced.ts region=join-chained
|
|
766
1401
|
prisma.$from("User")
|
|
767
1402
|
.join("Post", "authorId", "User.id")
|
|
768
1403
|
.select("name")
|
|
@@ -772,24 +1407,25 @@ prisma.$from("User")
|
|
|
772
1407
|
##### SQL
|
|
773
1408
|
The resulting SQL will look like:
|
|
774
1409
|
|
|
775
|
-
```sql
|
|
776
|
-
SELECT name,
|
|
777
|
-
FROM User
|
|
778
|
-
JOIN Post ON authorId = User.id;
|
|
1410
|
+
```sql file=../usage-sqlite-v7/tests/readme/select-advanced.ts region=join-chained-sql
|
|
1411
|
+
SELECT name, title
|
|
1412
|
+
FROM User
|
|
1413
|
+
JOIN Post ON Post.authorId = User.id;
|
|
779
1414
|
```
|
|
780
1415
|
|
|
781
1416
|
#### Example - Column Aliases
|
|
782
|
-
```typescript
|
|
783
|
-
// Basic alias
|
|
1417
|
+
```typescript file=../usage-sqlite-v7/tests/readme/select-column-alias.ts region=basic
|
|
784
1418
|
prisma.$from("User")
|
|
785
1419
|
.select("User.name", "username");
|
|
1420
|
+
```
|
|
786
1421
|
|
|
787
|
-
|
|
1422
|
+
```typescript file=../usage-sqlite-v7/tests/readme/select-column-alias.ts region=multiple
|
|
788
1423
|
prisma.$from("User")
|
|
789
1424
|
.select("User.id", "userId")
|
|
790
1425
|
.select("User.email", "emailAddress");
|
|
1426
|
+
```
|
|
791
1427
|
|
|
792
|
-
|
|
1428
|
+
```typescript file=../usage-sqlite-v7/tests/readme/select-column-alias.ts region=mixed
|
|
793
1429
|
prisma.$from("User")
|
|
794
1430
|
.select("User.id")
|
|
795
1431
|
.select("User.name", "username")
|
|
@@ -799,19 +1435,23 @@ prisma.$from("User")
|
|
|
799
1435
|
##### SQL
|
|
800
1436
|
The resulting SQL will look like:
|
|
801
1437
|
|
|
802
|
-
```sql
|
|
803
|
-
|
|
804
|
-
|
|
1438
|
+
```sql file=../usage-sqlite-v7/tests/readme/select-column-alias.ts region=basic-sql
|
|
1439
|
+
SELECT User.name AS `username`
|
|
1440
|
+
FROM User;
|
|
1441
|
+
```
|
|
805
1442
|
|
|
806
|
-
|
|
807
|
-
SELECT User.id AS `userId`, User.email AS `emailAddress`
|
|
1443
|
+
```sql file=../usage-sqlite-v7/tests/readme/select-column-alias.ts region=multiple-sql
|
|
1444
|
+
SELECT User.id AS `userId`, User.email AS `emailAddress`
|
|
1445
|
+
FROM User;
|
|
1446
|
+
```
|
|
808
1447
|
|
|
809
|
-
|
|
810
|
-
SELECT
|
|
1448
|
+
```sql file=../usage-sqlite-v7/tests/readme/select-column-alias.ts region=mixed-sql
|
|
1449
|
+
SELECT id, User.name AS `username`, email
|
|
1450
|
+
FROM User;
|
|
811
1451
|
```
|
|
812
1452
|
|
|
813
1453
|
#### Example - Aliases with Joins
|
|
814
|
-
```typescript
|
|
1454
|
+
```typescript file=../usage-sqlite-v7/tests/readme/select-advanced.ts region=aliases-joins
|
|
815
1455
|
prisma.$from("User")
|
|
816
1456
|
.join("Post", "authorId", "User.id")
|
|
817
1457
|
.select("User.name", "authorName")
|
|
@@ -821,77 +1461,115 @@ prisma.$from("User")
|
|
|
821
1461
|
##### SQL
|
|
822
1462
|
The resulting SQL will look like:
|
|
823
1463
|
|
|
824
|
-
```sql
|
|
825
|
-
SELECT User.name AS `authorName`, Post.title AS `postTitle`
|
|
826
|
-
FROM User
|
|
827
|
-
JOIN Post ON authorId = User.id;
|
|
1464
|
+
```sql file=../usage-sqlite-v7/tests/readme/select-advanced.ts region=aliases-joins-sql
|
|
1465
|
+
SELECT User.name AS `authorName`, Post.title AS `postTitle`
|
|
1466
|
+
FROM User
|
|
1467
|
+
JOIN Post ON Post.authorId = User.id;
|
|
828
1468
|
```
|
|
829
1469
|
|
|
830
|
-
[!NOTE]
|
|
1470
|
+
> [!NOTE]
|
|
831
1471
|
> When using column aliases, you can reference the alias in `ORDER BY` clauses. The returned type will use the alias names instead of the original column names.
|
|
832
1472
|
|
|
833
1473
|
### Having
|
|
834
1474
|
|
|
835
|
-
`.having`
|
|
1475
|
+
`.having` accepts two overloads — a criteria object (same syntax as [`.where`](#where)) or a fn callback for SQL expressions and aggregate functions.
|
|
836
1476
|
|
|
837
|
-
####
|
|
1477
|
+
#### Criteria object
|
|
838
1478
|
|
|
839
|
-
```typescript
|
|
1479
|
+
```typescript file=../../shared-tests/readme/having.ts region=with-groupby
|
|
840
1480
|
prisma.$from("User")
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
1481
|
+
.join("Post", "authorId", "User.id")
|
|
1482
|
+
.groupBy(["name", "Post.content"])
|
|
1483
|
+
.having({
|
|
1484
|
+
"User.name": {
|
|
1485
|
+
"op": "LIKE",
|
|
1486
|
+
"value": "bob%"
|
|
1487
|
+
}
|
|
1488
|
+
})
|
|
1489
|
+
.select("email");
|
|
1490
|
+
```
|
|
1491
|
+
|
|
1492
|
+
```sql file=../../shared-tests/readme/having.ts region=with-groupby-sql
|
|
1493
|
+
SELECT email
|
|
1494
|
+
FROM User
|
|
1495
|
+
JOIN Post ON Post.authorId = User.id
|
|
1496
|
+
GROUP BY name, Post.content HAVING User.name LIKE 'bob%';
|
|
849
1497
|
```
|
|
850
1498
|
|
|
851
|
-
|
|
1499
|
+
#### fn overload — aggregate functions
|
|
1500
|
+
|
|
1501
|
+
Pass a callback returning `Array<[SQLExpr<T>, condition]>` pairs. The callback receives the full select-fn context, including all aggregate and string functions.
|
|
1502
|
+
|
|
1503
|
+
##### `countAll()` with comparison op
|
|
1504
|
+
|
|
1505
|
+
```typescript file=../../shared-tests/readme/having.ts region=agg-fn-tuple-countall
|
|
852
1506
|
prisma.$from("User")
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
"value": "stuart%"
|
|
858
|
-
}
|
|
859
|
-
});
|
|
1507
|
+
.join("Post", "authorId", "User.id")
|
|
1508
|
+
.groupBy(["User.name"])
|
|
1509
|
+
.having(({ countAll }) => [[countAll(), { op: '>', value: 1 }]])
|
|
1510
|
+
.select("User.name")
|
|
860
1511
|
```
|
|
861
1512
|
|
|
1513
|
+
```sql file=../../shared-tests/readme/having.ts region=agg-fn-tuple-countall-sql
|
|
1514
|
+
SELECT name
|
|
1515
|
+
FROM User
|
|
1516
|
+
JOIN Post ON Post.authorId = User.id
|
|
1517
|
+
GROUP BY User.name HAVING COUNT(*) > 1;
|
|
1518
|
+
```
|
|
862
1519
|
|
|
863
|
-
#####
|
|
1520
|
+
##### `count(col)` with bigint value
|
|
864
1521
|
|
|
865
|
-
```
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
1522
|
+
```typescript file=../../shared-tests/readme/having.ts region=agg-fn-tuple-count
|
|
1523
|
+
prisma.$from("User")
|
|
1524
|
+
.join("Post", "authorId", "User.id")
|
|
1525
|
+
.groupBy(["User.name"])
|
|
1526
|
+
.having(({ count }) => [[count('User.id'), { op: '>=', value: 2n }]])
|
|
1527
|
+
.select("User.name")
|
|
870
1528
|
```
|
|
871
1529
|
|
|
872
|
-
```
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
1530
|
+
```sql file=../../shared-tests/readme/having.ts region=agg-fn-tuple-count-sql
|
|
1531
|
+
SELECT name
|
|
1532
|
+
FROM User
|
|
1533
|
+
JOIN Post ON Post.authorId = User.id
|
|
1534
|
+
GROUP BY User.name HAVING COUNT(User.id) >= 2;
|
|
1535
|
+
```
|
|
1536
|
+
|
|
1537
|
+
##### String expr — `upper(col)` LIKE
|
|
1538
|
+
|
|
1539
|
+
```typescript file=../../shared-tests/readme/having.ts region=agg-fn-string-upper
|
|
1540
|
+
prisma.$from("User")
|
|
1541
|
+
.join("Post", "authorId", "User.id")
|
|
1542
|
+
.groupBy(["User.name"])
|
|
1543
|
+
.having(({ upper }) => [[upper('User.name'), { op: 'LIKE', value: 'John%' }]])
|
|
1544
|
+
.select("User.name")
|
|
1545
|
+
```
|
|
1546
|
+
|
|
1547
|
+
```sql file=../../shared-tests/readme/having.ts region=agg-fn-string-upper-sql
|
|
1548
|
+
SELECT name
|
|
1549
|
+
FROM User
|
|
1550
|
+
JOIN Post ON Post.authorId = User.id
|
|
1551
|
+
GROUP BY User.name HAVING UPPER(User.name) LIKE 'John%';
|
|
876
1552
|
```
|
|
877
1553
|
|
|
1554
|
+
Multiple pairs in one `.having()` call are AND-ed together. `.having()` can also be chained — each call appends an AND condition.
|
|
1555
|
+
|
|
878
1556
|
### Order By
|
|
879
1557
|
|
|
880
1558
|
`.orderBy`, takes an array of column names, with the optional suffix of `ASC` or `DESC`.
|
|
881
1559
|
|
|
882
1560
|
#### Example
|
|
883
1561
|
|
|
884
|
-
```typescript
|
|
1562
|
+
```typescript file=../usage-sqlite-v7/tests/readme/orderby.ts region=basic
|
|
885
1563
|
prisma.$from("User")
|
|
886
1564
|
.join("Post", "authorId", "User.id")
|
|
887
|
-
.orderBy(["name", "Post.content DESC"]);
|
|
1565
|
+
.orderBy(["name", "Post.content DESC"]);
|
|
888
1566
|
```
|
|
889
1567
|
|
|
890
1568
|
##### SQL
|
|
891
1569
|
|
|
892
|
-
```sql
|
|
893
|
-
FROM User
|
|
894
|
-
JOIN Post ON authorId = User.id
|
|
1570
|
+
```sql file=../usage-sqlite-v7/tests/readme/orderby.ts region=basic-sql
|
|
1571
|
+
FROM User
|
|
1572
|
+
JOIN Post ON Post.authorId = User.id
|
|
895
1573
|
ORDER BY name, Post.content DESC;
|
|
896
1574
|
```
|
|
897
1575
|
|
|
@@ -901,7 +1579,7 @@ ORDER BY name, Post.content DESC;
|
|
|
901
1579
|
|
|
902
1580
|
#### Example
|
|
903
1581
|
|
|
904
|
-
```typescript
|
|
1582
|
+
```typescript file=../usage-sqlite-v7/tests/readme/pagination.ts region=limit
|
|
905
1583
|
prisma.$from("User")
|
|
906
1584
|
.join("Post", "authorId", "User.id")
|
|
907
1585
|
.limit(1);
|
|
@@ -909,9 +1587,9 @@ prisma.$from("User")
|
|
|
909
1587
|
|
|
910
1588
|
##### SQL
|
|
911
1589
|
|
|
912
|
-
```
|
|
913
|
-
FROM User
|
|
914
|
-
JOIN Post ON authorId = User.id
|
|
1590
|
+
```sql file=../usage-sqlite-v7/tests/readme/pagination.ts region=limit-sql
|
|
1591
|
+
FROM User
|
|
1592
|
+
JOIN Post ON Post.authorId = User.id
|
|
915
1593
|
LIMIT 1;
|
|
916
1594
|
```
|
|
917
1595
|
|
|
@@ -920,71 +1598,374 @@ LIMIT 1;
|
|
|
920
1598
|
`.offSet`, the number of rows to skip. Requires `.limit` to have been used first.
|
|
921
1599
|
|
|
922
1600
|
#### Example
|
|
923
|
-
```typescript
|
|
1601
|
+
```typescript file=../usage-sqlite-v7/tests/readme/pagination.ts region=offset
|
|
924
1602
|
prisma.$from("User")
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
1603
|
+
.join("Post", "authorId", "User.id")
|
|
1604
|
+
.limit(1)
|
|
1605
|
+
.offset(1);
|
|
928
1606
|
```
|
|
929
1607
|
|
|
930
1608
|
##### SQL
|
|
931
1609
|
|
|
932
|
-
```
|
|
933
|
-
FROM User
|
|
934
|
-
JOIN Post ON authorId = User.id
|
|
935
|
-
LIMIT 1
|
|
936
|
-
OFFSET 1
|
|
1610
|
+
```sql file=../usage-sqlite-v7/tests/readme/pagination.ts region=offset-sql
|
|
1611
|
+
FROM User
|
|
1612
|
+
JOIN Post ON Post.authorId = User.id
|
|
1613
|
+
LIMIT 1
|
|
1614
|
+
OFFSET 1;
|
|
937
1615
|
```
|
|
938
1616
|
|
|
939
|
-
##
|
|
1617
|
+
## Select Functions
|
|
940
1618
|
|
|
941
|
-
|
|
942
|
-
- Support Select Functions
|
|
943
|
-
- [Aggregation #4](https://github.com/adrianbrowning/prisma-ts-select/issues/4)
|
|
944
|
-
- [String #5](https://github.com/adrianbrowning/prisma-ts-select/issues/5)
|
|
945
|
-
- [Date & Time #6](https://github.com/adrianbrowning/prisma-ts-select/issues/6)
|
|
946
|
-
- [Math #7](https://github.com/adrianbrowning/prisma-ts-select/issues/7)
|
|
947
|
-
- [Control Flow #8](https://github.com/adrianbrowning/prisma-ts-select/issues/8)
|
|
948
|
-
- [JSON #9](https://github.com/adrianbrowning/prisma-ts-select/issues/9)
|
|
949
|
-
- [Support a `Many-To-Many` join #19](https://github.com/adrianbrowning/prisma-ts-select/issues/19)
|
|
950
|
-
- [Select column alias #27](https://github.com/adrianbrowning/prisma-ts-select/issues/27)
|
|
951
|
-
- [Table name alias #28](https://github.com/adrianbrowning/prisma-ts-select/issues/28)
|
|
952
|
-
- [whereRaw supporting Prisma.sql](https://github.com/adrianbrowning/prisma-ts-select/issues/29)
|
|
1619
|
+
Pass a callback to `.select()` to use SQL expressions and aggregate functions. The callback receives a context object with all available functions for the active dialect.
|
|
953
1620
|
|
|
954
|
-
|
|
955
|
-
Changelog is available [here](https://github.com/adrianbrowning/prisma-ts-select/releases). We use [semantic versioning](https://semver.org/) for versioning.
|
|
1621
|
+
### Shared (all dialects)
|
|
956
1622
|
|
|
957
|
-
|
|
958
|
-
This project is licensed under the MIT License. See the LICENSE file for details.
|
|
1623
|
+
#### `lit(value)` — SQL literal
|
|
959
1624
|
|
|
960
|
-
|
|
1625
|
+
Produces a typed SQL literal from a JS value.
|
|
961
1626
|
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
- missing @deprecated
|
|
968
|
-
- ts-exptect-error - might not be needed
|
|
969
|
-
- GetColsFromTableType missing ts-expect-error - might not be needed
|
|
970
|
-
- DB needs to be in the same file.
|
|
1627
|
+
##### Example
|
|
1628
|
+
```typescript file=../usage-sqlite-v7/tests/readme/select-fns.ts region=lit-string
|
|
1629
|
+
prisma.$from("User")
|
|
1630
|
+
.select(({ lit }) => lit("hello"), "greeting");
|
|
1631
|
+
```
|
|
971
1632
|
|
|
1633
|
+
#### `countAll()` — COUNT(*)
|
|
972
1634
|
|
|
1635
|
+
The most common aggregate. Always produces `COUNT(*)`.
|
|
973
1636
|
|
|
974
|
-
|
|
1637
|
+
##### Example
|
|
1638
|
+
```typescript file=../usage-sqlite-v7/tests/readme/select-fns.ts region=count-all
|
|
1639
|
+
prisma.$from("User")
|
|
1640
|
+
.select(({ countAll }) => countAll(), "total");
|
|
1641
|
+
```
|
|
975
1642
|
|
|
976
|
-
|
|
1643
|
+
##### SQL
|
|
1644
|
+
```sql file=../usage-sqlite-v7/tests/readme/select-fns.ts region=count-all-sql
|
|
1645
|
+
SELECT COUNT(*) AS `total`
|
|
1646
|
+
FROM User;
|
|
1647
|
+
```
|
|
977
1648
|
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
1649
|
+
#### `count(col)` — COUNT(col)
|
|
1650
|
+
|
|
1651
|
+
```typescript file=../usage-sqlite-v7/tests/readme/select-fns.ts region=count-col
|
|
1652
|
+
prisma.$from("User")
|
|
1653
|
+
.select(({ count }) => count("User.id"), "cnt");
|
|
981
1654
|
```
|
|
982
1655
|
|
|
983
|
-
|
|
1656
|
+
#### `countDistinct(col)` — COUNT(DISTINCT col)
|
|
984
1657
|
|
|
985
|
-
|
|
1658
|
+
```typescript file=../usage-sqlite-v7/tests/readme/select-fns.ts region=count-distinct
|
|
1659
|
+
prisma.$from("User")
|
|
1660
|
+
.select(({ countDistinct }) => countDistinct("User.id"), "cnt");
|
|
1661
|
+
```
|
|
986
1662
|
|
|
1663
|
+
#### `sum(col)` / `avg(col)` / `min(col)` / `max(col)`
|
|
987
1664
|
|
|
988
|
-
|
|
1665
|
+
Standard numeric aggregates. **Return types vary by dialect** — `sum` and `avg` return `Decimal` on MySQL (matching Prisma's numeric precision model), `number` on SQLite and PostgreSQL. `min`/`max` always return `T | null` (NULL for empty sets) where `T` is the column's TypeScript type.
|
|
1666
|
+
|
|
1667
|
+
| Function | SQLite | MySQL | PostgreSQL |
|
|
1668
|
+
|---|---|---|---|
|
|
1669
|
+
| `sum(col)` | `number` | `Decimal` | `number` |
|
|
1670
|
+
| `avg(col)` | `number` | `Decimal` | `number` |
|
|
1671
|
+
| `min(col)` | `T \| null` | `T \| null` | `T \| null` |
|
|
1672
|
+
| `max(col)` | `T \| null` | `T \| null` | `T \| null` |
|
|
1673
|
+
|
|
1674
|
+
```typescript file=../usage-sqlite-v7/tests/readme/select-fns.ts region=sum
|
|
1675
|
+
prisma.$from("User")
|
|
1676
|
+
.select(({ sum }) => sum("User.age"), "total");
|
|
1677
|
+
```
|
|
1678
|
+
|
|
1679
|
+
#### String Functions (all dialects)
|
|
1680
|
+
|
|
1681
|
+
| Function | SQL | Returns |
|
|
1682
|
+
|---|---|---|
|
|
1683
|
+
| `upper(col)` | `UPPER(col)` | `string` |
|
|
1684
|
+
| `lower(col)` | `LOWER(col)` | `string` |
|
|
1685
|
+
| `length(col)` | `LENGTH(col)` | `number` |
|
|
1686
|
+
| `trim(col)` | `TRIM(col)` | `string` |
|
|
1687
|
+
| `ltrim(col)` | `LTRIM(col)` | `string` |
|
|
1688
|
+
| `rtrim(col)` | `RTRIM(col)` | `string` |
|
|
1689
|
+
| `replace(col, from, to)` | `REPLACE(col, 'from', 'to')` | `string` |
|
|
1690
|
+
|
|
1691
|
+
> **Note:** MySQL `LENGTH()` returns byte-length (not char-length). For character-length on multi-byte strings use a dialect-specific fn.
|
|
1692
|
+
|
|
1693
|
+
#### DateTime Functions (all dialects)
|
|
1694
|
+
|
|
1695
|
+
All dialects provide these functions. Return types differ for `year`/`month`/`day`/`hour`/`minute`/`second` — see note below.
|
|
1696
|
+
|
|
1697
|
+
| Function | SQL (MySQL / PG / SQLite) | Returns |
|
|
1698
|
+
|---|---|---|
|
|
1699
|
+
| `now()` | `NOW()` / `NOW()` / `datetime('now')` | `Date` |
|
|
1700
|
+
| `curDate()` | `CURDATE()` / `CURRENT_DATE` / `date('now')` | `Date` |
|
|
1701
|
+
| `year(col)` | `YEAR(col)` / `EXTRACT(YEAR FROM col)::integer` / `strftime('%Y', col)` | `number` (SQLite: `string`) |
|
|
1702
|
+
| `month(col)` | `MONTH(col)` / `EXTRACT(MONTH FROM col)::integer` / `strftime('%m', col)` | `number` (SQLite: `string`) |
|
|
1703
|
+
| `day(col)` | `DAY(col)` / `EXTRACT(DAY FROM col)::integer` / `strftime('%d', col)` | `number` (SQLite: `string`) |
|
|
1704
|
+
| `hour(col)` | `HOUR(col)` / `EXTRACT(HOUR FROM col)::integer` / `strftime('%H', col)` | `number` (SQLite: `string`) |
|
|
1705
|
+
| `minute(col)` | `MINUTE(col)` / `EXTRACT(MINUTE FROM col)::integer` / `strftime('%M', col)` | `number` (SQLite: `string`) |
|
|
1706
|
+
| `second(col)` | `SECOND(col)` / `EXTRACT(SECOND FROM col)::integer` / `strftime('%S', col)` | `number` (SQLite: `string`) |
|
|
1707
|
+
|
|
1708
|
+
> **Note:** `year`, `month`, `day`, `hour`, `minute`, and `second` return `string` on SQLite because `strftime()` always returns text (e.g. `'2024'`, `'03'`). MySQL and PostgreSQL return `number`.
|
|
1709
|
+
|
|
1710
|
+
DateTime column args also accept `SQLExpr<Date>`, enabling composition:
|
|
1711
|
+
|
|
1712
|
+
```typescript
|
|
1713
|
+
prisma.$from("Post").select(({ year, now }) => year(now()), "y");
|
|
1714
|
+
```
|
|
1715
|
+
|
|
1716
|
+
```typescript file=../usage-sqlite-v7/tests/readme/select-fns.ts region=upper
|
|
1717
|
+
prisma.$from("User")
|
|
1718
|
+
.select(({ upper }) => upper("User.name"), "uname");
|
|
1719
|
+
```
|
|
1720
|
+
|
|
1721
|
+
String fns accept a `SQLExpr<string>` as input, enabling composition:
|
|
1722
|
+
|
|
1723
|
+
```typescript file=../usage-sqlite-v7/tests/readme/select-fns.ts region=lower
|
|
1724
|
+
prisma.$from("User")
|
|
1725
|
+
.select(({ lower }) => lower("User.name"), "lname");
|
|
1726
|
+
```
|
|
1727
|
+
|
|
1728
|
+
```typescript file=../usage-sqlite-v7/tests/readme/select-fns.ts region=replace
|
|
1729
|
+
prisma.$from("User")
|
|
1730
|
+
.select(({ replace }) => replace("User.email", "@example.com", ""), "handle");
|
|
1731
|
+
```
|
|
1732
|
+
|
|
1733
|
+
#### Math Functions (all dialects)
|
|
1734
|
+
|
|
1735
|
+
| Function | SQL | Returns |
|
|
1736
|
+
|---|---|---|
|
|
1737
|
+
| `abs(col)` | `ABS(col)` | `number` |
|
|
1738
|
+
| `ceil(col)` | `CEIL(col)` | `number` |
|
|
1739
|
+
| `floor(col)` | `FLOOR(col)` | `number` |
|
|
1740
|
+
| `round(col, decimals?)` | `ROUND(col)` / `ROUND(col, n)` | `number` |
|
|
1741
|
+
| `power(base, exp)` | `POWER(base, exp)` | `number` |
|
|
1742
|
+
| `sqrt(col)` | `SQRT(col)` | `number` |
|
|
1743
|
+
| `mod(col, divisor)` | `MOD(col, divisor)` | `number` |
|
|
1744
|
+
| `sign(col)` | `SIGN(col)` | `number` |
|
|
1745
|
+
| `exp(col)` | `EXP(col)` | `number` |
|
|
1746
|
+
|
|
1747
|
+
Math fns accept `SQLExpr<number>` or a column reference, enabling composition:
|
|
1748
|
+
|
|
1749
|
+
```typescript
|
|
1750
|
+
// Absolute value of a literal
|
|
1751
|
+
prisma.$from("User")
|
|
1752
|
+
.select(({ abs, lit }) => abs(lit(-5)), "absVal");
|
|
1753
|
+
|
|
1754
|
+
// Round to 2 decimal places
|
|
1755
|
+
prisma.$from("User")
|
|
1756
|
+
.select(({ round, lit }) => round(lit(4.567), 2), "val");
|
|
1757
|
+
|
|
1758
|
+
// Compose: sqrt(power(x, 2))
|
|
1759
|
+
prisma.$from("User")
|
|
1760
|
+
.select(({ sqrt, power }) => sqrt(power("User.age", 2)), "val");
|
|
1761
|
+
```
|
|
1762
|
+
|
|
1763
|
+
#### Control Flow Functions (all dialects)
|
|
1764
|
+
|
|
1765
|
+
| Function | SQL | Returns |
|
|
1766
|
+
|---|---|---|
|
|
1767
|
+
| `cond(criteria)` | *(WhereCriteria → SQL condition string)* | `SQLExpr<boolean>` |
|
|
1768
|
+
| `coalesce(...args)` | `COALESCE(a, b, ...)` | `SQLExpr<T>` |
|
|
1769
|
+
| `nullif(expr1, expr2)` | `NULLIF(a, b)` | `SQLExpr<T \| null>` |
|
|
1770
|
+
| `caseWhen(cases, elseVal?)` | `CASE WHEN ... THEN ... END` | `SQLExpr<T \| null>` |
|
|
1771
|
+
|
|
1772
|
+
`cond()` converts a `WhereCriteria` object into a `SQLExpr<unknown>` — useful when you need a condition expression outside of a dedicated function. Note: `$if()`/`iif()` and `caseWhen()` all accept `WhereCriteria` directly, so `cond()` is rarely needed.
|
|
1773
|
+
|
|
1774
|
+
```typescript file=../usage-sqlite-v7/tests/readme/select-fns-control-flow.ts region=coalesce
|
|
1775
|
+
prisma.$from("User")
|
|
1776
|
+
.select(({ coalesce, lit }) => coalesce("User.email", lit("unknown")), "contact")
|
|
1777
|
+
```
|
|
1778
|
+
|
|
1779
|
+
```typescript file=../usage-sqlite-v7/tests/readme/select-fns-control-flow.ts region=nullif
|
|
1780
|
+
prisma.$from("User")
|
|
1781
|
+
.select(({ nullif, lit }) => nullif(lit(0), lit(0)), "val")
|
|
1782
|
+
```
|
|
1783
|
+
|
|
1784
|
+
```typescript file=../usage-sqlite-v7/tests/readme/select-fns-control-flow.ts region=case-when
|
|
1785
|
+
prisma.$from("User")
|
|
1786
|
+
.select(({ caseWhen, lit }) => caseWhen([
|
|
1787
|
+
{ when: { age: { op: ">=", value: 18 } }, then: lit("adult") },
|
|
1788
|
+
], lit("minor")), "status")
|
|
1789
|
+
```
|
|
1790
|
+
|
|
1791
|
+
```typescript file=../usage-sqlite-v7/tests/readme/select-fns-control-flow.ts region=cond
|
|
1792
|
+
prisma.$from("User")
|
|
1793
|
+
.select(({ cond }) => cond({ age: { op: ">", value: 0 } }), "flag")
|
|
1794
|
+
```
|
|
1795
|
+
|
|
1796
|
+
#### Combining with `.groupBy()`
|
|
1797
|
+
|
|
1798
|
+
```typescript file=../usage-sqlite-v7/tests/readme/select-fns.ts region=count-groupby
|
|
1799
|
+
prisma.$from("User")
|
|
1800
|
+
.join("Post", "authorId", "User.id")
|
|
1801
|
+
.groupBy(["User.name"])
|
|
1802
|
+
.select("User.name")
|
|
1803
|
+
.select(({ countAll }) => countAll(), "postCount");
|
|
1804
|
+
```
|
|
1805
|
+
|
|
1806
|
+
##### SQL
|
|
1807
|
+
```sql
|
|
1808
|
+
SELECT User.name, COUNT(*) AS `postCount`
|
|
1809
|
+
FROM User
|
|
1810
|
+
JOIN Post ON Post.authorId = User.id
|
|
1811
|
+
GROUP BY User.name;
|
|
1812
|
+
```
|
|
1813
|
+
|
|
1814
|
+
---
|
|
1815
|
+
|
|
1816
|
+
### MySQL-specific
|
|
1817
|
+
|
|
1818
|
+
| Function | SQL | Returns |
|
|
1819
|
+
|---|---|---|
|
|
1820
|
+
| `distinct(col)` | `DISTINCT col` | `ColType` (use inside `avg`, `sum`, `count`, `groupConcat`) |
|
|
1821
|
+
| `groupConcat(col, sep?)` | `GROUP_CONCAT(col SEPARATOR sep)` | `string` |
|
|
1822
|
+
| `bitAnd(col)` | `BIT_AND(col)` | `number` |
|
|
1823
|
+
| `bitOr(col)` | `BIT_OR(col)` | `number` |
|
|
1824
|
+
| `bitXor(col)` | `BIT_XOR(col)` | `number` |
|
|
1825
|
+
| `stddev(col)` | `STDDEV(col)` | `number` |
|
|
1826
|
+
| `stddevSamp(col)` | `STDDEV_SAMP(col)` | `number` |
|
|
1827
|
+
| `variance(col)` | `VARIANCE(col)` | `number` |
|
|
1828
|
+
| `varSamp(col)` | `VAR_SAMP(col)` | `number` |
|
|
1829
|
+
| `jsonArrayAgg(col)` | `JSON_ARRAYAGG(col)` | `JSONValue` |
|
|
1830
|
+
| `jsonObjectAgg(key, val)` | `JSON_OBJECTAGG(key, val)` | `JSONValue` |
|
|
1831
|
+
| `concat(...cols)` | `CONCAT(a, b, ...)` | `string` |
|
|
1832
|
+
| `substring(col, start, len?)` | `SUBSTRING(col, start, len)` | `string` |
|
|
1833
|
+
| `left(col, n)` | `LEFT(col, n)` | `string` |
|
|
1834
|
+
| `right(col, n)` | `RIGHT(col, n)` | `string` |
|
|
1835
|
+
| `repeat(col, n)` | `REPEAT(col, n)` | `string` |
|
|
1836
|
+
| `reverse(col)` | `REVERSE(col)` | `string` |
|
|
1837
|
+
| `lpad(col, len, pad)` | `LPAD(col, len, 'pad')` | `string` |
|
|
1838
|
+
| `rpad(col, len, pad)` | `RPAD(col, len, 'pad')` | `string` |
|
|
1839
|
+
| `locate(substr, col)` | `LOCATE('substr', col)` | `number` |
|
|
1840
|
+
| `space(n)` | `SPACE(n)` | `string` |
|
|
1841
|
+
| `$if(cond, trueVal, falseVal)` | `IF(cond, a, b)` | `T` |
|
|
1842
|
+
| `ifNull(col, fallback)` | `IFNULL(col, fallback)` | `NonNullable<T>` |
|
|
1843
|
+
| `greatest(...args)` | `GREATEST(a, b, ...)` | `T \| null` |
|
|
1844
|
+
| `least(...args)` | `LEAST(a, b, ...)` | `T \| null` |
|
|
1845
|
+
| `dateAdd(col, n, unit)` | `DATE_ADD(col, INTERVAL n unit)` | `Date` |
|
|
1846
|
+
| `dateSub(col, n, unit)` | `DATE_SUB(col, INTERVAL n unit)` | `Date` |
|
|
1847
|
+
| `dateFormat(col, fmt)` | `DATE_FORMAT(col, 'fmt')` | `string` |
|
|
1848
|
+
| `dateDiff(d1, d2)` | `DATEDIFF(d1, d2)` | `number` |
|
|
1849
|
+
| `quarter(col)` | `QUARTER(col)` | `number` |
|
|
1850
|
+
| `weekOfYear(col)` | `WEEKOFYEAR(col)` | `number` |
|
|
1851
|
+
| `dayName(col)` | `DAYNAME(col)` | `string` |
|
|
1852
|
+
| `lastDay(col)` | `LAST_DAY(col)` | `Date` |
|
|
1853
|
+
| `pi()` | `PI()` | `number` |
|
|
1854
|
+
| `ln(x)` | `LN(x)` | `number` |
|
|
1855
|
+
| `log(x)` | `LOG(x)` | `number` |
|
|
1856
|
+
| `log2(x)` | `LOG2(x)` | `number` |
|
|
1857
|
+
| `log10(x)` | `LOG10(x)` | `number` |
|
|
1858
|
+
| `truncate(x, n)` | `TRUNCATE(x, n)` | `number` |
|
|
1859
|
+
| `rand(seed?)` | `RAND()` / `RAND(seed)` | `number` |
|
|
1860
|
+
|
|
1861
|
+
> **Note:** MySQL `LOG(x)` is natural log (ln). `rand()` returns a float in [0, 1).
|
|
1862
|
+
|
|
1863
|
+
`unit` is one of: `'MICROSECOND' | 'SECOND' | 'MINUTE' | 'HOUR' | 'DAY' | 'WEEK' | 'MONTH' | 'QUARTER' | 'YEAR'`
|
|
1864
|
+
|
|
1865
|
+
> **Note:** `jsonArrayAgg` and `jsonObjectAgg` require MySQL 5.7.22+.
|
|
1866
|
+
|
|
1867
|
+
---
|
|
1868
|
+
|
|
1869
|
+
### PostgreSQL-specific
|
|
1870
|
+
|
|
1871
|
+
| Function | SQL | Returns |
|
|
1872
|
+
|---|---|---|
|
|
1873
|
+
| `greatest(...args)` | `GREATEST(a, b, ...)` | `T` |
|
|
1874
|
+
| `least(...args)` | `LEAST(a, b, ...)` | `T` |
|
|
1875
|
+
| `distinct(col)` | `DISTINCT col` | `ColType` (use inside `avg`, `sum`, `count`, `stringAgg`, `arrayAgg`) |
|
|
1876
|
+
| `stringAgg(col, sep)` | `STRING_AGG(col, sep)` | `string` |
|
|
1877
|
+
| `arrayAgg(col)` | `ARRAY_AGG(col)` | `unknown[]` |
|
|
1878
|
+
| `stddevPop(col)` | `STDDEV_POP(col)` | `number` |
|
|
1879
|
+
| `stddevSamp(col)` | `STDDEV_SAMP(col)` | `number` |
|
|
1880
|
+
| `varPop(col)` | `VAR_POP(col)` | `number` |
|
|
1881
|
+
| `varSamp(col)` | `VAR_SAMP(col)` | `number` |
|
|
1882
|
+
| `boolAnd(col)` | `BOOL_AND(col)` | `boolean` |
|
|
1883
|
+
| `boolOr(col)` | `BOOL_OR(col)` | `boolean` |
|
|
1884
|
+
| `jsonAgg(col)` | `JSON_AGG(col)` | `JSONValue[]` |
|
|
1885
|
+
| `bitAnd(col)` | `BIT_AND(col)` | `number` |
|
|
1886
|
+
| `bitOr(col)` | `BIT_OR(col)` | `number` |
|
|
1887
|
+
| `jsonObjectAgg(key, val)` | `JSON_OBJECT_AGG(key, val)` | `JSONValue` |
|
|
1888
|
+
| `concat(...cols)` | `CONCAT(a, b, ...)` | `string` |
|
|
1889
|
+
| `substring(col, start, len?)` | `SUBSTRING(col, start, len)` | `string` |
|
|
1890
|
+
| `left(col, n)` | `LEFT(col, n)` | `string` |
|
|
1891
|
+
| `right(col, n)` | `RIGHT(col, n)` | `string` |
|
|
1892
|
+
| `repeat(col, n)` | `REPEAT(col, n)` | `string` |
|
|
1893
|
+
| `reverse(col)` | `REVERSE(col)` | `string` |
|
|
1894
|
+
| `lpad(col, len, pad)` | `LPAD(col, len, 'pad')` | `string` |
|
|
1895
|
+
| `rpad(col, len, pad)` | `RPAD(col, len, 'pad')` | `string` |
|
|
1896
|
+
| `initcap(col)` | `INITCAP(col)` | `string` |
|
|
1897
|
+
| `strpos(col, substr)` | `STRPOS(col, 'substr')` | `number` |
|
|
1898
|
+
| `splitPart(col, delimiter, field)` | `SPLIT_PART(col, 'delimiter', field)` | `string` |
|
|
1899
|
+
| `btrim(col, chars?)` | `BTRIM(col)` / `BTRIM(col, 'chars')` | `string` |
|
|
1900
|
+
| `md5(col)` | `MD5(col)` | `string` |
|
|
1901
|
+
| `extract(field, col)` | `EXTRACT(field FROM col)` | `number` |
|
|
1902
|
+
| `dateTrunc(unit, col)` | `DATE_TRUNC('unit', col)` | `Date` |
|
|
1903
|
+
| `age(ts1, ts2?)` | `AGE(ts1)` / `AGE(ts1, ts2)` | `string` (PG `interval` mapped to string) |
|
|
1904
|
+
| `toDate(text, fmt)` | `TO_DATE(text, 'fmt')` | `Date` |
|
|
1905
|
+
| `pi()` | `PI()` | `number` |
|
|
1906
|
+
| `ln(x)` | `LN(x)` | `number` |
|
|
1907
|
+
| `log(x)` | `LOG(x)` | `number` |
|
|
1908
|
+
| `logBase(base, x)` | `LOG(base, x)` | `number` |
|
|
1909
|
+
| `trunc(x, n?)` | `TRUNC(x)` / `TRUNC(x, n)` | `number` |
|
|
1910
|
+
| `div(x, y)` | `DIV(x, y)` | `number` |
|
|
1911
|
+
| `random()` | `RANDOM()` | `number` |
|
|
1912
|
+
|
|
1913
|
+
> **Note:** PG `LOG(x)` is log base 10 (unlike MySQL where it is natural log). `random()` returns a float in [0, 1).
|
|
1914
|
+
|
|
1915
|
+
`field` for `extract` is one of: `'YEAR' | 'MONTH' | 'DAY' | 'HOUR' | 'MINUTE' | 'SECOND' | 'DOW' | 'DOY' | 'EPOCH' | 'WEEK' | 'QUARTER'`
|
|
1916
|
+
|
|
1917
|
+
`unit` for `dateTrunc` is one of: `'microseconds' | 'milliseconds' | 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year' | 'decade' | 'century' | 'millennium'`
|
|
1918
|
+
|
|
1919
|
+
---
|
|
1920
|
+
|
|
1921
|
+
### SQLite-specific
|
|
1922
|
+
|
|
1923
|
+
| Function | SQL | Returns |
|
|
1924
|
+
|---|---|---|
|
|
1925
|
+
| `iif(cond, trueVal, falseVal)` | `IIF(cond, a, b)` | `T` |
|
|
1926
|
+
| `ifNull(col, fallback)` | `IFNULL(col, fallback)` | `NonNullable<T>` |
|
|
1927
|
+
| `distinct(col)` | `DISTINCT col` | `ColType` (use inside `avg`, `sum`, `count`, `groupConcat`; sep with ≥ 3.44) |
|
|
1928
|
+
| `groupConcat(col, sep?)` | `GROUP_CONCAT(col, sep)` | `string` |
|
|
1929
|
+
| `total(col)` | `TOTAL(col)` | `number` |
|
|
1930
|
+
| `concat(...cols)` | `a \|\| b \|\| ...` | `string` |
|
|
1931
|
+
| `substr(col, start, len?)` | `SUBSTR(col, start, len)` | `string` |
|
|
1932
|
+
| `instr(col, substr)` | `INSTR(col, 'substr')` | `number` |
|
|
1933
|
+
| `char(...codes)` | `CHAR(n1, n2, ...)` | `string` |
|
|
1934
|
+
| `hex(col)` | `HEX(col)` | `string` |
|
|
1935
|
+
| `unicode(col)` | `UNICODE(col)` | `number` |
|
|
1936
|
+
| `strftime(fmt, col)` | `strftime('fmt', col)` | `string` |
|
|
1937
|
+
| `julianday(col)` | `julianday(col)` | `number` |
|
|
1938
|
+
| `date(col)` | `date(col)` | `string` |
|
|
1939
|
+
| `datetime(col)` | `datetime(col)` | `string` |
|
|
1940
|
+
| `random()` | `RANDOM()` | `number` |
|
|
1941
|
+
| `log(x)` | `LOG(x)` | `number` |
|
|
1942
|
+
| `log2(x)` | `LOG2(x)` | `number` |
|
|
1943
|
+
| `log10(x)` | `LOG10(x)` | `number` |
|
|
1944
|
+
|
|
1945
|
+
> **Note:** SQLite `random()` returns a random integer (not float). `log`, `log2`, `log10` require SQLite 3.35+. `total()` behaves like `SUM()` but returns `0.0` instead of `NULL` for empty sets. SQLite uses the `||` operator for string concatenation.
|
|
1946
|
+
|
|
1947
|
+
> **Note:** SQLite stores `DateTime` differently between Prisma v6 (integer milliseconds) and v7 (ISO 8601 text). All SQLite datetime fns automatically normalise both formats via a `CASE WHEN typeof(...) = 'integer' THEN datetime(.../1000, 'unixepoch') ELSE ... END` wrapper, so they work correctly on both versions.
|
|
1948
|
+
|
|
1949
|
+
---
|
|
1950
|
+
|
|
1951
|
+
## Security
|
|
1952
|
+
|
|
1953
|
+
All queries execute via Prisma's `$queryRawUnsafe`. The `esc()` helper escapes single quotes in string literals, but **never pass user-supplied values directly** to query builder methods — use Prisma's parameterized queries for user input.
|
|
1954
|
+
|
|
1955
|
+
`whereRaw()` in particular inserts SQL verbatim. Only use it with trusted, static SQL strings.
|
|
1956
|
+
|
|
1957
|
+
## Future updates
|
|
1958
|
+
|
|
1959
|
+
- Support specifying `JOIN` type [issue#2](https://github.com/adrianbrowning/prisma-ts-select/issues/2)
|
|
1960
|
+
- Support additional Select Functions
|
|
1961
|
+
- [JSON #9](https://github.com/adrianbrowning/prisma-ts-select/issues/9)
|
|
1962
|
+
- [CAST #71](https://github.com/adrianbrowning/prisma-ts-select/issues/71)
|
|
1963
|
+
- [whereRaw supporting Prisma.sql](https://github.com/adrianbrowning/prisma-ts-select/issues/29)
|
|
1964
|
+
|
|
1965
|
+
## Changelog / Versioning
|
|
1966
|
+
Changelog is available [here](https://github.com/adrianbrowning/prisma-ts-select/releases). We use [semantic versioning](https://semver.org/) for versioning.
|
|
1967
|
+
|
|
1968
|
+
## License
|
|
1969
|
+
This project is licensed under the MIT License. See the LICENSE file for details.
|
|
989
1970
|
|
|
990
1971
|
|