I encountered a very strange bug today. I am getting sideblinded by this so bad as it's breaking my entire application.
So, I have this little framework that I've built where I have a standard modell, so snippeting this will be a little long and descriptive.
<?php include('inc/inc.php'); ?>
<?php
if(!empty($_POST['answer']) && !empty($_POST['levelstart'])){
if($stmt = $site->answerQuestion($_POST['levelstart'], $_POST['answer'])){
if($stmt[0]){
echo json_encode(array('success' => true, 'correct' => $stmt[1], 'correctanswer' => $stmt[2], 'round_end' => $stmt[3]));
}else{
echo json_encode(array('success' => false, 'error' => 'error occurred'.$stmt[1]));
}
}else{
echo json_encode(array('sucess' => false, 'error' => 'Unknown error'));
}
}else{
echo json_encode(array('success' => false, 'error' => 'Provide all necessary parameters.'));
}
?>
this piece of code outputs the following.
INSERT INTO quiz_level_starts (`user_id`, `question_id`, `time`, `round_id`, `type`, `success`, `ref_id`) VALUES ('4', '10', '1471887809', '', '1', '1', '905'){"success":false,"error":"error occurred23000"}
The generated query above is only a dummy one that i simple put together so I don't need the parameterization for simply testing. The "error" key in the json array contains error data, and the errorcode is dumped there.
23000 is the mysql error code for there being a duplicate unique column, but there is no unique column that I'm using in the query(see table struct below.)
Since the function answerQuestion is a very long one, I'll only paste the related lines next. In $site->answerQuestion it calls a function called "insertLevelStarts" which is supposed to insert an entry to the db.
This is how i call it:
if($stmtss = $this->db->insertLevelStarts($_SESSION['user']['id'], $stmts['return'][0]['id'], time(), $roundid, 1, 1, $levelstart)){
And this is how it's declared, also the rest of the related and unknown code:
public function insertLevelStarts($user_id, $question_id, $time, $round_id, $type = 0, $success = 0, $refid = 0){
/*
Type=0 start 1 stop
success=0 for start 1 if successfull on stop
*/
$query = 'INSERT INTO quiz_level_starts (`user_id`, `question_id`, `time`, `round_id`, `type`, `success`, `ref_id`) VALUES (:user_id, :question_id, :time, :round_id, :type, :success, :refid)';
echo $this->genFakeQuery($query, array(
':user_id' => $user_id,
':question_id' => $question_id,
':time' => $time,
':type' => $type,
':success' => $success,
':refid' => $refid,
':round_id' => $round_id
));
return $this->execInsert($query, array(
':user_id' => $user_id,
':question_id' => $question_id,
':time' => $time,
':type' => $type,
':success' => $success,
':refid' => $refid,
':round_id' => $round_id
)
);
}
public function genFakeQuery($query, $array){
foreach($array as $key => $val){
$query = str_replace($key, "'$val'", $query);
}
return $query;
}
public function execUpdate($query, $preparray, $updatearr){
try {
$stmt = $this->db->prepare($query);
$stmt->execute(array_merge($preparray, $updatearr));
$rows = $stmt->rowCount();
if($rows > 0){
return array('type' => 'rowsaffected', 'return' => $rows);
}else{
return array('type' => 'noreturn', 'return' => 'none');
}
} catch(PDOException $ex) {
return array('type' => 'error', 'return' => $ex);
}
}
public function updateClause($query, $update, $updatearr){
if(count($update) > 0){
$count = 0;
foreach($update as $k => $v){
if($count > 0){
$query .= ',';
}
$query .= " `$k` = :$k";
$updatearr[":$k"] = $v;
$count++;
}
}
return array('query' => $query, 'updatearr' => $updatearr);
}
The aforementioned query
INSERT INTO quiz_level_starts (`user_id`, `question_id`, `time`, `round_id`, `type`, `success`, `ref_id`) VALUES ('4', '10', '1471887809', '', '1', '1', '905')
inserts into a table looking like this:
CREATE TABLE IF NOT EXISTS `quiz_level_starts` (
`id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
`question_id` int(11) NOT NULL,
`time` int(11) NOT NULL,
`type` int(11) NOT NULL,
`success` int(11) NOT NULL,
`ref_id` int(11) NOT NULL,
`round_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE `quiz_level_starts`
ADD PRIMARY KEY (`id`);
ALTER TABLE `quiz_level_starts`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
Will greatly appriciate any help recieved.
I assume that the error occurs because round_id is an integer field that can not be NULL and has no default value and you pass it an empty value.
Try if this query works:
INSERT INTO quiz_level_starts (`user_id`, `question_id`, `time`, `round_id`, `type`, `success`, `ref_id`) VALUES ('4', '10', '1471887809', '0', '1', '1', '905')
Related
Hello I try with PDO to insert data to Sqlite, i have tried many ways, but I always get following errors: Call to a member function bindParam() on boolean.
I see also the bindParam() or bindValue return false if an error exist. But I don't find an error.
thx in advance
function insertCostumers(){
$costumers = 'INSERT IGNORE INTO costumers(first_name,last_name,age)
VALUES(:first_name,:last_name,:age)';
$stmt = $this->pdo->prepare($costumers);
$data = [['firstName' => 'Hans',
'lastName' => 'Meier',
'age' => 32],
['firstName' => 'Anna',
'lastName' => 'Mueller',
'age' => 35],
['firstName' => 'Steffi',
'lastName' => 'Gygax',
'age' => 67]];
$stmt->bindParam(
':first_name', $firstName,
':last_name', $lastName,
'age', $age);
foreach ($data as $d) {
// Set values to bound variables
$firstName = $d['firstName'];
$lastName = $d['lastName'];
$age = $d['age'];
// Execute statement
$stmt->execute();
}
die('hello');
}
require "SQLiteConnection.php";
require "SQLiteCreateTable.php";
$sqlite = new SQLiteCreateTable((new SQLiteConnection())->connect());
// create new tables
$sqlite->createTables();
$sqlite->insertCostumers();
$tables = $sqlite->getOrderList();
require "index.view.php";
#SebastianBrosch Thats the Create Statement.
public function createTables() {
$commands = ['CREATE TABLE IF NOT EXISTS costumers (
costumer_id integer PRIMARY KEY,
first_name text NOT NULL,
last_name text NOT NULL,
age integer NOT NULL
)',
'CREATE TABLE IF NOT EXISTS orders (
order_id integer PRIMARY KEY,
order_nr integer NOT NULL,
costumer_id integer,
FOREIGN KEY (costumer_id) REFERENCES costumers (costumer_id)
ON DELETE CASCADE ON UPDATE NO ACTION)'];
// execute the sql commands to create new tables
foreach ($commands as $command) {
$this->pdo->exec($command);
}
}
The variable $stmt is not a PDOStatement object. It is a boolean value (in this case false).
Your INSERT statement is not valid. Try the following instead (missing OR):
$costumers = 'INSERT OR IGNORE INTO costumers(first_name, last_name, age)
VALUES(:first_name, :last_name, :age)';
You can use the methods PDO::errorInfo and PDO::errorCode to get further information.
$costumers = 'INSERT OR IGNORE INTO costumers(first_name,last_name,age)
VALUES(:first_name,:last_name,:age)';
$stmt = $this->pdo->prepare($costumers);
if ($stmt === false) {
echo $this->pdo->errorCode().': '.$this->pdo->errorInfo();
}
You also use $firstName and $lastName before init:
function insertCostumers() {
$costumers = 'INSERT OR IGNORE INTO costumers(first_name, last_name, age)
VALUES(:first_name, :last_name, :age)';
$stmt = $this->pdo->prepare($costumers);
$data = [['firstName' => 'Hans',
'lastName' => 'Meier',
'age' => 32],
['firstName' => 'Anna',
'lastName' => 'Mueller',
'age' => 35],
['firstName' => 'Steffi',
'lastName' => 'Gygax',
'age' => 67]];
foreach ($data as $d) {
$firstName = $d['firstName'];
$lastName = $d['lastName'];
$age = $d['age'];
$stmt->bindParam(':first_name', $firstName, PDO::PARAM_STR);
$stmt->bindParam(':last_name', $lastName, PDO::PARAM_STR);
$stmt->bindParam(':age', $age, PDO::PARAM_INT);
$stmt->execute();
}
}
To make sure the combination of first_name and last_name is unique, you need to add a UNIQUE constraint to your table costumers. Use the following CREATE TABLE statement:
CREATE TABLE IF NOT EXISTS costumers (
costumer_id INTEGER PRIMARY KEY,
first_name TEXT NOT NULL,
last_name TEXT NOT NULL,
age INTEGER NOT NULL,
UNIQUE (first_name, last_name)
);
You can see the difference with and without the UNIQUE constraint on these following demo:
http://sqlfiddle.com/#!7/79b1c/1/1
I normally parse a data array to the db obj as follows
$this->db->update('myTable', array('col1' => "foo", 'col2' => 'bar' ,'col3' => 'abc'), array('id' => $id));
Is there a way of only updating the columns that have a null value? Currently i'm having to run a query first and then loop through. Not very efficient.
Here are a couple of solutions:
$sql = 'UPDATE myTable SET col1 = IF(col1 IS NULL, ?, col1), col2 = IF(col2 IS NULL, ?, col2), col3 = IF(col3 IS NULL, ?, col3)';
$this->db->query($sql, ['foo', 'bar', 'abc']);
or
$this->db->set('col1', 'IF(col1 IS NULL, "foo", col1)', FALSE)
->set('col2', 'IF(col2 IS NULL, "bar", col2)', FALSE)
->set('col3', 'IF(col3 IS NULL, "abc", col3)', FALSE)
->update('myTable');
I have a mySQL table defined as:
CREATE TABLE IF NOT EXISTS `tweet` (
`user_mail` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
`date_sent` datetime NOT NULL,
`date_edited` datetime NOT NULL,
`message` tinytext CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
PRIMARY KEY (`user_mail`,`date_sent`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
And two differents entries in this table as follows:
INSERT INTO `tweet` (`user_mail`, `date_sent`, `date_edited`, `message`) VALUES
('RichardSpencer#gmail.com', '2016-01-22 14:07:45', '', 'First tweet !');
INSERT INTO `tweet` (`user_mail`, `date_sent`, `date_edited`, `message`) VALUES
('JohnDoe#gmail.com', '2016-01-22 14:07:45', '', 'Second tweet !');
I'm using this PHP function to retrieve all tweets (at this moment there are only 2 tweets) of my table 'tweet'
function getAllTweets($con, $limit){
$stmt = mysqli_prepare($con, 'SELECT * FROM tweet ORDER BY date_sent DESC LIMIT 0, ?') or die('Oups erreur dans la requète SELECT avec le message: '. mysqli_error($con));
mysqli_stmt_bind_param($stmt, 'i', $limit);
mysqli_stmt_execute($stmt);
mysqli_stmt_bind_result($stmt, $datas['user_mail'], $datas['date_sent'], $datas['date_edited'], $datas['message']);
while($datas == mysqli_stmt_fetch($stmt)){
$result []= $datas;
}
return $result;
}
And I'm calling this function like this:
$allTweets = getAllTweets($con, 99);
When I see var_dump($allTweets);I get 2 tweets as I was especting but Both results are the same tweet . . . here is the var_dump result
array (size=2)
0 =>
array (size=4)
'user_mail' => string 'johnDoe#gmail.com' (length=17)
'date_sent' => string '2016-01-22 17:25:34' (length=19)
'date_edited' => string '0000-00-00 00:00:00' (length=19)
'message' => string 'Second tweet !' (length=14)
1 =>
array (size=4)
'user_mail' => string 'johnDoe#gmail.com' (length=17)
'date_sent' => string '2016-01-22 17:25:34' (length=19)
'date_edited' => string '0000-00-00 00:00:00' (length=19)
'message' => string 'Second tweet !' (length=14)
If you see my table I should have one tweet with the message "First tweet !"
And a second tweet with a message "Second tweet !"
Where is my mistake ?
<?php
$db = new PDO('mysql:host=localhost:3306;dbname=DB1;', 'user1','123456');
$sql = 'SELECT * FROM events';
$out = array();
foreach($db->query($sql) as $row) {
$out[] = array(
'id' => $row->title,
'title' => $row->name,
'url' => $row->url,
'class' => $row->class,
'start' => $row->start . '000',
'end' => $row->end .'000'
);
}
echo json_encode(array('success' => 1, 'result' => $out));
exit;
?>
Table Structure
Field Type Null Key Default Extra
id int(5) NO NULL
title text NO NULL
url text NO NULL
class text NO NULL
start datetime NO NULL
end datetime NO NULL
It displays output as
{"success":1,"result": [{"id":null,"title":null,"url":null,"class":null,"start":"000","end":"000"}, {"id":null,"title":null,"url":null,"class":null,"start":"000","end":"000"}]}
i want to print data in tables instead of null
Thank You.
You are not querying your rows the proper way.
Instead of :
'id' => $row->title,
use
'id' => $row['title'],
for more information, see example 1 in the official documentation
not tried this but you could change this:
foreach($db->query($sql) as $row) {
to
foreach($db->query($sql) as (object)$row) {
... which might allow the syntax used to access records.
$res (array)-> (count 50 (!) )
Example:
(
[1] => Array
(
[artistname] => Lady GaGa
[songname] => Love Games
[duration] => 3:31
[url] => 7e91a5ca16ae
[server] => 3
)
[2] => Array
(
[artistname] => DJ Layla
[songname] => Single Lady
[duration] => 3:20
[url] => f0906a3087eb
[server] => 3
)
[3] => Array
(
[artistname] => Lady Gaga
[songname] => Bad Romance (Bimbo Jones Clean Radio Remix)
[duration] => 3:59
[url] => 36e77d5a80357
[server] => 3
)
}
PHP code:
$massquery = '';
foreach($res as $value)
{
if(!get_magic_quotes_gpc())
{
$value['artistname'] = mysql_escape_string($value['artistname']);
$value['songname'] = mysql_escape_string($value['songname']);
$value['duration'] = mysql_escape_string($value['duration']);
$value['url'] = mysql_escape_string($value['url']);
$value['server'] = mysql_escape_string($value['server']);
}
$value['artistname'] = trim($value['artistname']);
$value['songname'] = trim($value['songname']);
$value['duration'] = trim($value['duration']);
$value['url'] = trim($value['url']);
$value['server'] = trim($value['server']);
$sh = mysql_query("SELECT `artistname`,`songname`,`server` FROM `music` WHERE `artistname`='".$value['artistname']."' AMD `songname`='".$value['songname']."' AND `server`='".$value['server']."' LIMIT 1");
if(!mysql_num_rows($sh))
{
$massquery .= '("'.$value['artistname'].'", "'.$value['songname'].'", "'.$value['duration'].'", "'.$value['url'].'", "'.$value['server'].'"),';
}
}
if(!empty($massquery))
{
$massquery = substr($massquery, 0, -1);
$query = mysql_query('INSERT INTO `music` (`artistname`, `songname`, `duration`, `url`, `server`) VALUES '.$massquery);
}
mysql_close($mysql);
It turns out 50 requests "SELECT" to the database, which is very bad = (
How can I optimize this code?
From answers:
CREATE TABLE `music` (
`id` int(50) NOT NULL auto_increment,
`artistname` varchar(50) NOT NULL,
`songname` varchar(50) NOT NULL,
`duration` varchar(6) NOT NULL,
`url` varchar(255) NOT NULL,
`server` int(5) NOT NULL,
PRIMARY KEY (`id`),
KEY `artistname` (`artistname`,`songname`,`server`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO `music` VALUES ('test', 'btest', 1);
...
SELECT `artistname` , `songname` , `server`
FROM `music`
WHERE FALSE
OR (
`artistname` = 'test'
AND `songname` = 'btest'
AND `server` = '1'
)
OR (
`artistname` = 'sas'
AND `songname` = 'asf'
AND `server` = '1'
)
LIMIT 0 , 30
How do I INSERT those songs that are not yet in the database?
Sorry for bad english
You want to insert new records only if no other record with the tuple (artistname,songname,server) (already) exists.
If you create a unique index for these three fields MySQL won't insert a doublet. Then you can either use something like
INSERT IGNORE INTO
tablename
(a,b,c,x,y,z)
VALUES
(1,2,3,4,5,6),
(7,8,9,10,11,12),
...
(95,96,97,98,99,100)
or a prepared statement, e.g.
$pdo = new PDO("mysql:host=localhost;dbname=test", 'localonly', 'localonly');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
/* test table */
$pdo->exec('
CREATE TEMPORARY TABLE foo (
id int auto_increment,
artistname varchar(64) not null,
songname varchar(64) not null,
duration varchar(16) not null,
url varchar(64) not null,
server int not null,
primary key(id),
unique key (artistname,songname,server)
)
');
$data = array(
array(':artistname' => 'Lady GaGa', ':songname' => 'Love Games', ':duration' => '3:31', ':url' => '7e91a5ca16ae', ':server' => 3),
array(':artistname' => 'DJ Layla', ':songname' => 'Single Lady', ':duration' => '3:20', ':url' => 'f0906a3087eb', ':server' => 3),
array(':artistname' => 'Lady Gaga', ':songname' => 'Bad Romance (Bimbo Jones Clean Radio Remix)', ':duration' => '3:59', ':url' => '36e77d5a80357', ':server' => 3)
);
/* the "actual" test script */
$stmt = $pdo->prepare('
INSERT IGNORE INTO
foo
(duration, artistname, songname, server, url)
VALUES
(:duration, :artistname, :songname, :server, :url)
');
// first run, all three records should be inserted
foreach( $data as $params ) {
$stmt->execute($params);
}
// second run
// same artist/songname, different server
$newData = $data[0]; $newData[':server'] = 4;
$data[] = $newData;
// and a completly new record
$data[] = array(':artistname' => 'xyz', ':songname' => 'The ABC song', ':duration' => '2:31', ':url' => 'whatever', ':server' => 2);
// again insert all records (including the three that have already been inserted)
foreach( $data as $params ) {
$stmt->execute($params);
}
/* fetch all records */
foreach( $pdo->query('SELECT * FROM foo', PDO::FETCH_NUM) as $row ) {
echo join(', ', $row), "\n";
}
prints
1, Lady GaGa, Love Games, 3:31, 7e91a5ca16ae, 3
2, DJ Layla, Single Lady, 3:20, f0906a3087eb, 3
3, Lady Gaga, Bad Romance (Bimbo Jones Clean Radio Remix), 3:59, 36e77d5a80357, 3
4, Lady GaGa, Love Games, 3:31, 7e91a5ca16ae, 4
5, xyz, The ABC song, 2:31, whatever, 2
The first three records have not been duplicated.
Create a single select for all the relevant cases like this, and verify the results by means of PHP:
$sh = "SELECT `artistname`,`songname`,`server` FROM `music` WHERE ";
$pq = ""
foreach($res as $value)
{
if(!get_magic_quotes_gpc())
{
$value['artistname'] = mysql_escape_string($value['artistname']);
$value['songname'] = mysql_escape_string($value['songname']);
$value['duration'] = mysql_escape_string($value['duration']);
$value['url'] = mysql_escape_string($value['url']);
$value['server'] = mysql_escape_string($value['server']);
}
$value['artistname'] = trim($value['artistname']);
$value['songname'] = trim($value['songname']);
$value['duration'] = trim($value['duration']);
$value['url'] = trim($value['url']);
$value['server'] = trim($value['server']);
$sh .= $pq . `(artistname`='".$value['artistname']."' AMD `songname`='".$value['songname']."' AND `server`='".$value['server']."')");
$pq = " OR ";
}
$res = mysql_query($sh);
The select query you wrote can't be inside the foreach, or of course it'll query each time. Turn your $res into a long WHERE clause:
$sql = "SELECT `artistname`,`songname`,`server` FROM `music` WHERE FALSE ";
foreach($res as $value)
{
// ...
$sql .= "OR (`artistname`='".$value['artistname']."' AND `songname`='".$value['songname']."' AND `server`='".$value['server'].")";
}
and then run that query against the database and build your INSERT query.