PDO - quotes are generating MySQL syntax error - php

I'm trying to bind parameters in what I think is the correct way, but I get a 1064 error from MySQL, suggesting that there are too many quotes involved. The written code looks like this:
$db_host = 'localhost';
$db_username = 'root';
$db_password = 'Password123';
$db_database = 'db_test';
$dbh = new PDO("mysql:host=$db_host", $db_username, $db_password);
$stmt = $dbh->prepare("DROP DATABASE :db_database;");
$stmt->bindParam(':db_database', $db_database, PDO::PARAM_STR, 7);
$stmt->execute() or exit(print_r($stmt->errorInfo(), true));
and displays the error message:
Array ( [0] => 42000 [1] => 1064 [2] => You have an error in your SQL
syntax; check the manual that corresponds to your MySQL server version
for the right syntax to use near ''db_test'' at line 1 )
It worked fine when I inserted the variable $db_database the wrong way, i.e. directly into the sql syntax. Also, I have checked that magic_quotes are turned off, if that has any impact on it. Right now I'm stuck, because it feels like I have done things correctly (but obviously I haven't). Can anyone see the error?

You are using emulation it seems, because the database wouldn't even let you PREPARE this (set $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES,false);)
You can NOT name identifiers (column/table/databasenames), functions, etc. in parameters, that is not how prepared statements work, you can only send 'true' strings / floats / ints. In short, if you need an identifier or function name somewhere, it should be in the query before your ->prepare() it.

Try the following:
$dbh->query("DROP TABLE `{$db_database}`");
You should stick with prepare/execute when working with tables rather than databases. Also, you will only see a performance increase with prepare and execute when issuing multiple similar queries. Quote From the PHP Manpage:
For a query that you need to issue multiple times, you will realize
better performance if you prepare a PDOStatement object using
PDO::prepare() and issue the statement with multiple calls to
PDOStatement::execute().

Related

Why can't I bind a variable to a 'SHOW VARIABLE LIKE' query with PDO/MySQL?

I've been digging over the PHP PDO documentation and I can't figure out why this query is failing.
Here's the query:
$var = 'information_schema_stats_expiry';
$stmt = $pdo->prepare('SHOW VARIABLES LIKE :var');
$stmt->execute([':var' => $var]);
When executed I get this error:
PDOException: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '?' at line 1
I know I can work around it with slightly altered queries. These are a few I've tested that do work.
$var = 'information_schema_stats_expiry';
$stmt = $this->pdo->prepare('SHOW VARIABLES LIKE "' . $var . '"');
$stmt->execute();
Or,
$var = 'information_schema_stats_expiry';
$stmt = $this->pdo->prepare('SHOW VARIABLES WHERE variable_name=:var');
$stmt->execute([':var' => $var]);
Or, the same as above, but with
SHOW VARIABLES WHERE variable_name LIKE :var
However, I'm trying to understand why the first query doesn't work. It reminds me of the issues where people try to bind a variable for a LIMIT clause, but it fails because the number gets quoted; but, in this case, the variable name needs to be quoted, so I would think the query would work fine. Is this a bug, or is there a documented reason why this query would be failing?
To sum up, this query works fine when I run it directly through a MySQL client, or as a static query in PHP:
SHOW VARIABLES LIKE "information_schema_stats_expiry"
However, if I try to make a prepared statement with the exact same syntax, it it will fail:
SHOW VARIABLES LIKE :var
Is there any obvious reason this isn't working?
This is running on PHP 8 with MySQL 8.
It's a MySQL limitation. Not all statements can be prepared. (There is a PREPARE syntax which sets up parameter binding, which we don't usually see in PHP/PDO. You ought to be getting a warning of sorts.)
SHOW VARIABLES is definitely exempt:
https://dev.mysql.com/doc/refman/8.0/en/sql-prepared-statements.html#prepared-statements-permitted

PHP escape SQL Query algorithm

