we have a function if we call it by using SELECT we get the error code ORA-14551 "Cannot preform a DML operation inside a query"
select pkg_tools.replace_site(1121,3343) from dual;
how to run this function and get the results
when we run it in SQL developer in this way:
declare
v_return VRACHAR2(200);
begin
v_return := pkg_tools.replace_site(1121,3343);
end;
works with no errors
and we need this function to be called inside PHP
note: I can not paste this function here, because it's to long, but it does allot of operations including insert and update!
A function that does DML cannot be called in a SELECT statement regardless of the calling language.
If you want to do DML and return a value, it would make much more sense to create a stored procedure with an OUT parameter rather than using a function. So it would make much more sense to
CREATE OR REPLACE PROCEDURE proc_name_return( p_1 IN NUMBER,
p_2 IN NUMBER,
p_ret OUT VARCHAR2 )
AS
BEGIN
p_ret := pkg_tools.replace.site( p_1, p_2 );
END;
and then call that stored procedure from PHP
$sql = 'BEGIN proc_name_return( :p_1, :p_2, :p_ret ); END;';
If you don't want to do that, my guess is that you could do something like this as well (adapted from one of the scripts on page 164 of the Underground PHP and Oracle Manual)
<?php
$c = oci_connect('hr', 'hrpwd', 'localhost/XE');
$s = oci_parse($c, "begin :ret :=pkg_tools.replace_site(1121,3343); end;");
oci_bind_by_name($s, ':ret', $r, 200);
oci_execute($s);
echo "Result is: ".$r;
?>
Related
Before this is closed as a duplicate out of hand, let me say that there are a few questions that ask this on Stack Overflow, but none of them have marked answers, and the unmarked ones didn't work for me.
In my MySql database, I have a simple procedure:
DELIMITER //
CREATE PROCEDURE foo (
IN a INT,
OUT b INT
) BEGIN
SELECT a INTO b;
END //
DELIMITER ;
If I call this in phpMyAdmin, I get what I expect:
SELECT 2 INTO #b;
CALL foo(1, #b);
SELECT #b;
| #b |
+----+
| 1 |
When I try to call this from my php code, however, stmt->execute() returns false.
$a = 1;
$pdoLink = get_pdo_connection($db);
$stmt = $pdoLink->prepare('CALL `mydb`.`foo`(:a, :b)');
$stmt->bindParam(':a', $a);
$stmt->bindParam(':b', $b, PDO::PARAM_INT, 4);
// I've also tried length values of 1, 2, and 16
$stmt->execute(); // returns false
I thought maybe it had to do with calling a procedure in the first place, so I replaced my procedure with:
DELIMITER //
CREATE PROCEDURE foo (
IN a INT,
IN b INT
) BEGIN
SELECT a, b;
END //
DELIMITER ;
This, however, does work from php, so it must be something having to do with output parameters. I'm using code that's only slightly altered from the PDO Documentation, so I really have no idea what's wrong with it.
I am not looking for an answer that uses mysqli's multi_query to do something like
$sql = "CALL `mydb`.`foo`(1, #b);
SELECT #b;";
$link->multi_query($sql);
// ...
I'm looking for how to do this using PDO and a single query.
Someone tried to mark this as a duplicate of Calling stored procedure with Out parameter using PDO. The marked answer to this question uses multiple queries.
You say you used phpMyAdmin to test if your query works, but phpMyAdmin is built in PHP and uses mySQL Improved, so the problem is not on PHP.
Or better, to talk with MySQL you have to go through a driver that interprets the data between PHP and a system library; usually the libmysql, this interfaces with the MySQL server. It's all too complicated!
Many information on the web, say to set the PDO :: ATTR_EMULATE_PREPARES to false, to prevent the PHP emulates the preparation of the parameters passed to the query.
Other information, always taken from the web, they say to do it only if the PHP is compiled through the php5-mysqlnd module.
In practice I noticed that the best way for me was to cast the variables that carry non-string values.
So your code could become
...
$stmt->bindParam(':a', (int)$a);
$stmt->bindParam(':b', (int)$b, PDO::PARAM_INT, 4);
...
My two cents
I connect to a Oracle DB with "Oracle SQL Developer" tool, and I can run successfully statements like this (for example):
DECLARE
p0_ VARCHAR2(32000) := 'Charlie';
p1_ FLOAT := 35;
p2_ VARCHAR2(32000) := 'Spain';
...
BEGIN
Users.Validate(p0_,p1_,p2_ ....);
END;
Also, I can run succesfully simple querys like Select * from .... The server responses in 4-5 seconds.
My problem:
I have a Laravel 5.7.13 project with oracle connection (yajra/laravel-oci8 module) and its works ok when I run simple querys like Select ...:
DB::select("Select ...");
But I can not run the beginning statement:
DB::select("DECLARE
p0_ VARCHAR2(32000) := 'Charlie';
p1_ FLOAT := 35;
p2_ VARCHAR2(32000) := 'Spain';
...
BEGIN
Users.Validate(p0_,p1_,p2_ ....);
END;");
I also tried with DB::Select(DB::raw("DECLARE ...")) statement, and DB::Statement(...) but not working.
I am using Xampp and apache, and when I try to run this statements, Apache does not responds, no errors, simply it looks like is trying to execute the statement, but not finishes, even though I have a timeout of 10 seconds (max_execution_time=10 in php.ini, and also forced by my php code with the instruction: ini_set('max_execution_time', 10)).
How can I execute this kind of statement from Laravel?
Could be a user permissions problem? (I have configured in Laravel and "Oracle SQL Developer" tool the same user and connection)
Thanks!
As far as concernes, it is not possible to execute a PL/SQL block directly from Lavarel.
So the best (only ?) option would be to first create a stored procedure directly in the database, using SQLDeveloper or another Oracle client, and then to invoke it from Lavarel, passing it the relevant arguments.
Here is an example based on your use case :
1) Create the stored procedure (NB : the max size of an Oracle varchar2 is 4000, not 32000) :
CREATE OR REPLACE PROCEDURE myproc
(
p0 IN VARCHAR2(4000),
p1 IN FLOAT,
p2 IN VARCHAR2(4000)
)
AS
BEGIN
Users.Validate(p0,p1,p2);
END;
2) Call the stored procedure from Lavarel :
DB::statement('exec myproc("Charlie", 35, "Spain")');
If you need to return something from the procedure, use DB::select instead of DB::statement.
I have this function stored on my SQL SERVER DATABASE
CREATE FUNCTION [dbo].[fn_md5] (#data VARCHAR(10), #data2 VARCHAR(10))
RETURNS BINARY(16) AS
BEGIN
DECLARE #hash BINARY(16)
EXEC master.dbo.XP_MD5_EncodeKeyVal #data, #data2, #hash OUT
RETURN #hash
END
To get value that i need i some how to execute like this: [dbo].[fn_md5]('data1','data2')
How i do this in php with pdo?
If i use
$exc_line = $db->prepare("exec [dbo].[fn_md5]('data1','data2')");
$exc_line->execute();
$exc_line_data = $exc_line->fetch();
I get no results...
To resolve an issue, lets just compile the answer.
Stored functions executing by SELECT statement. Stored procedures - by EXEC statement, according to docs.
So use this:
SELECT [dbo].[fn_md5]('data1','data2')
I launch a pl/sql script by a PHP application but I notice when there is either an insert, an update or a delete in the script, the script stops. While there is no problem if I use this script directly in sqldeveloper.
Is there something particular to do in order to use insert, delete, update ?
Thanks
procedure update_nbr_execution
(dem_id in number)
IS
BEGIN
UPDATE BCN_DEMANDE_EXTRACTION
SET nombre_execution = nvl(nombre_execution,0) + 1
WHERE id = dem_id;
END;
In my php file :
$query = "BEGIN
ecrire_requete(:demande_id, :p_nom);
END;";
$stid = oci_parse($conn, $query);
$tabvars = oci_new_collection($conn,'MYTABLETYPE');
oci_bind_by_name($stid, ':p_nom', $tabvars, -1, SQLT_NTY);
oci_bind_by_name($stid, ':demande_id', $_POST['demande_id']);
oci_execute($stid, OCI_DEFAULT);
And update_nbr_execution is call by ecrire_requete.
There is a trigger involve in update_nbr_execution, a field date is automatically update with the fiel nbr_execution. May it come from the trigger ?
Edit : after isolating some part, I'm now getting this error : ORA-04088: error during execution of trigger. So it does come from the trigger which looks like this :
create or replace
TRIGGER BCN_FORMAT_NOM_FICHIER_BI
BEFORE INSERT OR UPDATE ON BCN_DEMANDE_EXTRACTION
REFERENCING NEW AS NEW
FOR EACH ROW
DECLARE
BEGIN
if inserting then
:new.FORMAT_NOM_FICHIER_DONNEES:='bcn_<nom_lot>_<id demande>_<n° version>_<description>_<date>.dat';
:new.FORMAT_NOM_FICHIER_CONTROLE:='bcn_<nom_lot>_<id demande>_<n° version>_<date>.ctr';
:new.FORMAT_NOM_FICHIER_JETON:='bcn_<nom_lot>_<id demande>_<n° version>_<date>.jet';
:new.FORMAT_NOM_FICHIER_ZIP:='bcn_<nom_lot>_<id demande>_<n° version>_<date>';
:new.CREATED_AT:=TO_CHAR(SYSDATE,'DD/MM/YY');
:new.UPDATED_AT:=TO_CHAR(SYSDATE,'DD/MM/YY');
else
:new.UPDATED_AT:=TO_CHAR(SYSDATE,'DD/MM/YY');
end if;
END;
This is probably a date format issue.
If CREATED_AT and UPDATED_AT are both dates, then = TO_CHAR(SYSDATE,'DD/MM/YY')
will convert SYSDATE to a string, and then implicitly convert the string back to a date. The implicit date conversion
depends on NLS_DATE_FORMAT, which is set by each client.
You probably have SQL Developer set to use something like DD/MM/YY, but PHP is using something else. If you just
want to remove the time from SYSDATE, you should use TRUNC(SYSDATE) instead.
I would like to group multiple queries into a single function that lives in PostgreSQL. The function will be queried using PDO.
The function is:
CREATE OR REPLACE FUNCTION "test_multipe_refcursor"()
RETURNS SETOF refcursor AS $BODY$
DECLARE
parentRC refcursor;
childRC refcursor;
BEGIN
open parentRC FOR
SELECT * FROM parent;
RETURN NEXT parentRC;
open childRC FOR
SELECT * FROM child;
RETURN NEXT childRC;
RETURN;
END;$BODY$
LANGUAGE 'plpgsql' VOLATILE;
ALTER FUNCTION "test_multipe_refcursor"() OWNER TO postgres;
Here's the PHP code. "Database" as a singleton class that sets up the usual connection properties, nothing special.
$database = Database::load();
$sql = "select * from test_multipe_refcursor();";
$p = $database->query($sql);
$i = 1;
do
{
$this->set('set' . $i, $p->fetchAll(PDO::FETCH_ASSOC));
$i++;
} while ($p->nextRowset());
$p->closeCursor();
And the result.
PDOException: SQLSTATE[IM001]: Driver does not support this function: driver does not support multiple rowsets in xxxx.php on line 32
This would seem to indicate that it's not supported, but then again, I cannot find a list defining exactly what is.
Has anyone managed to get this working?
References:
http://www.sitepoint.com/forums/showthread.php?p=3040612#post3040612
PostgreSQL function returning multiple result sets
http://ca.php.net/manual/en/pdostatement.nextrowset.php
Support for returning multiple resultsets is still on the PostgreSQL todo list and it will definitely not hit 8.4. As for the setof refcursors method, What you are trying to do doesn't work because the function isn't returning multiple rowsets - it is returning one rowset of refcursors. I'm not sure if using refcursors client side works, but I don't find it likely, even if the client-server protocol supports it, it is unlikely that PDO has an API for that.
But why are you trying to return multiple resultsets in one query? You can always do the queries separately.
Near the bottom of this PostgreSQL doc page, there is a section describing how you can pass back one or more cursors from a function. Basically, you get the caller to specify the name of the cursor(s) as parameters:
CREATE FUNCTION myfunc(refcursor, refcursor) RETURNS SETOF refcursor AS $$
BEGIN
OPEN $1 FOR SELECT * FROM table_1;
RETURN NEXT $1;
OPEN $2 FOR SELECT * FROM table_2;
RETURN NEXT $2;
END;
$$ LANGUAGE plpgsql;
-- need to be in a transaction to use cursors.
BEGIN;
SELECT * FROM myfunc('a', 'b');
FETCH ALL FROM a;
FETCH ALL FROM b;
COMMIT;
The page is for PostgreSQL 8.4, but this documentation snippet is present at least as far back as 8.1 (the version I'm running). As the comment says, you need to be inside a transaction to use cursors, as they are implicitly closed at the end of each transaction (i.e. at the end of every statement if autocommit mode is on).