PHP PDO & Large Objects (LOB) broken after update - php

A few months ago, my Ubuntu package auto-updated PHP from 7.0.8 to 7.0.13, at which point my script for updating photos stored on a SQL database started failing. I got around this by reinstalling 7.0.8. Last month, I was again auto-updated to 7.0.15 and my script failed again.
My script writes a jpg image to a MS-SQL database, using PDO & FreeTDS, plus Large Objects (LOB) to handle the photo. I emphasise that it worked up to PHP version 7.0.8. The following is a test script isolating my problem.
<?php
$dsn = 'dblib:dbname=photos;host=gary';
$id = 693925;
$dbh = new PDO($dsn, $user, $password);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
try {
$photo = file_get_contents("coco.jpg");
$query = "UPDATE photo_table SET photo = :photo WHERE id = :id";
$stmt = $dbh->prepare($query);
$stmt->bindValue(":photo", $photo, PDO::PARAM_LOB);
$stmt->bindValue(":id", $id, PDO::PARAM_INT);
$stmt->execute();
}
}
catch (PDOException $e) {
echo $e->getMessage();
}
The result is an "Incorrect syntax" error!?
SQLSTATE[HY000]: General error:
102 Incorrect syntax near '����'.[102] (severity 15) [(null)]
Using the latest available PHP version 7.0.15, reading from the database works, including reading the photo as a Large Object. There is no problem writing every other field to the database, it only fails on my image.
Despite searching over the last few weeks, I still have to find someone else reporting the same problem.
I am after any advice, either a change to the code, or some configuration settings to allow LOBs to work again.

I suggest you use bindParam instead of bindValue always because in bindParam
Unlike PDOStatement::bindValue(), the variable is bound as a
reference and will only be evaluated at the time that
PDOStatement::execute() is called.
$photo = file_get_contents("coco.jpg");//change this to below
$photo = fopen($_FILES['file']['tmp_name'], 'rb');
$query = "UPDATE photo_table SET photo = :photo WHERE id = :id";
$stmt = $dbh->prepare($query);
$stmt->bindValue(":photo", $photo, PDO::PARAM_LOB);//change to this below
$stmt->bindParam(":photo", $photo, PDO::PARAM_LOB);
$stmt->bindValue(":id", $id, PDO::PARAM_INT);//change this to below
$stmt->bindParam(":id", $id, PDO::PARAM_INT);
This is just only suggestions check here...... http://php.net/manual/en/pdo.lobs.php & http://www.php.net/manual/en/pdostatement.bindparam.php#refsect1-pdostatement.bindparam-description

My solution/workaround was to convert the binary from the image into hexadecimal representation before sending the data to SQL.
$photo = bin2hex(file_get_contents("coco.jpg"));
converting it back again during the SQL statement.
$query =
"UPDATE photo_table SET photo=CONVERT(varbinary(max), :photo, 2) WHERE id = :id";

Related

PHP - insert in prepared query doesn't work

I have hard time trying to do a simple insert into my sqlite3 db using php7.0
This code prints result correctly, so SELECT's work
$dbconn = new PDO('sqlite:/home/rooter/Desktop/XSS/db/ex1');
$stmtinit = $dbconn->prepare("select count() from tokens;");
$stmtinit->execute();
$rows = $stmtinit->fetch(PDO::FETCH_BOTH);
$tks = $rows[0];
print "tokens_size:".$tks;
But code below doesn't do anything and doesn't throw any error/warning/notice. Table tokens has unchanged size.
$dbconn = new PDO('sqlite:/home/rooter/Desktop/XSS/db/ex1');
$stmt = $dbconn->prepare("insert into tokens(user, token) values ('plaintxt','plaintxt')");
$stmt->execute();
If i try to do that insert manually in db, it works properly.
Following advice from Fred -ii- i turned error mode on
$dbconn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
Which resulted in message: "attempt to write a readonly database". That happened because when I was creating database I was root.

PDO + MsSQL + freetds = wrong characters in INSERT queries

