Ruby DBI 提取无法处理 MySQL 中的全零日期

Ruby DBI fetch can't handle all-zero dates in MySQL

我正在尝试使用一些相当简单的 CGI/Ruby 和 DBI 来访问我们的数据库:

#!/usr/bin/ruby -w

require "dbi"
dbh = DBI.connect("dbi:Mysql:my:mydb", "XXXX", "XXXX")

...

query = "select ETA from mydb.mytable where ID = #{someval}"
rows = dbh.execute(query)
while row = rows.fetch() do
    # Do some stuff
    ...
end

大部分时间都可以正常工作,但我打破了记录并出现错误:

/usr/lib/ruby/vendor_ruby/dbd/Mysql.rb:120:in `parse': invalid date (ArgumentError)
        from /usr/lib/ruby/vendor_ruby/dbd/Mysql.rb:120:in `parse'
        from /usr/lib/ruby/vendor_ruby/dbi/row.rb:66:in `block in convert_types'
        from /usr/lib/ruby/vendor_ruby/dbi/row.rb:65:in `each'
        from /usr/lib/ruby/vendor_ruby/dbi/row.rb:65:in `each_with_index'
        from /usr/lib/ruby/vendor_ruby/dbi/row.rb:65:in `convert_types'
        from /usr/lib/ruby/vendor_ruby/dbi/row.rb:75:in `set_values'
        from /usr/lib/ruby/vendor_ruby/dbi/handles/statement.rb:226:in `fetch'
        from /usr/lib/cgi-bin/test:39:in `block in <main>'
        from /usr/lib/cgi-bin/test:36:in `each'
        from /usr/lib/cgi-bin/test:36:in `<main>'

经过一些侦探工作后,我发现它的日期为 0000-00-00,fetch() 不喜欢。未定义的日期是可以的,Perl 中的 DBI 可以处理所有零日期,只是 Ruby.

中的 DBI

我可以修复数据库,我会尝试让将值写入数据库的应用程序也修复,但我认为我的 Ruby 应该对这些事情有弹性。有没有办法解决这个问题,也许使用 rescue 以某种方式?

这是我想出的解决方案:

query = "select ETA from mydb.mytable where ID = #{someval}"
rows = dbh.execute(query)
begin
    while row = rows.fetch() do
        # Do some stuff
        ...
    end
rescue Exception => e
    puts "#{e}<br>\n"
    retry
end

这工作得很好,就好像重试开始一个新的 while 循环一样,行保持其状态,因此提取在下一条记录上恢复。

唯一的问题是很难识别不良记录。为了解决这个问题,我在没有违规字段的情况下发出了更多查询。我有点难看的解决方案:

results = Hash.new
hit_error = false

query = "select ETA,UNIQUE_ID from mydb.mytable where ID = #{someval}"
rows = dbh.execute(query)
begin
    while row = rows.fetch() do
        # Do some stuff
        ...
        results[row[1]] = row[0]
    end
rescue Exception => e
    hit_error = true
    retry
end
if hit_error
    query = "select UNIQUE_ID from mydb.mytable where ID = #{someval}"
    rows = dbh.execute(query)
    while row = rows.fetch() do
        id = row[0]
        unless results.has_key?(id)
            begin
                query = "select ETA for mydb.mytable where UNIQUE_ID = #{id} limit 1"
                error = dbh.execute(query)
                error.fetch()    # expect this to hit same error as before
                puts "Unexpected success for UNIQUE_ID #{id}<br>\n"
            rescue Exception => e
                puts "#{e} at UNIQUE_ID #{id}<br>\n"
            end
        end
     end
  end

最后我不确定使用 DBI/Ruby 的有效性如何,它似乎已被弃用。 MySQL/Ruby 也一样。我也试过 Sequel 但收效甚微。

另一种解决方案是使用 fetch_hash。这会将所有数据作为字符串获取,这有点尴尬,因为您需要转换为日期、整数等,但确实让您有机会在显式转换中捕获错误。这也使得识别不良记录变得更加容易:

query = "select ETA from mydb.mytable where ID = #{someval}"
rows = dbh.execute(query)
while row = rows.fetch_hash() do
    begin
        eta = Date.parse row["ETA"]
        # Do something with the ETA
        ...
    rescue Exception => e
        puts "Error parsing date '#{row["ETA"]}': #{e}<br>\n"
        # Make do without the ETA
        ...
    end
end
如果使用 mysql 适配器,

Sequel 可以处理无效日期:

DB = Sequel.connect('mysql://user:password@host/database')
DB.convert_invalid_date_time = :string # or nil
DB.get(Sequel.cast('0000-00-00', Date))
# => "0000-00-00"