I think it might be a flaw in my pdo fetching data method,
public function fetch_all($query, $params = array())
{
try
{
# prepare the query
$stmt = $this->connection->prepare($query);
# if $params is not an array, let's make it array with one value of former $params
if (!is_array($params)) $params = array($params);
# execute the query
$stmt->execute($params);
# return the result
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
catch (PDOException $e)
{
# call the get_error function
$this->get_error($e);
}
}
All the parameters that have been passed into this method will become strings, but I need integers for sql LIMIT query, such as below
$sql = "
SELECT *
FROM root_pages
ORDER BY root_pages.pg_created DESC
LIMIT ?,?";
items = $connection->fetch_all($sql,array('0','6'));
It returns this error,
2SQLSTATE[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 ''0','6'' at line 32
How can I fix it?
EDIT:
As suggested, I changed the code in my method to this below,
# fetch a multiple rows of result as a nested array ( = multi-dimensional array)
public function fetch_all($query, $params = array())
{
try
{
# prepare the query
$stmt = $this->connection->prepare($query);
# if $params is not an array, let's make it array with one value of former $params
//if (!is_array($params)) $params = array($params);
foreach($params as $k=>$p){
if(is_numeric($p)){
$stmt->bindParam($k+1, $p, PDO::PARAM_INT);
}
else{
$stmt->bindParam($k+1, $p, PDO::PARAM_STR);
}
}
$stmt->execute();
# execute the query
//$stmt->execute($params);
# return the result
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
catch (PDOException $e)
{
# call the get_error function
$this->get_error($e);
}
}
$items = $connection->fetch_all($sql,array(0,6));
then I get a different error,
2SQLSTATE[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 ''6'' at line 32
EDIT:
I just changed it to,
if(is_int($p)){..}
but still getting the same error... sigh...
you should pass your parameter with type PDO::PARAM_INT, like:
$sth->bindParam(':limit', $limit, PDO::PARAM_INT);
You can't do this via placeholders.
PDO always quotes params that aren't null, even when they're integers. Normally this isn't such a bad thing, but LIMIT clauses can not handle quoted integers.
You're going to need to fall back to good old fashioned concatenation. Because you know that these are going to be integers, you can treat them safely in your code by calling intval or casting them prior to concatenation.
$limit = intval($thing_that_provides_limit);
$offset = intval($thing_that_provides_offset);
$sql = "
SELECT *
FROM root_pages
ORDER BY root_pages.pg_created DESC
LIMIT {$offset}, {$limit}";
Try removing the quotes around the 0 and the 6:
$connection->fetch_all($sql,array(0,6));
This should make the query:
LIMIT 0,6
With quotes on the 0 and the 6, the query is:
LIMIT '0','6'
EDIT: Call bindParam before you call execute.
foreach($params as $k=>$p){
if(is_int($p)){
$stmt->bindParam($k+1, $p, PDO::PARAM_INT);
}
else{
$stmt->bindParam($k+1, $p, PDO::PARAM_STR);
}
}
$stmt->execute();
And then call fetch_all like so:
$connection->fetch_all($sql,array(0,6));
Try:
$items = $connection->fetch_all($sql,array(0,6));
Note the lack of quotes around the 0 and 6 - this makes PHP treat them as the integers they are, and not the strings you had.
Related
I am trying to create an update query and I am looping in some set stuff to a var called $str and I cant seem to get it to work.
if (is_numeric($id)) {
if (!empty($values) && !empty($table_name)) {
$str = '';
$sql = "UPDATE `$table_name` SET :update_values WHERE `$column_name` = :id";
// Its one because we dont use ID like that
$i = 1;
foreach ($values as $key => $value) {
if ($key != $column_name) {
// Exclude the last one from having a comma at the end
if ($i == count($values) - 1) {
$str .= "$key='" . $value . "'";
} else {
$str .= "$key='" . $value . "', ";
$i++;
}
}
}
$query = $this->dbh->prepare($sql);
$query->bindValue('update_values', $str, PDO::PARAM_STR);
$query->bindValue(':id', $id, PDO::PARAM_INT);
$query->execute();
return true;
} else {
return false;
}
} else{
return false;
}
}
Output:
Fatal error: Uncaught PDOException: SQLSTATE[42000]: Syntax error or
access violation: 1064 You have an error in your SQL syntax; check the
manual that corresponds to your MariaDB server version for the right
syntax to use near ''note_name=\'yeet\', note_date=\'2020-02-20\',
note_desc=\'asdasdasdasdadsasdads' at line 1
Am I making any obvious mistakes?
Also for the life of me I don't know what the backslashes in front of the values mean.
In MySQL, identifiers cannot be provided as values.
References to columns must appear in the text of the SQL statement, they cannot be provided through bind parameters. This holds true for table names, column names, function names.
There is no workaround; this is a by-design restriction. There's several reasons for this. One of the most straightforward reasons is understanding how a SQL statement gets prepared, the information that is needed to come up with an execution plan, the tables and columns have to be known at prepare time (for the semantic check and privilege check. The actual values can be deferred to execution time.
Bind placeholders are for providing values, not identifiers.
With the code given, what MySQL is seeing something along the lines of
UPDATE `mytable` SET 'a string value' WHERE `id_col` = 42
And MySQL is balking at the 'a string value'.
We can (and should) use bind parameters for values.
We could dynamically generate SQL text that looks like this:
UPDATE `mytable`
SET `col_one` = :val1
, `col_two` = :val2
WHERE `id_col` = :id
and after the SQL text is prepared into statement, we can bind values:
$sth->bindValue(':val1', $value_one , PDO::PARAM_STR );
$sth->bindValue(':val2', $value_two , PDO::PARAM_STR );
$sth->bindValue(':id' , $id , PDO::PARAM_INT );
and then execute
I have seen similar questions answered already but I can't seem to apply the same solutions to my code.
$a=1;
$results = DB::query('SELECT posts.`postbody`, posts.`filepost`, posts.`likes`, posts.`posted_at`, users.`id`, posts.`id_of_post` FROM posts, users WHERE posts.`post_id` = users.`id` ORDER BY id_of_post DESC LIMIT :a', array(':a'=>$a));
class DB {
private static function connect() {
$pdo = new PDO('mysql:host=127.0.0.1;dbname=SocialNetwork;charset=utf8', 'root', '');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $pdo;
}
public static function query($query, $params = array()) {
$statement = self::connect()->prepare($query);
$statement->execute($params);
if (explode(' ', $query)[0] == 'SELECT') {
$data = $statement->fetchAll();
return $data;
}
}
}
For the record the following code works fine.
$results = DB::query('SELECT posts.`postbody`, posts.`filepost`, posts.`likes`, posts.`posted_at`, users.`id`, posts.`id_of_post` FROM posts, users WHERE posts.`post_id` = users.`id` ORDER BY id_of_post DESC LIMIT 1');
Not ideal, but you could do away with the PDO parameters.
$a = 1;
$sql = "SELECT stuff FROM table LIMIT {$a};";
Then run your query from the $sql string.
As stated in the previous answers if you do not define:
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
You have to define the parameter to be binded as integer:
foreach($params as $key => $value){
if(is_numeric($value))
$statement->bindParam($key,$value,PDO::PARAM_INT);
else
$statement->bindParam($key,$value,PDO::PARAM_STR);
}
$statement->execute();
This is still not a perfect solution, but if you trust the key value pairs(ie they are from code, not user input) it's good enough.
In MySQL's LIMIT clause, it's an error to do this:
LIMIT '1'
Because LIMIT must take an integer, not a string.
If PDO is configured to emulate prepare() (by interpolating values into your SQL string), it's likely to make the interpolated value a quoted string, which will cause an error.
To avoid this, you must use a native integer as your bound variable and you just specify PDO::PARAM_INT.
$statement = self::connect()->prepare($query);
$statement->bindParam('a', $a, PDO::PARAM_INT);
$statement->execute();
That will let the driver know to avoid putting quotes around the interpolated value.
You can also avoid the error if you set the PDO attribute to disable emulated prepares. I always do this, because I don't trust "emulated prepare."
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
See also my tests I wrote up here: Parametrized PDO query and `LIMIT` clause - not working
I'm trying to bind a search term and a limit value to a PDO execute query, but I get error messages no matter which way I do it
public static function searchUsersByName($searchTerm, $results = null) {
//getDBConnection
if($results == null) {
$results = 5;
}
$searchTerm = '%'.$searchTerm.'%';
$query = $database->prepare("SELECT user_id, user_firstname, user_lastname
FROM users_details
WHERE user_firstname LIKE :searchTerm
OR user_lastname LIKE :searchTerm
LIMIT :results");
$query->bindParam(':searchTerm', $searchTerm, PDO::PARAM_STR);
$query->bindParam(':results', $results, PDO::PARAM_INT);
$query->execute();
$search_results = array();
foreach ($query->fetchAll() as $user) {
$search_results[$user->user_id] = new stdClass();
$search_results[$user->user_id]->user_id = $user->user_id;
$search_results[$user->user_id]->user_firstname = $user->user_firstname;
$search_results[$user->user_id]->user_lastname = $user->user_lastname;
}
return $search_results;
}
This is the error I get from this:
PDOStatement::execute(): 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 "5"
It works fine if I take out the bind for LIMIT and just hardcode 5 into the SQL query, but I want to be able to change it if possible
$query->execute(array(':searchTerm' => '%'.$searchTerm.'%', ':results' => $results));
I've tried doing it this way, but of course PDO automatically puts quotes around the values its inserting via this method, and as far as I know you can't put a PDO::PARAM_INT in while using this method.
What am I doing wrong?
Could it be that $results is not an integer? The error seems like your PHP code is posting a string into the query, which would explain the error.
I am guessing this is the issue because of the following piece of code
if($results == null) {
$results = 5;
}
How is $results set in the first place? Via GET/POST? Then it might have been converted to a string.
I've tried your piece of code myself and casting it to an int fixed it for me.
$query->bindParam(':results', intval($results), PDO::PARAM_INT);
I'm trying to create a function that will grab all posts within a table. I also wanted to add a optional LIMIT parameter. Here is an example:
function get_all_posts($conn, $limit = 0) {
if ($limit > 0) {
$stmt = $conn->prepare("SELECT * FROM posts LIMIT :limit");
$stmt->execute(array(
':limit' => $limit
));
$results = $stmt->fetchAll();
return $results ? $results : false ;
} else {
$stmt = $conn->prepare("SELECT * FROM posts");
$stmt->execute();
$results = $stmt->fetchAll();
return $results ? $results : false ;
}
}
If I call the function without using the limit parameter it works and displays all the posts. But if I call the function like this: get_all_posts($conn, "1"); Then I get this error:
Fatal error: Uncaught exception 'PDOException' with message '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 ''1'' at line 1' in /Applications/MAMP/htdocs/sandbox/blog2/functions.php:19 Stack trace: #0 /Applications/MAMP/htdocs/sandbox/blog2/functions.php(19): PDOStatement->execute(Array) #1 /Applications/MAMP/htdocs/sandbox/blog2/index.php(12): get_all_posts(Object(PDO), '1') #2 {main} thrown in /Applications/MAMP/htdocs/sandbox/blog2/functions.php on line 19
Can anyone show me where I've gone wrong?
1 is not a string, so don't put quotes around it here: get_all_posts($conn, 1);
By default PDO execute() treats parameters as a string. As such it is quoting "1". You will need to use bindParam().
Although MySQL may handle this, you should bind this parameter accordingly (as an INT). See this related question for more detail.
Like Sammitch said, its because its a string, not an integer. Use this to fix:
if (is_numeric($limit)) {
$limit = (int)$limit;
...
to clear any variable type issues
In the LIMIT clause you need an integer parameter.
In your code you pass the :limit' parameter's value viaexecute` which are all strings.
A string is not an integer. This mismatch creates your issue.
Instead bin the parameter as integer and you are fine.
$stmt = $conn->prepare("SELECT * FROM posts LIMIT :limit");
$stmt->bindParam(':limit', $limit, PDO::PARAM_INT);
$success = $stmt->execute();
$results = $stmt->fetchAll();
As you accept the $limit variable via a function parameter, you should sanitize it's value as well:
$limit = (int) $limit;
That will ensure you are using a variable of type integer when binding it as an integer parameter.
This question already has answers here:
How to apply bindValue method in LIMIT clause?
(11 answers)
Closed 7 years ago.
I'm using PDO in my application. But I have a problem while I'm working with prepared statements in a query that contains LIMIT. What's the problem?
Codes:
$start = 0;
$rows = 20;
$sql = "SELECT * FROM tbl_news ORDER BY date DESC LIMIT ?, ?";
$q = $db->prepare($sql);
$q->execute(array($start , $rows));
Error:
check the manual that corresponds to your MySQL server version for the right syntax to use near ''0', '20''
You can do like this:
$sql = SELECT * FROM tbl_news ORDER BY date DESC LIMIT :start, :rows";
$q = $db->prepare($sql);
$q->bindParam(':start', $start, PDO::PARAM_INT);
$q->bindParam(':rows',$rows, PDO::PARAM_INT);
Regarding to post LIMIT keyword on MySQL with prepared statement , the code below could solve my problem.
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, FALSE);
Thanks Álvaro G. Vicario and Maerlyn
It is a known bug which was fixed in 5.5.6 from memory.
From the article:
LIMIT doesn't allow variables in any context.
Its arguments must be integer constants.
Further Edit: (There is contention on the matter)
User variables are accepted arguments of LIMIT clause in prepared statements, and SQL syntax for prepared statements can be used in stored procedures.
Third Edit:
This link explains that these should work with prepared statements.
I just stumbled upon the same problem. For me, using my own statement class (extending PDOStatement) with my own execute() method fixed it.
This is the class:
class MyPDOStatement extends PDOStatement {
public function execute($input_parameters = null) {
if (is_array($input_parameters)) {
$i = 1;
foreach ($input_parameters as $p) {
// default to string datatype
$parameterType = PDO::PARAM_STR;
// now let's see if there is something more appropriate
if (is_bool($p)) {
$parameterType = PDO::PARAM_BOOL;
} elseif (is_null($p)) {
$parameterType = PDO::PARAM_NULL;
} elseif (is_int($p)) {
$parameterType = PDO::PARAM_INT;
}
// parameters passed to execute() are input-only parameters, so use
// bindValue()
$this->bindValue($i, $p, $parameterType);
$i++;
}
}
return parent::execute();
}
}
To tell PDO to use this statement class instead of the default one, do this:
$db = new PDO(...);
$db->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('MyPDOStatement'));
Now the code in the question will work:
$start = 0;
$rows = 20;
$sql = "SELECT * FROM tbl_news ORDER BY date DESC LIMIT ?, ?";
$q = $db->prepare($sql);
$q->execute(array($start , $rows));
The only thing you have to make shure is that the variables bound to the statement have the correct type, integer. If you have a numeric string, e.g. from the $_GET array, you can do something like this:
if (isset($_GET['start']) && is_numeric($_GET['start'])
&& is_int($_GET['start'] + 0) {
$start = (int) $_GET['start'];
}
I'm not shure if there is an easier way for the last thing, but at least it works fine for me.
date is a reserved word you have to wrap it with back-ticks