How to insert Ruby array items into SQLite? - ruby

I want to execute my INSERT strings within a Ruby file that has the following array:
names_arr = ["Jack", "Susan", "Peter"]
In the same Ruby file, I've required the SQLite3 gem and have this code:
names_arr.each do |each_name|
db.execute('INSERT INTO users (name) VALUES ("#{each_name}");')
end
I am expecting this in my "users" table:
id name
1 Jack
2 Susan
3 Peter
Instead, I'm getting this:
id name
1 #{each_name}
2 #{each_name}
3 #{each_name}
It's clear I'm not interpolating properly, but I've tried a lot of different things and can't figure out where I'm wrong.
I am using SQLite3 and Ruby.

You have several problems in this code:
names_arr.each do |each_name|
db.execute('INSERT INTO users (name) VALUES ("#{each_name}");')
end
You're trying to use Ruby's string interpolation inside a single quoted string but string interpolation only works in double quoted strings (and heredocs, %Q{...} strings, regex literals, ...).
Standard SQL uses single quotes for string literals, not double quotes. Some databases let you get away with double quotes but it is a really bad habit to get into.
You shouldn't use string interpolation to build SQL anyway, you're not hacking PHP in 1999 so you should use placeholders and let the database library deal with the quoting.
The SQLite driver's execute understands ? placeholders so you can say this:
names_arr.each do |each_name|
db.execute('INSERT INTO users (name) VALUES (?)', each_name)
end

Related

SQLite3 database insert using variables

I am writing a script that exports results into a SQLite database. I can not get the code to work when I use variables.
Here is an excerpt:
require 'sqlite3'
require 'shodan'
table_name = "#{Date.today.strftime("%B")}#{Time.now.year}_Findings"
db = SQLite3::Database.new "shodan_test.db"
db.execute ("CREATE TABLE #{table_name} (ip string , ports string)")
results = api.host(target)
ip = results["ip_str"].to_s
ports = results["ports"].to_s
db.execute ("insert into #{table_name} (ip, ports) values (#{ip}, #{ports})")
The code fails at the last line. I can remove the variables and the code works. I'm a bit confused since the CREATE TABLE works with a variable.
Your problem is that you're not quoting your strings. The result is that SQLite sees things like
insert into ... values (192.168.0.6, whatever is in ports)
and that's not valid SQL.
SQLite3::Database#execute understands placeholders so you should use them:
db.execute("insert into #{table_name} (ip, ports) values (?, ?)", ip, ports)
The placeholders (?) in the query will be replaced with properly quoted and escaped versions of their values. Using placeholders avoids all the usual quoting and injection problems that plague using string interpolation for SQL.
Also note that I've removed the space between execute and (. If you put a space there then Ruby thinks your "method calling parentheses" are actually "expression grouping parentheses" and it will complain about not understanding what those commas are doing. If you're only passing one argument (such as in db.execute ("CREATE ...")) then it won't matter because the parentheses are effectively ignored but it does matter when there are multiple arguments. In any case, o.m (arg) is a bad habit to get into, you should say o.m(arg) or leave the parentheses out.

single quote escape inside double quotes using sequel fetch function in ruby

using raw SQL when I use the IN statement inside a query using sequel's fetch function, I can't escape a single quote by writing where stuff IN ...
#values='stuff1\'','stuff2\''
db.fetch("query...where IN (?)", "#{#values}")
outputs query...where stuff IN ('stuff1'',''stuff2') instead of ('stuff1','stuff2')
Quite frustrating that I'd probably have to write a Sequel equivalent for the raw query or use a different ORM just because of this escape issue. Any thoughts?
You should probably do something like:
#values = ['stuff1', 'stuff2']
db.fetch("query...where IN ?", #values)
If I understand the Sequel documentation correctly, using String#lit or Sequel.lit should turn a Ruby string into a literal string and bypass the automatic escaping mechanism; therefore, this should work (untested):
#values='stuff1\'','stuff2\''.lit
db.fetch("query...where IN (?)", "#{#values}")
The usual caveats when working with raw SQL strings (SQL injection attacks, inefficient SQL due to forced re-parsing of statements etc.) apply :-)
This works:
values = Sequel.lit("'stuff1', 'stuff2'")
db.fetch("SELECT * FROM TABLE1 WHERE COL1 IN ?", values)
See also: How to pass a list to an IN clause via a placeholder with Ruby Sequel

SQLAlchemy: Force column alias quoting

