UPDATING an IMAGE column in Sybase with PREPARED statement fails - sybase-ase

This is with ASE 15.7 and ESQL/C. Updating a row in a CURSOR with a prepared statement:
EXEC SQL BEGIN DECLARE SECTION;
CS_IMAGE image[512];
EXEC SQL END DECLARE SECTION;
strcpy(image, "foo");
EXEC SQL PREPARE updt FROM "UPDATE image_test SET nettodaten = ? WHERE CURRENT OF hc_image_test";
EXEC SQL EXECUTE updt USING :image;
fails with the message from ASE:
** SQLCODE=(-201)
** ASE Error
** Procedure *sq1625128094_1900377684ss* expects parameter
Invalid pointer param number 4, pointer value 0x(nil)
, which was not supplied.
What could be the reason for this?
Update 1
I modified the ESQL/C code a bit so it looks like this:
strcpy(anweisung, "UPDATE image_test SET katkey = ?, nettodaten = ? WHERE CURRENT OF hc_image_test");
EXEC SQL PREPARE updt FROM :anweisung;
EXEC SQL EXECUTE updt USING :key, :blobfld;
EXEC SQL CLOSE hc_image_test;
The error remains the same as above. But a look into the produced C-code by CPRE shows an interesting detail:
/*
** SQL STATEMENT: 17
** EXEC SQL EXECUTE updt USING :key, :blobfld;
*/
{
_SQL_CT_HANDLES * _sql;
_sqlinitctx(&_sql, CS_CURRENT_VERSION, CS_TRUE, &sqlca, (long
*)NULL, (CS_CHAR *)NULL);
if (_sql != (_SQL_CT_HANDLES *) NULL)
{
_sql->stmtData.persistent = CS_FALSE;
_sql->stmttype = SQL_EXECUTE;
_sql->connName.lnlen = CS_UNUSED;
_sql->stmtName.fnlen = 4;
strcpy(_sql->stmtName.first_name, "updt");
if ((_sql->retcode = _sqlprolog(_sql)) == CS_SUCCEED)
{
_sql->retcode = ct_dynamic(_sql->conn.command,
CS_EXECUTE, "updt", 4, NULL, CS_UNUSED);
if (_sql->retcode == CS_SUCCEED)
{
_sql->dfmtCS_INT_TYPE.count = 0;
_sql->dfmtCS_INT_TYPE.status = CS_INPUTVALUE;
_sql->retcode = ct_param(_sql->conn.command,
&_sql->dfmtCS_INT_TYPE, &key, (CS_INT)
CS_UNUSED, (CS_SMALLINT) 0);
_sql->dfmtCS_INT_TYPE.status = 0;
}
_sql->retcode = ct_send(_sql->conn.command);
_sql->hastate = (_sql->retcode == CS_RET_HAFAILOVER);
_sql->retcode = _sqlResults(_sql);
_sql->retcode = _sqlepilog(_sql);
}
if (sqlca.sqlcode < 0)
{
error_handler();
}
As one can see above, a pointer to the host variable CS_INT :key is placed with a call to ct_param() into the internal structures, but not any reference to the host variable CS_IMAGE :blobfld. And that's why ASE is complaining with a missing param value because it sees two question marks ? in the prepared UPDATE statement. This looks to me somehow as a bug in the CPRE compiler and not in ASE itself.

I have contacted through our maintenance contract the company SAP (the issue number there is 390674 / 2018) and some engineer is looking into this CPRE issue...
For the moment, I'm adding to the C-code produce by CPRE, short before the ct_send() call, the following lines of code to add the missing reference to the IMAGE column:
/* additional code for host var :blobfld */
if (_sql->retcode == CS_SUCCEED)
{
_sql->dfmtCS_IMAGE_TYPE.count = 0;
_sql->dfmtCS_IMAGE_TYPE.maxlength = (CS_INT) 65535;
_sql->dfmtCS_IMAGE_TYPE.format = CS_FMT_UNUSED;
_sql->dfmtCS_IMAGE_TYPE.status = CS_INPUTVALUE;
_sql->retcode = ct_param(_sql->conn.command,
&_sql->dfmtCS_IMAGE_TYPE, blobfld, (CS_INT) ImageLengthField,
(CS_SMALLINT) 0);
_sql->dfmtCS_IMAGE_TYPE.status = 0;
}
/* end of additional code for host var :blobfld */
_sql->retcode = ct_send(_sql->conn.command);
...
And all works fine.

Related

Fatal Error while calling MySQL stored function from PHP using MySQLi

Server details:
PHP v5.3.5
Using MySQLi library client api version: mysqlnd 5.0.7-dev - 091210 - $Revision: 304625 $
MySQL Server v5.5.9
I have a stored function in MySQL called f_get_owner_locations( _in int ). It constructs a text variable that holds the locations of whichever condos a specific owner owns.
If I run a
SELECT f_get_owner_locations( 3 );
from the MySQL command line, it does what it's supposed to do and returns one row:
+----------------------------+
| f_get_owner_locations( 3 ) |
+----------------------------+
| A-01 |
+----------------------------+
However, whenever I try and run it through PHP using the MySQLi library as such:
$sql = "SELECT f_get_owner_locations( 3 )";
$location = $GLOBALS['db']->fetch( $sql );
I get this error:
Fatal error: Call to a member function fetch_field() on a non-object
in ~/kernel/Database.php on line 328
That line refers to this:
/**
* Binds results from a returning SQL statement to an array we can
* loop through.
*
* #param $statement Statement object we're binding from.
* #return Array of values being returned.
* #since 0.1
*/
private final function _bindResult( $statement )
{
$results = NULL;
$bind = array( );
//Get the result set, so we can loop through the fields.
$result = $statement->result_metadata( );
//Loop through the fields and get a reference to each.
while( $column = $result->fetch_field() ) //<=<=<=LINE 328
$bind[] = &$results[$column->name];
//Do the actual binding.
call_user_func_array( array( $statement, 'bind_result'), $bind );
//Free the memory since we already have the result.
$result->free_result();
return $results;
} //_bindResult
Keep in mind it doesn't fail when the SQL statement doesn't involve a function call. i.e. This works:
$sql = "SELECT `id` FROM `owners`";
$owners = $GLOBALS['db']->fetch( $sql );
But as soon as I add in the need to get the condos they own into it (and this statement works through the MySQL command line as well):
$sql = "SELECT `id`, f_get_owner_locations(`id`) FROM `owners`";
$owners = $GLOBALS['db']->fetch( $sql );
It gives me that error about call to a member function on a non-object.
I'm stumped. Doing a var_dump on $result right before the while loops, in my _bindResults method gives me 1 proper dump, and then stops and that error is there.
object(mysqli_result)#11 (5) {
["current_field"]=>
int(1)
["field_count"]=>
int(1)
["lengths"]=>
NULL
["num_rows"]=>
int(0)
["type"]=>
int(1)
}
Note: The call to f_get_owner_locations is the 2nd field on that select list, so it's not storing the right field count, despite saying that it needs to loop to the correct amount of fields.
Any suggestions to get around this little road block or a confirmation that this is a bug within the MySQLi library or a problem with my binding code would be much appreciated.
UPDATE:
The following code:
mysql_connect( ... );
mysql_query( "select f_get_owner_locations(3)" );
die( mysql_error() );
Gave me this output:
FUNCTION f_get_owner_locations does not exist.
I'm more wondering if this is just a failure on PHP/MySQLi's part than mine?
UPDATE 2:
As requested, the code used to create the function:
drop function if exists f_get_owner_locations;
delimiter |
create function f_get_owner_locations( _in int )
returns text deterministic
begin
declare _output text default "";
declare _location varchar(255);
declare _count int default 0;
declare _done int default 0;
declare _cursor cursor for
select
condos.location
from
owners left join
condo_owners on owners.id = condo_owners.owner left join
condos on condo_owners.condo = condos.id
where
owners.id = _in;
declare continue handler for not found set _done = 1;
open _cursor;
repeat
fetch _cursor into _location;
set_count = _count + 1;
set_output = concat( _output, ", ", _location );
until _done end repeat;
set _count = _count - 1;
close _cursor;
set _output = trim( leading ", " from _output );
set _output = substring( _output from 1 for (_count * 6) );
set _output = trim( trailing ", " from _output );
return _output;
end;|
delimiter ;
Granted with a bit of refactoring that could could be a small bit cleaner, but that's what I used.
MySQL stored procedures can return multiple result sets, where the last result set is the actual data you're interested in. One of the few things that mysqli does correctly is support multiple result sets.
Try checking mysqli::more_results. Also check the disclaimers on the manual page for mysqli::store_result about how mysqli handles multiple result sets wherein one might not have data. You'll end up working with the mysqli_result class instead of mysqli_stmt.
So, after more testing and what not, I find that it's not actually a bug in the MySQLi libraries or in my code.
The Solution? Give the database user the "Execute" permission for the database in MySQL. I wrote and tested the SQL statements and functions while I was logged in as root, not the actual user that the script was using.
Oh the joys of IT.

PHP cannot retrieve [result code] and [output parameter] assigned in the BEGIN CATCH… END CATCH block of SQL server 2016 stored procedure

I have a simple table and a stored proc like the following:
CREATE TABLE dbo.Test
(
TestTime DATETIME
)
INSERT INTO dbo.Test (TestTime)
VALUES('2018-12-21T13:25:45')
CREATE PROCEDURE dbo.TestProc
#pErrorMsg NVARCHAR(512) OUTPUT
AS
BEGIN
DECLARE #result INTEGER
SET #result = 0
SET #pErrorMsg = N'OK';
BEGIN TRY
UPDATE dbo.Test
SET TestTime = '2018-12-21T13:25:45a' -- wrong time value to raise error!
WHERE TestTime = '2018-12-21T13:25:45'
END TRY
BEGIN CATCH
SET #pErrorMsg = ERROR_MESSAGE()
SET #result = 1
END CATCH
RETURN #result
END
--Test the proc, and it seems to work fine:
BEGIN
DECLARE #res INTEGER
SET #res = 0
DECLARE #errMsg NVARCHAR(128)
EXEC #res = dbo.TestProc #errMsg OUTPUT
SELECT #res AS Result, #errMsg AS ErrorMsg
END
The stored proc can return the error code as well as the ERROR_MESSAGE() which I assign in the CATCH block.
Result | ErrorMsg
1 | Conversion failed when converting date and/or time from character string.
My php code:
$sql = "{? = call dbo.TestProc(?)}";
$res = 123;
$errorMsg = utf8_encode("abcd1234");
$paramRes = array(&$res, SQLSRV_PARAM_OUT, SQLSRV_PHPTYPE_INT);
$paramErrorMsg = array(&$errorMsg, SQLSRV_PARAM_OUT);
$params = array($paramRes, $paramErrorMsg);
$affectedRows = 0;
$conn = sqlsrv_connect(<your_server>, <your_credential>);
$stmt = sqlsrv_query($conn, $sql, $params);
$affectedRows = sqlsrv_rows_affected($stmt); // this works well
$nxtRes = sqlsrv_next_result($stmt); // This asks the sql driver to update the result code and output parameter variables
echo "</br>result code=" . strval($res) . ", error msg=" . $errorMsg . "</br>"
The issue is that if I modify the stored proc so that it can update the data successfully, my php code can capture the return code (0) and error message ("OK"), but if I just keep the stored proc as above, my php code cannot capture those things.
Could someone who had the same issue before give me some hints.
Thanks.
I use php7 and MS PHP driver 5.2 for SQL server.
I had similar problems and I suggest the following:
always put SET NOCOUNT ON at the beginning of your stored procedures.
fetch all result sets from sqlsrv_query(). INSERT, UPDATE or DELETE statements also return the number of affected rows as a result set (when SET NOCOUNT is OFF), not only SELECT statements.
I usually fetch results this way:
<?php
...
do {
# Fetch data, if it's a SELECT statement
#while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
#}
$affected = sqlsrv_rows_affected($stmt);
} while (sqlsrv_next_result($stmt));
...
?>
I've reproduced your test case and SET NOCOUNT ON works. I hope this helps.

SQL Server stored procedure output parameter is not returning correct output after insert statement using PHP and SQL Server

SQL Server stored procedure is not returning correct output while using it with PHP.
My development environment:
PHP 7.1
SQL Server 2017 Express Edition
Ubuntu 16.04
I have the following SQL Server stored procedure:
CREATE PROCEDURE [dbo].[spTest]
#TestID BIGINT,
#QueryStatus BIGINT = 0 OUT
AS
BEGIN
SET #QueryStatus = 0;
BEGIN TRY
BEGIN TRANSACTION
IF(#TestID = 1)
BEGIN
INSERT INTO dbo.tblErrorLog (ErrorMessage)
VALUES('Test Data');
SET #QueryStatus = 1;
END
ELSE
BEGIN
SET #QueryStatus = -1;
END
COMMIT TRANSACTION
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
SET #QueryStatus = -999;
INSERT INTO dbo.tblErrorLog (ErrorMessage)
VALUES(ERROR_MESSAGE());
END CATCH
END
And using following PHP code for executing this stored procedure:
$SQL = "{call spTest (#TestID=?,?)}";
//$SQLParam=array($Email,$Referer);
$QueryResult=-997;
$TestID=1;
$SQLParam = array(
array($TestID,SQLSRV_PARAM_IN),
array(&$QueryResult, SQLSRV_PARAM_OUT,SQLSRV_PHPTYPE_INT,SQLSRV_SQLTYPE_INT)
);
$SQLResult = $SQLServer_DB_Manager->executeQuery($SQL,$SQLParam);
echo "Query Result for Test ID 1: ".$QueryResult." <br/>";
$QueryResult=-997;
$TestID=0;
$SQLParam = array(
array($TestID,SQLSRV_PARAM_IN),
array(&$QueryResult, SQLSRV_PARAM_OUT,SQLSRV_PHPTYPE_INT,SQLSRV_SQLTYPE_INT)
);
$SQLResult = $SQLServer_DB_Manager->executeQuery($SQL,$SQLParam);
echo "Query Result for Test ID 0: ".$QueryResult." <br/>";
Finally, Result is
Query Result for Test ID 1: -997
Query Result for Test ID 0: -1
But as per the stored procedure, the expected out for first execution must be 1. If I am going to comment out the Insert statement in the stored procedure, I will get the expected result. Is there anyone who can help me to find out what is wrong?

PHP - MSSQL getting results from Stored Procedures

I'm an old school developer and just getting in the WWW programming world. I'm developing an application with HTML, CSS, PHP and MSSQL Server 2008 R2 for the company i'm working with.
In my application I'm using stored procedures to insert, modify, delete or query information from/to the database. Not using TSQL instructions at all, just executing stored procedures from the PHP code.
I'm using PHP 5 and SQLSRV driver for database interaction.
Everything working fine so far, but now I'm stuck on the Insert piece... If everything is ok, the SP inserts the record, if not, it doesn't... but i'm not seeing the result until i query the table again just to see if the record is there or not.
Im using the following code in PHP to run the SP that inserts the record in the table:
function spinserta($tabla, $columnas, $valores, $cnct) {
$stmt = 'Exec spinsert #tabla=?,#columnas=?,#valores=?';
$params = array($tabla,$columnas,$valores);
$result = sqlsrv_query($cnct,$stmt,$params) ;
return $result;
}
if the transaction is not succesful, im not getting anything in the $result variable and would like to have the resulting message from the SP in order to display an error message to the user.
How to get the resulting message from the SP (no matters if it is an error or not)?
Thanks in advance!
after hours of researching.... finally got the concept! here is the thing: the original PHP code was:
function spinserta($tabla, $columnas, $valores, $cnct) {
$stmt = 'Exec spinsert #tabla=?,#columnas=?,#valores=?';
$params = array($tabla,$columnas,$valores);
$result = sqlsrv_query($cnct,$stmt,$params) ;
return $result;
}
and the original SP was:
ALTER PROCEDURE [dbo].[spinsert]
#tabla varchar(50),
#columnas varchar(8000),
#valores varchar(8000)
AS
BEGIN
SET NOCOUNT ON;
declare #orden varchar(8000)
declare #return_value int
set #orden='Insert into ' + #tabla + ' (' + #columnas + ') values (' + #valores + ')';
execute (#orden);
return
END
very straight forward... When the php code was executed and the SP succeded, the variable $result was loaded with "Resource id#14", if the SP failed, the $result value was null.
Things were working well!!! But not the way i wanted. Then i found this article: http://msdn.microsoft.com/en-us/library/ms178592.aspx
Based on that I modified the SP:
ALTER PROCEDURE [dbo].[spinsert]
#tabla varchar(50),
#columnas varchar(8000),
#valores varchar(8000)
AS
BEGIN
SET NOCOUNT ON;
declare #orden varchar(8000)
declare #return_value int
begin try
set #orden='Insert into ' + #tabla + ' (' + #columnas + ') values (' + #valores + ')';
set #return_value=0;
execute (#orden);
end try
begin catch
DECLARE #ErrorMessage NVARCHAR(4000);
DECLARE #ErrorSeverity INT;
DECLARE #ErrorState INT;
SELECT #return_value = ERROR_NUMBER()
SELECT
#ErrorMessage = ERROR_MESSAGE(),
#ErrorSeverity = ERROR_SEVERITY(),
#ErrorState = ERROR_STATE();
RAISERROR (#ErrorMessage, -- Message text.
#ErrorSeverity, -- Severity.
#ErrorState -- State.
);
end catch
return #return_value
END
and the PHP code:
function spinserta($tabla,$columnas,$valores,$cnct) {
$tsql = 'Exec spinsert #tabla=?,#columnas=?,#valores=?';
$params = array($tabla,$columnas,$valores);
$stmt = sqlsrv_query($cnct,$tsql,$params) ;
$errors=sqlsrv_errors();
if ($stmt === false or $stmt===0) {
foreach( $errors as $error ) {
$stmt=str_replace("'","","Error: ". $error['code']. " - " . $error['message']);
}
} else {
$stmt="1";
}
return $stmt;
}
There were two problems with my original approach, 1 at database engine side, the SP was not really generating a system error, even though the statement failed. With the Try-Catch, technique, pluse the RAISEERROR concept, the SP was finally generating the system error when the statemente failed. After this, it was just matter of minor adjustments to the PHP code.
With this approach, the validation of information sent to the database, is done at Database Engine side, eliminating the need of writing a lot of code, just to validate the fields in the forms at the submission time. what is needed is to ensure database tables, relationships, constraints, integrity and others are well applied, and the database will protect itself against incorrect data. If errors with information provided in the form are submited, the database will reject them and the code will show to the user the proper errors behind.
I would like to see if something similar is doable with MySQL..., i think so!
Many thanks to Maximus2012!!! Cheers!!!
This is some code that I have in one of my applications. See if it helps:
//Query SQL
$tsql = "Exec spinsert #tabla=?,#columnas=?,#valores=?";
$params = array($tabla,$columnas,$valores);
//Execute the stored query
$stmt = sqlsrv_query($conn, $tsql, $params);
if ($stmt === false)
{
echo "<h3>Error in query preparation or execution.</h3>";
ListErrors();
die;
}
else {
echo "Insert Successful";
}
// this should help for the non-insert/update case
$arr = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
var_dump($arr);

MySQL stored procedure caused `Commands out of sync`

Call procedure works all right in MySQL terminal, but in PHP, caused Commands out of sync; you can't run this command nowCommands out of sync; you can't run this command now
My procedure is
delimiter $$
create procedure getMostSimilar (IN vU_ID INT, IN voffset INT, IN vsize INT)
BEGIN
set #offset = voffset;
set #size = vsize;
set #uid = vU_ID;
prepare SimilarStmt from
"SELECT U_ID, getSimilarity(U_ID, ?) AS similar FROM Answer WHERE U_ID != ? GROUP BY U_ID ORDER BY similar DESC LIMIT ?, ?";
execute SimilarStmt using #uid, #uid, #offset, #size;
deallocate prepare SimilarStmt;
END
$$
where getSimilarity is a function.
In PHP:
function getMostSimilar($U_ID, $offset, $size){
$query = sprintf("CALL getMostSimilar(%s, %s, %s)",
$U_ID, $offset, $size);
$result = mysql_query($query);
print mysql_error();
if (!$result){
return $query;
}
$ans = array();
$len = 0;
while($row = mysql_fetch_assoc($result)){
$ans[$len] = $row;
$len++;
}
return $ans;
}
What should I do now? Thanks!
C.5.2.14. Commands out of sync If you
get Commands out of sync; you can't
run this command now in your client
code, you are calling client functions
in the wrong order.
This can happen, for example, if you
are using mysql_use_result() and try
to execute a new query before you have
called mysql_free_result(). It can
also happen if you try to execute two
queries that return data without
calling mysql_use_result() or
mysql_store_result() in between.
http://dev.mysql.com/doc/refman/5.0/en/commands-out-of-sync.html
EDIT
I think you need to rewrite the getMostSimilar stored procedure, instead of using prepare and execute (which I thinks is fooling mysql) if you use the parameters in the procedure like in this example I think your error will be fixed.
HTH
There seems to be a nasty bug (or feature) that is manifested when calling a stored procedure that returns a result set.. I.e. a stored procedure that ends with a select statement without an INTO clause (see example below).
The mysqli driver (propably) returns 2 result sets. The first being the one returned from the stored procedure and the second a dummy, empty result set. It is like a multiple query command was issued. One solution to this (that does not break on usual (e.g. SELECT) queries), is to consume this dummy result set after proccessing the legit one (the first).
Example php code
function do_query($con, $sql)
{
if ( !($result = mysqli_query($con, $sql)) )
throw new QueryException(mysqli_error($con));
if ($result === true)
return true;
while ($row = mysqli_fetch_assoc( $result )) {
// process rows
}
// Hack for procedures returning second dummy result set
while(mysqli_more_results($con)) {
mysqli_next_result($con);
// echo "* DUMMY RS \n";
}
}
Example stored procedure:
CREATE PROCEDURE selectStaleHeaders()
NOT DETERMINISTIC
SELECT TT.*
FROM one_pretty_table AS TT
LEFT JOIN another AS AN on TT.fk_id = AN.id
WHERE TT.id IS NULL;
i know it is late and there is already an answer, but i was getting the same message for a whole different reason, so i will leave my solution here:
it was actually a very silly error. It was just a typo in a parameter name.
My function had a parameter named preferable:
create function call_client (pereferable int, client_id int) returns int
in the function body, i was using the parameter preferable with the wrong name:
if prefered = 1 then
...
end if;
once i changed prefered for preferable it started working again.

Resources