PDO sqlsrv: right trimming on LIKE on char fields - php

I noticed that if you open a connection with PDO :: SQLSRV_ATTR_ENCODING = PDO :: SQLSRV_ENCODING_UTF8 (default configuration) there is a problem when using LIKE with named parameters on char fields.
No automatic trim of the padding spaces is performed, an operation that is performed in all other cases.
How to reproduce the problem:
Create a table and insert data in it:
CREATE TABLE testDB.dbo.TEST_TABLE (
ID_FIELD int IDENTITY(1,1) NOT NULL,
CHAR_FIELD char(15) COLLATE Latin1_General_CI_AS DEFAULT ' ' NOT NULL
CONSTRAINT TEST_TABLEK00 PRIMARY KEY (ID_FIELD)
);
INSERT INTO TEST_TABLE (CHAR_FIELD) VALUES ('Test data'), ('MyString'), ('My data 123');
Then on PHP I get this results
$options = array();
$pdo = new PDO("sqlsrv:Server=testServer;Database=testDB", 'test', 'test', $options);
$stmt = $pdo->prepare("SELECT * FROM TEST_TABLE WHERE CHAR_FIELD = 'Test data'");
$stmt->execute();
$results = $stmt->fetchAll(); //Returns 1 row
$stmt = $pdo->prepare("SELECT * FROM TEST_TABLE WHERE CHAR_FIELD LIKE 'Test data'");
$stmt->execute();
$results = $stmt->fetchAll(); //Returns 1 row
$stmt = $pdo->prepare("SELECT * FROM TEST_TABLE WHERE CHAR_FIELD = :CHAR_FIELD");
$value = 'Test data';
$stmt->bindParam('CHAR_FIELD', $value, PDO::PARAM_STR);
$stmt->execute();
$results = $stmt->fetchAll(); //Returns 1 row
$stmt = $pdo->prepare("SELECT * FROM TEST_TABLE WHERE CHAR_FIELD LIKE :CHAR_FIELD");
$value = 'Test data';
$stmt->bindParam('CHAR_FIELD', $value, PDO::PARAM_STR);
$stmt->execute();
$results = $stmt->fetchAll(); //Returns 0 rows
$stmt = $pdo->prepare("SELECT * FROM TEST_TABLE WHERE CHAR_FIELD LIKE :CHAR_FIELD");
$value = 'Test data ';
$stmt->bindParam('CHAR_FIELD', $value, PDO::PARAM_STR);
$stmt->execute();
$results = $stmt->fetchAll(); //Returns 1 row
$options = array(PDO::SQLSRV_ATTR_ENCODING => PDO::SQLSRV_ENCODING_SYSTEM);
$pdo = new PDO("sqlsrv:Server=testServer;Database=testDB", 'test', 'test', $options);
$stmt = $pdo->prepare("SELECT * FROM TEST_TABLE WHERE CHAR_FIELD LIKE :CHAR_FIELD");
$value = 'Test data';
$stmt->bindParam('CHAR_FIELD', $value, PDO::PARAM_STR);
$stmt->execute();
$results = $stmt->fetchAll(); //Returns 1 row
The behavior of the like together with the named parameter, when opening the connection to the DB in UTF8, is not uniform with all the other behaviors.
Only in that case is the field not automatically trimmed while in all other cases it is. Personally, it seems to me more of a bug than a deliberate behavior.
I find myself managing a huge application, developed over 10 years and which can interface with different databases (mysql, postgres, oracle and sqlserver).
Changing the queries one by one is impossible, I would need a solution at the configuration level (some flags to set in the PDO or on SqlSever itself) or at least find a way to normalize this behavior to all the others automatically via code.

Related

Prepared statements when working with set operations

