Reputation: 51
I have a SQL statement stored inside a variable:
I get this "statement" from the ".sql" file using file operations
set statement "SELECT build_package,
replace(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(substring(build_package, length(build_package) - position('/' in reverse(build_package)) + 2), '-BSD',''),'-LNX',''),'-esxi',''),'.repo',''),'.vmdk',''),'.qcow2',''),'.rpm',''),'.tbz',''),'-version',''),'.el7.x86_64','') ,'-NR',''),'-kvm',''),'-x86_64',''),'ptsvpl','svpts'),'spbvpl','svspb'),'sdevpl','svsde'),'-amd64',''),'.noarch',''),'.el6',''),'.x86_64',''),'tsevpl','svtse')
FROM scheduler_jobs WHERE id = '1617075' order by id DESC"
here the where clause is e.g.: WHERE id = 1617075
the id is dynamically generated.
So I need to first store the sql without WHERE id = 1617075
clause and add the where clause later when i get it inside the 'statement' variable.
Basically add some data called $id
in the statement.
Upvotes: 0
Views: 192
Reputation: 4078
Use a prepared statement with bound variables.
The example from the documentation:
set statement [db prepare {
select phone_num from directory
where first_name = :firstname and last_name = :lastname
}]
set firstname Fred
set lastname Flintstone
As the documentation explains,
The prepare object command against the connection accepts arbitrary SQL code to be executed against the database. The SQL code may contain bound variables, which are strings of alphanumeric characters or underscores (the first character of the string may not be numeric), prefixed with a colon (:). If a bound variable appears in the SQL statement, and is not in a string set off by single or double quotes, nor in a comment introduced by --, it becomes a value that is substituted when the statement is executed. A bound variable becomes a single value (string or numeric) in the resulting statement.
Drivers are responsible for ensuring that the mechanism for binding variables prevents SQL injection.
In other words, while it is the driver's responsibility, you should be protected against SQL Injection. In other words, if the id
isn't simply 1617075
but rather 1617075; DROP TABLE scheduler_jobs;--
, you should still be okay, because the driver has properly escaped this (instead of you having to do this, missing an edge case which leaves you vulnerable).
For your example, leaving out all the REPLACE
s, this would become
set statement [db prepare {
SELECT build_package
FROM scheduler_jobs
WHERE id = :scheduler_job_id
ORDER BY id DESC
}]
set scheduler_job_id 1617075
Upvotes: 2
Reputation: 3003
Just use string map.
First save your string as a template with a tag in it and remove any part you want to substitute:
set statement "SELECT build_package,
replace(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(substring(build_package, length(build_package) - position('/' in reverse(build_package)) + 2), '-BSD',''),'-LNX',''),'-esxi',''),'.repo',''),'.vmdk',''),'.qcow2',''),'.rpm',''),'.tbz',''),'-version',''),'.el7.x86_64','') ,'-NR',''),'-kvm',''),'-x86_64',''),'ptsvpl','svpts'),'spbvpl','svspb'),'sdevpl','svsde'),'-amd64',''),'.noarch',''),'.el6',''),'.x86_64',''),'tsevpl','svtse')
FROM scheduler_jobs <WHERE_CLAUSE> order by id DESC"
Here the tag I choose is , but you could use what you prefer.
And then:
set query_cmd [string map {<WHERE_CLAUSE> "WHERE id = '123456'"} $statement]
puts $query_cmd
This could be done for many different part of the string you need to change, just use another tag.
set query_cmd [string map {<TAG1> $v1 <TAG2> $v2...} $statement]
You can of course put inside the string a quoted variable like:
...WHERE id = '\$id'...
And then use subst, but I think in this case the string map could be more useful.
Upvotes: 0