2
votes

In my attempt to learn Ruby, I've been reading Mr. Neighborly's Humble Little Ruby Book.

Most examples have been very easy to follow giving me a good introduction to Ruby, however I can't run DB related examples easily.

I'm trying to run this code: (slightly modified from the example given in the book)

#!/usr/bin/ruby
require 'rubygems'
require 'dbi'

DBI.connect('DBI:SQLite3:testdb', 'ruby', 'ruby') do | dbh |
  dbh.do('CREATE TABLE slugs(name varchar(20), age int);') rescue puts "TABLE slugs already exists."

  sql = "INSERT INTO slugs (name, age) VALUES (?, ?)"

  dbh.prepare(sql) do |st|
    1.upto(20) do |i|
      st.execute("slug #{i}", "#{i}")
    end
  end

end

When run, it inserts one row in the database, then it gives me the following error:

/var/lib/gems/1.8/gems/sqlite3-ruby-1.3.2/lib/sqlite3/statement.rb:41:in `bind_param': library routine called out of sequence (SQLite3::MisuseException)
    from /var/lib/gems/1.8/gems/sqlite3-ruby-1.3.2/lib/sqlite3/statement.rb:41:in `bind_params'
    from /var/lib/gems/1.8/gems/sqlite3-ruby-1.3.2/lib/sqlite3/statement.rb:37:in `each'
    from /var/lib/gems/1.8/gems/sqlite3-ruby-1.3.2/lib/sqlite3/statement.rb:37:in `bind_params'
    from /var/lib/gems/1.8/gems/dbd-sqlite3-1.2.5/lib/dbd/sqlite3/statement.rb:71:in `bind_params'
    from /var/lib/gems/1.8/gems/dbi-0.4.5/lib/dbi/handles/statement.rb:115:in `execute'
    from /media/dev/ruby-prax/moi.rb:12
    from /media/dev/ruby-prax/moi.rb:11:in `upto'
    from /media/dev/ruby-prax/moi.rb:11
    from /var/lib/gems/1.8/gems/dbi-0.4.5/lib/dbi/handles/database.rb:61:in `prepare'
    from /media/dev/ruby-prax/moi.rb:10
    from /var/lib/gems/1.8/gems/dbi-0.4.5/lib/dbi/handles/driver.rb:41:in `connect'
    from /var/lib/gems/1.8/gems/dbi-0.4.5/lib/dbi.rb:148:in `connect'
    from /media/dev/ruby-prax/moi.rb:5
TABLE slugs already exists.

I am on Ubuntu 10.04 at the moment. Version info:

tlee@tim-ubuntu:/media/dev/ruby-prax$ ruby -v
ruby 1.8.7 (2010-01-10 patchlevel 249) [x86_64-linux]
tlee@tim-ubuntu:/media/dev/ruby-prax$ gem list

*** LOCAL GEMS ***

abstract (1.0.0)
daemons (1.1.0)
dbd-mysql (0.4.4)
dbd-odbc (0.2.5)
dbd-sqlite3 (1.2.5)
dbi (0.4.5)
deprecated (3.0.0, 2.0.1)
erubis (2.6.6)
eventmachine (0.12.10)
extlib (0.9.15)
json_pure (1.4.6)
mysql (2.8.1)
rack (1.2.1)
sqlite3-ruby (1.3.2)
thin (1.2.7)
thor (0.14.1)
tlee@tim-ubuntu:/media/dev/ruby-prax$ sqlite3 --version
3.6.22
tlee@tim-ubuntu:/media/dev/ruby-prax$ 

What am I doing wrong?

3

3 Answers

3
votes

I experienced the same problem. There is an issue on github about the SQLite driver for DBD failing when you use a prepared INSERT statement more than once. Personally, I've taken the recommendation in the answer there of moving to RDBI, since Ruby/DBI is apparently no longer being maintained. Moving to RDBI required very minimal code changes.

1
votes

Your table definition is:

slugs(name varchar(20), age int);

but you are trying to insert:

st.execute("slug #{i}", "#{i}")

Note that "#{i}" is not an integer, it's a string. Change it to i, as in this example:

st.execute("slug #{i}", i) 

Then see what happens.

0
votes

I somehow think that several of sqlite3 libraries on Ruby are missing reset and clear methods for the statement class.

When same SQL statement is executed repeatedly, a statement is prepared once and executed with a fresh set of values. But after the statement is executed and before it is re-bound, it needs to be reset (and often cleared). The point is that it is faster to reset a used statement than "compiling and optimizing" the same SQL over and over again. Most of you probably know all this... but here's the link to the SQLite documentation of the relevant part:

https://www.sqlite.org/c3ref/stmt.html

I don't see reset and clear methods in SQLite3::Statement class, so these might be somehow missed from this implementation, or there is some other mechanism to reset/clear automatically when reused, but that mechanism is somehow not triggered. However, it is not even mentioned in the documentation... at least I couldn't find it.

I think clear_bindings and reset methods are missing from SQLite3::Statement class.

https://github.com/sparklemotion/sqlite3-ruby/issues/158