使用 RDBI 作为替代 Ruby/DBI
使用RDBI来替代Ruby/DBI
总结
以前主要使用Ruby 1.8,但现在想开始使用Ruby 2.0了。
连接数据库的库有很多,但之前主要使用的是dbi、dbd-pg和pg(直到1.8版本)。
-
- Ruby/DBI https://rubygems.org/gems/dbi
DBD::Pg https://rubygems.org/gems/dbd-pg
pg https://rubygems.org/gems/pg
但是根据开发者(erikh)说,似乎dbi只支持到Ruby 1.8。
https://github.com/erikh/ruby-dbi/issues/5
在上述问题中,开发者推荐使用他创建的名为RDBI的Ruby/DBI的”Version2″。
我們要調查的是RDBI以及其用於PostgreSQL的驅動程式rdbi-driver-postgresql。
安装
只有宝石
$ gem install pg
$ gem install rdbi
$ gem install rdbi-driver-postgresql
执行
#-*- coding:utf-8 -*-
require 'rdbi'
require 'rdbi-driver-postgresql'
dbh = RDBI.connect(:PostgreSQL, :dbname=>"test", :port=>5432, :user=>"user", :password=>"pass")
dbh.execute("SELECT NOW()").fetch(:first) #=> ["2014-11-28 18:13:27.232+09"]
dbh.disconnect
要使用RDBI来完成之前在Ruby/DBI中所做的事情。
看起来,Ruby/DBI的代码和它完全不是相同的用法。
让我们简单比较一下。
需要 ()
require 'dbi'
#require 'dbd-pg' #不要
require 'rdbi'
require 'rdbi-driver-postgresql' #必要
由于RDBI不会自动加载驱动程序。
连接到数据库.
dbh = DBI.connect("dbi:Pg:dbname", "user", "pass")
# or
#dbh = DBI.connect("dbi:Pg:database=dbname;port=5432;host=localhost", "user", "pass")
dbh = RDBI.connect(:PostgreSQL, :dbname=>"dbname", :user=>"user", :password=>"pass")
# or
#dbh = RDBI.connect(RDBI::Driver::PostgreSQL, :dbname=>"dbname", :user=>"user", :password=>"pass")
在connect函数的第一个参数中指定Driver的符号或类。
进行选择
#1行
dbh.select_one("SELECT * FROM table_name") #=> [1, "aaa"]
#全行
dbh.select_all("SELECT * FROM table_name") #=> [[1, "aaa"], [2, "bbb"], [3, "ccc"]]
#1行
dbh.execute("SELECT * FROM table_name").fetch(:first) #=> [1, "aaa"]
#全行
dbh.execute("SELECT * FROM table_name").fetch(:all) #=> [[1, "aaa"], [2, "bbb"], [3, "ccc"]]
交易
dbh["AutoCommit"] = false
begin
# トランザクション中の処理
dbh.commit
rescue
# 失敗
dbh.rollback
end
dbh.execute("BEGIN").fetch
begin
# トランザクション中の処理
dbh.execute("COMMIT")
rescue
# 失敗
dbh.execute("ROLLBACK")
end
使用 transaction 方法时不知道为何会出现错误。
rdbi-driver-postgresql(0.9.2) 的错误
在进行调查时,我们发现了一些错误,于是我们开始寻找原因。
总结一下,我们发现了大约两个由rdbi-driver-postgresql引起的错误。
如果列名重复,fetch(:first)的结果会出现问题。
在PostgreSQL中,对于SELECT语句,表是可选的,不是必需的。如果在psql中执行以下SQL:
SELECT 3,2,1,NOW();
?column? | ?column? | ?column? | now
----------+----------+----------+---------------------------
3 | 2 | 1 | 2014-12-19 14:43:24.77+09
(1 row)
像这样,列名中出现了多次“?column?”。
这种SQL在rdbi-driver-postgresql中不能正常工作。
dbh.execute("SELECT 3,2,1,NOW()").fetch(:first)
#=> [1, "2014-12-19 14:43:24.77+09"]
只要按照以下方式写,就能够回避。
dbh.execute("SELECT 3,2,1,NOW()").fetch(:all)[0]
#=> [3, 2, 1, "2014-12-19 14:43:24.77+09"]
当连续执行SQL时会出现错误。
在PreparedStatement中,可以指定名称,但他使用了Time.now.to_f作为名称,如果连续执行会导致名称重复并出现错误。
10.times do
dbh.execute("SELECT 1")
end
如果要避免这个问题,在每次执行之前都进行sleep可能是一个选择,但这样做很愚蠢,最好修复有问题的部分。
--- lib/ruby/gems/2.0.0/gems/rdbi-driver-postgresql-0.9.2/lib/rdbi/driver/postgresql.rb.original Fri 11 28 10:32:52 2014
+++ lib/ruby/gems/2.0.0/gems/rdbi-driver-postgresql-0.9.2/lib/rdbi/driver/postgresql.rb Fri 12 12 10:11:51 2014
@@ -175,9 +175,9 @@ class RDBI::Driver::PostgreSQL < RDBI::Driver
end
def next_row
- val = @handle[@index].values
+ val = self[@index]
@index += 1
- fix_dates(val)
+ val
end
def result_count
@@ -189,11 +189,11 @@ class RDBI::Driver::PostgreSQL < RDBI::Driver
end
def first
- fix_dates(@handle[0].values)
+ self[0]
end
def last
- fix_dates(@handle[-1].values)
+ self[result_count-1]
end
def rest
@@ -206,7 +206,7 @@ class RDBI::Driver::PostgreSQL < RDBI::Driver
end
def [](index)
- fix_dates(@handle[index].values)
+ fix_dates(fetch_range(index, index)[0])
end
def last_row?
@@ -281,7 +281,7 @@ class RDBI::Driver::PostgreSQL < RDBI::Driver
def initialize( query, dbh )
super( query, dbh )
- @stmt_name = ('stmt_' + Time.now.to_f.to_s).tr('.', '_')
+ @stmt_name = ('stmt_' + self.object_id.to_s + Time.now.to_f.to_s).tr('.', '_')
ep = Epoxy.new( query )
@index_map = ep.indexed_binds