Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,249 @@
|
||||
module orm
|
||||
|
||||
const tx_callback_closed_err = 'transaction callback must not close the transaction explicitly'
|
||||
|
||||
enum TxKind {
|
||||
root
|
||||
savepoint
|
||||
}
|
||||
|
||||
@[heap]
|
||||
struct TxRootState {
|
||||
mut:
|
||||
next_savepoint_id int = 1
|
||||
active bool = true
|
||||
}
|
||||
|
||||
@[heap]
|
||||
struct TxInner {
|
||||
mut:
|
||||
conn TransactionalConnection
|
||||
kind TxKind
|
||||
active bool = true
|
||||
savepoint_name string
|
||||
root_state &TxRootState = unsafe { nil }
|
||||
}
|
||||
|
||||
// Tx is an ORM transaction handle that can be used anywhere an orm.Connection is accepted.
|
||||
pub struct Tx {
|
||||
mut:
|
||||
inner &TxInner = unsafe { nil }
|
||||
}
|
||||
|
||||
@[heap]
|
||||
struct SavepointInner {
|
||||
mut:
|
||||
conn TransactionalConnection
|
||||
owner &TxInner = unsafe { nil }
|
||||
name string
|
||||
active bool = true
|
||||
}
|
||||
|
||||
// Savepoint is a manual savepoint handle created from an active transaction.
|
||||
pub struct Savepoint {
|
||||
mut:
|
||||
inner &SavepointInner = unsafe { nil }
|
||||
}
|
||||
|
||||
// begin starts a new ORM transaction on the provided connection.
|
||||
pub fn begin(mut conn TransactionalConnection) !Tx {
|
||||
conn.orm_begin()!
|
||||
return Tx{
|
||||
inner: &TxInner{
|
||||
conn: conn
|
||||
kind: .root
|
||||
root_state: &TxRootState{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// transaction runs a callback inside a transaction and commits or rolls back automatically.
|
||||
pub fn transaction[T](mut conn TransactionalConnection, f fn (mut Tx) !T) !T {
|
||||
mut tx := begin(mut conn)!
|
||||
result := f(mut tx) or {
|
||||
callback_err := err
|
||||
if !tx.is_active() {
|
||||
return error(tx_callback_closed_err)
|
||||
}
|
||||
tx.rollback() or {
|
||||
return error('transaction callback failed: ${callback_err.msg()}; rollback failed: ${err.msg()}')
|
||||
}
|
||||
return callback_err
|
||||
}
|
||||
if !tx.is_active() {
|
||||
return error(tx_callback_closed_err)
|
||||
}
|
||||
tx.commit()!
|
||||
return result
|
||||
}
|
||||
|
||||
// commit commits an active transaction.
|
||||
pub fn (mut tx Tx) commit() ! {
|
||||
tx.ensure_active('commit')!
|
||||
match tx.inner.kind {
|
||||
.root {
|
||||
tx.inner.conn.orm_commit()!
|
||||
tx.inner.root_state.active = false
|
||||
}
|
||||
.savepoint {
|
||||
tx.inner.conn.orm_release_savepoint(tx.inner.savepoint_name)!
|
||||
}
|
||||
}
|
||||
|
||||
tx.inner.active = false
|
||||
}
|
||||
|
||||
// rollback rolls back an active transaction.
|
||||
pub fn (mut tx Tx) rollback() ! {
|
||||
tx.ensure_active('rollback')!
|
||||
match tx.inner.kind {
|
||||
.root {
|
||||
tx.inner.conn.orm_rollback()!
|
||||
tx.inner.root_state.active = false
|
||||
}
|
||||
.savepoint {
|
||||
tx.inner.conn.orm_rollback_to(tx.inner.savepoint_name)!
|
||||
tx.inner.conn.orm_release_savepoint(tx.inner.savepoint_name)!
|
||||
}
|
||||
}
|
||||
|
||||
tx.inner.active = false
|
||||
}
|
||||
|
||||
// savepoint creates a manual savepoint inside an active transaction.
|
||||
pub fn (mut tx Tx) savepoint() !Savepoint {
|
||||
tx.ensure_active('create a savepoint')!
|
||||
name := tx.next_savepoint_name()!
|
||||
tx.inner.conn.orm_savepoint(name)!
|
||||
return Savepoint{
|
||||
inner: &SavepointInner{
|
||||
conn: tx.inner.conn
|
||||
owner: tx.inner
|
||||
name: name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// transaction runs a nested transaction backed by a savepoint.
|
||||
pub fn (mut tx Tx) transaction[T](f fn (mut Tx) !T) !T {
|
||||
tx.ensure_active('start a nested transaction')!
|
||||
name := tx.next_savepoint_name()!
|
||||
tx.inner.conn.orm_savepoint(name)!
|
||||
mut nested := Tx{
|
||||
inner: &TxInner{
|
||||
conn: tx.inner.conn
|
||||
kind: .savepoint
|
||||
savepoint_name: name
|
||||
root_state: tx.inner.root_state
|
||||
}
|
||||
}
|
||||
result := f(mut nested) or {
|
||||
callback_err := err
|
||||
if !nested.is_active() {
|
||||
return error(tx_callback_closed_err)
|
||||
}
|
||||
nested.rollback() or {
|
||||
return error('transaction callback failed: ${callback_err.msg()}; rollback failed: ${err.msg()}')
|
||||
}
|
||||
return callback_err
|
||||
}
|
||||
if !nested.is_active() {
|
||||
return error(tx_callback_closed_err)
|
||||
}
|
||||
nested.commit()!
|
||||
return result
|
||||
}
|
||||
|
||||
// rollback rolls back to the savepoint and releases it.
|
||||
pub fn (mut sp Savepoint) rollback() ! {
|
||||
sp.ensure_active('rollback')!
|
||||
sp.inner.conn.orm_rollback_to(sp.inner.name)!
|
||||
sp.inner.conn.orm_release_savepoint(sp.inner.name)!
|
||||
sp.inner.active = false
|
||||
}
|
||||
|
||||
// release releases the savepoint without rolling back.
|
||||
pub fn (mut sp Savepoint) release() ! {
|
||||
sp.ensure_active('release')!
|
||||
sp.inner.conn.orm_release_savepoint(sp.inner.name)!
|
||||
sp.inner.active = false
|
||||
}
|
||||
|
||||
fn (tx Tx) is_active() bool {
|
||||
return !isnil(tx.inner) && !isnil(tx.inner.root_state) && tx.inner.active
|
||||
&& tx.inner.root_state.active
|
||||
}
|
||||
|
||||
fn (tx Tx) ensure_active(action string) ! {
|
||||
if isnil(tx.inner) {
|
||||
return error('transaction is not initialized')
|
||||
}
|
||||
if !tx.inner.active {
|
||||
return error('transaction is inactive; cannot ${action}')
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut tx Tx) next_savepoint_name() !string {
|
||||
if isnil(tx.inner) || isnil(tx.inner.root_state) {
|
||||
return error('transaction is not initialized')
|
||||
}
|
||||
name := 'v_orm_sp_${tx.inner.root_state.next_savepoint_id}'
|
||||
tx.inner.root_state.next_savepoint_id++
|
||||
return name
|
||||
}
|
||||
|
||||
fn (sp Savepoint) ensure_active(action string) ! {
|
||||
if isnil(sp.inner) {
|
||||
return error('savepoint is not initialized')
|
||||
}
|
||||
owner_tx := Tx{
|
||||
inner: sp.inner.owner
|
||||
}
|
||||
if !sp.inner.active || isnil(sp.inner.owner) || !owner_tx.is_active() {
|
||||
return error('savepoint is inactive; cannot ${action}')
|
||||
}
|
||||
}
|
||||
|
||||
// select forwards ORM select queries through the active transaction.
|
||||
pub fn (mut tx Tx) select(config SelectConfig, data QueryData, where QueryData) ![][]Primitive {
|
||||
tx.ensure_active('use the transaction')!
|
||||
return tx.inner.conn.select(config, data, where)
|
||||
}
|
||||
|
||||
// insert forwards ORM insert queries through the active transaction.
|
||||
pub fn (mut tx Tx) insert(table Table, data QueryData) ! {
|
||||
tx.ensure_active('use the transaction')!
|
||||
tx.inner.conn.insert(table, data)!
|
||||
}
|
||||
|
||||
// update forwards ORM update queries through the active transaction.
|
||||
pub fn (mut tx Tx) update(table Table, data QueryData, where QueryData) ! {
|
||||
tx.ensure_active('use the transaction')!
|
||||
tx.inner.conn.update(table, data, where)!
|
||||
}
|
||||
|
||||
// delete forwards ORM delete queries through the active transaction.
|
||||
pub fn (mut tx Tx) delete(table Table, where QueryData) ! {
|
||||
tx.ensure_active('use the transaction')!
|
||||
tx.inner.conn.delete(table, where)!
|
||||
}
|
||||
|
||||
// create forwards ORM create queries through the active transaction.
|
||||
pub fn (mut tx Tx) create(table Table, fields []TableField) ! {
|
||||
tx.ensure_active('use the transaction')!
|
||||
tx.inner.conn.create(table, fields)!
|
||||
}
|
||||
|
||||
// drop forwards ORM drop queries through the active transaction.
|
||||
pub fn (mut tx Tx) drop(table Table) ! {
|
||||
tx.ensure_active('use the transaction')!
|
||||
tx.inner.conn.drop(table)!
|
||||
}
|
||||
|
||||
// last_id forwards the last inserted id through the wrapped connection.
|
||||
pub fn (mut tx Tx) last_id() int {
|
||||
if isnil(tx.inner) {
|
||||
return 0
|
||||
}
|
||||
return tx.inner.conn.last_id()
|
||||
}
|
||||
Reference in new issue
Block a user