Prepared statement return false always - php

While trying to insert data into the database using prepared statement the prepared statement always returns false and not complete the connection.
I'm using this connection on cpanel (not sure if that's related) tried to change the order tried to change the data type.
$conn = mysqli_connect($servername,$username,$password,$database);
// $sql=$conn->prepare("insert into asset 'assetName'=?, 'grp' ='?' ,'Descrip' = '?' , 'enteredValue' = '?', 'depreciationRate' = '?','entrydate'='?' 'availability'= '?' ,'enteredBy' = '?' , 'updatedOn' = '?' , 'isPeriodic' = '?' , 'assetType' = '?','Frequency'='?','ExitDate'='?'");
if($sql = $conn->prepare("INSERT INTO `asset`(`id`, `assetName`, `grp`, `Descrip`, `enteredValue`, `depreciationRate`, `entrydate`, `availability`, `enteredBy`, `updatedOn`, `isPeriodic`, `assetType`, `Frequency`, `ExitDate`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)")){
$sql->bind_param("sssssssssss",$name,$group,$value,$depreciation,$entryDate,$availability,$enteredBy,$updatedOn,$isPeriodic,$type,$frequency,$exitDate);
$sql->execute();
always return false and nothing has been inserted in the database.

As I said in the comments:
Well you have 14 ? and 11 s by my count. OR sssssssssss and ?????????????? Which as most of us know, is gonna throw an error as your placeholder count doesn't match your values
If you can put your data in an array you can use that array to build your query.
if($sql = $conn->prepare("INSERT INTO `asset`(`id`, `assetName`, `grp`, `Descrip`, `enteredValue`, `depreciationRate`, `entrydate`, `availability`, `enteredBy`, `updatedOn`, `isPeriodic`, `assetType`, `Frequency`, `ExitDate`) VALUES (".implode(',', array_fill(0,count($data), '?')).")")){
$sql->bind_param(str_repeat('s', count($data)),...$data);
Lets walk thought this a bit
Basically you can create your arguments with the same length as the $data with these 2 pieces of code:
implode(',', array_fill(0,count($data), '?')) //implode "?" with "," equal to the length of data
str_repeat('s', count($data)) //create 's' equal to the length of data
Then the real magic happens here with the ... "variadic" (variable length arguments):
$sql->bind_param(str_repeat('s', count($data)),...$data);
In PHP v5.6+ you can just inject the data using ... in and it will unwind it for you. Or in other words, put each array item in as a new argument.
For the fields (columns)
If you want to do the fields too, that is a bit more tricky. You have to be careful of what is in those if you put that data directly into the SQL. For example a User could edit the keys used in a $_POST request in such a way as to do SQLInjection if you just concatenate the Post Keys into the SQL.
One of the simplest ways to solve this is to have a whitelist of fields like so (matched to the column names):
//all allowed column names for this query (case sensitive)
$whitelist = ["id", "assetName", ...];
You can use array_intersect_key to retain only the data you want for the query (assuming the data has matched keys). The keys will be safe to use now in the query as they must match what is in the $whitelist.
//remove unknown keys form input data (~ retain only known ones)
//array_flip($whitelist) = ["id"=>0, "assetName"=>1, ...];
$data = array_intersect_key($_POST, array_flip($whitelist));
if($sql = $conn->prepare("INSERT INTO `asset`(`".implode("`,`", array_keys($data))."`)VALUES(".implode(',', array_fill(0,count($data), '?')).")".)){
$sql->bind_param(str_repeat('s', count($data)),...$data);
Other things
The only thing this doesn't cover is if you want all the fields in $whitelist to always be present. You can solve this with validation of the incoming data or you can merge in some empty fields to insure that all the data is present.
$default = array_fill_keys($whitelist, ''); //["id"=>"", "assetName"=>"", ...] ~ create empty "default" row
//$default['updatedOn'] = date('Y-m-d'); //you can also manually set a value
$data = array_intersect_key(
array_merge(
$default,
$_POST //["id"=>"1", ...] ~ missing assetName
), array_flip($whitelist)); //-> ["id"=>"1","assetName"=>""]
Array fill keys (similar to array fill from before) takes a list of keys, and adds a value in for each. So that gives us an array who's keys are the the values of $whitelist and an empty string for each items value. I call this a default row.
Then we merge this with our original data. Data in the first array will be overwritten by any data with matched keys for the second array ($_POST in my example). So if a key is present like id in the example above, it overwrite the empty one.
Anything not overwritten keeps the empty value from the default row we made. Then the array intersect key removes anything extra like before.
*PS I didn't test any of this so please forgive any syntax errors.
Enjoy!

You have to execute the statement once you've bound the data.
$sql->execute();
The number of parameters are also inconsistent as pointed out by the comments.

I think you don't execute your query calling the execute method :
$conn = mysqli_connect($servername,$username,$password,$database);
// $sql=$conn->prepare("insert into asset 'assetName'=?, 'grp' ='?' ,'Descrip' = '?' , 'enteredValue' = '?', 'depreciationRate' = '?','entrydate'='?' 'availability'= '?' ,'enteredBy' = '?' , 'updatedOn' = '?' , 'isPeriodic' = '?' , 'assetType' = '?','Frequency'='?','ExitDate'='?'");
if($sql = $conn->prepare("INSERT INTO `asset`(`id`, `assetName`, `grp`, `Descrip`, `enteredValue`, `depreciationRate`, `entrydate`, `availability`, `enteredBy`, `updatedOn`, `isPeriodic`, `assetType`, `Frequency`, `ExitDate`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)")){
$sql->bind_param("sssssssssss",$name,$group,$value,$depreciation,$entryDate,$availability,$enteredBy,$updatedOn,$isPeriodic,$type,$frequency,$exitDate);
sql->execute();
sql->close(); // close connection

Related

Can you pass multiple params using OR to an SQL/PHP single bind statement?

I have a search bar that passes data to the server. I am taking the sentence sent and breaking it into individual words.
I am then comparing a column against each word in the sentence.
$term = filter_var($input['term'], FILTER_SANITIZE_STRING);
$terms = explode(" ", $term);
$size = sizeof($terms);
$posts = DB::select('SELECT * FROM cars
WHERE color = ?',
$terms[0] || $terms[1] || $terms[2] || $terms[3] || $terms[4] );
What is the proper way to bind with multiple parameters on one bind?
This way would get messy, as I would want to search additional columns.
for ($i=0; $i < $size ; $i++) {
$posts = DB::select('SELECT * FROM cars
WHERE color = ? AND
WHERE model =?',
$terms[$i], $terms[$i],);
}
What is the proper way to bind with multiple parameters on one bind.
Think of this rule: You can use a parameter in an SQL query in place of one single scalar value.
That is, where you would normally use in your SQL statement one numeric constant, one quoted string constant, or one quoted date constant, you can replace that one query element with one parameter.
Parameters can not be used in place of:
Lists of multiple values
SQL expressions
SQL keywords
Identifiers like table names, column names, or database names
If you want to compare your color column to multiple values, you need multiple parameter placeholders.
$posts = DB::select('SELECT * FROM cars
WHERE color IN (?, ?, ?, ?)');
It doesn't work to pass a string containing a comma-separated list of values to a single placeholder. You end up with a query that works as if you had written it this way:
SELECT * FROM cars WHERE color IN ('12,34,56,78');
This query will run without error, but it won't give you want you want. In a numeric context, the string '12,34,56,78' has a numeric value of 12. It ignores all the rest of the characters in the string after the first non-numeric character ,. So it will succeed in searching for color 12, but it will fail to find the other colors.
PDO makes it easy to deal with lists of values, because when it is time to supply the values for a parameterized query, you can simply pass an array to the PDOStatement::execute() function.
If you don't know how many color values you need to search for, you can use PHP builtin functions to make a list of question mark placeholders that is the same length as your array of color values:
$list_of_question_marks = implode(',', array_fill(1, count($color_values), '?'));
$sql = "SELECT * FROM cars WHERE color IN ($list_of_question_marks)"
$stmt = $pdo->prepare($sql);
$stmt->execute($color_values);
You should use In to search between various items, and if it's a search, a OR operator would work better:
$posts = DB::select('SELECT * FROM cars
WHERE color in (?) or
model in (?)',
implode(',', $terms), implode(',', $terms));

Insert array and non array values to database using a single sql statement

I have this, working, code, for inserting array to database:
$c = array_map(function ($reqNo,$officer,$product,$quantity){return "'$reqNo','$officer','$product','$quantity'";} , $reqNo,$officer,$product,$quantity);
if(!$insert = mysql_query("INSERT INTO request (RNO,UID,PID,QtyR) VALUES (".implode('),(', $c).")"))
Now, the problems is that i would like also to insert, alongside the array, none array values to the same database table, using the same sql insert statement..here's my code so far,
if(!$insert = mysql_query("INSERT INTO request (RNO,UID,PID,QtyR,Iuse,Designation,QtyA,QtyA1,QtyI,Rdate,Rtime,bar) VALUES (".implode('),(', $c).",'replacement','ICTU','-1','-1','-1',CURDATE(),CURTIME(),'1' )"))
and here's the error i'm getting:
Column count doesn't match value count at row 1
Any ideas on how to go about this?
I did get a way around it, though not sure if it's the correct way..this' how i did it:
//first, i got the length of the array
for($i=0;$i<count($product);$i++){}
//i used array_fill() to duplicate the single values into the length of the array
$new_usage = array_fill(0,$i,$usage);
$new_designation = array_fill(0,$i,$designation);
$QtyA = array_fill(0,$i,"-1");
$QtyA1 = array_fill(0,$i,"-1");
$QtyI = array_fill(0,$i,"-1");
$date = array_fill(0,$i,date("Y-m-d"));
$time = array_fill(0,$i,date("H:i:s"));
$bar = array_fill(0,$i,"1");
//then i put the above new arrays into array_map(), together with the original array
$c = array_map(function ($reqNo,$officer,$product,$quantity,$new_usage,$new_designation,$QtyA,$QtyA1,$QtyI,$date,$time,$bar){return "'$reqNo','$officer','$product','$quantity','$new_usage','$new_designation','$QtyA','$QtyA1','$QtyI','$date','$time','$bar'";} , $reqNo,$officer,$product,$quantity,$new_usage,$new_designation,$QtyA,$QtyA1,$QtyI,$date,$time,$bar);
//from there, i imploded the array_map into the sql insert statement
if(!$insert = mysql_query("INSERT INTO request (RNO,UID,PID,QtyR,Iuse,Designation,QtyA,QtyA1,QtyI,Rdate,Rtime,bar) VALUES (".implode('),(', $c).")")){
...
I still don't know if this' the right way to go about it, but all in all, it did work.

PHP, MySQL - Return which value was used when input is an array?

I'm running a PDO query, something like:
$inputArr = array(val1, val2, val3, ...);
$qMarks = str_repeat('?,', count($inputArr) - 1) . '?';
$stmt = $db->prepare("SELECT id, name, type, level
FROM table
WHERE name IN ($qMarks)");
$stmt->execute($inputArr);
... parse the rows that have been returned
And this works exactly as expected, no hang-ups or anything.
My problem is that I need to know which value from $inputArr was used to get each row returned.
I've tried
WHERE name IN ($qMarks) AS inputVal
and
WHERE name IN ($qMarks AS inputVal)
but those crash the query.
How can I determine which input array value was used to return each row in the output?
EDIT 1
Yes, I understand that the input search value would be name, for this particular case, but the query above is only for demonstration purposes of how I am putting the search values into the query.
The actual is much more complex, and returns any name value with is close (but not always identical).
The AS keyword is not going to work as you expect it. It's mainly used for aliasing subqueries. You can't (to my knowledge) use it in a WHERE clause.
The scenario you've outlined should have the 'name' in $row['name']. If it was a different variable that you wanted to see, you'd simply add it in your SELECT clause.
Great question, and simple answer:
The WHERE name IN $qMarks)"); part of your code is only obtaining the values in your database that are matching your array, so what you can do is see which values of name are present in the row you fetched. For example:
$rows_fetched = $stmt->fetchAll(PDO::FETCHASSOC);
$inputArray = array();
foreach($rows_fetched as $value)
{
$inputArray[] = $value['name'];
}
print_r($inputArray);//printing the results
Now you have the array $inputArray with all the values used to return each row in the output. Let me know if that worked for you!

PHP Multiple Value String in Single Query

I've got a HTML Textarea with 100-1000 of User:Password pairs each line and I want to insert them now in my database using PDO. Since I am really new to PDO I need your help with it and maybe you know some optimaziations for a faster insert or easier code.
This could be the content of my textarea:
User1:Pass1
User2:Pass2
User3:Pass3
And this is what I tried with:
$query = "INSERT INTO Refs (username,password,targetLevel,idOrder) VALUES :accounts";
$ps_insert = $db->prepare($query);
//this iy my textarea from the form
$accounts = $_POST['accounts'];
// Convert Accountaray to user:password tuples
// Write the SQL Statement of the VALUE pairs into
// an string array in this style '(user,password)'
$msg = explode("\n", $accounts);
for($i = 0; $i < sizeof($msg); $i++)
{
list($user, $pass) = explode(":", $msg[$i]);
if(!empty($user)){
$insert_parts[$i]="('" . $user . "','" . $pass . "',10,0)";
}
}
// Content of this string is: (user1,pass1,10,0), (user2,pass2,10,0), (user3,pass3,10,0)
$accountInserts = implode(',', $insert_parts);
$ps_insert->bindParam(':accounts', $insert_parts);
$ps_insert->execute();
Earlier I used the "well known" MySQL Queries, but I want to use PDO because I will use it for other things as well with common prepared statements. Thanks for the help!
Problem: The MySQL Insert doesn't work. How should I solve this? Any suggestions for (speed/code) optimizations?
The bind argument specifies a single scalar value. In your INSERT statement, the bind placeholder :account represents the value assigned to one column. You can only supply data values through a bind variable, you can't include parens and commas to be interpreted as SQL text.
If you supply a value for a bind placeholder, such as:
"('foo','bar',7,1)"
That will be interpreted as a single value, that won't be "seen" as SQL text, but just a character string, the value to be assigned to a single column.
You'd need a SQL statement that looks something like this:
INSERT INTO Refs (username,password,targetLevel,idOrder) VALUES (:v1, :v2, :v3, :v4)
And you'd need to supply a value for each bind placeholder.

PHP MYSQL - empty variable fails on insert

I have an insert statement that inserts variables collected from a form POST on the previous page. If the variables from the form are not filled in it fails on insert (presumably because it is inserting an empty string...) I have the dataype set to allow NULL values - how do I insert null values if the field was left empty from the form POST?
$query = "
INSERT INTO songs (
userid,
wavURL,
mp3URL,
genre,
songTitle,
BPM
) VALUES (
'$userid',
'$wavFile',
'$mp3File',
'$genre',
'$songTitle',
'$BPM'
)
";
$result = mysql_query($query);
The exact manner depends on if you are writing the query or binding parameters to a prepared statement.
If writing your own, it would look something like this:
$value = empty($_POST['bar']) ? null : $_POST['bar'];
$sql = sprintf('INSERT INTO foo (bar) VALUES (%s)',
$value === null ? 'NULL', "'".mysql_real_escape_string($value)."'");
$result = mysql_query($sql);
The main point is that you need to pass in the string NULL (without quotes) if the value should be null and the string 'val' if the value should be "val". Note that since we are writing string literals in PHP, in both cases there is one more pair of quotes in the source code (this makes one pair in the first case, two pairs in the second).
Warning: When inserting to the database directly from request variables, it is very easy to be wide open to SQL injection attacks. Do not be another victim; read about how to protect yourself and implement one of the universally accepted solutions.
For what I understand when something is not filled the post variable is not set as an empty value but rather not set at all so in php you'd do for example:
$genre = isset($_POST['genre']) ? $_POST['genre'] : NULL;
Here's how I do it. I don't like sending anything to an SQL query right from POST (always sanitize!) in the following cas you just run through the POST vars one by one and assign them to a secondary array while checking for 0 length strings and setting them to NULL.
foreach ($_POST as $key => $value) {
strlen($value)=0 ? $vars[$key] = NULL : $vars[$key] = $value
}
Then you can build your SQL query from the newly created $vars[] array.
As Jon states above, this would be the place to also escape strings, strip code and basically do all your server side validation prior to data being inserted into the db.

Categories