Binding a date string parameter in an MS Access PDO query - php

I've made a PDO database class which I use to run queries on an MS Access database.
When querying using a date condition, as is common in SQL, dates are passed as a string. Access usually expects the date to be surrounded in hashes however. E.g.
SELECT transactions.amount FROM transactions WHERE transactions.date = #2013-05-25#;
If I where to run this query using PDO I might do the following.
//instatiate pdo connection etc... resulting in a $db object
$stmt = $db->prepare('SELECT transactions.amount FROM transactions WHERE transactions.date = #:mydate#;'); //prepare the query
$stmt->bindValue('mydate', '2013-05-25', PDO::PARAM_STR); //bind the date as a string
$stmt->execute(); //run it
$result = $stmt->fetch(); //get the results
As far as my understanding goes the statement that results from the above would look like this as binding a string results in it being surrounded by quotes:
SELECT transactions.amount FROM transactions WHERE transactions.date = #'2013-05-25'#;
This causes an error and prevents the statement from running.
What's the best way to bind a date string in PDO without causing this error? I'm currently resorting to sprintf-ing the string which I'm sure is bad practise.
Edit: if I pass the hash-surrounded date then I still get the error as below:
Fatal error: Uncaught exception 'PDOException' with message
'SQLSTATE[22018]: Invalid character value for cast specification:
-3030 [Microsoft][ODBC Microsoft Access Driver] Data type mismatch in criteria expression. (SQLExecute[-3030] at
ext\pdo_odbc\odbc_stmt.c:254)' in
C:\xampp\htdocs\ips\php\classes.php:49 Stack trace: #0
C:\xampp\htdocs\ips\php\classes.php(49): PDOStatement->execute() #1
C:\xampp\htdocs\ips\php\classes.php(52): database->execute() #2
C:\xampp\htdocs\ips\try2.php(12): database->resultset() #3 {main}
thrown in C:\xampp\htdocs\ips\php\classes.php on line 49

Normally when using a prepared statement or a parameterized query you don't need to worry about delimiting string and date values; all of that is handled for you "behind the scenes".
I just tried the following and it worked for me:
<?php
$connStr =
'odbc:Driver={Microsoft Access Driver (*.mdb, *.accdb)};' .
'Dbq=C:\\Users\\Gord\\Desktop\\Database1.accdb;' .
'Uid=Admin;Pwd=;';
$dbh = new PDO($connStr);
$sql =
"INSERT INTO tblDateTest (dateCol) VALUES (?)";
$newDateTimeValue = "2013-06-30 17:18:19";
$sth = $dbh->prepare($sql);
if ($sth->execute(array($newDateTimeValue))) {
echo "Done\r\n";
}
else {
$arr = $sth->errorInfo();
print_r($arr);
}

You should put your date (entire value) in the bindValue method. Example:
$stmt = $db->prepare('SELECT transactions.amount FROM transactions WHERE transactions.date = :mydate'); //prepare the query
$stmt->bindValue('mydate', '#2013-05-25#', PDO::PARAM_STR); //bind the date as a string

Related

SQLSTATE[HY093]: Invalid parameter number: parameter was not defined (php + pdo)

