Skip to content
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

Transaction improvements #203

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
12 changes: 5 additions & 7 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
language: crystal
script:
- crystal spec
- crystal spec -D $DB_TYPE
- crystal tool format --check
services:
- mysql
Expand All @@ -18,11 +18,9 @@ before_script:
- psql -c 'create database crecto_test;' -U postgres
- psql $PG_URL < spec/migrations/pg_migrations.sql
- sqlite3 ./crecto_test.db < spec/migrations/sqlite3_migrations.sql
- if [ ! -z "$PG_URL" ]; then cp ./spec/travis_pg_repo.cr ./spec/repo.cr; fi
- if [ ! -z "$MYSQL_URL" ]; then cp ./spec/travis_mysql_repo.cr ./spec/repo.cr; fi
- if [ ! -z "$SQLITE3_PATH" ]; then cp ./spec/travis_sqlite_repo.cr ./spec/repo.cr; fi
- cp ./spec/travis_${DB_TYPE}_repo.cr ./spec/repo.cr
env:
matrix:
- PG_URL=postgres://postgres@localhost:5432/crecto_test
- MYSQL_URL=mysql://root@localhost/crecto_test
- SQLITE3_PATH=sqlite3://./crecto_test.db
- PG_URL=postgres://postgres@localhost:5432/crecto_test DB_TYPE=pg
- MYSQL_URL=mysql://root@localhost/crecto_test DB_TYPE=mysql
- SQLITE3_PATH=sqlite3://./crecto_test.db DB_TYPE=sqlite
90 changes: 72 additions & 18 deletions spec/transactions_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,8 @@ describe Crecto do
user = User.new

expect_raises Crecto::InvalidChangeset do
Repo.transaction! do
Repo.insert!(user)
Repo.transaction! do |tx|
tx.insert!(user)
end
end
end
Expand All @@ -193,8 +193,8 @@ describe Crecto do
user = User.new
user.name = "this should insert in the transaction"

Repo.transaction! do
Repo.insert(user)
Repo.transaction! do |tx|
tx.insert(user)
end

users = Repo.all(User, Query.where(name: "this should insert in the transaction"))
Expand All @@ -207,8 +207,8 @@ describe Crecto do

user = quick_create_user("this should delete")

Repo.transaction! do
Repo.delete!(user)
Repo.transaction! do |tx|
tx.delete!(user)
end

users = Repo.all(User, Query.where(id: user.id))
Expand All @@ -222,8 +222,8 @@ describe Crecto do

Repo.delete_all(Post)

Repo.transaction! do
Repo.delete_all(User)
Repo.transaction! do |tx|
tx.delete_all(User)
end

users = Repo.all(User)
Expand All @@ -235,8 +235,8 @@ describe Crecto do

user.name = "this should have changed 89ffsf"

Repo.transaction! do
Repo.update(user)
Repo.transaction! do |tx|
tx.update(user)
end

user = Repo.get!(User, user.id)
Expand All @@ -248,8 +248,8 @@ describe Crecto do
quick_create_user_with_things("testing_update_all", 123)
quick_create_user_with_things("testing_update_all", 123)

Repo.transaction! do
Repo.update_all(User, Query.where(name: "testing_update_all"), {things: 9494})
Repo.transaction! do |tx|
tx.update_all(User, Query.where(name: "testing_update_all"), {things: 9494})
end

Repo.all(User, Query.where(things: 123)).size.should eq 0
Expand All @@ -268,12 +268,12 @@ describe Crecto do
insert_user = User.new
insert_user.name = "all_transactions_insert_user"

Repo.transaction! do
Repo.insert!(insert_user)
Repo.delete!(delete_user)
Repo.delete_all(Post)
Repo.update!(update_user)
Repo.update_all(User, Query.where(name: "perform_all"), {name: "perform_all_io2oj999"})
Repo.transaction! do |tx|
tx.insert!(insert_user)
tx.delete!(delete_user)
tx.delete_all(Post)
tx.update!(update_user)
tx.update_all(User, Query.where(name: "perform_all"), {name: "perform_all_io2oj999"})
end

# check insert happened
Expand Down Expand Up @@ -336,6 +336,60 @@ describe Crecto do
Repo.all(User, Query.where(name: "perform_all")).size.should eq 2
Repo.all(User, Query.where(name: "perform_all_io2oj999")).size.should eq 0
end

# This only works for postgres for now
{% begin %}
{{ flag?(:pg) ? :it.id : :pending.id }} "allows reading records inserted inside the transaction" do
insert_user = User.new
insert_user.name = "insert_user"

