CakePHP 2.2 with PostgreSQL Failed new row insert - Database Error: Undefined table: 7 ERROR: relation "table_id_seq" does not exist - postgresql

My problem is as follows.
After deleting multiple rows from table, inserting new record into same table results in error.
Database Error
Error: SQLSTATE[42P01]:
Undefined table: 7 ERROR: relation "order_details_id_seq" does not exist
Table
CREATE TABLE schema.order_details (
id serial NOT NULL,
order_id integer NOT NULL,
field_1 integer,
field_2 real,
field_3 character varying(15),
CONSTRAINT order_details_pkey PRIMARY KEY (id )
)
WITH (
OIDS=FALSE
);
Insert is
INSERT INTO "schema"."order_details" ("order_id", "field_1", "field_2", "field_3")
VALUES (37, 1, 2, 'value');
Sequence "schema"."order_details_id_seq" in used schema exists.
CREATE SEQUENCE schema.order_details_id_seq
INCREMENT 1
MINVALUE 1
MAXVALUE 9223372036854775807
START 37
CACHE 1;
Models.
// Model
class Order extends AppModel {
public $useDbConfig = 'other_data';
public $hasMany = array(
'OrderDetail' => array(
'className' => 'OrderDetail',
'foreignKey' => 'order_id',
'dependent' => true,
'order' => array(
'OrderDetail.order_id',
'OrderDetail.field_1'
))
);
class OrderDetail extends AppModel {
public $useDbConfig = 'other_data';
public $belongsTo = array(
'Order' => array(
'className' => 'Order',
'foreignKey' => 'order_id',
'dependent' => true
),
// model Order save code on recreation of order
$this->OrderDetail->deleteAll(array('OrderDetail.order_id' => $this->id));
At this point tried to insert $this->OrderDetail->query('VACUUM FULL ANALYZE order_details'); with no effect
foreach ($details as $d) {
$this->OrderDetail->create();
$this->OrderDetail->save($d /*array(
'order_id' => $this->id,
'field_1' => 1,
'field_2' => 2,
'field_3' => 'value'
)*/);
}
I get error on first foreach loop.
Weirdest thing is that problem appears and disappears after some time randomly.
Any suggestions on what it could be and how to get rid of it?
Currently solved problem using code.
$this->Order->id = $id;
$this->Order->delete();
It fires 2 queries for each row (100 extra in my case!) of delete statements instead of two in case of
$this->OrderDetail->deleteAll(array('OrderDetail.order_id' => $id));
So for this time it has space for improvement.
EDIT: Currently code works as it should with tweaked DboSource.

It seems that cake was looking in public schema for sequence where it is not located.
Fixed it by tweaking to include schema name in last insert getter inf file Model/Datasource/DboSource.php create method with this diff
## -1006,7 +1006,7 ##
if ($this->execute($this->renderStatement('create', $query))) {
if (empty($id)) {
- $id = $this->lastInsertId($this->fullTableName($model, false, false), $model->primaryKey);
+ $id = $this->lastInsertId($this->fullTableName($model, false, true), $model->primaryKey);
}
$model->setInsertID($id);
$model->id = $id;
I know that modifying core is not the way to go, but as long as it is working it is fine with me.

This happened to me because I modified the name of the table, but PostgreSQL did not change the name of the sequences. Knowing this, I changed the name of the sequences that affected this table and it was resolved.

To prevent this error, use this convention to name your sequence when using cakephp: table_name_id_seq. For example:
table name: user
sequence name should be: user_id_seq
If you alredy have sequences, you can rename it in posgres like this
alter sequence user_seq rename to user_id_seq
I'm not a fun of this way to name sequence but it prenvent this kind of errors in my case

Related

How to setup these relations correct in Backpack for Laravel

I have a Problem with relations in Laravel / with Backpack for Laravel.
This program is for creating menues for my dad´s restaurant.
I have these tables:
- dishes (Table with the Names of the Dishes)
-- id (auto inc)
-- menuname (Name of the Dish)
- weeklymenues
-- id (auto inc)
-- start_date (Monday of the selected week)
-- end_date (Friday of the selected week)
-- menu_monday (There should be the id of the dish)
-- menu_tuesday (...)
-- menu_wednesday (...)
.....
How can i do that correctly?
In the CRUD Controller i am setting the Field:
$this->crud->addField([
'label' => "Monday",
'type' => 'select2',
'name' => 'menu_mondy', // the db column for the foreign key
'entity' => 'menu', // the method that defines the relationship in your Model
'attribute' => 'menuname', // foreign key attribute that is shown to user
'model' => "App\Models\Menu" // foreign key model
]);
And in the menues model i have set this relation:
public function menu() {
return $this->belongsTo('\App\Models\Menu');
}
Everytime I want to save the CRUD, the program wants to save something in the dishes table:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'dish_id' in 'field list' (SQL: update `weeklymenues` set `dish_id` = 1, `weeklymenues`.`updated_at` = 2019-06-25 14:13:14 where `id` = 15)
What am I doing wrong? How can I set the relations correct?
Thanks in advance!!
Are "dishes" (from the table definition) and "Menu" (from the code) actually the same thing?
In your field definition, you set the model to be the "menu" class, shouldn't it be the "dishes" class?
I would have used the relationship type
CRUD::addField([
'label' => 'Monday',
'type' => 'relationship',
'name' => 'menu', // the method that defines the relationship in your Model
'attribute' => 'menuname', // foreign key attribute that is shown to user
'model' => \App\Models\dishes::class, // foreign key model
'placeholder' => 'Select a dish for Monday'
]);
---
-c:\xampp\htdocs\bpwebsite\app\Models\weeklymenues.php
---
protected $fillable = ['start_date', 'end_date', 'menu_monday']
---

Insert query using foreach

I'm trying to generate a statement sheet. For that, I try to insert data into Mysql using foreach. It works great. But I try to escape the insert query if any duplicate row found in database. I do not want to escape the whole process, but it should escape if a duplicate row found and continue with the next insert query.
$gps = $this->Webmastermodel->acquireStatementDetail();
foreach($gps as $statement){
$insertdata = array(
'product_invoice_id' => $statement['InvoiceNo'],
'PUC' => $statement['productPUC'],
'productQTY' => $statement['productQty'],
'product_sub_total' => $statement['productSubTotal'],
'commissionGain_on_SubTotal' => $statement['commisionChargedOnProductSubTotal']
);
$this->db->insert('partner_business', $insertdata);
}
I would use insert_batch if i were you:
$insertdata = array();
foreach($gps as $statement)
{
$insertdata[] = array(
'product_invoice_id' => $statement['InvoiceNo'],
'PUC' => $statement['productPUC'],
'productQTY' => $statement['productQty'],
'product_sub_total' => $statement['productSubTotal'],
'commissionGain_on_SubTotal' => $statement['commisionChargedOnProductSubTotal']
);
}
$this->db->insert_batch('partner_business', $insertdata);
But since your question is about skipping if a row already exits, you can modify your query and use replace instead:
$this->db->replace('partner_business', $insertdata);
Keeping in mind that its all about providing the right primary_key in your $data array, if there is and existing id it will update its data else it will insert a new row.

CakePHP 3: Model Unit Test fails - "duplicate key value"

I'm using Postgres (which I think is related to the problem), and CakePHP 3.
I have the following unit test to just check to make sure that a valid dataset can get saved by the model. When I run the following test, with a standard "bake'd" Model unit test, I get the error below.
I think this is the problem:
We are using fixtures to add some base data. This is the only place that I think might be causing a problem. To add credence to this, while the unit tests were running I ran the following command to get the next auto-incrementing id value and it returned 1, even though it returned the proper number in non-test DB. Select nextval(pg_get_serial_sequence('agencies', 'id')) as new_id;
Unit Test:
public function testValidationDefault()
{
$agencyData = [
'full_name' => 'Agency Full Name',
'mode' => 'transit',
'request_api_class' => 'Rest\Get\Json',
'response_api_class' => 'NextBus\Generic',
'realtime_url_pattern' => 'http://api.example.com',
'routes' => '{"123": {"full_route": "123 Full Route", "route_color": "#123456"}}'
];
$agency = $this->Agencies->newEntity($agencyData);
$saved = $this->Agencies->save($agency);
$this->assertInstanceOf('App\Model\Entity\Agency', $saved);
}
Error:
PDOException: SQLSTATE[23505]: Unique violation: 7 ERROR: duplicate key value violates unique constraint "agencies_pkey"
DETAIL: Key (id)=(1) already exists.
Things I've tried
Copied that same code into a controller, and it successfully added the entity in the table.
Adding an id of 200. Same error appears.
Update 1
The fixture for this does have the ID field set each record. Deleting them from the fixture does work, but it breaks other unit tests that rely on some relational data.
I don't like this solution, but adding the following before saving the entity does work.
$this->Agencies->deleteAll('1=1');
[UPDATE: My other answer is the real solution to this problem.! You don't have to do this anymore...]
Here is a less dirty workaround that doesn't require deleting all the records:
use Cake\Datasource\ConnectionManager;
...
$connection = ConnectionManager::get('test');
$results = $connection->execute('ALTER SEQUENCE <tablename>_id_seq RESTART WITH 999999');
//TEST WHICH INSERTS RECORD(s)...
It appears that the auto-incrementing doesn't get properly set/reset during the setUp() or tearDown()... so manually setting it to something really high (greater than the number of existing records) prevents the "duplicate key..." error.
The benefit of this hack (over deleteAll('1=1')) is that you can still subsequently run tests that reference existing DB data.
It might be a problem in your fixture definition. The Cake PHP documentation uses a _constraints field specifying that the id field is a primary key:
'_constraints' => [
'primary' => ['type' => 'primary', 'columns' => ['id']],
]
I believe I've finally figured out the REAL solution to this problem!
I believe this issue stems from a default fixture setting that results from using the bake command to generate fixtures.
When you bake a model it creates the boilerplate for it's fixtures. Notice the autoIncrement for the ID property in the code below? Contrary to what you might think, this should not but true. When I set it to null and remove the ids from the items in the $records array I no longer get uniqueness errors.
public $fields = [
'id' => ['type' => 'integer', 'length' => 10, 'autoIncrement' => true, 'default' => null, 'null' => false, 'comment' => null, 'precision' => null, 'unsigned' => null],
'nickname' => ['type' => 'text', 'length' => null, 'default' => null, 'null' => false, 'comment' => null, 'precision' => null],
...
public $records = [
[
// 'id' => 1,
'nickname' => 'Foo bar',
'width' => 800,
...
The ninja wizards on the CakePHP project are the heroes: source
CakePHP ticket
If id fields are removed from fixture records then they will utilize auto-incrementing when inserted, leaving the table's ID sequence in the right place for inserts that happen during tests. I believe that is why it works for #emersonthis as described above.
That solution has another problem, though: you can't create dependable relationships between fixture records because you don't know what IDs they will get. What do you put in the foreign ID field of a related table? This has led me back to his original solution of just altering the table sequence after records with hard-coded IDs have been inserted. I do it like this in affected TestCases now:
public $fixtures = [
'app.articles',
'app.authors',
];
...
public function setUp()
{
$connection = \Cake\Datasource\ConnectionManager::get('test');
foreach ($this->fixtures as $fixture) {
$tableName = explode('.', $fixture)[1];
$connection->execute("
SELECT setval(
pg_get_serial_sequence('$tableName', 'id'),
(SELECT MAX(id) FROM $tableName)
)");
}
}
This moves the auto-increment sequence to the highest previously-used ID. The next time an ID is generated from the sequence it will be one higher, resolving the problem in all cases.
Including one of these solutions in an upcoming CakePHP release is being discussed here.

FuelPHP ORM Primary Key on model cannot be changed

I've been banging my head with this ORM error:
Fuel\Core\FuelException [ Error ]: Primary key on model Model_CustomValue cannot be changed.
Here are relevant info from my models I'm having issues with:
<?php
use Orm\Model;
class Model_Purchase extends Model
{
protected static $_has_many = array(
'customvalues' => array(
'model_to' => 'Model_CustomValue',
'key_to' => 'purchase_id',
'cascade_delete' => true,
)
);
protected static $_properties = array(
'id',
'customer_id',
'payment_id',
'audit_id',
'created_at',
'updated_at',
);
<?php
use Orm\Model;
class Model_CustomValue extends Model
{
protected static $_table_name = 'customvalues';
protected static $_primary_key = array('purchase_id', 'customfield_id');
protected static $_belongs_to = array(
'purchase' => array(
'key_from' => 'purchase_id',
'model_to' => 'Model_Purchase',
'key_to' => 'id',
),
);
When trying to save the Model_Purchase with an array of Model_CustomValue objects as a property named 'customvalues' on the $purchase object, I get the "Primary key on model Model_CustomValue cannot be changed."
I've tried swapping the key_from/to in the "belongs_to" on the Model_CustomValue, but to no avail.
I'm using Fuel 1.6 (hash: 6e6d764)
Please let me know if more information would be helpful, and I'll provide.
From the FuelPHP forum thread, Harro answered:
You can not have a column which is at the same time FK and PK. Which
you have on your Model_CustomValue.
The reason for that is that when you disconnect a relation, the FK
will be set to NULL, which should not happen with a PK.
I then clarified, for those of us who may need specific examples from the original example, I confirmed the following:
So just re-stating why that's not allowed:
Model_CustomValue uses the "purchase_id" as part of its PK as well as the FK to Model_Purchase. And if the two Models were to be unlinked, that would lead to a null portion of the PK for Model_CustomValue -- which obviously isn't allowed.

Yii fail to retrieve max column value

I have two models, one is Auction, the other is Bid.
An Auction has many Bids. they are associated by foreign key auction_id in Bid
Now, I want to find the max value of the Bid's price for each Auction.
$dataProvider = new CActiveDataProvider('Auction', array('criteria' => array(
'with' => array(
'bids' => array(
'alias'=>'b',
'group' => 'auction_id',
'select' => 'max(b.price) as maxprice'
)
)
)
)
);
And I have defined a maxprice property in Auction's model class.
However, if I try to retrieve the maxprice property, it returns NULL.
To be more specific, I render the $dataprovider to a view page, it fails to get the maxprice property.
PS:
I executed the query in mysql, the query result turns out to be correct.
So, there must be something wrong with the Yii code
SQL code:
SELECT `t`.`id` , max(b.price) as maxprice
FROM `auction` `t`
LEFT OUTER JOIN `bid` `b` ON (`b`.`auction_id`=`t`.`id`) GROUP BY auction_id
Put the value you want before the relation, like so:
$dataProvider = new CActiveDataProvider('Auction', array('criteria' => array(
'select' => 't.*, max(b.price) as maxprice',
'with' => array(
'bids' => array(
'alias'=>'b',
'group' => 'auction_id',
'together'=>true,
)
You can replace the "t.*" with specific field names if you like.
OR you can simply use the select, join and group attributes on your Auction model and skip the relation altogether.