-
-
Notifications
You must be signed in to change notification settings - Fork 55
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
Require ResultSet to just implement read
, optionally implementing read(T.class)
. Fixes #5
#9
Conversation
read?(t).not_nil! | ||
# Reads the next column value as a **type** | ||
def read(type : Class) | ||
type.cast(read) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- shouldn't this base implementation simplify the code of
dummy_driver#read(T)
? - does Class match structs and unions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, a lot. I think the dummy driver can just have all the values as an array of DB::Any. I'll try :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, Class matches any type, even union types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bcardiff I don't know how to easily simplify the specs, I can see DummyResultSet
receives the query and splits it by commas. I'd leave it like this for now so read(Class)
is also tested.
2b165bb
to
64140f1
Compare
I've just updated this PR with some additions. Sample code: DB.open "sqlite3://%3Amemory%3A" do |db|
db.exec "create table contacts (name string, age integer)"
db.exec "insert into contacts values (?, ?)", "John Doe", 30
# `read(*types)` to read multiple values at once
db.query "select name, age from contacts" do |rs|
rs.each do
name, age = rs.read(String, Int32)
pp name, age
# name # => "John Doe"
# age # => 30
end
end
# `query_one` to read a single row
# This raises if there are no rows, or more than one row
name, age = db.query_one "select name, age from contacts", &.read(String, Int32)
pp name, age
# name # => "John Doe"
# age # => 30
# Another way:
name, age = db.query_one "select name, age from contacts", as: {String, Int32}
pp name, age
# name # => "John Doe"
# age # => 30
# Another way, just read the name:
name = db.query_one "select name from contacts", as: String
pp name
# name # => "John Doe"
# similar to `query_one`, but returns nil if there are no rows (still raises if there are many rows)
result = db.query_one? "select name, age from contacts", &.read(String, Int32)
pp result, typeof(result)
# result # => {"John Doe", 30}
# typeof(result) # => (Tuple(String, Int32) | Nil)
# Another way:
result = db.query_one? "select name, age from contacts", as: {String, Int32}
pp result, typeof(result)
# result # => {"John Doe", 30}
# typeof(result) # => (Tuple(String, Int32) | Nil)
# Another way, just read the name:
name = db.query_one? "select name from contacts", as: String
pp name, typeof(name)
# name # => "John Doe"
# typeof(name) # => String | Nil
db.exec "insert into contacts values (?, ?)", "Foo Bar", 40
# `query_all` to read an array of values
people = db.query_all "select name, age from contacts order by age desc", &.read(String, Int32)
pp people, typeof(people)
# people # => [{"Foo Bar", 40}, {"John Doe", 30}]
# typeof(people) # => Array(Tuple(String, Int32))
# Another way:
people = db.query_all "select name, age from contacts order by age desc", as: {String, Int32}
pp people, typeof(people)
# people # => [{"Foo Bar", 40}, {"John Doe", 30}]
# typeof(people) # => Array(Tuple(String, Int32))
# Another way, just read the name:
names = db.query_all "select name from contacts order by age desc", as: String
pp names, typeof(names)
# names # => ["Foo Bar", "John Doe"
# typeof(names) # => Array(String)
end This fixes #10 in a much better way. And the nice thing about I think with this we could easily migrate EDIT: I think the /cc @bcardiff @waj @spalladino |
3927fab
to
c3d1d64
Compare
But other than that I think we can merge this. :-) I like it. |
@bcardiff The problem is that db.query_one "select name from contacts where id = ? limit 1", 123, &.read(String)
# ^-- argument so it would be hard to distinguish between arguments and types. |
if only something like this would work...
But well, let's keep the |
…read(T.class)`. Fixes #5
c3d1d64
to
9c88f71
Compare
@bcardiff The first form, I like the I've updated the PR with those forms, and updated the sample code in the comment above. |
My only "problem" or question with the # This is OK, returns a string and an int32
db.query_one "...", as: {String, Int32}
# What about this?
db.query_one "...", as: Tuple(String, Int32) Right now the second way doesn't compile, because that ends up calling Of course if a DB driver allows you to read a tuple from a single column, the second form will work. |
I also left the block versions in all cases, because you can then do: point = db.query_one("select lat, lng from locations where id = ?", 123) { |rs| Point.new(*rs.read(Float64, Float64)) }
point # => Point(...) |
It's nice how |
The
scalar
method now also returns a union, and one can cast it to the needed type.