|
1 | 1 | import type { SchemaMeta } from './meta' |
2 | | -import type { DatabaseSchema } from './schema' |
| 2 | +import type { DatabaseSchema, InferTableName, ModelRecord } from './schema' |
3 | 3 | import { sql as bunSql } from 'bun' |
4 | 4 | import { config } from './config' |
5 | 5 |
|
@@ -1083,6 +1083,7 @@ export interface BaseSelectQueryBuilder< |
1083 | 1083 | */ |
1084 | 1084 | toSQL: () => string |
1085 | 1085 | execute: () => Promise<SelectedRow<DB, TTable, TSelected>[]> |
| 1086 | + executeTakeFirst: () => Promise<SelectedRow<DB, TTable, TSelected> | undefined> |
1086 | 1087 | // Laravel-style retrieval helpers |
1087 | 1088 | /** |
1088 | 1089 | * # `get` |
@@ -1248,6 +1249,7 @@ export interface UpdateQueryBuilder<DB extends DatabaseSchema<any>, TTable exten |
1248 | 1249 | * ``` |
1249 | 1250 | */ |
1250 | 1251 | execute: () => Promise<number> |
| 1252 | + executeTakeFirst?: () => Promise<{ numUpdatedRows?: number }> |
1251 | 1253 | } |
1252 | 1254 |
|
1253 | 1255 | export interface DeleteQueryBuilder<DB extends DatabaseSchema<any>, TTable extends keyof DB & string> { |
@@ -1295,6 +1297,7 @@ export interface DeleteQueryBuilder<DB extends DatabaseSchema<any>, TTable exten |
1295 | 1297 | * ``` |
1296 | 1298 | */ |
1297 | 1299 | execute: () => Promise<number> |
| 1300 | + executeTakeFirst?: () => Promise<{ numDeletedRows?: number }> |
1298 | 1301 | } |
1299 | 1302 |
|
1300 | 1303 | export interface QueryBuilder<DB extends DatabaseSchema<any>> { |
@@ -1557,6 +1560,81 @@ export interface QueryBuilder<DB extends DatabaseSchema<any>> { |
1557 | 1560 | insertGetId: <TTable extends keyof DB & string>(table: TTable, values: Partial<DB[TTable]['columns']>, idColumn?: keyof DB[TTable]['columns'] & string) => Promise<any> |
1558 | 1561 | updateOrInsert: <TTable extends keyof DB & string>(table: TTable, match: Partial<DB[TTable]['columns']>, values: Partial<DB[TTable]['columns']>) => Promise<boolean> |
1559 | 1562 | upsert: <TTable extends keyof DB & string>(table: TTable, rows: Partial<DB[TTable]['columns']>[], conflictColumns: (keyof DB[TTable]['columns'] & string)[], mergeColumns?: (keyof DB[TTable]['columns'] & string)[]) => Promise<any> |
| 1563 | + |
| 1564 | + /** |
| 1565 | + * # `create(table, values)` |
| 1566 | + * |
| 1567 | + * Inserts a row and returns the created record. |
| 1568 | + */ |
| 1569 | + create: <TTable extends keyof DB & string>( |
| 1570 | + table: TTable, |
| 1571 | + values: Partial<DB[TTable]['columns']>, |
| 1572 | + ) => Promise<DB[TTable]['columns']> |
| 1573 | + |
| 1574 | + /** |
| 1575 | + * # `createMany(table, rows)` |
| 1576 | + * |
| 1577 | + * Inserts multiple rows. Returns void. |
| 1578 | + */ |
| 1579 | + createMany: <TTable extends keyof DB & string>( |
| 1580 | + table: TTable, |
| 1581 | + rows: Partial<DB[TTable]['columns']>[], |
| 1582 | + ) => Promise<void> |
| 1583 | + |
| 1584 | + /** |
| 1585 | + * # `firstOrCreate(table, match, [defaults])` |
| 1586 | + * |
| 1587 | + * Returns the first matching row, or creates one with defaults merged and returns it. |
| 1588 | + */ |
| 1589 | + firstOrCreate: <TTable extends keyof DB & string>( |
| 1590 | + table: TTable, |
| 1591 | + match: Partial<DB[TTable]['columns']>, |
| 1592 | + defaults?: Partial<DB[TTable]['columns']>, |
| 1593 | + ) => Promise<DB[TTable]['columns']> |
| 1594 | + |
| 1595 | + /** |
| 1596 | + * # `updateOrCreate(table, match, values)` |
| 1597 | + * |
| 1598 | + * Updates the first matching row with values or creates a new one if none exists, then returns it. |
| 1599 | + */ |
| 1600 | + updateOrCreate: <TTable extends keyof DB & string>( |
| 1601 | + table: TTable, |
| 1602 | + match: Partial<DB[TTable]['columns']>, |
| 1603 | + values: Partial<DB[TTable]['columns']>, |
| 1604 | + ) => Promise<DB[TTable]['columns']> |
| 1605 | + |
| 1606 | + /** |
| 1607 | + * # `save(table, values)` |
| 1608 | + * If values contain the primary key and a row exists, updates it; otherwise creates a new row. Returns the row. |
| 1609 | + */ |
| 1610 | + save: <TTable extends keyof DB & string>( |
| 1611 | + table: TTable, |
| 1612 | + values: Partial<DB[TTable]['columns']>, |
| 1613 | + ) => Promise<DB[TTable]['columns']> |
| 1614 | + |
| 1615 | + /** |
| 1616 | + * # `remove(table, id)` |
| 1617 | + * Deletes by primary key and returns adapter's first result object. |
| 1618 | + */ |
| 1619 | + remove: <TTable extends keyof DB & string>( |
| 1620 | + table: TTable, |
| 1621 | + id: DB[TTable]['columns'][DB[TTable]['primaryKey'] & keyof DB[TTable]['columns']] | any, |
| 1622 | + ) => Promise<any> |
| 1623 | + |
| 1624 | + /** |
| 1625 | + * # `find(table, id)` |
| 1626 | + * Fetch by primary key. Returns the row or undefined. |
| 1627 | + */ |
| 1628 | + find: <TTable extends keyof DB & string>( |
| 1629 | + table: TTable, |
| 1630 | + id: DB[TTable]['columns'][DB[TTable]['primaryKey'] & keyof DB[TTable]['columns']] | any, |
| 1631 | + ) => Promise<DB[TTable]['columns'] | undefined> |
| 1632 | + |
| 1633 | + /** |
| 1634 | + * # `rawQuery(sql)` |
| 1635 | + * Execute a raw SQL string (single statement) with no parameters. |
| 1636 | + */ |
| 1637 | + rawQuery: (query: string) => Promise<any> |
1560 | 1638 | } |
1561 | 1639 |
|
1562 | 1640 | // Typed INSERT builder to expose a structured SQL literal in hovers |
@@ -2686,6 +2764,59 @@ export function createQueryBuilder<DB extends DatabaseSchema<any>>(state?: Parti |
2686 | 2764 | const built = bunSql`INSERT INTO ${bunSql(String(table))} ${bunSql(rows as any)} ON CONFLICT (${bunSql(targetCols as any)}) DO UPDATE SET ${bunSql(setCols.reduce((acc, c) => ({ ...acc, [c]: (bunSql as any)(`EXCLUDED.${c}`) }), {}))}` |
2687 | 2765 | return (built as any).execute() |
2688 | 2766 | }, |
| 2767 | + async save(table, values) { |
| 2768 | + const pk = meta?.primaryKeys[String(table)] ?? 'id' |
| 2769 | + const id = (values as any)[pk] |
| 2770 | + if (id != null) { |
| 2771 | + await (this as any).updateTable(table).set(values as any).where({ [pk]: id } as any).execute() |
| 2772 | + const row = await (this as any).selectFrom(table).find(id) |
| 2773 | + if (!row) |
| 2774 | + throw new Error('save() failed to retrieve updated row') |
| 2775 | + return row |
| 2776 | + } |
| 2777 | + return await (this as any).create(table, values) |
| 2778 | + }, |
| 2779 | + async remove(table, id) { |
| 2780 | + return await (this as any).deleteFrom(table).where({ id } as any).execute() |
| 2781 | + }, |
| 2782 | + async find(table, id) { |
| 2783 | + return await (this as any).selectFrom(table).find(id) |
| 2784 | + }, |
| 2785 | + async rawQuery(query: string) { |
| 2786 | + return await (bunSql as any).unsafe(query) |
| 2787 | + }, |
| 2788 | + async create(table, values) { |
| 2789 | + const pk = meta?.primaryKeys[String(table)] ?? 'id' |
| 2790 | + const id = await (this as any).insertGetId(table, values, pk) |
| 2791 | + const row = await (this as any).selectFrom(table).find(id) |
| 2792 | + if (!row) |
| 2793 | + throw new Error('create() failed to retrieve inserted row') |
| 2794 | + return row |
| 2795 | + }, |
| 2796 | + async createMany(table, rows) { |
| 2797 | + await (this as any).insertInto(table).values(rows).execute() |
| 2798 | + }, |
| 2799 | + async firstOrCreate(table, match, defaults) { |
| 2800 | + const existing = await (this as any).selectFrom(table).where(match as any).first() |
| 2801 | + if (existing) |
| 2802 | + return existing |
| 2803 | + return await (this as any).create(table, { ...(match as any), ...(defaults as any) }) |
| 2804 | + }, |
| 2805 | + async updateOrCreate(table, match, values) { |
| 2806 | + const existing = await (this as any).selectFrom(table).where(match as any).first() |
| 2807 | + if (existing) { |
| 2808 | + await (this as any).updateTable(table).set(values as any).where(match as any).execute() |
| 2809 | + const pk = meta?.primaryKeys[String(table)] ?? 'id' |
| 2810 | + const id = (existing as any)[pk] |
| 2811 | + const refreshed = id != null |
| 2812 | + ? await (this as any).selectFrom(table).find(id) |
| 2813 | + : await (this as any).selectFrom(table).where(match as any).first() |
| 2814 | + if (!refreshed) |
| 2815 | + throw new Error('updateOrCreate() failed to retrieve updated row') |
| 2816 | + return refreshed |
| 2817 | + } |
| 2818 | + return await (this as any).create(table, { ...(match as any), ...(values as any) }) |
| 2819 | + }, |
2689 | 2820 | async count(table, column) { |
2690 | 2821 | const col = column ? bunSql(String(column)) : bunSql`*` |
2691 | 2822 | const q = bunSql`SELECT COUNT(${col}) as c FROM ${bunSql(String(table))}` |
|
0 commit comments