| 232 | } |
| 233 | |
| 234 | async function begin(options, fn) { |
| 235 | !fn && (fn = options, options = '') |
| 236 | const queries = Queue() |
| 237 | let savepoints = 0 |
| 238 | , connection |
| 239 | , prepare = null |
| 240 | |
| 241 | try { |
| 242 | await sql.unsafe('begin ' + options.replace(/[^a-z ]/ig, ''), [], { onexecute }).execute() |
| 243 | return await Promise.race([ |
| 244 | scope(connection, fn), |
| 245 | new Promise((_, reject) => connection.onclose = reject) |
| 246 | ]) |
| 247 | } catch (error) { |
| 248 | throw error |
| 249 | } |
| 250 | |
| 251 | async function scope(c, fn, name) { |
| 252 | const sql = Sql(handler) |
| 253 | sql.savepoint = savepoint |
| 254 | sql.prepare = x => prepare = x.replace(/[^a-z0-9$-_. ]/gi) |
| 255 | let uncaughtError |
| 256 | , result |
| 257 | |
| 258 | name && await sql`savepoint ${ sql(name) }` |
| 259 | try { |
| 260 | result = await new Promise((resolve, reject) => { |
| 261 | const x = fn(sql) |
| 262 | Promise.resolve(Array.isArray(x) ? Promise.all(x) : x).then(resolve, reject) |
| 263 | }) |
| 264 | |
| 265 | if (uncaughtError) |
| 266 | throw uncaughtError |
| 267 | } catch (e) { |
| 268 | await (name |
| 269 | ? sql`rollback to ${ sql(name) }` |
| 270 | : sql`rollback` |
| 271 | ) |
| 272 | throw e instanceof PostgresError && e.code === '25P02' && uncaughtError || e |
| 273 | } |
| 274 | |
| 275 | if (!name) { |
| 276 | prepare |
| 277 | ? await sql`prepare transaction '${ sql.unsafe(prepare) }'` |
| 278 | : await sql`commit` |
| 279 | } |
| 280 | |
| 281 | return result |
| 282 | |
| 283 | function savepoint(name, fn) { |
| 284 | if (name && Array.isArray(name.raw)) |
| 285 | return savepoint(sql => sql.apply(sql, arguments)) |
| 286 | |
| 287 | arguments.length === 1 && (fn = name, name = null) |
| 288 | return scope(c, fn, 's' + savepoints++ + (name ? '_' + name : '')) |
| 289 | } |
| 290 | |
| 291 | function handler(q) { |