From f11ae18c6e5d10625326c37afa7963205103c75e Mon Sep 17 00:00:00 2001 From: "Brian J. Cardiff" Date: Fri, 13 Sep 2019 14:34:28 -0300 Subject: [PATCH] Remove duplication to handle array args --- src/db/pool_statement.cr | 18 +-- src/db/query_methods.cr | 329 +++++---------------------------------- src/db/statement.cr | 35 ++--- 3 files changed, 57 insertions(+), 325 deletions(-) diff --git a/src/db/pool_statement.cr b/src/db/pool_statement.cr index 9df1027..390b921 100644 --- a/src/db/pool_statement.cr +++ b/src/db/pool_statement.cr @@ -15,13 +15,8 @@ module DB end # See `QueryMethods#exec` - def exec(*args) : ExecResult - statement_with_retry &.exec(*args) - end - - # See `QueryMethods#exec` - def exec(*, args : Array) : ExecResult - statement_with_retry &.exec(args: args) + def exec(*t_args, args : Array? = nil) : ExecResult + statement_with_retry &.exec(*t_args, args: args) end # See `QueryMethods#query` @@ -30,13 +25,8 @@ module DB end # See `QueryMethods#query` - def query(*args) : ResultSet - statement_with_retry &.query(*args) - end - - # See `QueryMethods#query` - def query(*, args : Array) : ResultSet - statement_with_retry &.query(args: args) + def query(*t_args, args : Array? = nil) : ResultSet + statement_with_retry &.query(*t_args, args: args) end # See `QueryMethods#scalar` diff --git a/src/db/query_methods.cr b/src/db/query_methods.cr index 93bf993..f4174c4 100644 --- a/src/db/query_methods.cr +++ b/src/db/query_methods.cr @@ -7,10 +7,11 @@ module DB # 2. `#scalar` reads a single value of the response. A union of possible values is returned. # 3. `#query` returns a `ResultSet` that allows iteration over the rows in the response and column information. # - # Arguments can be passed by position + # Arguments can be passed by position or as an array. # # ``` # db.query("SELECT name FROM ... WHERE age > ?", age) + # db.query("SELECT name FROM ... WHERE age > ?", args: [age]) # ``` # # Convention of mapping how arguments are mapped to the query depends on each driver. @@ -34,8 +35,15 @@ module DB # result.close # end # ``` - def query(query, *args) - build(query).query(*args) + # + # Note: to use a dynamic list length of arguments use `args:` keyword argument. + # + # ``` + # result = db.query "select name from contacts where id = ?", args: [10] + # ``` + # + def query(query, *t_args, args : Array? = nil) + build(query).query(*t_args, args: args) end # Executes a *query* and yields a `ResultSet` with the results. @@ -48,9 +56,9 @@ module DB # end # end # ``` - def query(query, *args) + def query(query, *t_args, args : Array? = nil) # CHECK build(query).query(*args, &block) - rs = query(query, *args) + rs = query(query, *t_args, args: args) yield rs ensure rs.close end @@ -64,8 +72,8 @@ module DB # ``` # name = db.query_one "select name from contacts where id = ?", 18, &.read(String) # ``` - def query_one(query, *args, &block : ResultSet -> U) : U forall U - query(query, *args) do |rs| + def query_one(query, *t_args, args : Array? = nil, &block : ResultSet -> U) : U forall U + query(query, *t_args, args: args) do |rs| raise DB::Error.new("no rows") unless rs.move_next value = yield rs @@ -82,8 +90,8 @@ module DB # ``` # db.query_one "select name, age from contacts where id = ?", 1, as: {String, Int32} # ``` - def query_one(query, *args, as types : Tuple) - query_one(query, *args) do |rs| + def query_one(query, *t_args, args : Array? = nil, as types : Tuple) + query_one(query, *t_args, args: args) do |rs| rs.read(*types) end end @@ -97,8 +105,8 @@ module DB # ``` # db.query_one "select name, age from contacts where id = ?", 1, as: {name: String, age: Int32} # ``` - def query_one(query, *args, as types : NamedTuple) - query_one(query, *args) do |rs| + def query_one(query, *t_args, args : Array? = nil, as types : NamedTuple) + query_one(query, *t_args, args: args) do |rs| rs.read(**types) end end @@ -111,8 +119,8 @@ module DB # ``` # db.query_one "select name from contacts where id = ?", 1, as: String # ``` - def query_one(query, *args, as type : Class) - query_one(query, *args) do |rs| + def query_one(query, *t_args, args : Array? = nil, as type : Class) + query_one(query, *t_args, args: args) do |rs| rs.read(type) end end @@ -129,8 +137,8 @@ module DB # name = db.query_one? "select name from contacts where id = ?", 18, &.read(String) # typeof(name) # => String | Nil # ``` - def query_one?(query, *args, &block : ResultSet -> U) : U? forall U - query(query, *args) do |rs| + def query_one?(query, *t_args, args : Array? = nil, &block : ResultSet -> U) : U? forall U + query(query, *t_args, args: args) do |rs| return nil unless rs.move_next value = yield rs @@ -150,8 +158,8 @@ module DB # result = db.query_one? "select name, age from contacts where id = ?", 1, as: {String, Int32} # typeof(result) # => Tuple(String, Int32) | Nil # ``` - def query_one?(query, *args, as types : Tuple) - query_one?(query, *args) do |rs| + def query_one?(query, *t_args, args : Array? = nil, as types : Tuple) + query_one?(query, *t_args, args: args) do |rs| rs.read(*types) end end @@ -168,8 +176,8 @@ module DB # result = db.query_one? "select name, age from contacts where id = ?", 1, as: {age: String, name: Int32} # typeof(result) # => NamedTuple(age: String, name: Int32) | Nil # ``` - def query_one?(query, *args, as types : NamedTuple) - query_one?(query, *args) do |rs| + def query_one?(query, *t_args, args : Array? = nil, as types : NamedTuple) + query_one?(query, *t_args, args: args) do |rs| rs.read(**types) end end @@ -185,8 +193,8 @@ module DB # name = db.query_one? "select name from contacts where id = ?", 1, as: String # typeof(name) # => String? # ``` - def query_one?(query, *args, as type : Class) - query_one?(query, *args) do |rs| + def query_one?(query, *t_args, args : Array? = nil, as type : Class) + query_one?(query, *t_args, args: args) do |rs| rs.read(type) end end @@ -197,9 +205,9 @@ module DB # ``` # names = db.query_all "select name from contacts", &.read(String) # ``` - def query_all(query, *args, &block : ResultSet -> U) : Array(U) forall U + def query_all(query, *t_args, args : Array? = nil, &block : ResultSet -> U) : Array(U) forall U ary = [] of U - query_each(query, *args) do |rs| + query_each(query, *t_args, args: args) do |rs| ary.push(yield rs) end ary @@ -211,8 +219,8 @@ module DB # ``` # contacts = db.query_all "select name, age from contacts", as: {String, Int32} # ``` - def query_all(query, *args, as types : Tuple) - query_all(query, *args) do |rs| + def query_all(query, *t_args, args : Array? = nil, as types : Tuple) + query_all(query, *t_args, args: args) do |rs| rs.read(*types) end end @@ -224,8 +232,8 @@ module DB # ``` # contacts = db.query_all "select name, age from contacts", as: {name: String, age: Int32} # ``` - def query_all(query, *args, as types : NamedTuple) - query_all(query, *args) do |rs| + def query_all(query, *t_args, args : Array? = nil, as types : NamedTuple) + query_all(query, *t_args, args: args) do |rs| rs.read(**types) end end @@ -236,8 +244,8 @@ module DB # ``` # names = db.query_all "select name from contacts", as: String # ``` - def query_all(query, *args, as type : Class) - query_all(query, *args) do |rs| + def query_all(query, *t_args, args : Array? = nil, as type : Class) + query_all(query, *t_args, args: args) do |rs| rs.read(type) end end @@ -250,8 +258,8 @@ module DB # puts rs.read(String) # end # ``` - def query_each(query, *args) - query(query, *args) do |rs| + def query_each(query, *t_args, args : Array? = nil) + query(query, *t_args, args: args) do |rs| rs.each do yield rs end @@ -259,8 +267,8 @@ module DB end # Performs the `query` and returns an `ExecResult` - def exec(query, *args) - build(query).exec(*args) + def exec(query, *t_args, args : Array? = nil) + build(query).exec(*t_args, args: args) end # Performs the `query` and returns a single scalar value @@ -268,259 +276,8 @@ module DB # ``` # puts db.scalar("SELECT MAX(name)").as(String) # => (a String) # ``` - def scalar(query, *args) - build(query).scalar(*args) - end - - # Executes a *query* and returns a `ResultSet` with the results. - # The `ResultSet` must be closed manually. - # - # ``` - # result = db.query "select name from contacts where id = ?", 10 - # begin - # if result.move_next - # id = result.read(Int32) - # end - # ensure - # result.close - # end - # ``` - def query(query, *, args : Array) - build(query).query(args: args) - end - - # Executes a *query* and yields a `ResultSet` with the results. - # The `ResultSet` is closed automatically. - # - # ``` - # db.query("select name from contacts where age > ?", 18) do |rs| - # rs.each do - # name = rs.read(String) - # end - # end - # ``` - def query(query, *, args : Array) - # CHECK build(query).query(args: args, &block) - rs = query(query, args: args) - yield rs ensure rs.close - end - - # Executes a *query* that expects a single row and yields a `ResultSet` - # positioned at that first row. - # - # The given block must not invoke `move_next` on the yielded result set. - # - # Raises `DB::Error` if there were no rows, or if there were more than one row. - # - # ``` - # name = db.query_one "select name from contacts where id = ?", 18, &.read(String) - # ``` - def query_one(query, *, args : Array, &block : ResultSet -> U) : U forall U - query(query, args: args) do |rs| - raise DB::Error.new("no rows") unless rs.move_next - - value = yield rs - raise DB::Error.new("more than one row") if rs.move_next - return value - end - end - - # Executes a *query* that expects a single row and returns it - # as a tuple of the given *types*. - # - # Raises `DB::Error` if there were no rows, or if there were more than one row. - # - # ``` - # db.query_one "select name, age from contacts where id = ?", 1, as: {String, Int32} - # ``` - def query_one(query, *, args : Array, as types : Tuple) - query_one(query, args: args) do |rs| - rs.read(*types) - end - end - - # Executes a *query* that expects a single row and returns it - # as a named tuple of the given *types* (the keys of the named tuple - # are not necessarily the column names). - # - # Raises `DB::Error` if there were no rows, or if there were more than one row. - # - # ``` - # db.query_one "select name, age from contacts where id = ?", 1, as: {name: String, age: Int32} - # ``` - def query_one(query, *, args : Array, as types : NamedTuple) - query_one(query, args: args) do |rs| - rs.read(**types) - end - end - - # Executes a *query* that expects a single row - # and returns the first column's value as the given *type*. - # - # Raises `DB::Error` if there were no rows, or if there were more than one row. - # - # ``` - # db.query_one "select name from contacts where id = ?", 1, as: String - # ``` - def query_one(query, *, args : Array, as type : Class) - query_one(query, args: args) do |rs| - rs.read(type) - end - end - - # Executes a *query* that expects at most a single row and yields a `ResultSet` - # positioned at that first row. - # - # Returns `nil`, not invoking the block, if there were no rows. - # - # Raises `DB::Error` if there were more than one row - # (this ends up invoking the block once). - # - # ``` - # name = db.query_one? "select name from contacts where id = ?", 18, &.read(String) - # typeof(name) # => String | Nil - # ``` - def query_one?(query, *, args : Array, &block : ResultSet -> U) : U? forall U - query(query, args: args) do |rs| - return nil unless rs.move_next - - value = yield rs - raise DB::Error.new("more than one row") if rs.move_next - return value - end - end - - # Executes a *query* that expects a single row and returns it - # as a tuple of the given *types*. - # - # Returns `nil` if there were no rows. - # - # Raises `DB::Error` if there were more than one row. - # - # ``` - # result = db.query_one? "select name, age from contacts where id = ?", 1, as: {String, Int32} - # typeof(result) # => Tuple(String, Int32) | Nil - # ``` - def query_one?(query, *, args : Array, as types : Tuple) - query_one?(query, args: args) do |rs| - rs.read(*types) - end - end - - # Executes a *query* that expects a single row and returns it - # as a named tuple of the given *types* (the keys of the named tuple - # are not necessarily the column names). - # - # Returns `nil` if there were no rows. - # - # Raises `DB::Error` if there were more than one row. - # - # ``` - # result = db.query_one? "select name, age from contacts where id = ?", 1, as: {age: String, name: Int32} - # typeof(result) # => NamedTuple(age: String, name: Int32) | Nil - # ``` - def query_one?(query, *, args : Array, as types : NamedTuple) - query_one?(query, args: args) do |rs| - rs.read(**types) - end - end - - # Executes a *query* that expects a single row - # and returns the first column's value as the given *type*. - # - # Returns `nil` if there were no rows. - # - # Raises `DB::Error` if there were more than one row. - # - # ``` - # name = db.query_one? "select name from contacts where id = ?", 1, as: String - # typeof(name) # => String? - # ``` - def query_one?(query, *, args : Array, as type : Class) - query_one?(query, args: args) do |rs| - rs.read(type) - end - end - - # Executes a *query* and yield a `ResultSet` positioned at the beginning - # of each row, returning an array of the values of the blocks. - # - # ``` - # names = db.query_all "select name from contacts", &.read(String) - # ``` - def query_all(query, *, args : Array, &block : ResultSet -> U) : Array(U) forall U - ary = [] of U - query_each(query, args: args) do |rs| - ary.push(yield rs) - end - ary - end - - # Executes a *query* and returns an array where each row is - # read as a tuple of the given *types*. - # - # ``` - # contacts = db.query_all "select name, age from contacts", as: {String, Int32} - # ``` - def query_all(query, *, args : Array, as types : Tuple) - query_all(query, args: args) do |rs| - rs.read(*types) - end - end - - # Executes a *query* and returns an array where each row is - # read as a named tuple of the given *types* (the keys of the named tuple - # are not necessarily the column names). - # - # ``` - # contacts = db.query_all "select name, age from contacts", as: {name: String, age: Int32} - # ``` - def query_all(query, *, args : Array, as types : NamedTuple) - query_all(query, args: args) do |rs| - rs.read(**types) - end - end - - # Executes a *query* and returns an array where the - # value of each row is read as the given *type*. - # - # ``` - # names = db.query_all "select name from contacts", as: String - # ``` - def query_all(query, *, args : Array, as type : Class) - query_all(query, args: args) do |rs| - rs.read(type) - end - end - - # Executes a *query* and yields the `ResultSet` once per each row. - # The `ResultSet` is closed automatically. - # - # ``` - # db.query_each "select name from contacts" do |rs| - # puts rs.read(String) - # end - # ``` - def query_each(query, *, args : Array) - query(query, args: args) do |rs| - rs.each do - yield rs - end - end - end - - # Performs the `query` and returns an `ExecResult` - def exec(query, *, args : Array) - build(query).exec(args: args) - end - - # Performs the `query` and returns a single scalar value - # - # ``` - # puts db.scalar("SELECT MAX(name)").as(String) # => (a String) - # ``` - def scalar(query, *, args : Array) - build(query).scalar(args: args) + def scalar(query, *t_args, args : Array? = nil) + build(query).scalar(*t_args, args: args) end end end diff --git a/src/db/statement.cr b/src/db/statement.cr index f43bd8b..3eae1f6 100644 --- a/src/db/statement.cr +++ b/src/db/statement.cr @@ -8,8 +8,8 @@ module DB end # See `QueryMethods#scalar` - def scalar(*args) - query(*args) do |rs| + def scalar(*t_args, args : Array? = nil) + query(*t_args, args: args) do |rs| rs.each do return rs.read end @@ -19,24 +19,20 @@ module DB end # See `QueryMethods#query` - def query(*args) - rs = query(*args) + def query(*t_args, args : Array? = nil) + rs = query(*t_args, args: args) yield rs ensure rs.close end # See `QueryMethods#exec` abstract def exec : ExecResult # See `QueryMethods#exec` - abstract def exec(*args) : ExecResult - # See `QueryMethods#exec` - abstract def exec(*, args : Array) : ExecResult + abstract def exec(*t_args, args : Array? = nil) : ExecResult # See `QueryMethods#query` abstract def query : ResultSet # See `QueryMethods#query` - abstract def query(*args) : ResultSet - # See `QueryMethods#query` - abstract def query(*, args : Array) : ResultSet + abstract def query(*t_args, args : Array? = nil) : ResultSet end # Represents a query in a `Connection`. @@ -68,14 +64,8 @@ module DB end # See `QueryMethods#exec` - def exec(*, args : Array) : DB::ExecResult - perform_exec_and_release(args) - end - - # See `QueryMethods#exec` - def exec(*args) - # TODO better way to do it - perform_exec_and_release(args) + def exec(*t_args, args : Array? = nil) : DB::ExecResult + perform_exec_and_release(args || t_args) end # See `QueryMethods#query` @@ -84,13 +74,8 @@ module DB end # See `QueryMethods#query` - def query(*, args : Array) : DB::ResultSet - perform_query_with_rescue args - end - - # See `QueryMethods#query` - def query(*args) - perform_query_with_rescue args + def query(*t_args, args : Array? = nil) : DB::ResultSet + perform_query_with_rescue(args || t_args) end private def perform_exec_and_release(args : Enumerable) : ExecResult