Provide prepared statement in rails - mysql

I am using rails-4.2.1 and is trying to fetch data from two tables subjects and elective_subjects table in a single query. As rails 4 does not support UNION , I wrote a raw sql query. I want to search by name in both tables. My code is given below
query = "(SELECT id as id, name as name, reference as reference from subjects where name like '#{search}') UNION (SELECT id as id, name as name, null as reference from elective_subjects where name like '#{search}')"
#subjects = ActiveRecord::Base.connection.execute(query)
It is working but when I provide ' in my search the query breaks. So how can I make it a prepared statement. So that sql injection can be avoided

This question is super old and no cares anymore, but I think it is a valid question, so here's the answer:
query = "(SELECT id as id, name as name, reference as reference from subjects where name like $1)
UNION
(SELECT id as id, name as name, null as reference from elective_subjects where name like $1)"
binds = [ActiveRecord::Relation::QueryAttribute.new('name', search, ActiveRecord::Type::Text.new)]
result = ApplicationRecord.connection.exec_query(query, 'SQL', binds, prepare: true)
#subjects = result.rows
That's how you create and use a prepared statement in rails.

I have solved the issue by escaping the search string using following statement.
search = Mysql2::Client.escape(search)

Related

Concatenation doesn't work in phpmyadmin XAMPP

I tried to use "||" for concatenation:
The query I used:
"SELECT id, name, age, age||id FROM myTable;"
This is the output:
Can anyone tell me why the output is not 201 (in first column) and 162(in second column)?? Also it gives similar outputs when I use two attributes that are of varchar datatype and the above two attributes are of int datatype.
Can anyone tell me why the output is not 201
its because, in mysql, you need to enable PIPES_AS_CONCAT. in order to work with ||
If the PIPES_AS_CONCAT SQL mode is enabled, || signifies the
SQL-standard string concatenation operator (like CONCAT()).
You can set it using phpmyadmin->variables->searchFor SQL_MODE
Refer mysql doc
But i would suggest you to use
CONCAT(columnName1, columnName2, ...)
There is no such concatenation in mySQL. This is Oracle SQL dialect. You have to use the CONCAT function in mysql
SELECT id, name, age, CONCAT(age,id) FROM myTable
visit the Mysql Documentations :
on this link you will find a list of String Functions and Operators
but you not use|| to concatenate caratcters or strings but do this :
SELECT id, name, age, concat(age,id) FROM myTable; or
SELECT id, name, age, concat_ws(' ',age,id) FROM myTable; if you want to space the age with id like this for example 23 1. 23 for age and 1 for id.

LIKE '[charlist]%' syntax not working in MySQL (phpMyAdmin)

There is table named Students. I want to extract the names of students whose name starts with either 'n' or 'p' or 'y'. I know that in TSQL (MS SQL server) I can write the query as follows and it works:
SELECT * FROM Students WHERE StudentName LIKE '[npy]%'
But when I execute the same query in MySQL (phpmyadmin) I was unable to retrieve the correct result set. I tried converting the letters of the student name into the same case which is mentioned in the charlist. I did bit of googling and found out that in MySQL we need to specify this as a regular expression. I executed the below query and got the expected result.
SELECT * FROM Students WHERE StudentName REGEXP '[[:<:]]n | [[:<:]]p | [[:<:]]y'
I want to know the reason why LIKE '[charlist]%' syntax is not working with MySQL. Is it due to the implementation in MySQL (MySQL doesn't support the syntax) or something wrong with the phpmyadmin version I'm using?
Any help regarding this is highly appreciated. Thanks :)
There is an even shorter way to write your query:
SELECT * FROM Students WHERE StudentName REGEXP '^[npy]'
And if you're concerned about being case sensitive:
SELECT * FROM Students WHERE StudentName REGEXP BINARY '^[npy]'
In MySQL, a REGEXP pattern match succeeds anywhere in the value, which differs from LIKE where the pattern must match the entire value.
The following link will give you a more complete answer:
MySQL Pattern Matching
MySQL :
Case insensitive: SELECT * FROM Students WHERE StudentName RLIKE '^[npy]' ;
Case sensitive : SELECT * FROM Students WHERE StudentName CAST(RLIKE as BINARY) '^[npy]' ;

Nesting an "SQL SELECT STATEMENT" as/inside the REGEX operator

