Skip to content
Closed
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
15 changes: 15 additions & 0 deletions spec/adapters/mysql_adapter_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -114,5 +114,20 @@ if Repo.config.adapter == Crecto::Adapters::Mysql
sql.should eq(["SELECT posts.* FROM posts INNER JOIN users ON users.id = posts.user_id"])
end
end

it "should generate sql for query syntax with lock" do
query = Query
.where(name: "fridge")
.where("users.things < ?", [124])
.order_by("users.name ASC")
.order_by("users.things DESC")
.limit(1)
Repo.config.get_connection.transaction do |tx|
Repo.lock(tx, User, query)
end
check_sql do |sql|
sql.should eq(["SELECT users.* FROM users WHERE (users.name=?) AND (users.things < ?) ORDER BY users.name ASC, users.things DESC LIMIT 1 FOR UPDATE"])
end
end
end
end
15 changes: 15 additions & 0 deletions spec/adapters/postgres_adapter_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -103,5 +103,20 @@ if Repo.config.adapter == Crecto::Adapters::Postgres
sql.should eq(["SELECT posts.* FROM posts INNER JOIN users ON users.id = posts.user_id"])
end
end

it "should generate sql for query syntax with lock" do
query = Query
.where(name: "fridge")
.where("users.things < ?", [124])
.order_by("users.name ASC")
.order_by("users.things DESC")
.limit(1)
Repo.config.get_connection.transaction do |tx|
Repo.lock(tx, User, query)
end
check_sql do |sql|
sql.should eq(["SELECT users.* FROM users WHERE (users.name=$1) AND (users.things < $2) ORDER BY users.name ASC, users.things DESC LIMIT 1 FOR UPDATE"])
end
end
end
end
28 changes: 28 additions & 0 deletions spec/repo_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -1301,6 +1301,34 @@ describe Crecto do
end
end

describe "#lock" do
it "should return rows" do
unless Repo.config.adapter == Crecto::Adapters::SQLite3
users = [] of User
Repo.config.get_connection.transaction do |tx|
users = Repo.lock(tx, User)
end
users.size.should be > 0
end
end

it "should return rows with Query" do
unless Repo.config.adapter == Crecto::Adapters::SQLite3
query = Query
.where(name: "fridge")
.where("users.things < ?", [124])
.order_by("users.name ASC")
.order_by("users.things DESC")
.limit(1)
users = [] of User
Repo.config.get_connection.transaction do |tx|
users = Repo.lock(tx, User, query)
end
users.size.should be > 0
end
end
end

# keep this at the end
describe "#delete_all" do
it "should delete destroy dependents" do
Expand Down
6 changes: 4 additions & 2 deletions src/crecto/adapters/base_adapter.cr
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ module Crecto
all(conn, queryable, query)
when :delete_all
delete(conn, queryable, query)
when :lock
all(conn, queryable, query, true)
end
end

Expand Down Expand Up @@ -153,8 +155,7 @@ module Crecto
builder << " SELECT " << ag << '(' << queryable.table_name << '.' << field << ") FROM " << queryable.table_name
end


private def all(conn, queryable, query)
private def all(conn, queryable, query, for_update = false)
params = [] of DbValue | Array(DbValue)

q = String.build do |builder|
Expand All @@ -177,6 +178,7 @@ module Crecto
limit(builder, query)
offset(builder, query)
group_by(builder, query)
builder << " FOR UPDATE" if for_update
end

execute(conn, position_args(q), params)
Expand Down
4 changes: 4 additions & 0 deletions src/crecto/live_transaction.cr
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,9 @@ module Crecto
def update_all(queryable, query, update_tuple : NamedTuple)
update_all(queryable, query, update_tuple.to_h)
end

def lock(queryable, query = Crecto::Repo::Query.new)
@repo.lock(@tx, queryable, query)
end
end
end
14 changes: 14 additions & 0 deletions src/crecto/repo.cr
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,20 @@ module Crecto
end
end

# Returns a list of locked *queryable* instances. Accepts an optional `query`
#
# ```
# Crecto::Repo.config.get_connection.transaction do |tx|
# users = Crecto::Repo.lock(tx, User)
# end
# ```
def lock(tx : DB::Transaction, queryable, query = Query.new) : Array
raise Crecto::InvalidAdapter.new "SQLite3 cannot use lock feature." if config.adapter == Crecto::Adapters::SQLite3
q = config.adapter.run(tx, :lock, queryable, query).as(DB::ResultSet)
results = queryable.from_rs(q)
results
end

{% for operation in %w[insert update delete] %}
private def run_operation(operation : Multi::{{operation.camelcase.id}}, tx)
{{operation.id}}(operation.instance, tx)
Expand Down