I have this query that without the inner join it works:
$sql = 'SELECT prodotti.nome, prodotti.prezzo, prodotti.sku, prodotti.produttore, fornitori.nome
FROM prodotti INNER JOIN fornitori
ON prodotti.fornitori_id = fornitori.id
WHERE prodotti.id = :prodotti.id';
$id = 1; // for example
// $this->db-> (is connection)
$stmt = $this->db->prepare($sql);
$stmt->bindParam(':prodotti.id', $id, PDO::PARAM_INT);
$stmt->execute();
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
$prodlist[$id] = $results;
var_dump($prodlist);
If I run it I get this error:
Fatal error: Uncaught PDOException: SQLSTATE [HY093]: Invalid parameter number: parameter was not defined in ........
It seems that the error lies is in the WHERE and in a particular way, reading online, I discovered that it could be a problem to do this: WHERE prodotti.id = prodotti.id;
suggest to do for example: WHERE prodotti.id =: id '; and then in the bindparam the same thing $stmt->bindParam(': id', $ id, PDO :: PARAM_INT);
in fact, doing so works halfway, in the sense that it gives me back the data called the first 'products' table ignoring the second 'suppliers'.
Can anyone tell me where am I wrong? Thanks
if I run this query on the SQL section of DB it works.
SELECT prodotti.nome, prodotti.prezzo, prodotti.sku, prodotti.produttore, fornitori.nome
FROM prodotti INNER JOIN fornitori
ON prodotti.fornitori_id = fornitori.id
WHERE prodotti.id = 1
as some users have pointed out to me elsewhere, and in fact it partially solves the problem, the point cannot be used in the binding, as reported in the guide at this link:
https://phpdelusions.net/pdo
"Note that PDO supports positional (?) and named (:email) placeholders, the latter always begins from a colon and can be written using letters, digits and underscores only. Also note that no quotes have to be ever used around placeholders."
now it works correctly! Thanks

Trouble converting to parameterized queries