I'm currently working with PHP 5.4.x and SQL Server 7 and I'm having TONS of issues with the PDO object for the ODBC Driver (Which is the only one that works on Sql Server 7), Statements throw errors everywhere ....
I finally got it working using PDO::query() method, BUT I need to escape the Input .... And PDO::quote IS NOT WORKING, I red the Documentation on php pdo docs about PDO and it says that PDO::quote is Not well implemented on PDO_ODBC, which might explain why im getting errors.
For Example: this
$escapedString = $pdoObject->quote($myQueryString);
returns False, it does not return the escaped string.
That been said,
Do you know a good way to escape input to prevent SQL INJECTION???
PS: Due to driver issues (old tech) I CANNOT Trust in SQL Statements, so is not an option.
Any ideas??
EDIT:
For Example. This does not work
getQueryFromFile is only retrieving a query from a file.
and SqlServerPdo is just a wrapper class I wrote over the PHP PDO so I get the connection as a Singleton
For the Record, the query actually WORKS, it has been tested on the Sql Server Engine
$conn = SqlServerPdo::connect();
$query = SqlServerPdo::getQueryFromFile('STUDENTS_FIND');
$statement = $conn->prepare($query);
$statement->bindParam(':id', $id, PDO::PARAM_INT);}
$statement->execute();
This throws the error:
text is incompatible with int (SQLExecute[206] at ext\pdo_odbc\odbc_stmt.c:133)
It seems as if the statement is treating the :id param as a text, not as an INT.
bindValue returns the same error

PDO bindParam/Value not working

