Can't PDO bind a value to multiple occurrences of a param in a query with a single bindParam()?
I'm surprised, I thought it was possible, but I didn't find any info on php's docs on this, neither on the web. Any clarification / alternative is welcome!
Note : I'm using php 5.3.6 / 5.3.8 (dev/prod)
Example :
Consider this prepared statement :
INSERT INTO table VALUES (:param1, 0), (:param1, 1);
Now, if I bind values to my query:
bindParam(":param1",$my_param1);
I have a PDO error :
SQLSTATE[HY093]: Invalid parameter number
See PDO::prepare
You cannot use a named parameter marker of the same name twice in a prepared statement
Related
In the mysql CLI I have prepared a statement like this:
PREPARE registrarUser FROM 'INSERT INTO Users (Users_name,Email,pass) values (?,?,?)'
In my database the prepared statements have to be done this way,instead of using a php method like this::
$conn->prepare("INSERT INTO Users (Users_name,Email,pass) VALUES (?, ?, ?)");
So I can't use the prepared statement or bind arguments.
I have tried this query which mimics the required statements in mysql CLI
$query = sprintf('
SET #Users_name = "%s";
SET #Email= "%s";
SET #pass = "%s";
EXECUTE registrarUser USING #Users_name, #Email, #pass;',$Users_name,$Email,$pass);
But it returns the following syntax error:
Errormessage: 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 'SET #Email= "eds#gmail.com"; SET #pass = "Thinkshap2"; EXECUTE registrar' at line 2
Does anyone know if there is a way to do it?
Thank you very much in advance;
No, it's not possible. Prepared statements have the session scope. Whenever you open a new connection in PHP, you open a new MySQL session. You can use PREPARE and EXECUTE in PHP, but both operations have to be done using the same session.
In other words, statements created with PREPARE do not persist on the database server. They only exist for the lifetime of the current session.
The reason why you are getting a syntax error in PHP is because you have concatenated multiple SQL statements together. You can't do that by default in PHP due to security considerations. Execute each one separately. For example, this works:
$stmt = $mysqli->query("PREPARE registrarUser FROM 'SELECT ?'");
$stmt = $mysqli->query("EXECUTE registrarUser USING 32");
Warning. Using PREPARE and EXECUTE from PHP defeats the main purpose of prepared statements usage in PHP. The main advantage is that you can separate variables from SQL syntax. You can't do that with PREPARE and EXECUTE. This is why both PDO and mysqli have prepared statements. Use mysqli::prepare() and mysqli_stmt::execute()
It's not possible.
From the MySQL manual
The scope of a prepared statement is the session within which it is created...
A prepared statement created in one session is not available to other sessions.
This question already has answers here:
Use bound parameter multiple times
(5 answers)
Closed 3 years ago.
I'm having an issue with the PDO statements for ODBC.
I'm using SQL SERVER 7 in Windows Server 2003 and PHP 5.4.x
For eg:
I have the query:
(this is not the actual query but it serves right for the example)
$query = SELECT * FROM table WHERE number = :number OR number = :number
in my php i have:
$conn = new PDO($connectionString);
$conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$statement = $conn->prepare($query);
$statement->bindParam(':number', $someNumber);
$statement->execute();
This throws error
COUNT field incorrect or syntax error
The thing is, bindParam is only binding the FIRST occurrence of :number ... AND trying to bind it again doesn't work either.
Is there a way to bind multiple named params with the same name?
I'm trying not to use positional params using the ? instead
Theoretical you could turn on emulation of prepared statements.
You must include a unique parameter marker for each value you wish to
pass in to the statement when you call PDOStatement::execute(). You
cannot use a named parameter marker of the same name more than once in
a prepared statement, unless emulation mode is on.
http://www.php.net/manual/en/pdo.prepare.php
I don't know too much about MsSQL to be honest, but I am quite sure there is some equivalent to User Defined Variables in MySQL. You could use those instead of parameters like I described in this answer:
https://stackoverflow.com/a/31068865/3391783
Just turn emulation on, changing this setting from false to true:
$conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
I have this function in PHP. I am trying to insert (if it's necessary) and then get the app_id from the table.
private function addApp($bundle_identifier,$os_id) {
$driver = new mysqli_driver();
$driver->report_mode = MYSQLI_REPORT_ALL;
//Insert or update app details
if ($stmt = $this->db->prepare("INSERT IGNORE INTO app (app_identifier,os_id) VALUES (?,?); SELECT app_id FROM app WHERE app_identifier = ? AND os_id = ?")){
$stmt->bind_param("ssss", $bundle_identifier,$os_id,$bundle_identifier,$os_id);
$stmt->execute();
$stmt->bind_result($app_id);
if (!isset($app_id)) {
echo "is set";
$app_id=$stmt->insert_id;
}
}
if($this->db->commit()){
return $app_id;
}
return 0;
}
The issue here is that stmt is always false with the error:
Uncaught exception 'mysqli_sql_exception' with message '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 'SELECT app_id
FROM app WHERE app_identifier = ? AND os_id = ?' at line 1'
The weird thing is that this query works fine in my SQL.
Is that a limitation of mysqli?
According to http://php.net/manual/en/mysqli.prepare.php :
The query must consist of a single SQL statement.
Which basically answers your question. You have to use two db calls for two queries. Or use something like http://php.net/manual/en/mysqli.multi-query.php
The below is kept for information only as it refers PDO, while the question is about mysqli. It's generally useful though.
I think the reason for this working in mysql, but not in mysqli is that the latter supports prepared statements natively, while the former uses emulation. As your expression contains two queries, all bound parameters are given by driver to the first query (out of which is uses two and discards the other two). The second query then gets no parameters and therefore question marks are syntax errors. With prepared statements emulation PHP actually substitutes question marks with the properly escaped values and so forms two valid queries.
You can enable emulation using $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, true), however, this may slightly affect performance.
See also http://www.php.net/manual/en/pdo.setattribute.php
(Side-note: Blocking ! in question titles doesn't stop smart-arses like me putting U+203C Double Exclamation Mark instead :p)
After a quick round of debugging, I found this:
$query = <<<END
SELECT
`column1`, `column2`,
SOME_FUNCTION(`column3`) -- process in PHP instead?
FROM `tablename`
WHERE `condition` BETWEEN ? AND ?
END;
$stmt = $pdo->prepare($query);
$stmt->execute(array(1,10));
And got this:
Uncaught Exception » RuntimeException » PDOException:
SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens
Do you see the problem?
The question mark in the comment -- process in PHP instead? is interpreted as a token to bind a parameter to! PDO then expects three parameters instead of the two that were passed.
Now, obviously the simple solution was to just rewrite the comment, but this feels like I'm avoiding what may be a bigger issue.
Is there perhaps something wrong with PDO, or is there an option I can set to have it understand MySQL comments?
You can have comments, but not those with ? in them or other things like :x that would be interpreted as placeholders.
PDO does not understand SQL syntax. It's treating all that text as something that needs to be examined for placeholders. When emulating prepared statements this is what will happen.
This question already has answers here:
Use bound parameter multiple times
(5 answers)
Closed 3 years ago.
I'm having an issue with the PDO statements for ODBC.
I'm using SQL SERVER 7 in Windows Server 2003 and PHP 5.4.x
For eg:
I have the query:
(this is not the actual query but it serves right for the example)
$query = SELECT * FROM table WHERE number = :number OR number = :number
in my php i have:
$conn = new PDO($connectionString);
$conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$statement = $conn->prepare($query);
$statement->bindParam(':number', $someNumber);
$statement->execute();
This throws error
COUNT field incorrect or syntax error
The thing is, bindParam is only binding the FIRST occurrence of :number ... AND trying to bind it again doesn't work either.
Is there a way to bind multiple named params with the same name?
I'm trying not to use positional params using the ? instead
Theoretical you could turn on emulation of prepared statements.
You must include a unique parameter marker for each value you wish to
pass in to the statement when you call PDOStatement::execute(). You
cannot use a named parameter marker of the same name more than once in
a prepared statement, unless emulation mode is on.
http://www.php.net/manual/en/pdo.prepare.php
I don't know too much about MsSQL to be honest, but I am quite sure there is some equivalent to User Defined Variables in MySQL. You could use those instead of parameters like I described in this answer:
https://stackoverflow.com/a/31068865/3391783
Just turn emulation on, changing this setting from false to true:
$conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);