MySQL functions as PDO parameters - php

I have a table with NOT NULL columns, NULL columns, and DEFAULT 'x' columns. Now, I need to prepare a query in PDO which accepts values from POST, bind values with the parameters, and when there's nothing specified by user, binds NULL with parameters that allow it, and let the default values for ones that do not.
Here's a simplified code of what I'm trying to run:
$_POST['allow_null'] = null;
$_POST['not_null'] = "DEFAULT()";
$_POST['has_value'] = "Value";
$query = $db_connection->prepare("INSERT INTO my_table allow_null, not_null, has_value
VALUES (:allow_null, :not_null, :has_value)");
$query->execute($_POST);
I'm running into the weird problem of PDO inserting the string "DEFAULT()" as the value instead. Like this:
| allow_null | not_null | has_value |
+------------+----------+-----------+
| NULL | DEFAULT()| Value |
How do I fix this?

The whole point of bound parameters is that they can only ever be interpreted as strings (or null). It is impossible to pass in a function this way. You need to structure your query so that the only thing being inserted or changed is the string itself.
To get the default value in this way, you can use the IFNULL function in your query, combined with the DEFAULT() function. Your SQL would look like this:
INSERT INTO my_table
(allow_null, not_null, has_value)
VALUES
(:allow_null, IFNULL(:not_null, DEFAULT(not_null)), :has_value)
Then to get the default value, just pass null as the parameter.

Related

Update Object value from Array of Objects Codeigniter

I have a query result in values $some = $query->result() , now $some[0] should be about containing following properties:
name
type(A/B)
status(0/1)
Now status of all objects contain null so i want to set 0 if type is A , and 1 if type is B, hence come to simple looping of Array of Objects as well as modifying them while looping , How to do this?
Edit:
Code and table details are given below
Table `test`:
Name| Type| status
Ali | A | NULL
Zeeshan| A | NULL
Waqar| B | NULL
$query = $this->db->query("SELECT test.name as name,test.type as type , test.status as status FROM test");
$some = $query->result();
You should be able to use an UPDATE statement. Since you didn't specify which version of SQL you are using, I can list some links to documentation for UPDATE for several common database versions:
MySQL
PostGreSQL
SQL SERVER (Transact-SQL)
And you should be able to set the value for status conditionally using a CASE statement:
UPDATE test
SET status = CASE type WHEN 'A' THEN 0 ELSE 1 END
You can see this in action on this sqlfiddle.

SQL Updating Optional Parameters PHP

We want to change the way we pass values from PHP to stored procedures (T-SQL). I only have minor experience with PHP but I will attempt to explain the process from discussions with our web developer.
Current Process
Example test table
In order to update a record, such as Field3 in this example, we would pass all existing values back to the stored procedure.
EXEC dbo.UpdateTest #ID = 1, #Field1 = 'ABC', #Field2 = 'DEF', #Field3 = 'GHI', #Field4 = 'JKL'
Lets say to update Field3, you must click a button. This would navigate to a new page which would run the stored procedure to update the data. As the new page is unaware of the values it has to run a SELECT procedure to retrieve the values before running an UPDATE.
The script would then redirect the user back to the page which reloads the updated data and the changes are reflected on screen.
New Process
What we would like to do is only pass the fields we want to change.
EXEC dbo.UpdateTest #ID = 1, #Field2 = 'DEF', #Field3 = 'GHI'
Our solution is simple. First we set all of the updatable fields to optional (so NULL can be passed). We then check to see if the parameter is NULL (is not passed), if it is then we ignore it and if it isn't we update it.
UPDATE
dbo.Test
SET
Field1 = NULLIF(ISNULL(#Field1,Field1),'-999')
,Field2 = NULLIF(ISNULL(#Field2,Field2),'-999')
,Field3 = NULLIF(ISNULL(#Field3,Field3),'-999')
,Field4 = NULLIF(ISNULL(#Field4,Field4),'-999')
WHERE
ID = #ID
However we still want the procedure to update the database record to NULL if a NULL value is passed. The workaround for this was to assign an arbitrary value to equal NULL (in this case -999), so that the procedure will update NULL if the arbitrary value (-999) is passed.
This solution is rather messy and, in my eyes, an inefficient way of solving the problem. Are there any better solutions? What are we doing wrong?
A huge thanks in advance to any replies
Valdimir's method is great as far as passing a flag variable to identify when the value is passed or not passed and his notes about arbitrarily picking a value are right on, but I would guess that there are some arbitrary values you may never have to worry about. such as -999 for a integer when you don't allow for negative numbers, or '|||||||' for a null string. Of course this breaks down some when you do want to use negative numbers but then you could potentially play around with numbers too big for a data type such as BIGINT as a parameter default -9223372036854775808 for an int.... The issue really comes down to your business case of whether values can or can not be allowed.
However if you go a route like that, I would suggest 2 things. 1) don't pass the value from PHP to SQL instead make that the default value in SQL and test if the parameter is the default value. 2) Add a CHECK CONSTRAINT to the table to ensure the values are not used and cannot be represented in the table
So something like:
ALTER TABLE dbo.UpdateTest
CHECK CONSTRAINT chk_IsNotNullStandInValue (Field1 <> '|||||||||||||||||||' AND Field2 <> -999)
CREATE PROCEDURE dbo.UpdateTest
#ParamId numeric(10,0)
,#ParamField1 NVARCHAR(250) = '|||||||||||||||||||'
,#ParamField2 INT = -99999 --non negative INT
,#ParamField3 BIGINT = -9223372036854775808 --for an int that can be negative
AS
BEGIN
DECLARE #ParamField3Value INT
BEGIN TRY
IF ISNULL(#ParamField3,0) <> -9223372036854775808
BEGIN
SET #ParamField3Value = CAST(#ParamField3 AS INT)
END
END TRY
BEGIN CATCH
;THROW 51000, '#ParamField3 is not in range', 1
END CATCH
UPDATE dbo.Test
SET Field1 = IIF(#ParamField1 = '|||||||||||||||||||',Field1,#ParamField1)
,Field2 = IIF(#ParamField2 = -99999,Field2,#ParamField2)
,Field3 = IIF(#ParamField3 = -9223372036854775808, Field3, #ParamField3Value)
WHERE
ID = #ParamId
END
The real problem with this method is the numeric data field allowing for negative numbers as you really don't have an appropriate way of determining when the value should be null or not unless you can pick a number that will always be out of range. And I definitely realize how bad of an idea the BIGINT for INT example is because now your procedure will accept a numeric range that it shouldn't!
Another method/slight variation of Vladimir's suggestion is to flag when to make a field null rather than when to update. This will take a little getting used to for your PHP team to remember to use but because these flags can also be optional they don't have to be burdensome to always include something like:
CREATE PROCEDURE dbo.UpdateTest
#ParamId numeric(10,0)
,#ParamField1 NVARCHAR(250) = NULL
,#MakeField1Null BIT = 0
,#ParamField2 INT = NULL
,#MakeField2Null BIT = 0
,#ParamField3 INT = NULL
,#MakeField3Null BIT = 0
AS
BEGIN
UPDATE dbo.Test
SET Field1 = IIF(ISNULL(#MakeField1Null,0) = 1,NULL,ISNULL(#ParamField1,Field1))
,Field2 = IIF(ISNULL(#MakeField2Null,0) = 1,NULL,ISNULL(#ParamField2,Field2))
,Field3 = IIF(ISNULL(#MakeField3Null,0) = 1,NULL,ISNULL(#ParamField3,Field3))
WHERE
ID = #ParamId
END
Basically if you are using the stored procedure to Update a table and it has nullable fields, I don't think I would recommend having the paramaters be optional as it leads to business cases/situations that can be messy in the future especially concerning numeric data types!
Your approach where you use a magic number -999 for the NULL value has a problem, as any approach with magic numbers have. Why -999? Why not -999999? Are you sure that -999 can not be a normal value for the field? Even if it is not allowed for a user to enter -999 for this field now, are you sure that this rule will remain in place in few years when your application and database evolve? It is not about being efficient or not, but about being correct or not.
If your fields in the table were NOT NULL, then you could pass a NULL value to indicate that this field should not be updated. In this case it is OK to use a magic value NULL, because the table schema guarantees that the field can't be NULL. There is a chance that the table schema will change in the future, so NULL can become a valid value for a field.
Anyway, your current schema allows NULLs, so we should choose another approach. Have an explicit flag for each field that would tell the procedure whether the field should be updated or not.
Set #ParamUpdateFieldN to 1 when you want to change the value of this field. Procedure would use the value that is passed in the corresponding #ParamFieldN.
Set #ParamUpdateFieldN to 0 when you don't want to change the value of this field. Set #ParamFieldN to any value (for example, NULL) and the corresponding field in the table will not change.
CREATE PROCEDURE dbo.UpdateTest
-- Add the parameters for the stored procedure here
#ParamID numeric(10,0), -- not NULL
-- 1 means that the field should be updated
-- 0 means that the fleld should not change
#ParamUpdateField1 bit, -- not NULL
#ParamUpdateField2 bit, -- not NULL
#ParamUpdateField3 bit, -- not NULL
#ParamUpdateField4 bit, -- not NULL
#ParamField1 nvarchar(250), -- can be NULL
#ParamField2 nvarchar(250), -- can be NULL
#ParamField3 nvarchar(250), -- can be NULL
#ParamField4 nvarchar(250) -- can be NULL
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRANSACTION;
BEGIN TRY
UPDATE dbo.Test
SET
Field1 = CASE WHEN #ParamUpdateField1 = 1 THEN #ParamField1 ELSE Field1 END
,Field2 = CASE WHEN #ParamUpdateField2 = 1 THEN #ParamField2 ELSE Field2 END
,Field3 = CASE WHEN #ParamUpdateField3 = 1 THEN #ParamField3 ELSE Field3 END
,Field4 = CASE WHEN #ParamUpdateField4 = 1 THEN #ParamField4 ELSE Field4 END
WHERE
ID = #ParamID
;
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
-- TODO: process the error
ROLLBACK TRANSACTION;
END CATCH;
END
So, parameters of the procedure are not optional, but you use #ParamUpdateFieldN flags to indicate which parameters hold useful values and which parameters should be ignored.
EXEC dbo.UpdateTest #ID = 1, #Field1 = 'ABC', #Field2 = 'DEF', #Field3 = 'GHI', #Field4 = 'JKL'
and
EXEC dbo.UpdateTest #ID = 1, #Field2 = 'DEF', #Field3 = 'GHI'
Are both valid ways to make use of the same stored procedure with MsSql or Sybase. When you don't send the values, it is the same as sending a null. Unless you set a default in the stored procedure. In that case the default is used instead of the null.
Not enough reputation to just comment.
In my opinion your solution is good enough as long as the arbitrary value cannot be a normal value for any of the fields.
However, I'd consider passing and storing something else besides NULL (“N/A” for example) when a field should not have an “actual” value and it’s purposely updated from the client side.

PDO prepare - preventing null value SQL update

I have optional input provided as the 'type' value. I'm trying to prevent the statement from updating the type if it is null data and continue to update the equipment value.
Im tempted to create seperate prepare statements dependant on PHP check of null, but then found the SQL ISNULL(). I havnt used it before and am unsure how to use it within a prepare, unless there is a better way of achieving this?
PHP:
$update = $db -> prepare("UPDATE room
SET type = ISNULL(#:type,:type), equipment = :equipment
WHERE room_id = :room_id");
$update -> bindParam(":room_id", $room_id);
$update -> bindParam(":type", $data['type']);
$update -> bindParam(":equipment", $equipmentList);
$update -> execute();
I get a 'SQLSTATE[42000]: Syntax error or access violation: 1582' when attempting the above.
UPDATE:
UPDATE room
SET type = IFNULL(:type, type), equipment = :equipment
WHERE room_id = :room_id
Ok this correct syntax fixes errors, thanks to eggyal! The problem now is that null :type values are updated and the database valuetype(which is a enum) is not used instead of the null. I double checked in php before SQL that the :type values is null.
Why is IFNULL still returning a null?
Use bindValue instead of bindParam, you are not altering the value of parameter, therefore you don't need to pass it by reference.
if the data type in MySQL can contain null then you can tell it that you are sending a NULL value. Code:
$update -> bindValue(":type", $data['type'], is_null($data['type'] ? PDO::PARAM_NULL : PDO::PARAM_STR);
If your target column cannot contain null then you do not need to execute the query at all.
MySQL's ISNULL() function takes only a single argument and returns either 1 or 0 indicating whether that argument was null or not, respectively. It is equivalent to the IS NULL comparison operator.
MySQL does have a 2-argument IFNULL() function, which may have been what you intended to use (though the arguments you've applied are nonsense)?
UPDATE room
SET type = IFNULL(:type, type),
equipment = :equipment
WHERE room_id = :room_id
If :type is not NULL, its value will be used; else the incumbent value of the type column will be used instead (yielding no change).
You can use the COALESCE() function. It takes as many arguments as you give it and returns the first not-null argument it encounters if any. Otherwise it returns NULL.
UPDATE room
SET type = COALESCE(:type, type), equipment = :equipment
WHERE room_id = :room_id

How to INSERT NULL value from PHP?

Let x is a column of the table t allowing NULL values.
Which value should $value variable take for the following PDO statement to insert NULL value?
$db->prepare("INSERT t SET x=:x")->execute(array(':x'=>$value));
You simply insert it as null:
$db->prepare("INSERT t SET x=:x")->execute(array(':x'=>null));
If you want, you can also add the data type of PDO::PARAM_NULL.
This was asked 9 years ago, so it may no longer be relevant, but SeanWM's existing answer, while it works, does not technically answer specifically what was asked because it does not assign any value to a variable.
Just for completeness:
Inserting the PHP null value into MySQL does result in NULL being inserted into the DB.
$value = null;
$sql = $db->prepare("INSERT t SET x=:x")->execute(array(':x'=>$value));
Output:
Note: be careful NOT to enclose the word null in quotes or it will insert the string "null" instead.

PHP: calling Oracle stored proc that returns a table

I have an Oracle stored proc that takes 2 parameters. userid as an input parameter and an Oracle table with 2 columns as second out parameter. How can I invoke the procedure from PHP? I think that the problem stands in the oci_bind_* for the second parameter. I've tried oci_bind_array_by_name but I always get PLS-00306: wrong number or types of arguments in call to GET_VALUES.
Can anyone help me, please?
Here is my code:
$tab=array();
$query = "begin GET_VALUES(:P_CUSTOMERCODE,:P_TAB); end;";
$stmt = oci_parse($ora_conn, $query) or die(oci_error());
oci_bind_by_name($stmt,":P_CUSTOMERCODE",$codUtente,255);
oci_bind_array_by_name($stmt,":P_TAB",$tab,100,100,SQLT_CHR);
oci_execute($stmt) or die(oci_error());
This might answer your problem: How to call package from php having procedure in oracle using oci drivers?
Not sure a multi-column table will work with oci_bind_array_by_name. Looking at the php manual, you can use this to bind a simple varray, assoc array or nested table, basically as simply 1 column list of values. You'd specify the type of array in the "type" param, using SQLT_CHR for varchar2 for example (if you defined an array like : type t_array is table of varchar2(100) index by pls_integer).
Seems you created a custom table of a custom record type(?), something like:
type t_rec is record (
col1 number,
col2 varchar2(100)
);
type t_tab is table of t_rec;
I don't see where you can bind to t_tab as an out param using php's oci8 calls, but I may be mistaken.

Categories