I've got a simple query that is not so easy to execute in PHP script:
SELECT `title` from `MY_TABLE` WHERE id in (30,32,33,44)
Usually I execute sql queries with prepared statements. I place a bunch of ? and than bind parameters. This time the numbers in parenthesis are an array of data I get from the user.
I tried this, but it does not work:
$ids = [30,32,33,44];
$stmt = $mysqli->prepare("
SELECT `title` from `MY_TABLE` WHERE id in (?)
");
// $stmt->bind_param();
$stmt->bind_param("i",$ids);
$stmt->execute();
$stmt->bind_result($title);
$stmt->store_result();
//fetch
How can I execute a set operation with prepared statements?
UPDATE:
After following your advice I came up with this
$ids = [30,32,33,44];
$questionMarks = rtrim(str_repeat('?,',count($ids)),", ");
$parameters = str_repeat('i',count($ids));
echo $questionMarks."<br>";
echo $parameters."<br>";
$stmt = $mysqli->prepare("
SELECT `title` from `MY_TABLE` WHERE id in (".$questionMarks.")
");
$scene_names = [];
$stmt->bind_param($parameters, $ids); //error here
$stmt->execute();
$stmt->bind_result($title);
$stmt->store_result();
I am still getting an error. This time it says:
Number of elements in type definition string doesn't match number of bind variables
I am not sure why it thinks that the number of elements (what is element in this case?) is wrong.
UPDATE 2:
Instead of:
$stmt->bind_param($parameters, $ids); //error here
I used:
$stmt->bind_param($parameters, ...$ids); //error gone
Taraam. Works fine.
Something like:
$ids = [30,32,33,44];
$types = array();
foreach($ids as $i){
array_push($types,'i');
}
$params = array_merge($ids,$types);
$sqlIN = str_repeat('?,',count($ids));
$sqlIN = rtrim($sqlIN, ',');
//Value of $sqlIN now looks like ?,?,?,?
$sql = "SELECT title from MY_TABLE WHERE id IN ($sqlIN)";
$stmt = $mysqli->prepare($sql);
call_user_func_array(array($stmt, 'bind_param'), $params);
$stmt->execute();
$stmt->bind_result($id);
$stmt->store_result();

PHP PDO search for a value in two or more columns using one string

When I want to find a value from a row using PDO I use the following method:
//Search whether user exists
$sqlQueryEmailLogin = $dbh->prepare("SELECT vendor_id, first_name, last_name, email_login, user_password, passport_id, login_attempts, login_last_attempt FROM $tableVendorDetails WHERE email_login = ?");
$sqlQueryEmailLogin->bindValue(1, $emailLogin);
$sqlQueryEmailLogin->execute();
and the following PHP code for the search field
$emailLogin = 'xyz#abc.com'
Now I'd like to search two columns or more and use the following code
$sql = "SELECT * FROM articles WHERE id = ? AND status = ?";
$stmt = $conn->prepare($sql);
$stmt->bindValue(1, $id);
$stmt->bindValue(2, $status);
$stmt->execute();
I'd like to search the two columns from a string. How should I go about it, please?
The string value i go is from a html form with one input box
I'd like a string that is capable of searching two values from a MySQL table e.g.
$search = $id; and
$seach = $status;
in this case both cancel each other
You could simplify it by using the method described by #gbestard. But you should also do this:
$search = 'asdf'; // fill this with your form input
$sql = "SELECT * FROM articles WHERE id = :id OR status = :status";
$stmt = $conn->prepare($sql);
$stmt->execute(array(
':id' => $search,
':status' => $search,
));
Notice the change to OR in the query, and supplying the $search multiple times...
That's what I'm using
$sql = "SELECT * FROM articles WHERE id = :id AND status = :status";
$stmt = $conn->prepare($sql);
$stmt->execute(array(':id' => $id , ':status' => $status));
Try the following
$sql = "SELECT * FROM articles WHERE id = :id AND status = :status";
$stmt = $conn->prepare($sql);
$stmt->bindValue(':id', $id);
$stmt->bindValue(':status', $status);
$stmt->execute();
See docs http://php.net/manual/en/pdostatement.bindvalue.php
You should use OR instead of AND. That way, you will get all rows that match either by id or by status.
SELECT * FROM articles WHERE id = ? OR status = ?

PDOStatement->prepare and PDOStatement->bindParam() combination not working [duplicate]

This question already has answers here:
Can PHP PDO Statements accept the table or column name as parameter?
(8 answers)
Closed 8 years ago.
I have some code that should loop through values and change entries in a table. The 5 values of the variables $change_val, $column, and $id all echo out correctly, so I assume there is something wrong with my usage of bindParam (but I am not sure what it is).
$connection = new PDO("mysql:host=localhost;dbname=logbook", $username, $password);
$perform_edit = $connection->prepare("UPDATE contacts SET :column = :value WHERE name_id = :name_id");
[Definition of Arrays]
for ($i = 1; $i <= 5; $i++) {
if (!empty($_POST[ $change_array[$i]])) {
$change_val = $_POST[$change_array[$i]];
$column = $column_array[$i];
$id = $_POST["name_id_ref"];
$perform_edit->bindParam(":column", $column, PDO::PARAM_STR);
$perform_edit->bindParam(":value", $_POST[$change_array[$i]], PDO::PARAM_STR);
$perform_edit->bindParam(":name_id", $_POST["name_id_ref"], PDO::PARAM_INT);
$perform_edit->execute();
}
}
The $_POST statement is there because the value I want is actually passed from another file. When I place appropriate echo statements within the loop, though, they all print out their correct value.
I've also tried bindValue, but that did not work either. I see no errors and things at least compile smoothly—just not as they should. Nothing in the table is changed.
What's wrong here?
You cannot use place holders for table or column names it would defeat the purpose of preparing a statement ahead of time if the structure of that statement changed.
You would need to pre-build your prepare statement with the correct column names, whether you name them by hand, string replacement, or implode a list of column names.
I don't have an environment to test on right now but something like:
//Some random values and DB column names
$arrLocation = array ('Victoria','Washington','Toronto','Halifax','Vancouver');
$arrName = array ('Sue', 'Bob', 'Marley', 'Tim', 'Fae');
$arrColumn = array (1 => 'name', 2 => 'age', 3 => 'location');
/* Build column & named placeholders
* $strSet = '`name` = :name, `age` = :age, `location` = :location';
*/
$strSet = '';
foreach ($arrColumn as $column) {
$strSet .= "`$column` = :$column, ";
}
$strSet = rtrim($strSet, ', ');
$connection = new PDO($dsn, $user, $pass);
/*
* Prepared statement then evaluates to:
* UPDATE `table` SET `name` = :name, `age` = :age, `location` = :location
* WHERE `id` = :id;
*/
$stmt = $connection->prepare("UPDATE `table` SET $strSet WHERE `id` = :id;");
$arrChange = array (
1 => $arrName[(rand(0, count($arrName)-1))],
2 => rand(0, 30),
3 => $arrLocation[(rand(0, count($arrLocation)-1))]
);
$idToUpdate = 1;
$stmt->bindParam(':id', $idToUpdate, PDO::PARAM_INT);
foreach($arrChange as $key=>$value) {
$stmt->bindValue(":$arrColumn[$key]", $value);
}
$stmt->execute();

PHP: PDOStatement simple MySQL Select doesn't work

I have the following PHP code doing a very simple select into a table.
$statement = $db->prepare("SELECT * FROM account WHERE fbid = :fbid");
$statement->bindParam(":fbid",$uid, PDO::PARAM_STR,45);
$out = $statement->execute();
print_r($out) // 1;
//$out = $statement->execute(array(':fbid' => $uid)); // also doesn't work
$row = $statement->fetch();
$out is true (success) yet $row is null.
EDIT:
$statement->debugDumpParams();
Outputs
SQL: [40] SELECT * FROM account WHERE fbid = :fbid Params: 1 Key: Name: [5] :fbid paramno=-1 name=[5] ":fbid" is_param=1 param_type=2
If I modify the code as follows:
$statement = $db->prepare("SELECT * FROM account WHERE fbid = $uid");
$out = $statement->execute();
$row = $statement->fetch();
$row contains the record I'm expecting.
I'm at a loss. I'm using the PDO::prepare(), bindParams() etc to protect against SQL Injection (maybe I'm mistaken on that).
EDIT:
In my example, $uid is a numerical string (ie a string containing only numbers). In the database, the column type is VARCHAR(45)
EDIT:
If I change the database type from VARCHAR(45) to BIGINT, both queries work. If I change the type in the database type back to VARCHAR(45) again, it works. So what gives?
Please halp.
You need to check your fbid value. it should be always string if its integer value is greater than 2^32 (unsigned), simply cast by (string)$uid is not work, and sprintf("%.0f",...) will only works when integer value less than 2^52, because on 32-bit OS when a number is greater than 2^31(32 unsigned) PHP will assume it is double type and default precise is only 14 decimal but fbid is 20.
You have to keep fbid in string contains only [0-9] in PHP, doesn't matter it is stored as BIGINT or VARCHAR in MySQL, MySQL accepts only string sql statement and always returns result in string format.
$mi = new mysqli("localhost", "root", "xxx", "test");
$uid = "12379739851403943597"; // Works
//$uid = 12379739851403943597; // never Works
//$uid = (string) 12379739851403943597; // get "1.2379739851404E+19" wrong string !
//$suid = sprintf("%.0f", $uid); // get "12379739851403943936" lost precise
$stmt = $mi->prepare("select * from bitest where id = ?");
$stmt->bind_param('s', $uid);
$stmt->execute();
$stmt->bind_result($id, $name);
$stmt->store_result();
print "numrow: " . $stmt->num_rows . " - \n";
$stmt->fetch();
print "$id - $name \n";
$stmt->free_result();
$stmt->close();
$pdo = new PDO('mysql:host=localhost;dbname=test', 'root', 'xxx');
$sql = "select * from bitest where id = ?";
$sth = $pdo->prepare($sql);
$sth->bindParam(1, $uid, PDO::PARAM_STR);
$sth->execute();
var_dump($sth->fetchAll(PDO::FETCH_ASSOC));
I think there may be an issue with your PDO installation.
$uid = 552192373; // my facebook uid for testing
$statement = $db->prepare("SELECT * FROM users WHERE facebook_uid = :fbid");
$statement->bindParam(":fbid",$uid, PDO::PARAM_STR,45);
$out = $statement->execute();
$row = $statement->fetch(PDO::FETCH_ASSOC);
echo '<pre>';
print_r($row);
echo '</pre>';
returns:
Array
(
[id] => 1
[facebook_name] => Jason Boehm
[facebook_uid] => 552192373
)
It's been a while... try passing a hash to execute instead:
$statement->execute(array( 'fbid' => $uid ));
Maybe try PDO::PARAM_INT
Aside from that, keep in mind bindParam() takes the variable as a reference. Maybe your demo code doesn't show you changing the value of that variable before execute() is called. See bindValue() if needed.
Try dropping the extra parameter,
$statement->bindParam (":fbid", $uid, PDO::PARAM_STR);
(edit)
Are you 100% positive there is no extra whitespace surrounding the UID? Test with trim() and pass by value:
$statement->bindValue (":fbid", trim($uid), PDO::PARAM_STR);

How can I properly use a PDO object for a parameterized SELECT query

I've tried following the PHP.net instructions for doing SELECT queries but I am not sure the best way to go about doing this.
I would like to use a parameterized SELECT query, if possible, to return the ID in a table where the name field matches the parameter. This should return one ID because it will be unique.
I would then like to use that ID for an INSERT into another table, so I will need to determine if it was successful or not.
I also read that you can prepare the queries for reuse but I wasn't sure how this helps.
You select data like this:
$db = new PDO("...");
$statement = $db->prepare("select id from some_table where name = :name");
$statement->execute(array(':name' => "Jimbo"));
$row = $statement->fetch(); // Use fetchAll() if you want all results, or just iterate over the statement, since it implements Iterator
You insert in the same way:
$statement = $db->prepare("insert into some_other_table (some_id) values (:some_id)");
$statement->execute(array(':some_id' => $row['id']));
I recommend that you configure PDO to throw exceptions upon error. You would then get a PDOException if any of the queries fail - No need to check explicitly. To turn on exceptions, call this just after you've created the $db object:
$db = new PDO("...");
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
I've been working with PDO lately and the answer above is completely right, but I just wanted to document that the following works as well.
$nametosearch = "Tobias";
$conn = new PDO("server", "username", "password");
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sth = $conn->prepare("SELECT `id` from `tablename` WHERE `name` = :name");
$sth->bindParam(':name', $nametosearch);
// Or sth->bindParam(':name', $_POST['namefromform']); depending on application
$sth->execute();
You can use the bindParam or bindValue methods to help prepare your statement.
It makes things more clear on first sight instead of doing $check->execute(array(':name' => $name)); Especially if you are binding multiple values/variables.
Check the clear, easy to read example below:
$q = $db->prepare("SELECT id FROM table WHERE forename = :forename and surname = :surname LIMIT 1");
$q->bindValue(':forename', 'Joe');
$q->bindValue(':surname', 'Bloggs');
$q->execute();
if ($q->rowCount() > 0){
$check = $q->fetch(PDO::FETCH_ASSOC);
$row_id = $check['id'];
// do something
}
If you are expecting multiple rows remove the LIMIT 1 and change the fetch method into fetchAll:
$q = $db->prepare("SELECT id FROM table WHERE forename = :forename and surname = :surname");// removed limit 1
$q->bindValue(':forename', 'Joe');
$q->bindValue(':surname', 'Bloggs');
$q->execute();
if ($q->rowCount() > 0){
$check = $q->fetchAll(PDO::FETCH_ASSOC);
//$check will now hold an array of returned rows.
//let's say we need the second result, i.e. index of 1
$row_id = $check[1]['id'];
// do something
}
A litle bit complete answer is here with all ready for use:
$sql = "SELECT `username` FROM `users` WHERE `id` = :id";
$q = $dbh->prepare($sql);
$q->execute(array(':id' => "4"));
$done= $q->fetch();
echo $done[0];
Here $dbh is PDO db connecter, and based on id from table users we've get the username using fetch();
I hope this help someone, Enjoy!
Method 1:USE PDO query method
$stmt = $db->query('SELECT id FROM Employee where name ="'.$name.'"');
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
Getting Row Count
$stmt = $db->query('SELECT id FROM Employee where name ="'.$name.'"');
$row_count = $stmt->rowCount();
echo $row_count.' rows selected';
Method 2: Statements With Parameters
$stmt = $db->prepare("SELECT id FROM Employee WHERE name=?");
$stmt->execute(array($name));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
Method 3:Bind parameters
$stmt = $db->prepare("SELECT id FROM Employee WHERE name=?");
$stmt->bindValue(1, $name, PDO::PARAM_STR);
$stmt->execute();
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
**bind with named parameters**
$stmt = $db->prepare("SELECT id FROM Employee WHERE name=:name");
$stmt->bindValue(':name', $name, PDO::PARAM_STR);
$stmt->execute();
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
or
$stmt = $db->prepare("SELECT id FROM Employee WHERE name=:name");
$stmt->execute(array(':name' => $name));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
Want to know more look at this link
if you are using inline coding in single page and not using oops than go with this full example, it will sure help
//connect to the db
$dbh = new PDO('mysql:host=localhost;dbname=mydb', dbuser, dbpw);
//build the query
$query="SELECT field1, field2
FROM ubertable
WHERE field1 > 6969";
//execute the query
$data = $dbh->query($query);
//convert result resource to array
$result = $data->fetchAll(PDO::FETCH_ASSOC);
//view the entire array (for testing)
print_r($result);
//display array elements
foreach($result as $output) {
echo output[field1] . " " . output[field1] . "<br />";
}

Categories