Invalid parameter number error in Codeigniter - sql-server

I am working on a process in Codeigniter to take a user-uploaded image (managed using the CI upload library) and insert it into a varbinary(max) field in a SQLServer database. My controller and model code are as follows.
if($this->upload->do_upload($upload_name)) {
//get temp image
$tmpName = $config['upload_path'] . $config['file_name'];
// Read it into $data variable
$fp = fopen($tmpName, 'rb');
$data = fread($fp, filesize($tmpName));
fclose($fp);
//insert into DB
$this->the_model->storeImage($data, $user_id);
//delete temp image
unlink($config['upload_path'] . $config['file_name']);
}
/***** Function from the_model ************/
function storePropertyImage($image_data, $user_id) {
$my_db = $this->load->database('admin');
$stmt = "INSERT INTO my_table (UserID, ImageData) VALUES (" . $my_db->escape($user_id) . ", " . $my_db->escape($image_data) . ")";
$insert = $my_db->query($stmt);
return $insert;
}
This all seems like it should be OK but when I run the code, I get the error:
Fatal error: Uncaught exception 'PDOException' with message
'SQLSTATE[HY093]: Invalid parameter number: mixed named and positional parameters'
in {my app path}\helpers\mssql_helper.php on line 213
I've done some googling on this error message and the results seem to indicate this is the result of there being a colon character in the $data value being sent to the model, making the DB think that I am trying to pass a named parameter when I am not. However I haven't been able to find any reports that match my specific use case or that have much info on how to correct the error.
I'd appreciate any insight on where I might be tripping up.

$image_data is a binary string. ->escape may not work on it, since it may escape random bytes in it, thus leaving you with a corrupted image. Also the binary string may contain quote characters (or other characters) that is making your query invalid.
Try to encode the binary string as hex before inserting into MySQL. You can use PHP's bin2hex for this.
$escaped_user_id = $my_db->escape($user_id);
$hex_image = bin2hex($image_data);
$stmt = "INSERT INTO my_table (UserID, ImageData) VALUES ({$escaped_user_id}, X'{$hex_image}')";
The X in X{$hex_image} is how MySQL handles literal hex strings: http://dev.mysql.com/doc/refman/5.1/en/hexadecimal-literals.html
If that doesn't work, you can also try UNHEX().
$escaped_user_id = $my_db->escape($user_id);
$hex_image = bin2hex($image_data);
$stmt = "INSERT INTO my_table (UserID, ImageData) VALUES ({$escaped_user_id}, UNHEX('{$hex_image}'))";
EDIT: I didn't notice you were using MSSQL and not MySQL. My bad. In MSSQL, you can insert literal hex strings with 0x.
$escaped_user_id = $my_db->escape($user_id);
$hex_image = bin2hex($image_data);
$stmt = "INSERT INTO my_table (UserID, ImageData) VALUES ({$escaped_user_id}, 0x{$hex_image})";

Related

Storing binary with JDBCTemplate

I have table like
create table test(payload varbinary(max))
I am trying to store text lines in compressed format in the database using the following code
String sql = "insert into test(payload) values (compress(:payload))
MapSqlParametersource msps = new MapSqlParameterSource();
msps.addValue("payload", "some text", Types.VARBINARY)
NamedParameterJdbcTemplate npjt = //;
npjt.update(sql, msps);
This gives the following error -
String is not in a valid hex format
If I provide the datatype in MapSqlParameterSource as VARCHAR, it doesn't give any error, but then using MSSQL's decompress function returns garbage value
select decompress(payload) from test

What causes DBD::ODBC::st fetchrow_array failed: st_fetch/SQLFetch and how to avoid it?

