使用 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
广告
将在 10 秒后关闭
bannerAds