Почему мой простой пример Ruby SQLite3 не работает?

Пытаясь изучить Ruby, я читал Mr. Скромная маленькая рубиновая книга Соседства.

За большинством примеров было очень легко следить, что дало мне хорошее введение в Ruby, однако я не могу легко запускать примеры, связанные с БД.

Я пытаюсь запустить этот код: (немного измененный по сравнению с примером, приведенным в книге)

#!/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

При запуске он вставляет одну строку в базу данных, а затем выдает следующую ошибку:

/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.

Я сейчас на Ubuntu 10.04. Информация о версии:

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$ 

Что я делаю неправильно?


person tim_wonil    schedule 23.11.2010    source источник


Ответы (3)


Я столкнулся с той же проблемой. На github есть ошибка о сбое драйвера SQLite для DBD, когда вы использовать подготовленный оператор INSERT более одного раза. Лично я принял рекомендацию в ответе перейти на RDBI, поскольку Ruby/DBI, по-видимому, не дольше сохраняется. Переход на RDBI потребовал очень минимальных изменений кода.

person Bkkbrad    schedule 02.01.2011
comment
Спасибо за это, я проверю это. - person tim_wonil; 10.01.2011
comment
Является ли RDBI хорошей идеей? Кажется, коммитов там нет, но я не уверен, что это из-за того, что проект был заброшен, или из-за того, что багов почти нет и он редко используется. - person reducing activity; 21.03.2019

Ваше определение таблицы:

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

но вы пытаетесь вставить:

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

Обратите внимание, что "#{i}" — это не целое число, а строка. Измените его на i, как в этом примере:

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

Тогда посмотрите, что произойдет.

person the Tin Man    schedule 23.11.2010
comment
Хороший подбор! Но после замены ничего не изменилось. Я наблюдаю точно такое же поведение. Фактически, для sqlite3 для этих простых вставок не имеет большого значения, определяю ли я столбцы как varchar или int. Я пробовал с слагами (имя, возраст) и все равно получил ту же ошибку. - person tim_wonil; 23.11.2010
comment
Я ожидаю, что он выдаст ту же ошибку, если вы попытаетесь передать числовое значение имени, поскольку это также несоответствие типа. Имя должно быть строкой, а возраст должен быть целым числом/фиксированным числом. - person the Tin Man; 23.11.2010
comment
Другая ошибка, которую вы можете увидеть при вызове базы данных во время тестирования, заключается в том, что у вас есть отдельное или уникальное предложение для поля, которое ограничивает это поле только уникальными значениями. Попытка перезапустить код, который пытается вставить последовательные значения, приведет БД в бешенство, заставляя удалять все строки перед каждым запуском. Но я не думаю, что вы определили какие-либо поля с отдельным предложением. - person the Tin Man; 23.11.2010
comment
SQLite рассматривает типы столбцов скорее как подсказки, чем как строгие правила, которые необходимо соблюдать. На веб-сайте SQLite есть дополнительная информация о том, как работают типы. - person Bkkbrad; 02.01.2011

Я почему-то думаю, что в некоторых библиотеках sqlite3 на Ruby отсутствуют методы сброса и очистки для класса операторов.

Когда один и тот же оператор SQL выполняется повторно, оператор готовится один раз и выполняется с новым набором значений. Но после выполнения оператора и до того, как он будет повторно связан, его необходимо сбросить (и часто очистить). Дело в том, что быстрее сбросить используемый оператор, чем «компилировать и оптимизировать» один и тот же SQL снова и снова. Большинство из вас, вероятно, все это знают... но вот ссылка на документацию по SQLite соответствующей части:

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

Я не вижу методов сброса и очистки в классе SQLite3::Statement, поэтому они могут быть каким-то образом упущены из этой реализации, или есть какой-то другой механизм для автоматического сброса/очистки при повторном использовании, но этот механизм каким-то образом не запускается. Однако в документации об этом даже не упоминается... по крайней мере, я не смог найти.

Я думаю, что в классе SQLite3::Statement отсутствуют методы clear_bindings и reset.

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

person Ryuji    schedule 09.06.2015