ReviewOS

also looking at this

stacks/bun-query-builder

fix(client): returningAll() is returning('*'), so it works on MySQL

#1160
Merged glennmichael123 wants to merge fix/returning-all-mysql into main
2 files +189 -66
packages/bun-query-builder/src/client.tsmodified+36-66
Changes to packages/bun-query-builder/src/client.ts
@@ -7562,34 +7562,22 @@ export function createQueryBuilder<DB extends AnyDatabaseSchema>(state?: Partial
75627562 return result
75637563 },
75647564 returningAll() {
7565 // As in returning(): no rows inserted, nothing to return.
7566 if (noRows)
7567 return emptyReturningBuilder()
7568 const returningSql = `${sqlText} RETURNING *`
7569 const q = prepareQuery(returningSql, params)
7570 const runFirst = async () => {
7571 const result = await runWithHooks<any[]>(q, 'insert')
7572 return Array.isArray(result) ? result[0] : result
7573 }
7574 return {
7575 toSQL: () => makeExecutableQuery(q, returningSql) as any,
7576 execute: () => runWithHooks<any[]>(q, 'insert'),
7577 get: () => runWithHooks<any[]>(q, 'insert'),
7578 first: runFirst,
7579 executeTakeFirst: runFirst,
7580 async firstOrFail() {
7581 const row = await runFirst()
7582 if (!row)
7583 throw new Error('Insert with RETURNING returned no rows')
7584 return row
7585 },
7586 async executeTakeFirstOrThrow() {
7587 const row = await runFirst()
7588 if (!row)
7589 throw new Error('Insert with RETURNING returned no rows')
7590 return row
7591 },
7592 } as any
7565 /*
7566 * `RETURNING *` is `returning('*')`, so this defers to it.
7567 *
7568 * It used to append the clause itself, a second implementation of
7569 * the same idea, and the two drifted: every fix returning() gained
7570 * stopped here. Most of all the MySQL branch, so `returningAll()`
7571 * emitted `RETURNING *` at a server that has no RETURNING and every
7572 * such write was a syntax error rather than a write - 63 call sites
7573 * in Stacks alone (stacksjs/stacks#2637). It also never reached the
7574 * empty-batch guard, and its handle was missing row accessors the
7575 * return type declares.
7576 *
7577 * mysqlReturningBuilder already reads `*` as "every column", so the
7578 * MySQL read-back needs nothing further.
7579 */
7580 return (this as any).returning('*')
75937581 },
75947582 } as any as TypedInsertQueryBuilder<DB, TTable>
75957583 },
@@ -7909,26 +7897,17 @@ export function createQueryBuilder<DB extends AnyDatabaseSchema>(state?: Partial
79097897 return { numUpdatedRows: result }
79107898 },
79117899 returningAll() {
7912 // Deferred for the same reason as returning(): holding this handle
7913 // while the parent gains a predicate must not execute the statement
7914 // as it stood beforehand. See #1110.
7915 const retAllText = () => `${sqlText} RETURNING *`
7916 const build = () => (params.length > 0 ? prepareQuery(retAllText(), params) : prepareQuery(retAllText()))
7917 return {
7918 toSQL: () => makeExecutableQuery(build(), retAllText()) as any,
7919 execute: () => runWithHooks<any[]>(build(), 'update'),
7920 async executeTakeFirst() {
7921 const result = await runWithHooks<any[]>(build(), 'update')
7922 return Array.isArray(result) ? result[0] : result
7923 },
7924 async executeTakeFirstOrThrow() {
7925 const result = await runWithHooks<any[]>(build(), 'update')
7926 const first = Array.isArray(result) ? result[0] : result
7927 if (!first)
7928 throw new Error('Update with RETURNING failed')
7929 return first
7930 },
7931 } as any
7900 /*
7901 * `returning('*')`, for the reasons given on insertInto's.
7902 *
7903 * The duplicate here missed the MySQL branch, which reads the rows
7904 * by the ids the predicate matched *before* the write, so on MySQL
7905 * this emitted `UPDATE ... RETURNING *` and wrote nothing at all
7906 * (stacksjs/stacks#2637). It also could not take a predicate added
7907 * after it - the #1110 shape - because the handle it returned had no
7908 * where() to delegate.
7909 */
7910 return (this as any).returning('*')
79327911 },
79337912 }
79347913 },
@@ -8183,24 +8162,15 @@ export function createQueryBuilder<DB extends AnyDatabaseSchema>(state?: Partial
81838162 return { numDeletedRows: result }
81848163 },
81858164 returningAll() {
8186 // Deferred, as returning() is — see #1110.
8187 const retAllText = () => `${sqlText} RETURNING *`
8188 const build = () => (delParams.length > 0 ? prepareQuery(retAllText(), delParams) : prepareQuery(retAllText()))
8189 return {
8190 toSQL: () => makeExecutableQuery(build(), retAllText()) as any,
8191 execute: () => runWithHooks<any[]>(build(), 'delete'),
8192 async executeTakeFirst() {
8193 const result = await runWithHooks<any[]>(build(), 'delete')
8194 return Array.isArray(result) ? result[0] : result
8195 },
8196 async executeTakeFirstOrThrow() {
8197 const result = await runWithHooks<any[]>(build(), 'delete')
8198 const first = Array.isArray(result) ? result[0] : result
8199 if (!first)
8200 throw new Error('Delete with RETURNING failed')
8201 return first
8202 },
8203 } as any
8165 /*
8166 * `returning('*')`, for the reasons given on insertInto's.
8167 *
8168 * Besides emitting `RETURNING *` at MySQL (stacksjs/stacks#2637),
8169 * the duplicate here skipped beforeDelete/afterDelete entirely, so
8170 * `.returningAll()` walked straight past an application delete
8171 * guard - the same defect returning() was fixed for.
8172 */
8173 return (this as any).returning('*')
82048174 },
82058175 }
82068176 },