Jump to content

Php Move To Pdo Causes Incompatible Type Clashes


Recommended Posts

In an effort to be a little bit safer and better with my queries i moved from mssql_* functions to PDO using sqlsrv.

 

One thing i did not take into account is the use of column type "image" that is used in a few areas.

 

I have a few dozen tables that have a column named NOTE of type "image"

 

I receive this error "Operand type clash: nvarchar is incompatible with image" and i'm wondering if anyone knows what i can do from here. I will note now, there is no changing the database in any fashion.

 

While writing this, i decided to google one last thing and ran across this, https://bugs.php.net/bug.php?id=36961 , which scares me because it's 6 years old, open and i'm wondering if it's the same error.

Edited by iarp

Are you specifically binding the input parameters/value without specifying a data type attribute or are you passing the data values as an array to the ->execute(...) method? Both of these default to string data types.

 

After a little research, the image type, which is now the varbinary(max) type, are large object data types. You would need to specifically bind the value/parameter and specify PDO::PARAM_LOB as the 3rd parameter in the bind method.

CREATE TABLE [dbo].[NOTES](
   [CREATEDDATE] [datetime] NOT NULL,
   [uSERID] [varchar]( NOT NULL,
   [NOTE] [image] NULL,
   [id] [varchar](15) NOT NULL
)

$values = array(
   'CREATEDDATE' => date('Y-m-d H:i:s.000'),
   'USERID' => 'CLIENT',
   'NOTE' => array($noteBody, 'LOB'),
   'id' => $id
);
Database::insert('NOTES', $values);


class Database
{
   public function conn() {
       try {
           $dsn = 'sqlsrv:server=127.0.0.1;Database = Tester';
           $user = 'sa';
           $pass = 'password';
           $conn = new PDO($dsn, $user, $pass);
           $conn->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
           return $conn;
       } catch(PDOException $e) {
           die($e->getMessage());
       }
   }

   public function insert($table, $values = array())
   {
       $dbh = self::conn();

       foreach ($values as $field => $v)
           $ins[] = ':' . $field;

       $ins = implode(',', $ins);
       $fields = implode(',', array_keys($values));
       $sql = "INSERT INTO $table ($fields) VALUES ($ins)";

       $sth = $dbh->prepare($sql);
       foreach ($values as $f => $v)
       {
           if (is_array($v)) {
               switch($v[1]){
                   case 'LOB':
                       $sth->bindValue(':' . $f, $v[0], PDO::PARAM_LOB);
                       break;
                   case 'INT':
                       $sth->bindValue(':' . $f, $v[0], PDO::PARAM_INT);
                       break;
                   case 'STR':
                   default:
                       $sth->bindValue(':' . $f, $v[0], PDO::PARAM_STR);
                       break;
                }
           } else {
               $sth->bindValue(':' . $f, $v, PDO::PARAM_STR);
           }
       }
       return $sth->execute();
       //return $this->lastId = $dbh->lastInsertId();
   }
}

 

Well, it would seem like the editor doesn't like my tabs for indentation.

Edited by iarp

From what I understand reading the docs, if you use PARAM_LOB, then the variable is supposed to be a stream resource (ie, fopen() or similar) rather than just the raw data.  So the code would end up looking something like this:

 

switch($v[1]){
case 'LOB':
	if (!is_resource($v[0])){
		$fp= tmpfile();
		fwrite($fp, $v[0]);
		rewind($fp);
		$v[0] = $fp;
	}
	$sth->bindValue(':' . $f, $v[0], PDO::PARAM_LOB);
	break;

 

That will open a temporary file to write the variable data too.  When the script finishes the file will be automatically deleted.

 

Before adding the if statement using tmpfile:

 

declare @p1 int
set @p1=NULL
exec sp_prepexec @p1 output,N'@P1 nvarchar(23),@P2 nvarchar(6),@P3 nvarchar(35),@P4 nvarchar(15)',N'INSERT INTO NOTES (CREATEDDATE,USERID,NOTE,id) VALUES (@P1,@P2,@P3,@P4)',N'2012-11-24 09:28:54.000',N'CLIENT',N'test test',N'7T0adxMbhDlve4S'
select @p1

 

Afterwards, using the tmpfile:

 


declare @p1 int
set @p1=NULL
exec sp_prepare @p1 output,N'@P1 nvarchar(23),@P2 nvarchar(6),@P3 nvarchar(max),@P4 nvarchar(15)',N'INSERT INTO NOTES (CREATEDDATE,USERID,NOTE,id) VALUES (@P1,@P2,@P3,@P4)',1
select @p1

 

So it would seem that, it's either not preparing the statement correctly, or not executing (even though i call $sth->execute() ). I checked all running statements against the database, that is the only one that calls sp_prepare instead of sp_prepexec.

Edited by iarp
This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.