So i'm trying to convert all of my SQL statements to prepared statements etc to prevent SQL injection attacks, but i'm having some issues fetching stuff etc
My code:
if($_GET["action"] == "ban"){
if(isset($_GET["username"])){
$username = $_GET["username"];
$banMsg = $_GET["banMsg"];
$email = "test#gmx.ch";
$sql = "SELECT * FROM bans WHERE username = ?";
$stmt = $db->prepare($sql);
$stmt->bind_param("s", $username);
$stmt->execute();
$result = $stmt->fetch();
$stmt->close();
if($result->num_rows > 0){ //LINE 61
die(json_encode(array("status" => 400, "message" => "User already banned")));
}
$result2 = $db->prepare("INSERT INTO bans (username, ip, email, message, expire, ban_creator) VALUES (?, ?, ?, ?, ?, ?)");
$result2->bind_param("sssssd", $username, null, $email, $banMsg, null, 1); // LINE 72^^
$result2->close();
if($result2){
updateBanCache();
die(json_encode(array("status" => 200, "message" => "Successfully banned")));
} else {
die(json_encode(array("status" => 400, "message" => "SQL error")));
}
}
Also $result = $stmt->get_result(); doesn't wanna work for me, i do have mysqlnd driver installed in my php / cpanel though.
Any pointers would be helpful thanks!
ERROR LOG:
[11-Nov-2020 04:46:04 America/New_York] PHP Notice: Trying to get property 'num_rows' of non-object in /home/public_html/index.php on line 61
[11-Nov-2020 04:46:04 America/New_York] PHP Fatal error: Uncaught Error: Cannot pass parameter 3 by reference in /home/elysianmenu/public_html/index.php:72
Stack trace:
#0 {main}
thrown in /home/public_html/index.php on line 72
SIDE NOTE: I also tried using $result = $stmt->get_result(); but I end up with error:
[11-Nov-2020 04:57:30 America/New_York] PHP Fatal error: Uncaught Error: Call to undefined method mysqli_stmt::get_result() in /home/public_html/index.php:55
Stack trace:
#0 {main}
thrown in /home/public_html/index.php on line 55
^^ Yes i do have the mysqlnd driver installed
From the docs: Fetch results from a prepared statement into the bound variables.
fetch() returns either TRUE, FALSE or NULL, but not the result set you expected. Instead, it sets the output to the variables you previously bound (using bind_param()) by reference. This is why you have to use variables, and not actual scalar types.
If your query did not return any rows, fetch() will return NULL. Update your code as follows:
$stmt = $db->prepare($sql);
$stmt->bind_param("s", $username);
$stmt->execute();
if ($stmt->fetch() === TRUE)
die(json_encode(array("status" => 400, "message" => "User already banned")));
$stmt->close();
And to fix the error on line 72, you have to pass the values by reference, using variables. Something like this:
$ip = NULL;
$expire = NULL;
$ban_creator = 1;
$result2->bind_param("sssssd", $username, $ip, $email, $banMsg, $expire, $ban_creator);
Don't forget to execute the query! You're checking $result2 before anything actually happened.
The action of banning a user MUST NOT come from a GET request ($_GET["action"]). This would make it incredibly simple for a webcrawler to stumble upon your banning script and ban all of your users (if it somehow found a list of usernames). The whole payload should be coming in as $_POST. The bottomline is: use $_POST when you are writing data, use $_GET when you are reading data.
You MUST NOT blindly trust the user input. You should be validating the data even before connecting to the db. If the payload is invalid, no resources should be engaged.
When you are only interested in the row count of a result set (and not the values in the result set), write COUNT(1) in your query. This way you can check the lone value to be zero or a non-zero value with no unnecessary overheads. Use something like this https://stackoverflow.com/a/51259779/2943403
ip, expire, and ban_creator should have default settings in your table declaration of NULL, NULL, and 1. You should only mention those columns in an INSERT query if you wish to store a different value. Your INSERT query should only be binding 3 parameters. And of course check the outcome of the executed insert, like this: $stmt->execute() : How to know if db insert was successful?

PHP/Oracle - OCI doesn't like my data type?

I'm new to OCI, but just trying to do a basic oci_bind_by_name in PHP to get it working. But even with a simple select statement and a dummy variable, it rejects the variable type.
Here's the code:
$conn = oci_connect($username, $password, $database);
$dummy = "dummy#dummy.com";
$u = oci_parse($conn, "select ca_email from pwv_google_group");
oci_bind_by_name($u, ':ca_email', $dummy);
But it just returns:
Warning: oci_bind_by_name(): ORA-01036: illegal variable name/number
Since this is a very simple query/parameter, I can only assume my syntax is wrong or something might be off on the Oracle side (ca_email really should be a varchar, but I'm using Datagrip which doesn't allow DESC command, so I don't know how to validate that). Is something else wrong?
oci_bind_by_name() is expecting you to bind a value for some form of input to the SQL statement - from the manual
bv_name The colon-prefixed bind variable placeholder used in the
statement. The colon is optional in bv_name. Oracle does not use
question marks for placeholders.
So for your example, it would be more like
$u = oci_parse($conn, "select * from pwv_google_group where ca_email = :ca_email");
oci_bind_by_name($u, ':ca_email', $dummy);
As you are trying to retrieve the values from the data you just need to fetch the data as in (hacked from example #3 in manual)
$u = oci_parse($conn, 'select ca_email from pwv_google_group');
oci_execute($u);
$row = oci_fetch_array($u, OCI_ASSOC+OCI_RETURN_NULLS);
foreach ($row as $item) {
print $item."<br>\n";
}

PDO Prepared Statement over ODBC Sybase "PARAM datastream" error

I am trying to convert some old PHP ODBC queries over to PDO Prepared statements and am getting an error I cannot find too much information on.
The Error is:
"[DataDirect][ODBC Sybase Wire Protocol driver][SQL Server]There is no host variable corresponding to the one specified by the PARAM datastream. This means that this variable '' was not used in the preceding DECLARE CURSOR or SQL command. (SQLExecute[3801] at ext\pdo_odbc\odbc_stmt.c:254)"
I am searching for a single row in the database using a 6 digit ID that is stored in the database as a VARCHAR but is usually a 6 digit number.
The database connection is reporting successful.
The ID passed by the query string is validated.
The prepared statement results in the above error.
The backup straight ODBC_EXEC statement in the else clause returns the data I am looking for.
//PDO Driver Connect to Sybase
try {
$pdo = new PDO("odbc:Driver={Sybase ASE ODBC Driver};NA=server,5000;Uid=username;Pwd=password;");
$pdo_status = "Sybase Connected";
} catch(PDOException $e) {
echo 'Connection failed: ' . $e->getMessage();
}
if((isset($_GET['id'])) AND ($_GET['id'] != "")) {
//Validate ID String
if(!preg_match("/^[A-Za-z0-9]{5,7}/",$_GET['id'])) {
$query1_id = FALSE;
echo "Invalid ID";
exit;
} else {
$query1_id = $_GET['id'];
}
$query1 = $pdo->prepare("SELECT * FROM People WHERE PersonId= ?");
$query1->execute(array($query1_id));
if($query1->errorCode() != 0) {
$person_data = $query1->fetch(PDO::FETCH_ASSOC);
echo "Person Data from PDO: ";
print_r($person_data);
} else {
$errors = $query1->errorInfo();
echo $errors[2];
//Try the old way to confirm data is there.
$odbc_query1 = "SELECT * FROM People WHERE PersonId='$query1_id' ";
$person_result = odbc_exec($conn,$odbc_query1) or die("Error getting Data, Query 1");
$person_data = odbc_fetch_array($person_result);
echo "Person Data from ODBC_EXEC: ";
print_r($person_data);
}
It also fails if I use:
$query1 = $pdo->prepare("SELECT * FROM People WHERE PersonId= :id ");
$query1->execute(array(":id"=>$query1_id));
Does anyone have experience with this error?
Edit: Sybase Manual says this about the error...
Error 3801: There is no host variable corresponding to the one specified by the PARAM datastream. This means that this variable `%.*s' was not used in the preceding DECLARE CURSOR or SQL command.
Explanation:
Adaptive Server could not perform the requested action. Check your command for missing or incorrect database objects, variable names, and/or input data.
Which is odd because my error (quoted at the top) doesn't tell me which variable has no host.
Also fails if I use...
$query1 = $pdo->prepare("SELECT * FROM People WHERE PersonId= :id ");
$query1->bindParam(':id',$query1_id,PDO::PARAM_STR); //Or PARAM_INT
$query1->execute();
The query works if I place the variable in the query like this...
$query1 = $pdo->prepare("SELECT * FROM People WHERE PersonId= '$query1_id'");
So I think it has something to do with the parameter not being bound to the placeholder but I can't figure out why.
If I can't work this out I'll have to revert to building my query as a string and hoping my input validation is bullet proof.
Your problem seems to be with the default data type PHP assigns to variables in the placeholders. The SQL Statement is looking for a number but PHP is interpreting it as something else. You can prevent this using quotes around the placeholder variable. Notice that in the statements that work you have apostrophes ('') around the value that PHP sees:
$query1 = $pdo->prepare("SELECT * FROM People WHERE PersonId= '$query1_id'");
Try this when using the placeholder it should be the same:
$query1 = $pdo->prepare("SELECT * FROM People WHERE PersonId= ':id'");

PHP PDO interface with MS SQL Server

I am having trouble with using PHP PDO interface with Microsoft SQL Server. The problem is with converting PHP number values to use in functions of MS SQL Server. I use the following statement to delete certain records:
$sql = "DELETE FROM table WHERE SUBSTRING(attribute, 1, ?) = ?";
I prepare and execute this statement with code (a little shrinked):
$query = $pdo->prepare ($sql);
$query->execute (array (strlen ('Text'), 'Text'));
But the query always fails. The error is:
SQLSTATE[42000]: [Microsoft][SQL Server Native Client 11.0][SQL Server]Argument data type nvarchar is invalid for argument 3 of substring function.
I am assuming the number from strlen is not parsed into a number, but I don't know how to fix this (except to manually add into the query).
I have found these links, however not very helpful.
http://social.msdn.microsoft.com/Forums/en-US/sqldriverforphp/thread/0f09ac5e-62cd-4ccf-b2cb-848aad23811e
http://drupal.org/node/1169202
The guys on Drupal had same error, but they fixed it with recreating function with casts. Is there any other way to fix this?
Thanks.
Not sure if this will solve it, but try something like this
$length = strlen('Text');
$text = "Text";
$sql = "DELETE FROM table WHERE SUBSTRING(attribute, 1, :len) = :text";
$query = $pdo->prepare ($sql);
$query->bindParam(':len', $length, PDO::PARAM_INT);
$query->bindParam(':text', $text, PDO::PARAM_STR);
$query->execute();
Try:
$query = $pdo->prepare ($sql);
$query->execute (array ((int) strlen ('Text'), 'Text'));

Categories