Steve Tuckner just submitted a DBD for SQLite3, how awesome
is that?
I haven't yet the time to integrate it, if somebody else
wants to
handle that they can go ahead.
Also, Steve raises an interesting point about ActiveRecord
... what's
the best way for DBI to work with ActiveRecord and
vice-versa? I
haven't looked heavily at AR's adapter code to know where
there may
be overlap and/or opportunities for consolidation.
Begin forwarded message:
> Anyway, I was doing some work with DBI and needed to a
DBD for
> SQLite3. So I wrote one. It is not complete or great or
anything.
> But it works, and since I see you are involved in
updating DBI, I
> thought I would send it to you!
>
> Also about DBI. I think it is great that you guys are
working on it
> (and would be willing to help myself), but it would be
even better
> if DBI could be added to so that an ActiveRecord
adapter could be
> made to run on top of DBI. That way DBI could become
more relevant
> than it currently is. Just my thoughts....
>
> Steve Tuckner
> require "sqlite3"
>
>
> module DBI
> module DBD
> module Sqlite3
>
> VERSION = "0.0.1"
>
> class Driver < DBI::BaseDriver
> def initialize
> super(VERSION)
> db = nil
> end
>
> def connect(dbname, user, auth, attr)
> hash = Utils.parse_params(dbname)
> begin
> return Database.new( hash["database"],
attr )
> rescue Exception => err
> raise DBI: atabaseE
rror.new("unable to open database", 1)
> end
> end
> end
>
> class Database < DBI::BaseDatabase
> def initialize(name, attr)
> db = SQLite3: atabase.
new( name )
> end
>
> def disconnect
> db.close
> end
>
> def prepare(query)
> return Statement.new(self, query)
> end
>
> def execute(query, *bind_vars)
> statement = Statement.new(self, query)
> statement.bind_params(*bind_vars)
>
> r = statement.execute
> statement
> end
>
> def __prepare(query)
> begin
> db.prepare(query)
> rescue Exception => err
> #puts "EXCEPTION: #{err.message}"
> raise DBI: atabaseE
rror.new("Unable to prepare query: '#
> ' BECAUSE '#{err.message}'", 1)
> end
> end
> end
>
> class Statement < DBI::BaseStatement
> def initialize(db, query)
> db = db
> query = query
> bind_vars_hash = {}
> bind_vars_in_order = []
> statement = db.__prepare(query)
> end
>
> #
> # Binds the value ((|value|)) to a placeholder.
> # The placeholder is represented by ((|param|)), which
is either a
> # (()) representing the name of the
> # placeholder used in the SQL statement (e.g., Oracle:
"SELECT *
> FROM EMP WHERE ENAME = :ename")
> # or a (()) that indicates the number of the
placeholder.
> # Placeholder numbers begin at 1.
> #
> # If ((|value|)) is a (()), then the default
SQL type is
> (()) or (()).
> # If ((|value|)) is a (()) or (()),
the default
> SQL type is (()).
> # If ((|value|)) is a (()), the default SQL
type is
> (()).
> #
> # ((|attribs|)) is not yet used in this version but
could be a
> hash containing more information
> # like parameter type, etc.
> #
> def bind_param(*args)
> param, value, attribs = args
> if (param.kind_of?(String)) then
> bind_vars_hash[param] = value
> else
> bind_vars_in_order[param-1] = value
> end
> end
>
> def bind_params(*bind_vars)
> if bind_vars.size == 1
> if bind_vars.kind_of?(Hash) then
> bind_vars_hash = bind_vars.shift
> elsif bind_vars.kind_of?(Array) then
> bind_vars_in_order = bind_vars.shift
> end
> else
> bind_vars_in_order = bind_vars
> end
> end
>
> def execute
> begin
> if ! bind_vars_in_order.empty? then
> results = statement.execute( bind_vars_in_order)
> elsif ! bind_vars_hash.empty?
> results = statement.execute( bind_vars_hash)
> else
> results = statement.execute()
> end
> rescue Exception => e
> statement.close
> #puts "Got exception: #{e.inspect}"
> if e.kind_of?(SQLite3::SQLException) then
> raise DBI: atabaseE
rror.new("Unable to execute query '#
> { query}' BECAUSE '#{e.message}'", 1) #FIXME:
can we get a errno?
> else
> raise
> end
> end
> rows = []
> columns = results.columns
> types = results.types
> results.each do |row|
> converted_row = convert(row)
> $TRACE.debug 5, "row = #{row.inspect},
converted_row = #
> {converted_row.inspect}"
> rows.push(converted_row)
> end
> end
>
> def finish
> statement.close if statement
> end
>
> def convert(row)
> $TRACE.debug 5, " types = #{ types.inspect}"
> converted_row = []
> row.zip( types).each do |val, dtype|
> case dtype
> when /integer/
> new_val = val.to_i
> when /varchar/
> new_val = val
> end
>
> converted_row.push(new_val)
> end
> converted_row
> end
>
> def rows
> rows
> end
>
> #
> # Fetches the current row.
> # Returns an (()) containing all column data or
(()) if
> # the last column has been read.
> #
> # Note: This method should not return a newly created
object on
> each call;
> # instead, you should return one and the same
(()) object
> but with
> # changed data.
> #
> def fetch
> rows.shift
> end
>
> def fetch_many(count)
> result = rows[0..(count-1)]
> rows = rows[count..-1]
> result
> end
>
> def fetch_all
> result = rows
> rows = []
> result
> end
>
> def cancel
> end
>
> #
> # Returns an (()) of (()) objects, one
for each column.
> # Each (()) object must have at least one key
'name' which
> # value is the name of that column.
> # Further possible values are 'sql_type' (integer,
e.g.,
> DBI::SQL_INT),
> # 'type_name' (string), 'precision' (= column
size), 'scale' (=
> decimal digits),
> # 'default', 'nullable', 'indexed', 'primary'
and 'unique'.
> #
> def column_info
> info = columns.map{|col|
{"name"=>col}}
> #puts "info = #{info.inspect}"
> info
> end
>
> #
> # Returns the RPC (Row Processed Count) of the last
executed
> statement, or
> # (()) if no such exists.
> #
> def rows
> nil
> end
> end
>
> end # module Sqlite3
> end # module DBD
> end # module DBI
Francis Hwang
http://fhwang.net/
_______________________________________________
Ruby-dbi-next mailing list
Ruby-dbi-next rubyforge.org
h
ttp://rubyforge.org/mailman/listinfo/ruby-dbi-next
|