I want SQLAlchemy to generate the following SQL code:
SELECT t171 AS "3Harm" FROM production
I've been playing around with something similar to this SQLAlchemy ORM snippet:
session.query(Production.t171.label('3harm'))
The problem here is that this doesn't properly quote "3harm" in the generated SQL. Instead of "3harm" this generates the unquoted 3harm, which is invalid because it starts with a numerical character and therefore raises the following Oracle exception:
ORA-00923: FROM keyword not found where expected
I can get this to work by capitalizing any character in the alias name:
session.query(Production.t171.label('3Harm'))
But I would still prefer to use all lowercase column names since the rest of my program is standardized for all lowercase. Any idea how to force quote the lowercase version?
Found the solution while looking for something else.
Any column can be forced to use quotes with column.quote = True.
So for the original example:
column = Production.t171.label('3harm')
column.quote = True
session.query(column)
Success!
The SQL you want to generate isn't valid; rather than this:
SELECT t171 AS '3Harm' FROM production
... you need the identifier to be enclosed in double quotes, not single quotes:
SELECT t171 AS "3Harm" FROM production
So it looks like you should be able to do this:
session.query(Production.t171.label('"3harm"'))
or maybe:
session.query(Production.t171.label("3harm"))
But I don't use SQLAlchemy and I don't have any way to check if either is valid; you might need to escape the double quotes in the first one, for instance, though from this perhaps the second is more likely to work... and I don't understand why 3Harm would work unquoted.

VBScript SQL select query clarification and variable substitution

I have read this entry (http://stackoverflow.com/questions/8513185/vbscript-to-correctly-re-format-a-delimited-text-file) many times and still do not understand the .Execute section.
WScript.Echo oTDb.Execute(Replace("SELECT * FROM [#T]", "#T", sTbl1)) _
.GetString( adClipString, , "|", vbCrLf, "" )
The pieces I am having trouble with are the [#T] and "#T".
I know it is the "#T" that is reading the filename in the schema file and and the [#T] must be using the "#T" as a substitute. What I cannot find out is where this is mentioned/spoken about.
Some addition questions I have are:
1. If the filename can be substituted with a variable then what else can?
2. What are the rules for maintaining variables
Do they have to start with the # symbol
Are there any reserved words
If they have to start with the # symbol, does the next character have to be a letter
As I am responsible for #Milton's worry/puzzlement:
There is no variable interpolation/substitution in VBScript. Other languages - e.g. Perl - will splice variables or even expression results into string literals when you mark the replacements with special symbols. No such funny letters in VBScript.
SQL dialects allow parameterized commands in which parts to be replaced are marked by ? and/or names prefixed by symbols like #. But here ADO never sees the #T - VBScript's Replace() function has interpolated the table name before the resulting strings is send to .Execute().
Building complex strings from parts (SQL statements, commandlines for .Run or .Exec, ...) by concatenation is cumbersome. The most important drawback is that you can't (proof) read the string anymore for all those " and &.
A simple workaround is to use Replace(), as in
[sResult = ] Replace("SELECT * FROM [#T]", "#T", sTbl1)
I used the # just for letting the placeholder stand out. As you would have to stack/nest the Replace() calls when you need more substitutions on the template, other strategies are worth considering:
writing a function that takes a template string and a dictionary of replacements to apply Regexp.Replace() to the string
using .NET's System.Text.StringBuilder and its .AppendFormat to do the slicing in a sprintf like style

Escaping single quotes in Ruby

so I'm making a little ruby script to parse a .csv and output a large MySQL query. The problem is some of the names I'm working with have single quotes, which messes up my SQL...
My attempt to gsub the quotes into escape quotes has failed, though I'm not sure why. But sure enough when I open my query.txt file a name like D'vinci would be just that, and not D\'vinci.
What am I doing wrong?
require 'csv'
filename = ARGV[0]
q = "INSERT INTO `table` (`username`, `password`, `firstname`, `lastname`, `email`) VALUES "
CSV.foreach(filename) do |row|
last_name, first_name, email, id = row.each {|c| c.gsub "'", "\'"}
q += "('#{id}', SHA1('password'), '#{first_name}', '#{last_name}', '#{email}'),\n"
end
q += ";"
File.open("query.txt", 'w').write(q)
Do not write your own SQL escaping. Please, always use the methods provided by the appropriate database driver.
If you're using MySQL, either mysql or the newer mysql2 gem, there's an escape function that should handle this for you.
It's not entirely clear why you're writing SQL in the first place when most databases have some kind of import-from-file function. MySQL in particular has LOAD DATA INFILE which can read in many different formats if used correctly.
each returns the receiver. So, your gsub inside that block is doing nothing. Change it to map, and it shall be fine. Or, you can keep the each and use gsub! instead.

Resources