Say I have a table called People with columns PersonID and Name and I can select a Person's Name like:
SELECT Name FROM People WHERE PersonID = 1
which for this example will return 'John'.
I also have another table called ForumPosts with the fields ForumPostID and PostContent where PostContent is just TEXT which for the purpose of this example can be something like "My Name is John" or "John likes football"
Now I want to perform a Query which based on a given initial PersonID will return all rows from ForumPosts where the Person's Name matches a word contained in the PostContent field.
A regex which will match single words (or in this case the person's name) is:
[[:<:]]*Person'sNameHere*[[:>:]]
So ideally I want my SQL logic to be something like:
Select * FROM ForumPosts WHERE PostContent
REGEX [[:<:]](SELECT Name FROM People WHERE PersonID = '1') [[:>:]]
However I am not sure if this is even possible or how I would structure the query.
Sounds like you want to create a regex dynamically. Regexes are just strings in MySQL, so you can just use CONCAT to create the string you want.
SELECT *
FROM ForumPosts
WHERE PostContent
REGEXP CONCAT('[[:<:]]',(SELECT Name FROM People WHERE PersonID = '1'),'[[:>:]]')
Even better, you can use a JOIN instead of a subquery
SELECT ForumPosts.*
FROM ForumPosts
JOIN People ON PersonID = 1
WHERE PostContent REGEXP CONCAT('[[:<:]]',People.Name,'[[:>:]]')
DEMO: http://sqlfiddle.com/#!2/40828/1
You can implement this logic using an exists subquery. If I understand your logic correctly:
select fp.*
from ForumPosts fp
where exists (select 1
from people p
where personid = '1' and
fp.PostContent regex concat('[[:<:]]', name, '[[:>:]]')
)

Override column value in WHERE clause?

I was wondering if it was possible to override column value in the Where clause of a SQL query (MySQL in my case).
To be more clear, here is an example :
Suppose a basic query is :
SELECT lastname, firstname FROM contacts WHERE lastname = "Doe";
Is it possible to force lastname and firstname to return value from an other table, just by modifying what is after the WHERE part ? Something like
SELECT lastname, firstname FROM contacts WHERE lastname = (SELECT name FROM companies);
I am currently testing a web application, and I found a SQL Injection flaw where I can change Doe to whatever I want, but I'm limited with only one query (mysql_query restriction of PHP) and addslashes (so no " and ').
possible could be
SELECT lastname, firstname FROM contacts WHERE lastname = "{0}" UNION SELECT {1} --
where {0} non existed value and {1} data from other tables
UPDATE from wiki example
$res = mysql_query("SELECT author FROM news WHERE id=" . $_REQUEST['id'] ." AND author LIKE ('a%')");
become
SELECT author FROM news WHERE id=-1 UNION SELECT password FROM admin/* AND author LIKE ('a%')
The syntax that you used in your SELECT ... WHERE clause is a standard SQL feature called a subquery.
In the context of your example there is a restriction on the subquery to return just single value. Otherwise your query is a valid SQL and you can change subquery to return multiple values (with implicit OR) using IN operator like this:
SELECT lastname, firstname FROM contacts
WHERE lastname IN (
SELECT name FROM companies
);
You can dig deeper into this subject to uncover correlated subquery.

How to get file extension of file as a result of sql query?

I have a table named datas and I'm executing a query like this:
SELECT linkurl AS DOWNLOADURL,
lastrevlevel AS VERSION,
code AS DESCRIPTION,
created AS RELEASEDATE,
name AS TYPE
FROM datas
WHERE id IN (SELECT child_id
FROM datas _datas
WHERE parent_id = (SELECT Max(id)
FROM datas
WHERE code = 'AN4307SW'))
It returns results like this:
DOWNLOADURL VERSION DESCRIPTION RELEASEDATE TYPE
/artifacts/download.txt 2.0 images 25/6/12 download.txt
In the Type column I am geting name of the file. I need to get the file extension of the file name in the Type column. How can I achieve this?
Examples:
TYPE
.txt
.pdf
.xls
You can use SUBSTRING_INDEX. Like this:
select linkurl as DOWNLOADURL,lastrevlevel as VERSION,
code as DESCRIPTION,created as RELEASEDATE,
SUBSTRING_INDEX(name,'.',-1) as TYPE
from datas where id in
(select child_id from datas _datas
where parent_id=( select max(id) from datas
where code = 'AN4307SW'))
EDIT
If you see the docs on this function I think this will apply well to you requirements.
Returns the substring from string str before count occurrences of the
delimiter delim. If count is positive, everything to the left of the
final delimiter (counting from the left) is returned. If count is
negative, everything to the right of the final delimiter (counting
from the right) is returned. SUBSTRING_INDEX() performs a
case-sensitive match when searching for delim.
This will also handle a case like this:
select SUBSTRING_INDEX('Test.Document.doc','.',-1);
EDIT2
If you are using oracle. Please tag the question in the correct matter next time. There is no SUBSTRING_INDEX in oracle. But what I can see you can do this quite easy:
SELECT SUBSTR('Test.Document.doc', INSTR('Test.Document.doc', '.',-1))
FROM dual;
Full query like this:
select linkurl as DOWNLOADURL,lastrevlevel as VERSION,
code as DESCRIPTION,created as RELEASEDATE,
SUBSTR(name, INSTR(name, '.',-1)) as TYPE
from datas where id in
(select child_id from datas _datas
where parent_id=( select max(id) from datas
where code = 'AN4307SW'))
Reference here
select linkurl as DOWNLOADURL,lastrevlevel as VERSION,
code as DESCRIPTION,created as RELEASEDATE,reverse(substring(reverse(name), 1,charindex('.', reverse(name))-1)) as TYPE
from datas where id in
(select child_id from datas _datas
where parent_id=( select max(id) from datas
where code = 'AN4307SW'))
think you'll need something like this
SELECT REPLACE(name,SUBSTRING(name ,0, CHARINDEX('.', name )),'')
SELECT REVERSE(SUBSTRING(REVERSE(name),1,LOCATE('.',REVERSE(name),1‌​)));
It can be done like this
SELECT SUBSTRING_INDEX(FILE_NAME,"." ,-1) from TABLE_NAME
SELECT
SUBSTRING(file_name,(LENGTH(file_name)-LOCATE('.',REVERSE(file_name)))+2)
FROM <table name> WHERE file_id=<file_id>;