Repo.transaction! do |tx|
id = tx.insert!(insert_user).instance.id
tx.get(User, id).should_not eq(nil)
tx.get!(User, id).should_not eq(nil)
tx.get(User, id, Query.new).should_not eq(nil)
tx.get!(User, id, Query.new).should_not eq(nil)
tx.get_by(User, id: id).should_not eq(nil)
tx.get_by!(User, id: id).should_not eq(nil)
tx.get_by(User, id: id).should_not eq(nil)
tx.get_by!(User, id: id).should_not eq(nil)
tx.get_by(User, Query.where(id: id)).should_not eq(nil)
tx.get_by!(User, Query.where(id: id)).should_not eq(nil)
tx.all(User, Query.where(id: id)).first.should_not eq(nil)
tx.all(User, Query.where(id: id), preload: [] of Symbol).first.should_not eq(nil)
end
end
{% end %}

# Sqlite doesn't support nesting transactions
{% unless flag?(:sqlite) %}
it "allows nesting transactions" do
Repo.delete_all(Post)
Repo.delete_all(User)

insert_user = User.new
insert_user.name = "nested_transactions_insert_user"
invalid_user = User.new
delete_user = quick_create_user("nested_transactions_delete_user")

Repo.transaction! do |tx|
tx.insert!(insert_user)

expect_raises Crecto::InvalidChangeset do
Repo.transaction! do |inner_tx|
inner_tx.delete!(delete_user)
inner_tx.insert!(invalid_user)
end
end
end

# check insert happened
Repo.all(User, Query.where(name: "nested_transactions_insert_user")).size.should eq 1

# check delete didn't happen
Repo.all(User, Query.where(name: "nested_transactions_delete_user")).size.should eq 1
end
{% end %}
end
end
end
98 changes: 94 additions & 4 deletions src/crecto/live_transaction.cr
Original file line number Diff line number Diff line change
@@ -1,13 +1,89 @@
require "./multi"
require "./repo/query"

module Crecto
class LiveTransaction(T)
alias Query = Repo::Query

def initialize(@tx : DB::Transaction, @repo : T)
end

def raw_exec(args : Array)
@repo.raw_exec(args, tx: @tx)
end

def raw_exec(*args)
@repo.raw_exec(*args, tx: @tx)
end

def raw_query(query, *args)
@repo.raw_query(query, *args, tx: @tx) do |rs|
yield rs
end
end

def raw_query(query, args : Array)
@repo.raw_query(query, args, tx: @tx)
end

def raw_query(query, *args)
@repo.raw_query(query, *args)
end

def raw_scalar(*args)
@repo.raw_scalar(*args, tx: @tx)
end

def all(queryable, query : Query, *, preload = [] of Symbol)
@repo.all(queryable, query, tx: @tx, preload: preload)
end

def all(queryable, query = Query.new)
@repo.all(queryable, query, tx: @tx)
end

def get(queryable, id)
@repo.get(queryable, id, tx: @tx)
end

def get!(queryable, id)
@repo.get!(queryable, id, tx: @tx)
end

def get(queryable, id, query : Query)
@repo.get(queryable, id, query, tx: @tx)
end

def get!(queryable, id, query : Query)
@repo.get!(queryable, id, query, tx: @tx)
end

def get_by(queryable, **opts)
@repo.get_by(queryable, @tx, **opts)
end

def get_by(queryable, query)
@repo.get_by(queryable, query, tx: @tx)
end

def get_by!(queryable, **opts)
@repo.get_by!(queryable, @tx, **opts)
end

def get_by!(queryable, query)
@repo.get_by!(queryable, query, tx: @tx)
end

def get_association(queryable_instance, association_name : Symbol)
@repo.get_association(queryable_instance, association_name, tx: @tx)
end

def get_association!(queryable_instance, association_name : Symbol)
@repo.get_association!(queryable_instance, association_name, tx: @tx)
end

{% for type in %w[insert insert! delete delete! update update!] %}
def {{type.id}}(queryable : Crecto::Model)
@repo.{{type.id}}(queryable, @tx)
@repo.{{type.id}}(queryable, tx: @tx)
end

def {{type.id}}(changeset : Crecto::Changeset::Changeset)
Expand All @@ -16,15 +92,29 @@ module Crecto
{% end %}

def delete_all(queryable, query = Crecto::Repo::Query.new)
@repo.delete_all(queryable, query, @tx)
@repo.delete_all(queryable, query, tx: @tx)
end

def update_all(queryable, query, update_hash : Multi::UpdateHash)
@repo.update_all(queryable, query, update_hash, @tx)
@repo.update_all(queryable, query, update_hash, tx: @tx)
end

def update_all(queryable, query, update_tuple : NamedTuple)
update_all(queryable, query, update_tuple.to_h)
end

def aggregate(queryable, aggregate_function : Symbol, field : Symbol)
@repo.aggregate(queryable, aggregate_function, field, tx: @tx)
end

def aggregate(queryable, aggregate_function : Symbol, field : Symbol, query : Crecto::Repo::Query)
@repo.aggregate(queryable, aggregate_function, field, query, tx: @tx)
end

def transaction!
@repo.transaction!(@tx) do |tx|
yield tx
end
end
end
end
Loading