Skip to content

create beginPrepared function #628

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jul 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ jobs:
sudo apt-get -y install "postgresql-${{ matrix.postgres }}"
sudo cp ./tests/pg_hba.conf /etc/postgresql/${{ matrix.postgres }}/main/pg_hba.conf
sudo sed -i 's/.*wal_level.*/wal_level = logical/' /etc/postgresql/${{ matrix.postgres }}/main/postgresql.conf
sudo sed -i 's/.*max_prepared_transactions.*/max_prepared_transactions = 100/' /etc/postgresql/${{ matrix.postgres }}/main/postgresql.conf
sudo sed -i 's/.*ssl = .*/ssl = on/' /etc/postgresql/${{ matrix.postgres }}/main/postgresql.conf
openssl req -new -x509 -nodes -days 365 -text -subj "/CN=localhost" -extensions v3_req -config <(cat /etc/ssl/openssl.cnf <(printf "\n[v3_req]\nbasicConstraints=critical,CA:TRUE\nkeyUsage=nonRepudiation,digitalSignature,keyEncipherment\nsubjectAltName=DNS:localhost")) -keyout server.key -out server.crt
sudo cp server.key /etc/postgresql/${{ matrix.postgres }}/main/server.key
Expand Down
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,26 @@ sql.begin('read write', async sql => {
})
```


#### PREPARE `await sql.prepare([name]) -> fn()`

Indicates that the transactions should be prepared using the `PREPARED TRANASCTION [NAME]` statement
instead of being committed.

```js
sql.begin('read write', async sql => {
const [user] = await sql`
insert into users (
name
) values (
'Murray'
)
`

await sql.prepare('tx1')
})
```

Do note that you can often achieve the same result using [`WITH` queries (Common Table Expressions)](https://www.postgresql.org/docs/current/queries-with.html) instead of using transactions.

## Data Transformation
Expand Down
12 changes: 11 additions & 1 deletion cjs/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ function Postgres(a, b) {
const queries = Queue()
let savepoints = 0
, connection
let transactionId = null

try {
await sql.unsafe('begin ' + options.replace(/[^a-z ]/ig, ''), [], { onexecute }).execute()
Expand All @@ -246,6 +247,7 @@ function Postgres(a, b) {
async function scope(c, fn, name) {
const sql = Sql(handler)
sql.savepoint = savepoint
sql.prepare = prepare
let uncaughtError
, result

Expand All @@ -266,7 +268,11 @@ function Postgres(a, b) {
throw e instanceof PostgresError && e.code === '25P02' && uncaughtError || e
}

!name && await sql`commit`
if (transactionId) {
!name && await sql.unsafe(`prepare transaction '${transactionId}'`)
}else{
!name && await sql`commit`
}
return result

function savepoint(name, fn) {
Expand All @@ -285,6 +291,9 @@ function Postgres(a, b) {
}
}

async function prepare(name) {
transactionId = name
}
function onexecute(c) {
connection = c
move(c, reserved)
Expand All @@ -294,6 +303,7 @@ function Postgres(a, b) {
}
}


function move(c, queue) {
c.queue.remove(c)
queue.push(c)
Expand Down
13 changes: 13 additions & 0 deletions cjs/tests/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,19 @@ t('Savepoint returns Result', async() => {
return [1, result[0].x]
})

t('Prepared transaction', async() => {
await sql`create table test (a int)`

await sql.begin(async sql => {
await sql`insert into test values(1)`
await sql.prepare('tx1')
})

await sql.unsafe("commit prepared 'tx1'")

return ['1', (await sql`select count(1) from test`)[0].count, await sql`drop table test`]
})

t('Transaction requests are executed implicitly', async() => {
const sql = postgres({ debug: true, idle_timeout: 1, fetch_types: false })
return [
Expand Down
20 changes: 20 additions & 0 deletions deno/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,26 @@ sql.begin('read write', async sql => {
})
```


#### PREPARE `await sql.prepare([name]) -> fn()`

Indicates that the transactions should be prepared using the `PREPARED TRANASCTION [NAME]` statement
instead of being committed.

```js
sql.begin('read write', async sql => {
const [user] = await sql`
insert into users (
name
) values (
'Murray'
)
`

await sql.prepare('tx1')
})
```