EDIT1:
I don't actually get an empty array as stated below. Instead, I get an empty response body because of the following exception:
DBD::ODBC::st fetchrow_array failed: st_fetch/SQLFetch (long truncated DBI attribute LongTruncOk not set and/or LongReadLen too small) (SQL-HY000) [state was HY000 now 01004]
Which I can see there are posts about. I will look at those to see if I can fix this on my on. Will edit if not successful.
First let me start by saying, I do not know Perl well at all. This could be a careless error — I hope it is. I am building a hash from an array that is returned from SQL or from JavaScript on the front-end and one of the keys in the hash, "short-desc" needs to have the value which in the code below will be coming from a SQL database.
BFHHOTH 15x24S/S +2 UP-HNDWHL-UNSPOKED-GALV 15"x24" flush escape hatch w/hinge, internal handwheel, T-handle on top steel cover and ring
However with the code (removed unnecessary cases from switch):
#!perl
use Switch;
use DBI;
use JSON;
use CGI qw /param/;
use CGI::Carp qw(fatalsToBrowser);
use IO::Compress::Gzip qw(gzip $GzipError);
use URI::Encode;
use URI::Escape;
my $gzip_ok;
my $accept_encoding = $ENV{HTTP_ACCEPT_ENCODING};
if ( $accept_encoding && $accept_encoding =~ /\bgzip\b/ ) {
# $gzip_ok = 1;
}
print "Content-Type: application/json\n";
if ($gzip_ok) {
print "Content-Encoding: gzip\n";
}
print "\n";
my $action = param('ACTION');
my %jsonData;
my #jsonArray;
my $azDSN = DBI->connect('dbi:ODBC:Driver={SQL Server Native Client 10.0};Server=myServer;Database=myDB;Uid=me;Pwd={myPass};Encrypt=yes;Connection Timeout=30;');
switch ($action) {
case "GETINFO" {
my $paramID = param('ID');
getInfo($paramID);
my $json_text = JSON->new->pretty->utf8->encode( \#jsonArray );
if ($gzip_ok) {
my $zipText;
gzip \$json_text, \$zipText,
or die "gzip failed: $GzipError\n";
print $zipText;
}
else {
print $json_text;
}
}
}
sub getInfo {
my $myID = $_[0];
my $statement = <<"SQL";
SELECT
trefQuoteItemsID,
quote_position,
description,
comments
FROM
myDB.dbo.myTable where tID = $myID;
SQL
my $sti = $azDSN->prepare($statement) or die $statement;
$sti->execute() or die $DBI::errstr;
while ( my #row = $sti->fetchrow_array ) {
my %tempData;
%tempData = (
"tref" => $row[0],
"position" => $row[1],
"short_desc" => $row[2],
"comments" => $row[3]
);
$jsonArray[$count] = {%tempData};
$count++;
}
}
An empty array is returned to me on the front-end.
Oddly, if the string is:
BFHHOTH 15x24S/S +2 UP-HNDWHL-UNSPOKED-
the array contains the correct object.
But empty again if the string is:
BFHHOTH 15x24S/S +2 UP-HNDWHL-UNSPOKED-G
Have also tried with strings:
qwertyuiopasdfghjklzxcvbnm1234567890qwe #length is 39
which lets the hash gets built and:
qwertyuiopasdfghjklzxcvbnm1234567890qwer #length is 40
which will return an empty array so hash doesn't get built.
Are there any Perl gurus who have any suggestions?
From what I can tell, it's related to "long object" columns.
DBD::ODBC says:
You can retrieve a lob in chunks like this:
$sth->bind_col($column, undef, {TreatAsLOB=>1});
while(my $retrieved = $sth->odbc_lob_read($column, \my $data, $length)) {
print "retrieved=$retrieved lob_data=$data\n";
}
NOTE: to retrieve a lob like this you must first bind the lob column specifying BindAsLOB or DBD::ODBC will 1) bind the column as normal and it will be subject to LongReadLen and b) fail odbc_lob_read.
NOTE: Some database engines and ODBC drivers do not allow you to retrieve columns out of order (e.g., MS SQL Server unless you are using cursors). In those cases you must ensure the lob retrieved is the last (or only) column in your select list.
NOTE: You can retrieve only part of a lob but you will probably have to call finish on the statement handle before you do anything else with that statement. When only retrieving part of a large lob you could see a small delay when you call finish as some protocols used by ODBC drivers send the lob down the socket synchronously and there is no way to stop it (this means the ODBC driver needs to read all the lob from the socket even though you never retrieved it all yourself).
NOTE: If your select contains multiple lobs you cannot read part of the first lob, the second lob then return to the first lob. You must read all lobs in order and completely or read part of a lob and then do no further calls to odbc_lob_read.
There's no mention if you can retrieve a lob any other way, so I don't know if you have to do it this way, but at least you know one way. But I believe you can work around the problem by increasing the connection's LongReadLen attribute.
You should be able to set the attribute as follows:
my $dbh = DBI->connect($dsn, $user, $passwd, {
...,
LongReadLen => ...,
});
You should also be able to set the attribute as follows:
$dbh->{LongReadLen} = ...;
Hopefully, someone can give you a better answer.

