I am migrating a database from MySQL to MSSQL.
[MySQL] I have a CHANGEDATE column that is of TIMESTAMP with default CURRENT_TIMESTAMP
[MSSQL] I have the same CHANGEDATE column that is of DATETIME and added a default constraint of GETDATE()
The codebase is PHP using CodeIgniter. I want the column to always be set so I don't allow NULL in either DBMS.
When I insert with MySQL, the property of the PHP model CHANGEDATE defaults to NULL. This triggers the default and the column entry is set to CURRENT_TIMESTAMP. The same code when configured to MSSQL however throws an error that NULL is not allowed in the column, which is valid, but I would rather MSSQL function like MySQL and insert the value of GETDATE() in that instance.
If I do unset($model->CHANGEDATE) or delete the property from my model, then it works as expected, but I wanted to know if there was a way to solve this just using MSSQL instead of updating all my PHP models.
class model {
public $CHANGEDATE;
...
}
ERROR (as described):
[Microsoft][ODBC Driver 11 for SQL Server][SQL Server]Cannot insert the value NULL into column 'CHANGEDATE'; column does not allow nulls. INSERT fails.
INSERT INTO Logs (..., CHANGEDATE, CHANGEBY) VALUES (..., NULL, NULL)
UPDATE:
CI should create support for DBMS specific keywords as #steoleary stated in his answer(for which I marked his correct). However, I found the best solution in my case was to slightly modify the core class DB_active_rec.php
function set(...){
...
foreach ($key as $k => $v)
{
if (is_null($v)) continue;
...
}
}
I assume that you already have the default set on your SQL server column and you don't allow NULLs, deafult constraints won't fire on a NULL value, they will only fire when no value is specified, or if you specify to insert the default value on insert like this:
INSERT INTO [dbo].[table]
([col1]
,[col2]
,[col3]
,[col4]) --Column with default constraint
VALUES
('bob',
'bobson',
1,
DEFAULT) --default keyword
Doing that will cause the default to fire and you shouldn't have to change your models.
I don't know how to express this in code igniter, but in SQL Server, it is really easy:
create table . . . (
changedate not null datetime default getdate()
)
No trigger is required.
Related
I have a table in Postgres with DDL like this one:
CREATE TABLE robots(
robot_id INTEGER NOT NULL CONSTRAINT robot_id_pkey PRIMARY KEY,
name TEXT
);
I know I can insert a record with following SQL statement:
INSERT INTO robots (robot_id, name) VALUES (nextval('robots_seq'), 'WALL-E');
I need to make CRUD operations in Phalcon for this table. Also I want to use ORM features.
So I do:
$robot = new Robots();
$robot->setRobotId(new \Phalcon\Db\RawValue("nextval('robots_seq')"));
$robot->setName('WALL-E');
$robot->save();
And get the following exception:
Uncaught PDOException: SQLSTATE[22P02]: Invalid text representation:
7 ERROR: invalid input syntax for integer: 'nextval('robots_seq')';
Is there any way to accomplish this ?
To tell Phalcon what is name of your model sequence use function getSequenceName:
public function getSequenceName() {
return 'category_id_seq';
}
Phalcon assumes that your models with serial (auto increment) columns will have a sequence named [table_name]_[column_name]_seq.
In your case, in order to make Phalcon take care of handling auto increment columns you should have a sequence named robots_robot_id_seq instead of robots_seq for the column robot_id (which I'd call just "id", by the way).
This way you do not have to worry about setting the id when creating an object and after saving Phalcon will fill that field automatically for you:
CREATE TABLE robots(
robot_id SERIAL PRIMARY KEY NOT NULL,
name TEXT
);
$robot = new Robots();
$robot->setName('WALL-E');
$robot->save();
$robot->getRobotId(); // Should return last "nextval()" sequence
Hope it helps.
I have switched from MySQL to MariaDB which has caused some "minor" problems. One has been bugging me for hours now and i can't find the solution.
I moved my database by exporting it from MySQL and importing it into MariaDB which went well..
When one of my update queries did not work i narrowed it down to this function in my database handler:
public function updateEquipment($type,$product,$acquisition,$calibration_interval,$equipment_no,$inspection_date,$equipment_id,$active)
{
$stmt = $this->conn->prepare("UPDATE equipment SET type = :type, acquisition = :acquisition, calibration_interval = :calibration_interval, equipment_no = :equipment_no, product = :product, inspection_date = :inspection_date, active = :active WHERE id = :equipment_id");
$stmt->bindParam(":equipment_id", $equipment_id,PDO::PARAM_INT);
$stmt->bindParam(":type", $type,PDO::PARAM_STR);
$stmt->bindParam(":acquisition", $acquisition,PDO::PARAM_STR);
$stmt->bindParam(":calibration_interval", $calibration_interval,PDO::PARAM_STR);
$stmt->bindParam(":equipment_no", $equipment_no,PDO::PARAM_STR);
$stmt->bindParam(":product", $product,PDO::PARAM_STR);
$stmt->bindParam(":inspection_date", $this->formatDateStrToTimeStamp($inspection_date),PDO::PARAM_STR);
$stmt->bindParam(":active", $active,PDO::PARAM_INT);
return $stmt->execute();
}
formatDateStrToTimeStamp function:
private function formatDateStrToTimeStamp($inspection_date)
{
$day = substr($inspection_date,0,2);
$month = substr($inspection_date,3,2);
$year = substr($inspection_date,6,4);
return date('Y-m-d H:i:s', strtotime($year."-".$month."-".$day));
}
As you can see, i have switched out the binding of my inspection_date with a string representing the timestamp i want to update. I tested the statement WITHOUT updating my timestamp and then it was working as expected. As soon as i add the timestamp (in my case i have inserted a static timestamp) the row will NOT update and execute does not return (it should return true or false).
Heres my table structure:
CREATE TABLE `equipment` (
`id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
`type` text NOT NULL,
`acquisition` text NOT NULL,
`calibration_interval` text NOT NULL,
`equipment_no` text NOT NULL,
`product` text NOT NULL,
`inspection_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`active` int(11) NOT NULL DEFAULT '1'
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Question: Are timestamps treated different in mariaDB, because i have not made any changes to my code since the switch, and i have simply imported my database from the export i made from my MySQL database.
After debugging my pants off (because im not very good at debugging web applications) i finally found the answer to my problem.
PDO's bindparam must bind a variable to a placeholder or questionmark which is also stated in the pdo documentation. In my case i tried both inserting a string directly when binding, and the original code with the error used the return value of a timestamp formater. In both cases i didn't use a variable when binding to my placeholder, hence the error....
I came across the the error when i debugged the function using Chrome's Advanced Rest Client which revealed an error: "Only variables should be passed by reference".
Solution 1:
$inspect = $this->formatDateStrToTimeStamp($inspection_date);
$stmt->bindParam(":inspection_date", $inspect,PDO::PARAM_STR);
Solution 2:
As pointed out by Ryan Vincent in the comments use bindValue instead (see his comment for further inspiration)
But still a bit confused:
I'm still a bit confused though, as the code previously ran on another host without problems. I cannot remember the PHP version or anything, but if someone could confirm that it was possible in previous version it would explain why...
I am getting the error MysqlError: Duplicate entry '1-5' for key 'PRIMARY' as shown below in the code. It only happened once (that I could detect, but it was random) and I couldn't find a cause (New Relic reported), but I cannot reproduce and I don't have much more information except the line number and the error given. The schema and code is below.
num_rows() is somehow returning a value that is not 1 even though it shouldn't. If someone can give some insight on how to debug or fix that would be helpful.
Here is my schema for location_items:
CREATE TABLE `phppos_location_items` (
`location_id` int(11) NOT NULL,
`item_id` int(11) NOT NULL,
`location` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
`cost_price` decimal(23,10) DEFAULT NULL,
`unit_price` decimal(23,10) DEFAULT NULL,
`promo_price` decimal(23,10) DEFAULT NULL,
`start_date` date DEFAULT NULL,
`end_date` date DEFAULT NULL,
`quantity` decimal(23,10) DEFAULT '0.0000000000',
`reorder_level` decimal(23,10) DEFAULT NULL,
`override_default_tax` int(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`location_id`,`item_id`),
KEY `phppos_location_items_ibfk_2` (`item_id`),
CONSTRAINT `phppos_location_items_ibfk_1` FOREIGN KEY (`location_id`) REFERENCES `phppos_locations` (`location_id`),
CONSTRAINT `phppos_location_items_ibfk_2` FOREIGN KEY (`item_id`) REFERENCES `phppos_items` (`item_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |
And the code:
//Lock tables involved in sale transaction so we do not have deadlock
$this->db->query('LOCK TABLES '.$this->db->dbprefix('customers').' WRITE, '.$this->db->dbprefix('receivings').' WRITE,
'.$this->db->dbprefix('store_accounts').' WRITE, '.$this->db->dbprefix('receivings_items').' WRITE,
'.$this->db->dbprefix('giftcards').' WRITE, '.$this->db->dbprefix('location_items').' WRITE,
'.$this->db->dbprefix('inventory').' WRITE,
'.$this->db->dbprefix('people').' READ,'.$this->db->dbprefix('items').' WRITE
,'.$this->db->dbprefix('employees_locations').' READ,'.$this->db->dbprefix('locations').' READ, '.$this->db->dbprefix('items_tier_prices').' READ
, '.$this->db->dbprefix('location_items_tier_prices').' READ, '.$this->db->dbprefix('items_taxes').' READ, '.$this->db->dbprefix('item_kits').' READ
, '.$this->db->dbprefix('location_item_kits').' READ, '.$this->db->dbprefix('item_kit_items').' READ, '.$this->db->dbprefix('employees').' READ , '.$this->db->dbprefix('item_kits_tier_prices').' READ
, '.$this->db->dbprefix('location_item_kits_tier_prices').' READ, '.$this->db->dbprefix('suppliers').' READ, '.$this->db->dbprefix('location_items_taxes').' READ
, '.$this->db->dbprefix('location_item_kits_taxes'). ' READ, '.$this->db->dbprefix('item_kits_taxes'). ' READ');
// other code for inserting data into other tables that are not relevant.
foreach($items as $line=>$item)
{
$cur_item_location_info->quantity = $cur_item_location_info->quantity !== NULL ? $cur_item_location_info->quantity : 0;
$quantity_data=array(
'quantity'=>$cur_item_location_info->quantity + $item['quantity'],
'location_id'=>$this->Employee->get_logged_in_employee_current_location_id(),
'item_id'=>$item['item_id']
);
$this->Item_location->save($quantity_data,$item['item_id']);
}
// other code for inserting data into other tables that are not relevant.
$this->db->query('UNLOCK TABLES');
class Item_location extends CI_Model
{
function exists($item_id,$location=false)
{
if(!$location)
{
$location= $this->Employee->get_logged_in_employee_current_location_id();
}
$this->db->from('location_items');
$this->db->where('item_id',$item_id);
$this->db->where('location_id',$location);
$query = $this->db->get();
return ($query->num_rows()==1);
}
function save($item_location_data,$item_id=-1,$location_id=false)
{
if(!$location_id)
{
$location_id= $this->Employee->get_logged_in_employee_current_location_id();
}
if (!$this->exists($item_id,$location_id))
{
$item_location_data['item_id'] = $item_id;
$item_location_data['location_id'] = $location_id;
//MysqlError: Duplicate entry '1-5' for key 'PRIMARY'
return $this->db->insert('location_items',$item_location_data);
}
$this->db->where('item_id',$item_id);
$this->db->where('location_id',$location_id);
return $this->db->update('location_items',$item_location_data);
}
}
function get_logged_in_employee_current_location_id()
{
if($this->is_logged_in())
{
//If we have a location in the session
if ($this->session->userdata('employee_current_location_id')!==FALSE)
{
return $this->session->userdata('employee_current_location_id');
}
//Return the first location user is authenticated for
return current($this->get_authenticated_location_ids($this->session->userdata('person_id')));
}
return FALSE;
}
It's not a good idea to check for existence prior to inserting data outside a transaction as this leaves open the possibility of data changing in the mean time. The fact that you've seen this error once but it isn't easily repeatable makes me wonder whether this might have happened.
Would suggest changing the code beneath the first if block in the save function to something that generates the following SQL instead:
INSERT INTO location_items (item_id, location_id)
VALUES ($item_id,$location_id)
ON DUPLICATE KEY UPDATE
This covers the existence check and insert or update in a single atomic statement. (To take this any further and say how to actually implement it I'd need access to the db code.)
EDIT: Sorry, only just noticed the db code is CodeIgniter. Am new to this framework but the above method looks perfectly possible from a brief look here. Something like this:
$sql = "INSERT INTO location_items (item_id, location_id)"
. " VALUES (?, ?)"
. " ON DUPLICATE KEY UPDATE";
$this->db->query($sql, array($item_id, $location_id));
(If for some reason you prefer not to do this, another way to keep it atomic would be to wrap the statements within a transaction instead ($this->db->trans_start(); before the existence check and $this->db->trans_complete(); after the insert/update. But IMO this introduces unnecessary complexity - personally much prefer the first method.)
Looks like a race condition. What likely happened is to roughly simultaneous calls to:
save($data,5);
both get to the exists check at the same time and see that there is no existing entry. Both then try to insert and the fastest gun wins.
You are not going to get a solution so long as the following conditions exist:
You cannot reproduce this problem yourself.
You do not share your source code and database for someone else to attempt to replicate.
I am not asking you to share your full source code. Rather, I am saying this to temper your expectations.
That being said, duplicates can exist for numerous reasons. It would help your question if you provided your version, but I did find one reason that could be a cause: Memory too low - could be reproducible if you lower your memory or put a high strain on your system. If you've had a hard time reproducing it, memory could well be why as you may not be trying to simulate that.
Other things to consider:
You may be wasting your time trying to duplicate something that just will not be duplicated.
If you are concerned you will experience this issue again, you should really consider logging. That can help you to track down the query which caused the issue. I would advise that you not have logging in a production environment and only in development, because it will probably lead to performance penalties that could well be significant. If this is a one-off issue you may never see it again, but it doesn't hurt to be prepared and armed with more information if the issue appears again.
Ultimately, debugging requires the ability to reproduce the error. A bug is part of a computer program, which means there are certain situations and environments in which this will occur, which can be reproduced. When you have no idea how or why a bug was caused there is nowhere to work back from. It is helpful to look to auxiliary concerns as the potential source of your issue by exploring bug reports, etc. If that fails, implement tools like logging that give you more information. This is the only way you will be able to find the root cause of this issue, or get any more specific insight from the SO community on how to do so.
I suspect that the problem could be related with the cache of where statements.
This suspect comes from this stackoverflow question.
Basically I think it could happen that:
- in one cycle this code is executed at the end of save method:
$this->db->where('item_id',$item_id);
$this->db->where('location_id',$location_id);
return $this->db->update('location_items',$item_location_data);
- in the subsequent cycle this code is executed in the exists method:
$this->db->where('item_id',$item_id);
$this->db->where('location_id',$location_id);
return $this->db->update('location_items',$item_location_data);
When executing the "exists" code the cache may still contain the where clauses of the previous statement and the new one (different) will be added.
This way the result will be empty and it seems that the row it is not in the table.
Try to use $this->db->flush_cache(); after the update in the save method.
Also try to use echo $this->db->last_query(); to see what is trying to do in the exists query.
may be ' phppos_location_items ' table is exist in past and a delete statement is executed over this table Delete from phppos_location_items;
in this case primary key column not accept previous values if you truncate the table then all previous record will be removed
Sorry its slightly long for a comment ..
What submits the form ? I assume its a button somewhere on a Page, When I have had a similar error its been due to a user double clicking on a button and 2 requests being sent, in a very close time to each other. This caused a check similar to yours to have this situatuion
Request 1 Check Insert
Request 2 Check Insert
As Request 2 was the last request (because the second click took priority) the error was shown though the first request completed all of the work.
i use the code
$("form").submit(function() {
$(this).submit(function() {
return false;
});
return true;
});
from this question
How to prevent form from submitting multiple times from client side?
Try to use
$insert = $this->db->insert('location_items',$item_location_data);
if($insert)
{
$this->db->reset();
//OR
try $this->db->_reset_write(); to flush all traces of the query
return $insert;
}
Solution 1:
Duplicate entry states that you have one more row which has a primary key same as that of some other previous row.
You can ignore this by statement
INSERT IGNORE INTO ..(rest is same, just add ignore)..
Solution 2:
If you wanna overwrite previous row with new one then you need to follow this query:
INSERT INTO TABLE (f1,f2) VALUES ('f1','f2') ON DUPLICATE KEY UPDATE f1='f1',f2='f2'
Solution: 3
Change your primary key by following these queries:
Create new field for primary key:
ALTER TABLE tablename ADD new_primary_key BIGINT NOT NULL FIRST;
Drop existing primary key:
ALTER TABLE tablename DROP PRIMARY KEY
Now make the new field created earlier as primary key with auto increment
ALTER TABLE tablename MODIFY new_primary_key BIGINT AUTO_INCREMENT PRIMARY KEY
(this will not affect other queries in the code, you can keep them as it is and just add LIMIT 1 in select statements)
I have two tables
CREATE TABLE angos
(
id int NOT NULL AUTO_INCREMENT PRIMARY KEY,
angos varchar(255) not null,
grammar varchar(3) not null,
updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT unique_input UNIQUE (angos)
) engine=InnoDB;
CREATE TABLE english
(
id int primary key,
angos_id int,
english varchar(255),
grammar_note varchar(500),
CONSTRAINT fk_angos_source FOREIGN KEY (angos_id) REFERENCES angos(id)
) engine=InnoDB;
...and some csv data that I have stored in a php array. I'm trying to insert the array data into two mysql tables, one of which has a fk of the other. I'm using php PDO for the insert. This is what I have...
$sql = <<<'EOD'
BEGIN;
INSERT INTO angos
(angos, grammar)
VALUES
(:angos, :grammar);
INSERT INTO english
(angos_id, english)
VALUES
(:angos_id, :english);
COMMIT;
EOD;
try
{
$q = $handler->prepare($sql);
foreach($csv as $value)
{
$q->execute(
array(
':angos' => $value[0],
':grammar' => 'con',
':english' => $value[1],
':angos_id' => 'LAST_INSERT_ID()'
)
);
}
}
catch(PDOException $e)
{
die( "Error: \n" . $e->getMessage() );
}
...the data fails to insert and the error message I'm currently getting is
Error: SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active. Consider using PDOStatement::fetchAll().
Alternatively, if your code is only ever going to run against mysql, you may enable query buffering by setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute.
What's happening?
By default you can only execute one query at once with PDOStatement::execute() method. If you like to use transactions have a look at the PDO::beginTransaction(), PDO::commit() and PDO::rollBack() methods of PDO. And create two separated queries.
But as you're told by PHP you can set the attribute PDO::MYSQL_ATTR_USE_BUFFERED_QUERY to run multiple queries in one prepared statement. But you could run intro trouble if you change the PDO driver e.g. from MySQL to PostgreSQL. Because PostgreSQL does not support the attribute mentioned above. So I suggest that you only use one statement per prepared statement. That makes the prepared statements also more reusable than a specfic construct of statements.
I have recently created a column family with CREATE TABLE in cqlsh console.
I have the following column family;
CREATE TABLE user (
id timeuuid PRIMARY KEY,
balance decimal,
is_free boolean,
player_id bigint,
)
I want to insert the following type of data;
{
"player_id": 104,
"balance": "0.00",
"is_free": false,
"bonus": "3+3+3"
}
when I do insert from cqlsh console the insertion is the following;
INSERT INTO keyspace.user (player_id, balance,is_free,bonus, id)
VALUES (104,0.00,'3+3+3',67c5cb00-d814-11e2-9e13-59eb8e4538e5)
Id is generated by UUID::uuid1()->string
When I try to insert from cqlsh, it gives no error. However, in phpcassa, it gives the following error :
Expected key 'ID' to be present in WHERE clause for 'users'
I already set the client's cql version to 3.0.0 by
$pool->get()->client->set_cql_version("3.0.0");
I already tried to insert timeuuid field like that '67c5cb00-d814-11e2-9e13-59eb8e4538e5'
By the way, the var_dump of the variable $cql that is executed is the following;
string 'INSERT INTO keyspace.user (player_id,balance,is_free,bonus,id) VALUES (104,0.00,false,'3+3+3','ca256040-d819-11e2-ae08-f78171d975c3')'
What is the problem here ?
execute_cql_query() function does not seem to be working in cql_version 3.0.0
although cql 3 support is not official in phpcassa, execute_cql3_query() function works stable & well for me by using the following code;
$this->_connection_pool->get()->client->execute_cql3_query($cql, ConsistencyLevel::ONE);
where $cql is the following;
string 'INSERT INTO keyspace.user (player_id,balance,is_free,bonus,id)
VALUES (104,0.00,false,'3+3+3',12128260-d8ea-11e2-b5b3-0be38f9377b6)'