mysql placeholder not working for DATE_ADD (perl) - mysql

I really appreciate your help on my last question. This is related but I didn't want to hide this question inside the other.. I'm having a problem using "DATE_ADD(NOW(), INTERVAL $interval)" inside a placeholder. Before I was using placeholders this line worked just fine, not it comes back as empty.
$store = qq(INSERT INTO main (creator_name,relationship,time) VALUES(?,?,?) );
my $sth = $dbh->prepare($store);
$sth->execute($data{creatorname},$data{relationship}, "DATE_ADD(NOW(), INTERVAL $interval)");
Is there another way to add DATE_NOW so it's synatically proper? I tried adding it back in like
$store = qq(INSERT INTO main (creator_name,relationship,time) VALUES(?,?, DATE_ADD(NOW(), INTERVAL $interval)) );
and it errored out saying the syntax was wrong. At least with the code on the top it executes, it just leaves the value empty. With this attempt it won't even try.

Would you expect the following to print ABC?
$x = 'uc "abc"';
print $x;
No, that would make no sense. Same thing in SQL.
The string DATE_ADD(NOW(), INTERVAL ...) is surely not a valid value for the time field.
it errored out saying the syntax was wrong.
I'm pretty sure the message was more specific than that. You also didn't provide the value of $interval, without which we have no way of knowing what resulted in a syntax error.

Changing your qq() to qq{} for clarity, this should be the fix:
$store = qq{
INSERT INTO main (creator_name,relationship,time)
VALUES(?, ?, DATE_ADD(NOW(), INTERVAL ? SECOND) )
};
my $sth = $dbh->prepare($store);
$sth->execute($data{creatorname},$data{relationship},$interval);
You'll need to change the word "SECOND" to the appropriate units, and $interval needs to be an integer.
Kudos for using prepared statements, but the reason why this doesn't work the way you tried it is very important: placeholders ? are not simply places where arbitrary text will be substituted into your query. This is why we make such a big deal about the importance of using prepared statements as one way to protect against sql injection. Placeholders are a hard delineation between "what is the query" and "what is the data" and they are sent to the server separately, not munged together.
Thus...
my $sth = $dbh->prepare(qq{SELECT * FROM user WHERE username = ? AND password = ?});
$sth->execute("hacker", "' OR 1 = 1; --");
...cannot be misinterpreted to become the dreaded...
SELECT * FROM user WHERE username = 'hacker' AND password = '' OR 1 = 1; --'
...when done in a prepared statement, as it would if you constructed your query with primitive string concatenation, because the server will never be confused between the two.
A placeholder substitutes exactly one scalar literal value, which "DATE_ADD(NOW(), INTERVAL $interval)" isn't. (Yes, it's a scalar string in Perl, but it contains an expression, not a literal value).

Related

SQL: current timestamp + 1 hour when inserting into DB