Do note that you can often achieve the same result using [`WITH` queries (Common Table Expressions)](https://www.postgresql.org/docs/current/queries-with.html) instead of using transactions.

## Data Transformation
Expand Down
12 changes: 11 additions & 1 deletion deno/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ function Postgres(a, b) {
const queries = Queue()
let savepoints = 0
, connection
let transactionId = null

try {
await sql.unsafe('begin ' + options.replace(/[^a-z ]/ig, ''), [], { onexecute }).execute()
Expand All @@ -247,6 +248,7 @@ function Postgres(a, b) {
async function scope(c, fn, name) {
const sql = Sql(handler)
sql.savepoint = savepoint
sql.prepare = prepare
let uncaughtError
, result

Expand All @@ -267,7 +269,11 @@ function Postgres(a, b) {
throw e instanceof PostgresError && e.code === '25P02' && uncaughtError || e
}

!name && await sql`commit`
if (transactionId) {
!name && await sql.unsafe(`prepare transaction '${transactionId}'`)
}else{
!name && await sql`commit`
}
return result

function savepoint(name, fn) {
Expand All @@ -286,6 +292,9 @@ function Postgres(a, b) {
}
}

async function prepare(name) {
transactionId = name
}
function onexecute(c) {
connection = c
move(c, reserved)
Expand All @@ -295,6 +304,7 @@ function Postgres(a, b) {
}
}


function move(c, queue) {
c.queue.remove(c)
queue.push(c)
Expand Down
13 changes: 13 additions & 0 deletions deno/tests/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,19 @@ t('Savepoint returns Result', async() => {
return [1, result[0].x]
})

t('Prepared transaction', async() => {
await sql`create table test (a int)`

await sql.begin(async sql => {
await sql`insert into test values(1)`
await sql.prepare('tx1')
})

await sql.unsafe("commit prepared 'tx1'")

return ['1', (await sql`select count(1) from test`)[0].count, await sql`drop table test`]
})

t('Transaction requests are executed implicitly', async() => {
const sql = postgres({ debug: true, idle_timeout: 1, fetch_types: false })
return [
Expand Down
2 changes: 2 additions & 0 deletions deno/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,8 @@ declare namespace postgres {
interface TransactionSql<TTypes extends Record<string, unknown> = {}> extends Sql<TTypes> {
savepoint<T>(cb: (sql: TransactionSql<TTypes>) => T | Promise<T>): Promise<UnwrapPromiseArray<T>>;
savepoint<T>(name: string, cb: (sql: TransactionSql<TTypes>) => T | Promise<T>): Promise<UnwrapPromiseArray<T>>;

prepare<T>(name: string): Promise<UnwrapPromiseArray<T>>;
}
}

Expand Down
12 changes: 11 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ function Postgres(a, b) {
const queries = Queue()
let savepoints = 0
, connection
let transactionId = null

try {
await sql.unsafe('begin ' + options.replace(/[^a-z ]/ig, ''), [], { onexecute }).execute()
Expand All @@ -246,6 +247,7 @@ function Postgres(a, b) {
async function scope(c, fn, name) {
const sql = Sql(handler)
sql.savepoint = savepoint
sql.prepare = prepare
let uncaughtError
, result

Expand All @@ -266,7 +268,11 @@ function Postgres(a, b) {
throw e instanceof PostgresError && e.code === '25P02' && uncaughtError || e
}

!name && await sql`commit`
if (transactionId) {
!name && await sql.unsafe(`prepare transaction '${transactionId}'`)
}else{
!name && await sql`commit`
}
return result

function savepoint(name, fn) {
Expand All @@ -285,6 +291,9 @@ function Postgres(a, b) {
}
}

async function prepare(name) {
transactionId = name
}
function onexecute(c) {
connection = c
move(c, reserved)
Expand All @@ -294,6 +303,7 @@ function Postgres(a, b) {
}
}


function move(c, queue) {
c.queue.remove(c)
queue.push(c)
Expand Down
1 change: 0 additions & 1 deletion tests/bootstrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ exec('createdb', ['postgres_js_test'])
exec('psql', ['-c', 'grant all on database postgres_js_test to postgres_js_test'])
exec('psql', ['-c', 'alter database postgres_js_test owner to postgres_js_test'])


export function exec(cmd, args) {
const { stderr } = spawnSync(cmd, args, { stdio: 'pipe', encoding: 'utf8' })
if (stderr && !stderr.includes('already exists') && !stderr.includes('does not exist'))
Expand Down
13 changes: 13 additions & 0 deletions tests/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,19 @@ t('Savepoint returns Result', async() => {
return [1, result[0].x]
})

t('Prepared transaction', async() => {
await sql`create table test (a int)`

await sql.begin(async sql => {
await sql`insert into test values(1)`
await sql.prepare('tx1')
})

await sql.unsafe("commit prepared 'tx1'")

return ['1', (await sql`select count(1) from test`)[0].count, await sql`drop table test`]
})

t('Transaction requests are executed implicitly', async() => {
const sql = postgres({ debug: true, idle_timeout: 1, fetch_types: false })
return [
Expand Down
2 changes: 2 additions & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,8 @@ declare namespace postgres {
interface TransactionSql<TTypes extends Record<string, unknown> = {}> extends Sql<TTypes> {
savepoint<T>(cb: (sql: TransactionSql<TTypes>) => T | Promise<T>): Promise<UnwrapPromiseArray<T>>;
savepoint<T>(name: string, cb: (sql: TransactionSql<TTypes>) => T | Promise<T>): Promise<UnwrapPromiseArray<T>>;

prepare<T>(name: string): Promise<UnwrapPromiseArray<T>>;
}
}

Expand Down