I'm trying to create an email confirmation script.
Here is my PHP code:
...
$q = $dbh->prepare("INSERT INTO `email_confirm` (UserID,token,tokenDate) VALUES(:id, :token, UTC_TIMESTAMP()) ON DUPLICATE KEY UPDATE token = VALUES(:token), tokenDate = UTC_TIMESTAMP();");
$result = $q -> execute( array( ":id" => $this->id, ":token" => $token ) );
...
When this runs, I receive the following error:
Caught exception: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '?), tokenDate = UTC_TIMESTAMP()' at line 1
I'm no expert in MySQL, but I couldn't find any syntax errors in my code, and I would love some help.
As documented under PDO::prepare:
You must include a unique parameter marker for each value you wish to pass in to the statement when you call PDOStatement::execute(). You cannot use a named parameter marker of the same name more than once in a prepared statement, unless emulation mode is on.
Whilst you could add a :token2 placeholder or similar that happens to be bound to the same value, actually MySQL's VALUES() function in the ON DUPLICATE KEY UPDATE clause takes a column name not a literal. Therefore this will do the trick:
$q = $dbh->prepare('
INSERT INTO email_confirm
(UserID, token, tokenDate)
VALUES
(:id, :token, UTC_TIMESTAMP())
ON DUPLICATE KEY UPDATE
token = VALUES(token),
tokenDate = UTC_TIMESTAMP()
');
However, you may want to look into Automatic Initialization and Updating for TIMESTAMP and DATETIME, rather than trying to reimplement the wheel.
Related
So the following code has been bugging me a bit:
$stm = $pdo->prepare("SELECT * FROM urls WHERE account=? AND NOT deleted LIMIT ?, 4");
$stm->execute($user, ($request-1)*4);
Whenever I execute this query it returns this error:
Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''0', 4' at line 1'
Upon looking it up it seems that using the ? placeholders automatically puts quotes around it unless you specify with a bindParam. Is there any way to fix this without having to use the bingparam function?
This is a known issue and why the limit should not really be used with bound parameters. However, you can overcome this by binding the parameter individually and naming it an int.
$stm = $pdo->prepare("SELECT * FROM urls WHERE account=? AND NOT deleted LIMIT ?, 4");
$stm->bindValue(1, $user);
$stm->bindValue(2, ($request-1)*4), PDO::PARAM_INT);
$stm->execute();
If you read PDOStatement::execute() it is caused because binding on the execute causes all parameters to be bound as strings.
As Your Common Sense pointed out, you can disable emulation mode and let MySQL sort out the placeholders by itself, though this may not work with all DB drivers (though will with MySQL) by:
$pdo->setAttribute( PDO::ATTR_EMULATE_PREPARES, false );
Or you can do an intval or type-cast to an int and put it directly within your statement if you'd prefer to do it that way.
I have this query:
$query = "INSERT INTO users (users_uuid, type_id) VALUES (UUID_SHORT(), :type_id)";
My question is if in case the UUID_SHORT() generated already exists, is there any way to tell MySQL to generate another UUID_SHORT() within that query? What I have in my mind now is to trap the return error response then execute again the query, which I find inefficient.
Based #eicto comment, I read ON DUPLICATE KEY UPDATE then tried to reconstruct my query, I achieve a new query:
$query = "INSERT INTO users (users_uuid, type_id) VALUES (UUID_SHORT(), :type_id) ON DUPLICATE KEY UPDATE (users_uuid) = VALUES(UUID_SHORT())";
However I received an error in my log that states:
"SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '(users_uuid) = VALUES(UUID_SHORT())' at line 1"
What does this mean?
I need a mechanism that inserts the data from a newly registered user to a database. I use PDO to do this, and this is my current code:
$dbc = new PDO('mysql:host=localhost;dbname=*****', *****, *****);
$insert_query = $dbc->prepare('INSERT INTO members(name, gender, email, pass)
VALUES(:name, :gender, :email, :pass) WHERE email = :email LIMIT 1');
$insert_query->execute(array(':name' => $name, ':gender' =>
$gender, ':email' => $email, ':pass' => $pass));
However, nothing happens when a user submits the register form. I tried to echo $gender, $name, $email and $pass, and they all showed correct values. I am new to PDO, and I'm not sure what the problem could be at all. Is it a syntax error of some sort, or is it something else?
I don't receive any error messages.
I don't believe it is valid to include a LIMIT clause in an INSERT statement. LIMIT is not among the optional clauses in the MySQL INSERT syntax reference.
You also have a WHERE clause, which is also invalid in an INSERT statement.
I suspect you don't have error reporting turned on, since this ought to have been a fatal error or thrown an exception if the statement failed to prepare() due to syntax errors. You would have gotten an error similar to
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'LIMIT 1'
$insert_query = $dbc->prepare('INSERT INTO members(name, gender, email, pass)
VALUES(:name, :gender, :email, :pass) WHERE email = :email LIMIT 1');
//------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^
By definition, an INSERT statement inserts exactly as many rows as you have () groups* for in your VALUES (), so there's no need for a LIMIT anyway. You could use a LIMIT in an INSERT INTO...SELECT... statement, but in that case, the LIMIT is a clause of the SELECT component rather than the INSERT.
* Ignoring potential key violations or other insert problems, obviously
The fact that you have conflated multiple components (WHERE,LIMIT) from UPDATE statements makes me wonder if you actually intended this to be an UPDATE rather than an INSERT statement to begin with. INSERT is only for new rows. UPDATE is for changing existing rows, and both WHERE and LIMIT are valid in UPDATE.
That would look like:
$upd_query = $dbc->prepare('UPDATE members SET name = :name, gender = :gender, email = :email, pass = :pass) WHERE email = :email LIMIT 1');
As "Michael" said, you can't use LIMIT n in an INSERT query.
After all, You cannot use a named parameter marker of the same name twice in a prepared statement.
Try swtching to UPDATE query syntax. Looks to be what you need.
The following is throwing a syntax error. The entire query works in a MySQL client with literals, but breaks down when passing from PHP. In PHP, each query works if submitted separately, but not if within START and COMMIT.
The error:
1064: You have an error in your SQL syntax; check the manual ...etc...right
syntax to use near 'INSERT INTO user_degrees (user_id, degree_id, acadcat_id
, inst_id) VALUES (' at line 2
Query:
$query="START TRANSACTION;
INSERT INTO user_degrees (user_id, degree_id, acadcat_id, inst_id)
VALUES ((SELECT id FROM users WHERE users.username = '$user')
, '$degreeid', '$studyfocus', '$institution');
UPDATE users
SET degree".$dnum." = (SELECT LAST_INSERT_ID())
WHERE username = '$user';
COMMIT;";
All the $vars are class properties and pass integers, except for $user, which passes a username session variable. $dnum is used to change column names between instances of the class and I might be concatenating it incorrectly within MySQL.
PHP's mysql driver only allows a single query per mysql_query() call as a security measure. You'll have to issue multiple separate queries:
$result = mysql_query("START TRANSACTION");
$result = mysql_query("INSERT ...");
$result = mysql_query("UPDATE ...");
$result = mysql_query("COMMIT;");
... with appropriate checking at each stage to make sure the query didn't fail (which I've omitted from here).
Note that this security measure only applies to top-level queries. One one top-level query per call. You can have as many subqueries as you want/need.
I want to use a prepared statement in which the passed-in parameters are for the ORDER BY and LIMIT clauses, like so:
$sql = 'SELECT * FROM table ORDER BY :sort :dir LIMIT :start, :results';
$stmt = $dbh->prepare($sql);
$stmt->execute(array(
'sort' => $_GET['sort'],
'dir' => $_GET['dir'],
'start' => $_GET['start'],
'results' => $_GET['results'],
)
);
But $stmt->fetchAll(PDO::FETCH_ASSOC); returns nothing.
Can someone point out what's the wrong thing I am doing? Can it be done? If not,what should I reference for a complete list of clauses where parameters can be used?
After using :
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
I got the message :
Uncaught exception 'PDOException' with
message 'SQLSTATE[42000]: Syntax error
or access violation: 1064 You have an
error in your SQL syntax; check the
manual that corresponds to your MySQL
server version for the right syntax to
use near ''0', '10'' at line 1
So, when you use an array for execute, it consider your inputs as string which is not a good idea for LIMIT
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sql = "SELECT * FROM table ORDER BY :sort :dir LIMIT :start, :results";
$stmt = $dbh->prepare($sql);
$stmt->bindParam(':start', $_GET['start'], PDO::PARAM_INT);
$stmt->bindParam(':results', $_GET['results'], PDO::PARAM_INT);
$stmt->bindParam(':sort', $_GET['sort']);
$stmt->bindParam(':dir', $_GET['dir']);
$stmt->execute();
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
print_r($data);
Prepared statements allow the DBMS to generate a query plan for your query before actually executing the query for your supplied parameters. Changing the fields for ORDER BY requires a different query plan, because ordering you data in different ways can drastically affect how the DBMS might choose to get the data: for instance, certain indexes may help in one case but not in another. For this reason the ORDER BY fields should form part of the SQL string passed into the prepare() method, rather than being bound to the query prior to execute().
As for the LIMIT clause, it's not clear whether its parameters would affect the query plan, so these may be bound later, possibly depending upon your DBMS. According to this SO answer it should be allowed.
You can't bind a parameter to specify a language keyword or a field name - it has to be replacing a literal. Therefore, your limit values I think are fine, but your order by is not. It will be best for you to manually replace sort and dir in the string. Escape them but don't use the DB tools to do so, since they aren't string literals. Basically ensure no special characters are present.
Although this question is rather old, I think it might still be of interest. For me it worked after I
used bindParam in combination with PDO::PARAM_INT like suggested before
converted the variable content into an integer value by invoking intval()
The relevant part of the code then looks like this:
$stmt->bindParam(':start', intval($_GET['start']), PDO::PARAM_INT);
$stmt->bindParam(':number', intval($_GET['number']), PDO::PARAM_INT);
Without using intval() I also received the error Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''0', 10' at line 1