Экранирование строк для вставки Ruby SQLite

Я создаю сценарий Ruby для импорта текстового файла с разделителями табуляции, состоящего примерно из 150 тыс. строк, в SQLite. Вот пока:

require 'sqlite3'

file = File.new("/Users/michael/catalog.txt")
string = []

# Escape single quotes, remove newline, split on tabs, 
# wrap each item in quotes, and join with commas
def prepare_for_insert(s)
  s.gsub(/'/,"\\\\'").chomp.split(/\t/).map {|str| "'#{str}'"}.join(", ")
end

file.each_line do |line|
  string << prepare_for_insert(line)
end

database = SQLite3::Database.new("/Users/michael/catalog.db")

# Insert each string into the database
string.each do |str|
  database.execute( "INSERT INTO CATALOG VALUES (#{str})")
end

Сценарий выдает ошибку в первой строке, содержащей одинарную кавычку, несмотря на gsub для выхода из одинарных кавычек в моем методе prepare_for_insert:

/Users/michael/.rvm/gems/ruby-1.9.3-p0/gems/sqlite3-1.3.5/lib/sqlite3/database.rb:91:
in `initialize': near "s": syntax error (SQLite3::SQLException)

Это ошибка в строке 15. Если я проверю эту строку с помощью puts string[14], я увижу, где она показывает ошибку рядом с «s». Выглядит так: 'Touch the Top of the World: A Blind Man\'s Journey to Climb Farther Than the Eye Can See'

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


person michaelmichael    schedule 08.03.2012    source источник


Ответы (1)


Не делайте этого вообще, интерполяция строк и SQL, как правило, плохая комбинация. Вместо этого используйте подготовленный оператор и позвольте драйверу разобраться с кавычками и экранированием:

# Ditch the gsub in prepare_for_insert and...
db  = SQLite3::Database.new('/Users/michael/catalog.db')
ins = db.prepare('insert into catalog (column_name) values (?)')
string.each { |s| ins.execute(s) }

Конечно, вы должны заменить column_name реальным именем столбца; вам не нужно указывать имена столбцов в INSERT, но вы всегда должны это делать. Если вам нужно вставить больше столбцов, добавьте больше заполнителей и аргументов в ins.execute.

Используя prepare и execute должно быть быстрее, безопаснее, проще, и это не заставит вас чувствовать, что вы пишете PHP в 1999 году. .

Кроме того, вам следует использовать стандартный анализатор CSV для анализа ваших вкладок. разделенные файлы, с форматами XSV не очень интересно иметь дело (на самом деле они совершенно злые), и у вас есть более важные дела, чем заниматься их ерундой и пограничными случаями, а что нет.

person mu is too short    schedule 08.03.2012
comment
Идеально. Теперь это работает. Есть 34 колонки. Итак, как ни глупо это выглядит, (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) — это правильный способ выразить ожидаемое количество столбцов? Я также включу парсер CSV. - person michaelmichael; 08.03.2012
comment
@michaelmichael: Да, и 34 аргумента для ins.execute, но вы можете поместить/сохранить аргументы в массив и ins.execute(*array) их, чтобы контролировать глупость. Вы также можете создать заполнитель, используя (['?'] * 34).join(',') (это нормально, поскольку вы точно знаете, с какими строками вы работаете, хотя попытка использовать неизвестные строки в SQL просто напрашивается на неприятности). - person mu is too short; 08.03.2012