Update query showing exception

BOX_ID column is integer rest are varchar
sql="UPDATE TOYS SET ?=?,?=?,?=? WHERE NAME=? AND LICENSE=? AND BOX_ID=? ";
p_statement2=connection.prepareStatement(sql);
p_statement2.setString(1, "NAME");
p_statement2.setString(2, "toy1");
p_statement2.setString(3, "VENDOR");
p_statement2.setString(4, "vendor1");
p_statement2.setString(5, "SIZE");
p_statement2.setString(6, "size1");
p_statement2.setString(7, "toyx");
p_statement2.setString(8, "license1");
p_statement2.setInt(9, 11);
try
{
p_statement2.executeUpdate();
}
catch(Exception kl)
{
kl.toString();
kl.printStackTrace();
p_statement2.close();
}
getting the following exception-
java.sql.SQLException: ORA-01747: invalid user.table.column, table.column, or column specification
It is expected. When you bind parameter using setString method, parameter value will be escaped and put in single/double quotes. In this case, your column name will also be put in single/double quotes and your database will throw error in this case.
Why did you set column names like ?? You can't do this. Try something like:
sql="UPDATE TOYS SET Name=?, Vendor=?, Size=? WHERE NAME=? AND LICENSE=? AND BOX_ID=? ";
p_statement2=connection.prepareStatement(sql);
p_statement2.setString(1, "toy1");
p_statement2.setString(2, "vendor1");
p_statement2.setString(3, "size1");
p_statement2.setString(4, "toyx");
p_statement2.setString(5, "license1");
p_statement2.setInt(6, 11);
try
{
p_statement2.executeUpdate();
}
catch(Exception kl)
{
kl.toString();
kl.printStackTrace();
p_statement2.close();
}
Try this one
//assign values dynamically
String col1 = "NAME";
String col2 = "VENDOR";
String col3 = "SIZE";
//create sql
sql="UPDATE TOYS SET "+col1+"=?,"+col2+"=?,"+col3+"=? WHERE NAME=? AND LICENSE=? AND BOX_ID=? ";
p_statement2=connection.prepareStatement(sql);
p_statement2.setString(1, "toy1");
p_statement2.setString(2, "vendor1");
p_statement2.setString(3, "size1");
p_statement2.setString(4, "toyx");
p_statement2.setString(5, "license1");
p_statement2.setInt(6, 11);
try
{
p_statement2.executeUpdate();
}
catch(Exception kl)
{
kl.toString();
kl.printStackTrace();
p_statement2.close();
}
finally , it's done.
i first made sql string under loop by getting column names by rsmd.getColumnName() method and also appended "=" operator and comma oprator as required. Then passed it to preparedstatement creating code and got it done.
thanks all for your support.

How to insert a file into sql-server via tiny_tds?