I've noticed a problem during SELECT queries in my internal project, regarding utf8 characters (šđčćž). After I'd fixed problem regarding freetds definition for charset and version in freetds.conf, I've started to receive right characters when I run SELECT queries.
But! Now I'm experiencing problem regarding INSERT/UPDATE queries and with SELECT query when I try to search fields with some of those characters (šđčćž), and that's only with PDO bindValue method.
For example, this is my code:
try {
$pdo = new \PDO(
"dblib:host=$host:$port;dbname=$database",
"$username",
"$password"
);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
echo "There was a problem connecting. " . $e->getMessage();
}
When I try insert values with following code, inserted values in database are right:
$query = "INSERT INTO dbo.TABLE (ID, NAME) VALUES (2, 'Beriša');";
$statement = $pdo->prepare($query);
$statement->execute();
//Result:
//2 Beriša
But, after using bindValue method (which I must use, as I work with Symfony2), problem happens:
$query = "INSERT INTO dbo.TABLE (ID, NAME) VALUES (?, ?);";
$statement = $pdo->prepare($query);
$statement->bindValue(1, 2, \PDO::PARAM_INT);
$statement->bindValue(2, 'Beriša', \PDO::PARAM_STR);
$statement->execute();
//Result:
//2 Beriša
Bellow, you can find my current freetds configuration:
[global]
tds version = 8.0
text size = 20971520
client charset = UTF-8
Database charset coalition is Croatian_CI_AS. Unfortunately, I can't change that, as it's old database, designed to work with old application, and for that application I'm working on web presentation.
I have found a solution after some time. It's not a perfect one, as I need to process each value (it's small fix for code, but still not clean enough). If anyone has another idea to make this one better, post it, pls.
$statement->bindValue(2, iconv('utf-8', 'Windows-1252', 'Beriša'), \PDO::PARAM_STR);

PHP PDO MSSQL Stored Procedure

I'm having problems trying to run a stored procedure on MSSQL2000 using PHP PDO. I've tried all the combinations, but cant get any results apart from Invalid cursor state error.
The procedure is inside a database that is used by another application. I'm just reaching inside it to pull information out to display on PHP. So modifying the stored procedure (even to instead that SET NOCOUNT ON) is a no-no.
The stored procedure takes two arguments - a start and end date. There is also a RETURN_VALUE parameter.
I can run the following command in the SQL Query Analyzer and dumps rows of data fine:
EXEC [availability_enquiry] '08-24-2015 0:0:0.000', '08-26-2015 0:0:0.000'
Seems pretty straight forward, but when I try to code it and run it, I get nothin:
$dbConn = null;
$connectionString = sprintf('%s:Driver=%s;Server=%s;Database=%s;TrustedConnection=yes;', 'odbc', '{SQL Server Native Client 10.0}', 'mypc', 'testdb');
$dbConn = new PDO($connectionString, 'root', '123qew');
$dbConn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sql = "EXEC [availability_enquiry] '08-24-2015 0:0:0.000', '08-26-2015 0:0:0.000'";
$stmt = $dbConn->query($sql, PDO::FETCH_ASSOC);
$data = $stmt->fetch();
print_r($data);
I get an PDO Expection: 'Invalid cursor state' on the $stmt->fetch() line.
$stmt = $dbConn->prepare($sql);
$stmt->execute();
$data = $stmt->fetch();
print_r($data);
Still get an PDO Expection: 'Invalid cursor state'. Must be something to do with the fetch. Try something else:
$data = array();
do {
$results[count($results)] = $stmt->fetchAll(PDO::FETCH_ASSOC);
}
while($stmt->nextRowset());
Still nothing!? Any ideas?
UPDATE1:
Tried another method:
$sql = "{CALL availability_enquiry (:startdate, :enddate)}";
$stmt = $dbConn->prepare($sql);
$startdate = "2015-08-24T00:00:00";
$enddate = "2015-08-26T00:00:00";
$stmt->execute(array(
':startdate'=>$startdate,
':enddate'=>$enddate
));
$data = $stmt->fetch();
print_r($data);
Also tried it as:
$startdate = "2015-08-24T00:00:00";
$stmt->bindParam(':startdate', $startdate, PDO::PARAM_STR);
$enddate = "2015-08-26T00:00:00";
$stmt->bindParam(':enddate', $enddate, PDO::PARAM_STR);
$stmt->execute();
But both give me an 'Invalid character value for cast specification' error message. Tried it with my own and the newly suggested date format.
If I use the same format of the date that is inside the table that the stored procedure uses:
$startdate = "2015-08-11 09:42:18.890";
I get an 'Invalid cursor state' error message. I'm hoping this is one step closer?

How to use mysqli bind_result