I am trying to get a database query page to work but cant seem to do so.
my Code so far (here I tried bindValue, but previously tried bindParam and got the same result):
var_dump($_POST);
$dbh = new PDO ("mysql:host=$myServer;dbname=$myDB", $myUser, $myPw);
$columName = $_POST["columName"];
$tblName = $_POST["tblName"];
$valueName = $_POST["valueName"];
$specificValue = $_POST["specificValue"];
$stmt = $dbh->prepare("SELECT :columName FROM :tblName Where :valueName = :specificValue");
$stmt->bindValue(":columName", $columName);
$stmt->bindValue(":valueName", $valueName);
$stmt->bindValue(":tblName", $tblName);
$stmt->bindValue(":specificValue", $specificValue);
$stmt->execute();
$result = $stmt->fetch();
if(empty($result)){echo "empty";}
print_r ($stmt);
print_r($result);
Printing result and $stmt brings following results:
empty
PDOStatement Object ( [queryString] => SELECT :columName FROM :tblName Where :valueName = :specificValue )
What did I do wrong? What could I try to get it to work?
I am new to the whole coding thing, so please ask if I forgot any code or other important information!
Thanks!
Placeholder parameters can only represent VALUES in the query. Tables, field names, sql key words, etc.. are all impossible to use placeholders on.
If you need to build a dynamic query and replace field/table names, then you'll have to use good old string construction methods, and be aware that you'll be opening yourself to SQL injection attacks again:
$sql = "SELECT * FROM $foo WHERE $bar = :baz";
$stmt = $dbh->prepare($sql);
$stmt->bindValue(':baz', $baz);
I'm afraid you need to rethink how parameterised queries work. It's not just a case of magically inserting data in a safe way. It's about distingushing between the structure of a query and the data.
So the database name, the column names, the table names and any SQL keywords are part of the structure of the query. Every time you run the query, they will be the same.
The data, however, can change between running the query.
So the structure needs to be in place when the query is prepared. However, you obviously can't just plonk the $columName variable etc into the query for SQL injection reasons. If you really need to have flexible queries like this (nb that you probably don't) you need to create a whitelist of allowed values, either in your code or retrieved from the database.
Your query is invalid (you're using parameters for object identifiers) but you are not getting any notification because you have neither configured PDO to throw exceptions nor are calling the error check functions manually.
Add the PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION option to PDO's constructor:
$dbh = new PDO ("mysql:host=$myServer;dbname=$myDB", $myUser, $myPw, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
Once you do so, you'll get a prompt exception on the exact issue, e.g.:
PDOException: SQLSTATE[42000]: Syntax error or access violation: 1064
You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use
near ''user' Where 'login' = 'john'' at line 1 in [...]
As you can see, this is trying to run a query like (e.g.):
SELECT 'user_id'
FROM 'user'
Where 'login' = 'john'
Additionally, beware of SQL injection. It's terribly unsafe to compose SQL queries using data from $_POST.

PHP PDO prepared query refuses to execute properly - escaping problem?

I'm having a problem with a query prepared in PHP with PDO. The code:
$link = new PDO("mysql:dbname=$dbname;host=127.0.0.1",$username,$password);
$link->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$query = $link->prepare("SELECT locality_name FROM :passedday GROUP BY locality_name ORDER BY locality_name DESC");
$query->bindParam(":passedday",$day); //Where day is, well, a day passed to the script elsewhere
$query->execute();
$result = $query->fetchAll();
$link = null;
//Do things with the $result.
The error message I am getting is:
SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''05_26_09' GROUP BY locality_name ORDER BY locality_name DESC' at line 1
When I execute the query on the server directly, it returns the appropriate result set without any problem. Any ideas what I'm doing wrong?
TIA.
Edit:
$day is passed as a GET argument. So, http://127.0.0.1/day.php?day=05_26_09 leads to $day = $_GET['day'];.
If 05_26_09 is supposed to bet the table's name, then I guess you've an escaping problem. Is your local operating system different from the live server?
I don't think you can use bindValue()/bindParam() for something else than values (eg. table name, field name). So I'm a bit suprised, that it works on your local system.
PDO uses mysql's C-API for prepared statements.
http://dev.mysql.com/doc/refman/5.0/en/mysql-stmt-prepare.html says:The markers are legal only in certain places in SQL statements. [...] However, they are not allowed for identifiers (such as table or column names)As a rule of thumb I use: "if you can't wrap it in single-quotes in an ad-hoc query string you can't parametrize it in a prepared statement"

mysqli->prepare function returns null

This is a terrible question because I don't have a simple way to reproduce it. However, I'm using the Zend Framework to connect to my MySQL database on OS X. Sometimes a call to the prepare function on a mysqli object returns null. The stated return values for the prepare function are false or a statement object.
I can't figure out where else to look for info on why the prepare statement is failing. Is there any way to get visibility into the prepare process to see why it is failing? All of my problems are coming up while a transaction is open.
Sorry for the lack of specifics, but I really can't nail down why this is happening.
Just to correct ToughPal, you should be using:
mysqli_query($db, "INSERT INTO table (variable1, variable2) VALUES (hello, mynameis);
Remember that you need to have the db connection defined and stated in the query first, before your actual SQL.
Remember to enclose the table name, column names and value data in backtick escapes.
Example prepared statement
$result = $db->query( 'INSERT INTO server (key, value) VALUES (:key, :value)',
array('key' => $foo, 'value' => $bar)
Can you let us know your DB query?
Try and execute your DB query with test data and see if the query works fine to start with. If the query is ok then we can look why the code fails.
Well I managed to find the issue over the weekend but was really only able to fix the symptoms and not the cause.
I didn't include any SQL in the original issue because the problem was happening randomly, the same code would sometimes work and sometimes not. The issue looks like it was a memory pointer problem. Whenever I had a problem Zend Debugger told me that I had a mysqli object. I believe this because otherwise I would've gotten an error when trying to run the prepare function on it. I have a singleton object that acts as a container for my mysqli connection but whenever the prepare function failed, === showed that the mysqli being used was not the same as the mysqli connection in my singleton object.
In the end, Zend Framework's only issue is that it doesn't fail if the the prepare function returns null. If you are seeing this problem use === to verify that the connection is actually the same as the one that you've previously initiated.
if you're doing something like this
$mysqli = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
$query = "...";
$mysqli->prepare($query);
then you can inspect mysqli::$error next to see useful errors about why prepare() failed
print_r($mysqli->error);

Categories