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.
Related
I have a script I'm trying to run via sqlplus. At the top of the script, I have "SET DEFINE OFF" set. However, when I run it, I'm receiving this error:
SP2-0552: Bind variable "CHANDAWALA" not declared.
I believe this is happening because I have a couple update statements that include colons within a string like this:
UPDATE ADDRESS SET ADDRESS_STREET_LINE1 = 'Moh: Chandawala'...
UPDATE ADDRESS SET ADDRESS_STREET_LINE1 = 'Moh:chandawala'...
So it's treating them like bind variables. I was under the impression that SET DEFINE OFF would make it treat anything within a quoted string as characters and not interpret anything within, including perceived bind variables.
This obviously isn't the case - So in this situation, what am I supposed to do? Do I have to escape these manually?
Thanks.
EDIT - The posted question at When or Why to use a "SET DEFINE OFF" in Oracle Database doesn't help me since that person is trying to escape ampersands for variable substitution, and not colons as bind variables. I'm looking for a way to tell the client to ignore anything (including colons) in quoted strings.
EDIT - Okay, so it sounds like Oracle shouldn't be interpreting that as a bind variable within quotes. So now the question is, why is it doing that?
Set Define Off is a SQLPlus command and it prevents SQLPlus from treating & as a special character. & is also specific to SQLPlus and have no meaning outside it.
When define is on, the SQLPlus replaces the substitution variables with actual values and then pass it to the Oracle SQL engine.
On the other hand, : are real bind variable that only Oracle SQL engine recognises and look for a to-be-bound value. The : are not recognized as bind meta character by SQLPlus and is passed as such to the SQL engine.
I cannot duplicate your issue. I created a table called dually with a column of a single character. When I attempt to run the update statement, no substitution is permitted.
SQL> UPDATE brianl.dually
2 SET dummy = ':x';
SET dummy = ':x'
*
ERROR at line 2:
ORA-12899: value too large for column "BRIANL"."DUALLY"."DUMMY" (actual: 2,
maximum: 1)
Have you tried using concatenation?
UPDATE ADDRESS SET ADDRESS_STREET_LINE1 = 'Moh' || ':' || Chandawala'
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
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
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
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.