I've looked around and have only seen this being used when using SELECT, not INSERT and it has gotten me quite confused.
What I've been trying is:
$statement = $pdo->prepare("INSERT INTO shipments (crate_type_id, username, arrival, quantity, type, status) VALUES ('$crate_type_id', '$username', 'DATE_ADD(NOW() + INTERVAL 1 HOUR)', '$quantity', 'purchase', 'active')");
I don't get any errors, it only inserts "0000-00-00 00:00:00". I've tried troubleshooting in phpmyadmin etc but haven't made any progress.
You should remove the quotes arount the date_add expression and use comma not +
statement = $pdo->prepare("INSERT INTO shipments
(crate_type_id, username, arrival, quantity, type, status)
VALUES ('$crate_type_id', '$username',
DATE_ADD(NOW() , INTERVAL 1 HOUR) ,
'$quantity', 'purchase', 'active')");
First of all, you're using PDO::prepare() incorrectly. The whole purpose of the tool is to separate code and data but you're injecting the latter into the former as if prepared statements had not been invented yet. You have several examples in the manual so I won't repeat that here but in short you have to:
Insert placeholders in your SQL code (? or :foo)
Provide actual values on execution
Secondly, I suggest you configure MySQL in strict mode so you don't end up with junk like 0000-00-00 00:00:00. You can set a proper SQL mode on connection, e.g.:
SET ##SESSION.sql_mode='TRADITIONAL'
Finally, your final SQL code is possibly not even valid, but apparently you aren't being notified about it. I recommend you add something like this to your connection code:
$pdo = new PDO($dsn, $user, $pass, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
... and ensure you've configured your PHP development box to display all errors.

php mysql select columns where their value it is smaller than current time (timestamp)

ok, so what i am trying to do is a simple task, but still i don't get it.
I want to select from a MySql table just the rows that their "last_action" column value is smaller than current time (timestamp generated by php function time() )
it's obvious that all the values previously inserted are smaller than current time :), but i don't know why my simple script doesn't trust "my instinct" :)
here is my php function written in CODEIGNITER, it's not big deal, the query it's important:
function get_exp_sessions(){
$time = time();
$query = $this->db->query('SELECT * FROM session WHERE last_action < "$time"');
echo $query->num_rows();
}
the result i get is ZERO.
if i put
last_action > $time
i get "3"
i don't get it...
Now I see it. Your quoting is incorrect. You have single quotes on the outer string, which cause the variable $time not to be interpolated in the string and instead passed literally as $time.
The literal string value $time gets cast to an integer by MySQL, and any non-numeric string will cast to zero.
MySQL is executing:
SELECT * FROM session WHERE last_action < 0
Switch the quotes - it is good practice and most portable to use single quotes around the variable in the SQL anyway, and indeed since it is an integer value generated by time() (known not to require escaping), it needn't be quoted.
$query = $this->db->query("SELECT * FROM session WHERE last_action < $time");
Though not absolutely necessary in this case, this would have been avoidable if $time had been bound as a parameter in Codeigniter instead of passed directly to the query.

Whats wrong with this MySQL Update Query?

1st I'll give you the query, and then I'll tell you what I am trying to achieve, as I could be soo wrong or soo close.
mysql_query("UPDATE link_building SET
ID=$ID,Site=$Site,Date=$Date,Target_Site=$Target_Site,
Target_Contact_Email=$Target_Contact_Email,
Target_Contact_Name=$Target_Contact_Name,
Link_Type=$Link_Type,Link_Acquired=$Link_Acquired,
Notes=$Notes,Link_URL=$Link_URL WHERE ID=" . $ID);
What am I trying to achieve?
I want to update the fields
("ID","Site","Date","Target_Site","Target_Contact_Email","Target_Contact_Name",
"Link_Type","Link_Acquired","Notes","Link_URL")
in the table link_building with the values stored in the variables
("$ID","$Site","$Date","$Target_Site","$Target_Contact_Email","$Target_Contact_Name",
"$Link_Type","$Link_Acquired","$Notes","$Link_URL")
But I only want to update the record whos Id is equal to $ID.
UPDATE: I DO NOT SEE ANY ERROR. ITS REDIRECTS TO link_building.php and displays success message but doesn't change the data in the MySQL table.
Try escaping the data and removing the update of the ID since its already in your conditions:
mysql_query("UPDATE link_building SET Site='".mysql_real_escape_string($Site)."',Date='".mysql_real_escape_string($Date)."',Target_Site='".mysql_real_escape_string($Target_Site)."', Target_Contact_Email='".mysql_real_escape_string($Target_Contact_Email)."', Target_Contact_Name='".mysql_real_escape_string($Target_Contact_Name)."', Link_Type='".mysql_real_escape_string($Link_Type)."',Link_Acquired='".mysql_real_escape_string($Link_Acquired)."', Notes='".mysql_real_escape_string($Notes)."',Link_URL='".mysql_real_escape_string($Link_URL)."' WHERE ID=" . intval($ID));
For one, you're forgetting that you still need to quote your strings;
mysql_query("UPDATE link_building SET Site='$Site', Date='$Date',".
"Target_Site='$Target_Site', Target_Contact_Email='$Target_Contact_Email',".
"Target_Contact_Name='$Target_Contact_Name', Link_Type='$Link_Type',".
"Link_Acquired='$Link_Acquired', Notes='$Notes', Link_URL='$Link_URL' ".
"WHERE ID=$ID");
Note the added 's around all strings.
Bonus remark; you should really be using mysql_real_escape_string() on your strings before passing them on to the database.
if your columns are named like Target Site (with a space in it), you should adress it like that in your query (wich will force you to add backticks to it). also, you'll have to add quotes to colums that store anything else that strings. your query should look like:
UPDATE
link_building
SET
ID = $ID,
Site = '$Site', // single quotes for values
Date = '$Date', // ...
´Target Site´ = '$Target_Site' // and ´ for fields
[...]
this should solve why the query doesn't work (in addition: not how a bit or formatting makes it much more readable).
you havn't given information about that, but please note that you should always sanitize your variables before using it (your code doesn't look like you do) to avoid sql-injections. you can do this using mysql_real_escape_string or, even better, start using prepared statements.

Using enum in drupal

I have a mysql table id,name,gender,age religion( enum('HIN','CHR','MUS') ,category(enum('IND','AMR','SPA') where last 2 are enum datatype and my code in drupal was
$sql="SELECT * FROM {emp} WHERE age=".$age." and religion=".$rel." and category=".$categ;
$result=db_query_range($sql,0,10);
while($data=db_fetch_object($result))
{
print $data->id." ".$data->name."<br>";
}
I get no result or error . I'm trying different query with each field and all are fine except using enum.
for ex: $sql='SELECT * FROM {emp} WHERE religion="'.$rel.'"';
Is there any problem in using enum datatype in drupal
Enum is not something that I believe drupal can make with the schema API, which is what you in most cases want to use for modules and stuff. Also you are lacking an ending ) in your reference to it, but I'm sure you did it right when your made the table.
Enum is only a constraint that is built into the database when inserting values. So if you try to insert an invalid value, you will insert an empty string instead. So it wont have any effect on Drupal querying to get data. It also wont have any effect when Drupal insert values, other than converting invalid values to empty strings. You might want to check the your data, to see if it is as expected. You might just get no results because your query doesn't match anything.
Another thing is the way you construct your queries is a big NO NO, as it's very insecure. What you should do is this:
db_query("SELECT ... '%s' ...", $var);
Drupal will replace %s with your var and make sure there is no SQL injection and other nasty things. %s indicates the var is a string, use %d for ints and there are a few others I can't remember just now. You can have several placeholders like this, and they will be insert in order, much like the t function.
Seconding Googletorps advise on using parameterized queries (+1). That would not only be more secure, but also make it easier to spot the errors ;)
Your original query misses some quotes around your (String) comparison values. The following should work (Note the added single quotes):
$sql = "SELECT * FROM {emp} WHERE age='" . $age . "' and religion='" . $rel . "' and category='" . $categ . "'";
The right way to do it would be something like this:
$sql = "SELECT * FROM {emp} WHERE age='%s' and religion='%s' and category='%s'";
$args = array($age, $rel, $categ);
$result = db_query_range($sql, $args ,0 , 10);
// ...

Why does my INSERT sometimes fail with "no such field"?

I've been using the following snippet in developements for years. Now all of a sudden I get a DB Error: no such field warning
$process = "process";
$create = $connection->query
(
"INSERT INTO summery (process) VALUES($process)"
);
if (DB::isError($create)) die($create->getMessage($create));
but it's fine if I use numerics
$process = "12345";
$create = $connection->query
(
"INSERT INTO summery (process) VALUES($process)"
);
if (DB::isError($create)) die($create->getMessage($create));
or write the value directly into the expression
$create = $connection->query
(
"INSERT INTO summery (process) VALUES('process')"
);
if (DB::isError($create)) die($create->getMessage($create));
I'm really confused ... any suggestions?
It's always better to use prepared queries and parameter placeholders. Like this in Perl DBI:
my $process=1234;
my $ins_process = $dbh->prepare("INSERT INTO summary (process) values(?)");
$ins_process->execute($process);
For best performance, prepare all your often-used queries right after opening the database connection. Many database engines will store them on the server during the session, much like small temporary stored procedures.
Its also very good for security. Writing the value into an insert string yourself means that you must write the correct escape code at each SQL statement. Using a prepare and execute style means that only one place (execute) needs to know about escaping, if escaping is even necessary.
Ditto what Zan Lynx said about placeholders. But you may still be wondering why your code failed.
It appears that you forgot a crucial detail from the previous code that worked for you for years: quotes.
This (tested) code works fine:
my $thing = 'abcde';
my $sth = $dbh->prepare("INSERT INTO table1 (id,field1)
VALUES (3,'$thing')");
$sth->execute;
But this next code (lacking the quotation marks in the VALUES field just as your first example does) produces the error you report because VALUES (3,$thing) resolves to VALUES (3,abcde) causing your SQL server to look for a field called abcde and there is no field by that name.
my $thing = 'abcde';
my $sth = $dbh->prepare("INSERT INTO table1 (id,field1)
VALUES (3,$thing)");
$sth->execute;
All of this assumes that your first example is not a direct quote of code that failed as you describe and therefore not what you intended. It resolves to:
"INSERT INTO summery (process) VALUES(process)"
which, as mentioned above causes your SQL server to read the item in the VALUES set as another field name. As given, this actually runs on MySQL without complaint and will fill the field called 'process' with NULL because that's what the field called 'process' contained when MySQL looked there for a value as it created the new record.
I do use this style for quick throw-away hacks involving known, secure data (e.g. a value supplied within the program itself). But for anything involving data that comes from outside the program or that might possibly contain other than [0-9a-zA-Z] it will save you grief to use placeholders.