im trying to use mysqli with bind_result but all i get is null values. My $stmt
number of rows is greater than 0 so i do have some data in it.
I dont realy understand what value should come into bind_result
I have read at the manual http://php.net/manual/en/mysqli-stmt.bind-result.php
And they dont explain what should i put in the bind_result.
Should i put there the column names? if yes, as strings? how do i get my wanted values?
Here is my code thanks for helping:
$sql = "SELECT * FROM comments WHERE workout_name = ? AND user = ?";
$stmt = $mysqli->prepare($sql) or trigger_error($mysqli->error."[$sql]");
$stmt->bind_param('ss', $workout_name, $user);
$workout_name = "rytg";
$user = "tomer";
$stmt->execute();
$stmt->store_result();
$stmt->bind_result($comment, $commented_user);
if($stmt->num_rows > 0)
{
$response["workouts"] = array();
while ($stmt->fetch())
{
// temp user array
$workouts = array();
$workouts["comment"] = $comment;
$workouts["user"] = $commented_user;
// push single product into final response array
array_push($response["workouts"], $workouts);
}
}
Your only problem is insufficient error reporting
error_reporting(E_ALL);
ini_set('display_errors',1);
Just add these lines at the top of your code and you will be immediately informed of the exact problem with your code.
Note that on the production server you have to turn displaying errors off and logging on
I don't have a working PHP installation next to me at the moment, so I can't verify it, but I believe you might have to bind both parameters and result before you execute the query, like so:
$workout_name = "rytg";
$user = "tomer";
$stmt = $mysqli->prepare($sql) or trigger_error($mysqli->error."[$sql]");
$stmt->bind_param('ss', $workout_name, $user);
$stmt->bind_result($comment, $commented_user);
$stmt->execute();
I'm not too sure about store_result() either. I don't recall having to use it while retrieving the results, so you might want to try running your code without it and see what happens.

Prepared Statement not returning anything

I know this particular query works, as I tested it with unprepared, procedural methods. Here it is:
$name = 'introduction';
$mysqli = new mysqli('localhost', 'user', 'pass', 'db') or die('There was a problem connecting to the database.');
$stmt = $mysqli->prepare("SELECT name, content FROM sections WHERE name = ?");
$stmt->bind_param('s', $name);
$stmt->execute();
$stmt->bind_result($content);
$stmt->fetch();
echo $content;
$stmt->close();
I realized that, since I have an id column as an index in the sections table, I needed to bind that as a result as well, given the above statement at php.net, (thanks again, Bill).
Here's the new code:
$name = 'introduction';
$mysqli = new mysqli('localhost', 'user', 'pass', 'db') or die('There was a problem connecting to the database.');
$stmt = $mysqli->prepare("SELECT name, content FROM sections WHERE name = ?");
$stmt->bind_param('s', $name);
$stmt->execute();
$stmt->bind_result($id, $name, $content);
$stmt->fetch();
echo $content;
$stmt->close();
Thanks again to all who can offer suggestions. (I'm curious: I find it hard to debug when using the OOP style of prepared statements in this way. Is there, for example, an easy way to simply see the query that was actually used?)
If I do the following, just as a quick-and-dirty example:
$name = 'introduction';
#mysql_connect('host', 'user', 'pass');
#mysql_select_db('db');
$query = "SELECT name,content FROM sections WHERE name = '$name'";
$result = mysql_query($query) or die(mysql_error());
while($row = mysql_fetch_object($result)) {
$content = $row->content;
echo $content;
}
My data appears and all is well. If, however, I do the following:
$name = 'introduction';
$mysqli = new mysqli('localhost', 'user', 'pass', 'db') or die('There was a problem connecting to the database.');
$stmt = $mysqli->prepare("SELECT name, content FROM sections WHERE name = ?");
$stmt->bind_param('s', $name);
$stmt->execute();
$stmt->bind_result($name, $content);
$stmt->fetch();
echo $content;
$stmt->close();
Which I believe is correct (feel free to yell if not, of course), I get nothing. What's more, with that code, when I do an html validation (just in case), I get an internal server warning (500), which I take to be a problem with the sql code. Am I just nuts?
I don't see anything wrong with your preparation of the statement or use of parameters, but there is something wrong in your binding results:
http://php.net/manual/en/mysqli-stmt.bind-result.php says:
Note that all columns must be bound
after mysqli_stmt_execute() and prior
to calling mysqli_stmt_fetch().
(emphasis mine)
The above doc should be taken as all columns in your query, not all columns in your table.
Okay, I just tried this myself. If I omit the $name column, it gives this warning:
PHP Warning: mysqli_stmt::bind_result(): Number of bind variables doesn't
match number of fields in prepared statement in mysqli.php on line 9
PHP Stack trace:
PHP 1. {main}() /Users/bill/workspace/PHP/mysqli.php:0
PHP 2. mysqli_stmt->bind_result() /Users/bill/workspace/PHP/mysqli.php:9
But it does fetch the data.
If I bind both $name and $content to the results of the query, it works without error or warning.
So I'm forced to ask you: are you sure there's a row in the database that matches your condition? That is, where name = 'introduction'? Keep in mind that in SQL, string comparisons are case-sensitive by default.
One mistake I see people make frequently is that they connect to a different database in their PHP script than the database they use for ad hoc queries. So you need to be absolutely sure you're verifying that the data exists in the right database.
Shouldn't that be
$stmt->bind_result($name, $content);
As you select 2 columns

Categories