In a data importing script:
client = TinyTds.Client.new(...)
insert_str = "INSERT INTO [...] (...) VALUE (...)"
client.execute(insert_str).do
So far so good.
However, how can I attach a .pdf file into the varbinary field (SQL Server 2000)?
I've recently had the same issue and using activerecord was not really adapted for what I wanted to do...
So, without using activerecord:
client = TinyTds.Client.new(...)
data = "0x" + File.open(file, 'rb').read.unpack('H*').first
insert_str = "INSERT INTO [...] (...) VALUE (... #{data})"
client.execute(insert_str).do
To send proper varbinary data, you need to read the file, convert it to hexadecimal string with unpack('H*').first and prepend '0x' to the result.
Here is PHP-MSSQL code to save binary data:
mssql_query("SET TEXTSIZE 2147483647",$link);
$sql = "UPDATE UploadTable SET UploadTable_Data = ".varbinary_encode($data)." WHERE Person_ID = '".intval($p_id)."'";
mssql_query($sql,$link) or
die('cannot upload_resume() in '.__FILE__.' on line '.__LINE__.'.<br/>'.mssql_get_last_message());
function varbinary_encode($data=null) {
$encoded = null;
if (!is_null($data)) {
$a = unpack("H*hex", $data);
$encoded = "0x";
$encoded .= $a['hex'];
}
return $encoded;
}
Here is PHP-MSSQL code to get binary data:
mssql_query("SET TEXTSIZE 2147483647",$link);
$sql = "SELECT * FROM UploadTable WHERE ID = 123";
$db_result = mssql_query($sql,$link);
// work with result like normal
I ended up using activerecord:
require 'rubygems'
require 'tiny_tds'
require 'activerecord-sqlserver-adapter'
..
my_table.create(:file_name => "abc.pdf", :file_data => File.open("abc.pdf", "rb").read)
For SQLServer 2000 support, go for 2.3.x version activerecord-sqlserver-adapter gem.

array deccleration

I have written a code in php to connect and insert into a MSSQL database. i don't know much in php because am new to this. i used odbc to connect database.user can enter his details through the form. after submitting the form the details are getting stored into a database.
while inserting rows into a database am not trying to insert duplicate values . for this i have given if conditions.these conditions are able to notice the user cname and name exist in the database if the same name exist. but the else part after these conditions not working i.e rows are not getting inserted. i put everything inside the while loop. how can i correct it?
this is my code written in php
$connect = odbc_connect('ServerDB','sa', 'pwd');//connects database
$query2="select count(*) from company";//this is needer for loop through
$result2=odbc_exec($connect,$query2);
while(odbc_fetch_row($result2));
{
$count=odbc_result($result2,1);
echo "</br>","$count";
}
$query1="select * from company";
$result1 = odbc_exec($connect, $query1);
# fetch the data from the database
$index=0;
while(odbc_fetch_row($result1))
{
$compar[$count] = odbc_result($result1, 1);
$namearray[$count] = odbc_result($result1, 2);
if($compar[$count]==$_POST['cname'])
{
echo "<script> alert(\"cname Exists\") </script>";
}
else if($namearray[$count]==$_POST['name'])
{
echo "<script> alert(\"Name Exists\") </script>";
}
else {
$query=("INSERT INTO company(cname,name) VALUES ('$_POST[cname]','$_POST[name]') ");
$result = odbc_exec($connect, $query);
echo "<script> alert(\"Row Inserted\") </script>";
} }
You generally don't allocate memory in PHP. The PHP interpreter handles it for you. Just go ahead and assign elements to your array and all the memory allocation is taken care of for you.
$index = 0;
Then you do $index++ until the last index is reached.
So index further on stays e.g. 12
So anywhere you can only access the index [12]
In PHP arrays are implemented as what would be a HashMap in Java. It will be sized automatically depending on what you put into it, and there is no way you can give an initial capacity.

Resources