From 3b4b32e4e665df3a8d07424122d62579318f8314 Mon Sep 17 00:00:00 2001 From: "Brian J. Cardiff" Date: Tue, 21 Jun 2016 12:47:12 -0300 Subject: [PATCH 1/6] update to crystal-db feature/5-type-extensibility branch --- shard.yml | 6 +++++- src/sqlite3/statement.cr | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/shard.yml b/shard.yml index c9a66dc..2a38cff 100644 --- a/shard.yml +++ b/shard.yml @@ -1,7 +1,11 @@ name: sqlite3 version: 0.1.0 +dependencies: + db: + github: bcardiff/crystal-db + branch: feature/5-type-extensibility + authors: - Ary Borenszweig - Brian J. Cardiff - diff --git a/src/sqlite3/statement.cr b/src/sqlite3/statement.cr index d0cde14..ccd726a 100644 --- a/src/sqlite3/statement.cr +++ b/src/sqlite3/statement.cr @@ -4,7 +4,7 @@ class SQLite3::Statement < DB::Statement check LibSQLite3.prepare_v2(@connection, sql, sql.bytesize + 1, out @stmt, nil) end - protected def perform_query(args : Slice(DB::Any)) + protected def perform_query(args : Enumerable) : DB::ResultSet LibSQLite3.reset(self) args.each_with_index(1) do |arg, index| bind_arg(index, arg) @@ -12,7 +12,7 @@ class SQLite3::Statement < DB::Statement ResultSet.new(self) end - protected def perform_exec(args : Slice(DB::Any)) + protected def perform_exec(args : Enumerable) : DB::ExecResult rs = perform_query(args) rs.move_next rs.close From bf26cdc24f68b598a91d0080079fd17c07beba65 Mon Sep 17 00:00:00 2001 From: "Brian J. Cardiff" Date: Tue, 21 Jun 2016 18:32:06 -0300 Subject: [PATCH 2/6] update DB::ExecResult#rows_affected to Int64 --- src/sqlite3/statement.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sqlite3/statement.cr b/src/sqlite3/statement.cr index ccd726a..a23fb74 100644 --- a/src/sqlite3/statement.cr +++ b/src/sqlite3/statement.cr @@ -17,7 +17,7 @@ class SQLite3::Statement < DB::Statement rs.move_next rs.close - rows_affected = LibSQLite3.changes(connection) + rows_affected = LibSQLite3.changes(connection).to_i64 last_id = LibSQLite3.last_insert_rowid(connection) DB::ExecResult.new rows_affected, last_id From c8d5acceae38060996bf78f56449b684999c7993 Mon Sep 17 00:00:00 2001 From: "Brian J. Cardiff" Date: Wed, 22 Jun 2016 00:45:00 -0300 Subject: [PATCH 3/6] Make sure queries (insert and query) work with any type --- spec/driver_spec.cr | 24 +++++++++++++++++++++--- src/sqlite3/statement.cr | 4 ++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/spec/driver_spec.cr b/spec/driver_spec.cr index 4f728b5..da0591c 100644 --- a/spec/driver_spec.cr +++ b/spec/driver_spec.cr @@ -46,6 +46,9 @@ def assert_filename(uri, filename) SQLite3::Connection.filename(URI.parse(uri)).should eq(filename) end +class NotSupportedType +end + describe Driver do it "should register sqlite3 name" do DB.driver_class("sqlite3").should eq(SQLite3::Driver) @@ -111,7 +114,7 @@ describe Driver do it "executes and selects blob" do with_db do |db| - slice = db.scalar(%(select X'53514C697465')) as Slice(UInt8) + slice = db.scalar(%(select X'53514C697465')).as(Slice(UInt8)) slice.to_a.should eq([0x53, 0x51, 0x4C, 0x69, 0x74, 0x65]) end end @@ -119,7 +122,7 @@ describe Driver do it "executes with bind blob" do with_db do |db| ary = UInt8[0x53, 0x51, 0x4C, 0x69, 0x74, 0x65] - slice = db.scalar(%(select cast(? as BLOB)), Slice.new(ary.to_unsafe, ary.size)) as Slice(UInt8) + slice = db.scalar(%(select cast(? as BLOB)), Slice.new(ary.to_unsafe, ary.size)).as(Slice(UInt8)) slice.to_a.should eq(ary) end end @@ -192,11 +195,26 @@ describe Driver do db.exec "create table table1 (col1 blob)" db.exec %(insert into table1 values (?)), Slice.new(ary.to_unsafe, ary.size) - slice = db.scalar("select cast(col1 as blob) from table1") as Slice(UInt8) + slice = db.scalar("select cast(col1 as blob) from table1").as(Slice(UInt8)) slice.to_a.should eq(ary) end end + it "raises on unsupported param types" do + with_db do |db| + expect_raises Exception, "SQLite3::Statement does not support NotSupportedType params" do + db.query "select 1", NotSupportedType.new + end + # TODO raising exception does not close the connection and pool is exhausted + end + + with_db do |db| + expect_raises Exception, "SQLite3::Statement does not support NotSupportedType params" do + db.exec "select 1", NotSupportedType.new + end + end + end + it "gets many rows from table" do with_mem_db do |db| db.exec "create table person (name string, age integer)" diff --git a/src/sqlite3/statement.cr b/src/sqlite3/statement.cr index a23fb74..52cb3ab 100644 --- a/src/sqlite3/statement.cr +++ b/src/sqlite3/statement.cr @@ -56,6 +56,10 @@ class SQLite3::Statement < DB::Statement check LibSQLite3.bind_blob(self, index, value, value.size, nil) end + private def bind_arg(index, value) + raise "#{self.class} does not support #{value.class} params" + end + private def check(code) raise Exception.new(@connection) unless code == 0 end From d65575cd77b95d55dd048e5f7052a37f88f23e4d Mon Sep 17 00:00:00 2001 From: "Brian J. Cardiff" Date: Wed, 22 Jun 2016 14:13:44 -0300 Subject: [PATCH 4/6] update Slice(UInt8) to Bytes --- spec/driver_spec.cr | 10 +++++----- src/sqlite3/result_set.cr | 6 +++--- src/sqlite3/statement.cr | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/spec/driver_spec.cr b/spec/driver_spec.cr index da0591c..bbf3212 100644 --- a/spec/driver_spec.cr +++ b/spec/driver_spec.cr @@ -114,7 +114,7 @@ describe Driver do it "executes and selects blob" do with_db do |db| - slice = db.scalar(%(select X'53514C697465')).as(Slice(UInt8)) + slice = db.scalar(%(select X'53514C697465')).as(Bytes) slice.to_a.should eq([0x53, 0x51, 0x4C, 0x69, 0x74, 0x65]) end end @@ -122,7 +122,7 @@ describe Driver do it "executes with bind blob" do with_db do |db| ary = UInt8[0x53, 0x51, 0x4C, 0x69, 0x74, 0x65] - slice = db.scalar(%(select cast(? as BLOB)), Slice.new(ary.to_unsafe, ary.size)).as(Slice(UInt8)) + slice = db.scalar(%(select cast(? as BLOB)), Bytes.new(ary.to_unsafe, ary.size)).as(Bytes) slice.to_a.should eq(ary) end end @@ -161,7 +161,7 @@ describe Driver do rs.column_type(0).should eq(String) rs.column_type(1).should eq(Int64) rs.column_type(2).should eq(Float64) - rs.column_type(3).should eq(Slice(UInt8)) + rs.column_type(3).should eq(Bytes) end end end @@ -193,9 +193,9 @@ describe Driver do ary = UInt8[0x53, 0x51, 0x4C, 0x69, 0x74, 0x65] db.exec "create table table1 (col1 blob)" - db.exec %(insert into table1 values (?)), Slice.new(ary.to_unsafe, ary.size) + db.exec %(insert into table1 values (?)), Bytes.new(ary.to_unsafe, ary.size) - slice = db.scalar("select cast(col1 as blob) from table1").as(Slice(UInt8)) + slice = db.scalar("select cast(col1 as blob) from table1").as(Bytes) slice.to_a.should eq(ary) end end diff --git a/src/sqlite3/result_set.cr b/src/sqlite3/result_set.cr index 53001a5..69c2841 100644 --- a/src/sqlite3/result_set.cr +++ b/src/sqlite3/result_set.cr @@ -52,13 +52,13 @@ class SQLite3::ResultSet < DB::ResultSet moving_column { |col| LibSQLite3.column_double(self, col) } end - def read(t : Slice(UInt8).class) : Slice(UInt8) + def read(t : Bytes.class) : Bytes moving_column do |col| blob = LibSQLite3.column_blob(self, col) bytes = LibSQLite3.column_bytes(self, col) ptr = Pointer(UInt8).malloc(bytes) ptr.copy_from(blob, bytes) - Slice(UInt8).new(ptr, bytes) + Bytes.new(ptr, bytes) end end @@ -74,7 +74,7 @@ class SQLite3::ResultSet < DB::ResultSet case LibSQLite3.column_type(self, index) when Type::INTEGER; Int64 when Type::FLOAT ; Float64 - when Type::BLOB ; Slice(UInt8) + when Type::BLOB ; Bytes when Type::TEXT ; String when Type::NULL ; Nil else diff --git a/src/sqlite3/statement.cr b/src/sqlite3/statement.cr index 52cb3ab..57d4f9d 100644 --- a/src/sqlite3/statement.cr +++ b/src/sqlite3/statement.cr @@ -52,7 +52,7 @@ class SQLite3::Statement < DB::Statement check LibSQLite3.bind_text(self, index, value, value.bytesize, nil) end - private def bind_arg(index, value : Slice(UInt8)) + private def bind_arg(index, value : Bytes) check LibSQLite3.bind_blob(self, index, value, value.size, nil) end From 497379ff81b509d93e0785a3e2e4c53deb837871 Mon Sep 17 00:00:00 2001 From: "Brian J. Cardiff" Date: Wed, 22 Jun 2016 16:17:47 -0300 Subject: [PATCH 5/6] extend sqlite with time (as text) support. --- spec/driver_spec.cr | 19 +++++++++++++++++++ src/sqlite3.cr | 4 ++++ src/sqlite3/result_set.cr | 12 +++++++++++- src/sqlite3/statement.cr | 4 ++++ 4 files changed, 38 insertions(+), 1 deletion(-) diff --git a/spec/driver_spec.cr b/spec/driver_spec.cr index bbf3212..19da869 100644 --- a/spec/driver_spec.cr +++ b/spec/driver_spec.cr @@ -25,6 +25,7 @@ def sqlite_type_for(v) when String ; "text" when Int32, Int64 ; "int" when Float32, Float64; "float" + when Time ; "text" else raise "not implemented for #{typeof(v)}" end @@ -200,6 +201,24 @@ describe Driver do end end + it "insert/get value date from table" do + with_db do |db| + value = Time.new(2016, 7, 22, 15, 0, 0, 0) + db.exec "create table table1 (col1 #{sqlite_type_for(value)})" + db.exec %(insert into table1 values (?)), value + + db.query "select col1 from table1" do |rs| + rs.move_next + rs.read(Time).should eq(value) + end + + db.query "select col1 from table1" do |rs| + rs.move_next + rs.read?(Time).should eq(value) + end + end + end + it "raises on unsupported param types" do with_db do |db| expect_raises Exception, "SQLite3::Statement does not support NotSupportedType params" do diff --git a/src/sqlite3.cr b/src/sqlite3.cr index 2fc0340..a5b49c4 100644 --- a/src/sqlite3.cr +++ b/src/sqlite3.cr @@ -1,2 +1,6 @@ require "db" require "./sqlite3/**" + +module SQLite3 + DATE_FORMAT = "%F %H:%M:%S.%L" +end diff --git a/src/sqlite3/result_set.cr b/src/sqlite3/result_set.cr index 69c2841..39e3e0b 100644 --- a/src/sqlite3/result_set.cr +++ b/src/sqlite3/result_set.cr @@ -22,7 +22,7 @@ class SQLite3::ResultSet < DB::ResultSet end end - {% for t in DB::TYPES %} + macro nilable_read_for(t) def read?(t : {{t}}.class) : {{t}}? if read_nil? moving_column { nil } @@ -30,6 +30,10 @@ class SQLite3::ResultSet < DB::ResultSet read(t) end end + end + + {% for t in DB::TYPES %} + nilable_read_for({{t}}) {% end %} def read(t : String.class) : String @@ -62,6 +66,12 @@ class SQLite3::ResultSet < DB::ResultSet end end + def read(t : Time.class) : Time + Time.parse read(String), SQLite3::DATE_FORMAT + end + + nilable_read_for Time + def column_count LibSQLite3.column_count(self) end diff --git a/src/sqlite3/statement.cr b/src/sqlite3/statement.cr index 57d4f9d..f7c36f2 100644 --- a/src/sqlite3/statement.cr +++ b/src/sqlite3/statement.cr @@ -56,6 +56,10 @@ class SQLite3::Statement < DB::Statement check LibSQLite3.bind_blob(self, index, value, value.size, nil) end + private def bind_arg(index, value : Time) + bind_arg(index, value.to_s(SQLite3::DATE_FORMAT)) + end + private def bind_arg(index, value) raise "#{self.class} does not support #{value.class} params" end From de8bd7a5c4056b0e494c7a89a2a9ba412b044b1a Mon Sep 17 00:00:00 2001 From: "Brian J. Cardiff" Date: Thu, 23 Jun 2016 15:13:34 -0300 Subject: [PATCH 6/6] use master branch of crystal-db --- shard.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/shard.yml b/shard.yml index 2a38cff..c61beb0 100644 --- a/shard.yml +++ b/shard.yml @@ -4,7 +4,6 @@ version: 0.1.0 dependencies: db: github: bcardiff/crystal-db - branch: feature/5-type-extensibility authors: - Ary Borenszweig