2012-03-08 24 views
5

Estoy creando un script de Ruby para importar un archivo de texto delimitado por tabulaciones de aproximadamente 150k líneas a SQLite. Aquí es hasta el momento:Escapar cadenas para insertar Ruby 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 

Los errores de script hacia fuera en la primera línea que contiene una comilla simple a pesar de la gsub para escapar comillas simples en mi método 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) 

Se erroring a cabo en línea 15. Si inspecciono esa línea con puts string[14], puedo ver dónde está mostrando el error cerca de "s". Se ve así: 'Touch the Top of the World: A Blind Man\'s Journey to Climb Farther Than the Eye Can See'

Parece que se escapó la comilla simple, entonces ¿por qué sigo recibiendo el error?

Respuesta

10

No lo hagas para nada, la interpolación de cadenas y SQL tienden a ser una mala combinación. Utilice una declaración preparada lugar y permitir que el acuerdo con el controlador de cotización y escapar:

# 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) } 

Debe reemplazar column_name con el nombre de columna reales, por supuesto; no es necesario que especifique los nombres de columna en un INSERT, pero siempre debe hacerlo de todos modos. Si necesita insertar más columnas, agregue más marcadores de posición y argumentos al ins.execute.

Usando prepare y execute debe ser más rápido, más seguro, más fácil, y no va a hacer que se sienta como que está escrito en PHP en 1999.

Además, se debe utilizar el standard CSV parser para analizar la pestaña separada, archivos, los formatos XSV no son muy divertidos de tratar (de hecho son francamente malvados) y usted tiene mejores cosas que hacer con su tiempo que lidiar con sus estupideces y casos extremos y qué no.

+0

Perfecto. Ahora funciona. Hay 34 columnas. Entonces, ridículo como parece, '(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,? ,?,?,?,?,?,?,?,?,?,?,?,?,?) 'es la forma correcta de expresar el número esperado de columnas? Incorporaré el analizador CSV también. – michaelmichael

+3

@michaelmichael: Sí, y 34 argumentos para 'ins.execute', pero puede poner/mantener los argumentos en una matriz y' ins.execute (* array) 'para mantener la necedad bajo control. También puedes construir el marcador de posición usando '(['?'] * 34) .join (',')' (esto está bien ya que sabes exactamente con qué cadenas estás trabajando, tratando de usar cadenas desconocidas en SQL es solo pidiendo problemas, sin embargo). –

Cuestiones relacionadas