Why when I query information in a table the updateDateColumn gets updated? - mysql

I am making a get request to my API and I want to use the table's changeDate field to run some logic, but the problem is that when I request the data this field gets updated and that's not the behavior I expected from the #UpdateDateColumn decorator since I'm just doing a simple get query with the Repository API. Why does this happens? Did I miss something in the docs?
I know this because I gave read only permissions (mysql) to my API, when I make a request it failed because of this.
This is the error:
QueryFailedError: The MySQL server is running with the --read-only option so it cannot execute this statement
This is my entity (part of it):
export class Cake{
#PrimaryGeneratedColumn()
id: number;
#Index()
#Column()
#Generated('uuid')
GUID: string;
#Column({ nullable: true })
flavor: string;
#UpdateDateColumn({ nullable: true })
changeDate: Date;
}
This is my query:
const cake= await this.cakeRepository.findOne({where: {GUID: guid}, select: ['id', 'GUID', 'changeDate']})
SQL for table creation
CREATE TABLE `Cake` (
`id` int NOT NULL AUTO_INCREMENT,
`GUID` varchar(36) NOT NULL,
`changeDate` datetime(6) DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
`flavor` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
KEY `IDX_6aeaf4ee4a4e43e805a15c4738` (`GUID`),
) ENGINE=InnoDB AUTO_INCREMENT=362 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

Related

TypeORM composite foreign key

In a first class, the primary key is composite:
#Entity({
name: 'ClassA',
database: Constants.DATABASE_NAME,
})
export class ClassA {
#PrimaryColumn({
name: 'Field1',
type: 'varchar',
length: 4,
})
field1: string;
#PrimaryColumn({
name: 'Field2',
type: 'varchar',
length: 2,
})
field2: string;
#PrimaryColumn({
name: 'Field3',
type: 'integer',
})
field3: number;
#OneToMany(() => ClassB, (classB) => classB.classA)
classB: ClassB[];
}
In another class, the first class is used as a foreign key:
#Entity({
name: 'ClassB',
database: Constants.DATABASE_NAME,
})
export class ClassB {
...
#ManyToOne(() => ClassA, (classA) => classA.classB, {
cascade: true,
})
#JoinColumn([
{ name: 'Field1', referencedColumnName: 'field1' },
{ name: 'Field2', referencedColumnName: 'field2' },
{ name: 'Field3', referencedColumnName: 'field3' },
])
classA: ClassA;
}
When I start NestJS using nest start, I get the error
code: "ER_FK_NO_INDEX_PARENT"
errno: 1822
sqlMessage: Failed to add the foreign key constraint. Missing index for constraint 'FK_1d19fe001872b5ee9ab545c18f8' in the referenced table 'ClassA'
sqlState: "HY000
I tried to alter the ClassA table and add indexes on each column of the primary key, and then an index of all columns of the primary key, but the error is still the same.
Is this possible to manage this case with TypeORM?
Relevant packages:
typeorm: 0.2.41
#nestjs/core: 8.0.0
#nestjs/typeorm: 8.0.3
mysql: 2.18.1
DB: MySQL v8.0 in a Docker container (mysql:latest)
Thank you.
Edit
It seems that the issue is that the table is created in more than one step with TypeORM. The table looks like this after the error message.
CREATE TABLE `ClassB` (
`Date` datetime NOT NULL,
`ClassAField1` varchar(4) NOT NULL,
`ClassAField2` varchar(2) NOT NULL,
`ClassAField3` int NOT NULL,
PRIMARY KEY (`Date`,`ClassAField1`,`ClassAField2`,`ClassAField3`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
The error happens on the following query, where TypeORM tries to add the foreign key:
ALTER TABLE `ClassB`
ADD CONSTRAINT `FK_479eaf779e0d5f0d0a83bb9e30d`
FOREIGN KEY (`ClassAField1`, `ClassAField2`, `ClassAField3`)
REFERENCES `LandPlots`(`Field1`,`Field2`,`Field3`) ON DELETE NO ACTION ON UPDATE NO ACTION
I made a few tests, and the following query works as intended:
CREATE TABLE `ClassB` (
`Date` datetime NOT NULL,
`ClassAField1` varchar(4) NOT NULL,
`ClassAField2` varchar(2) NOT NULL,
`ClassAField3` int NOT NULL,
PRIMARY KEY (`Date`),
KEY `FK_e6b2926df2c758d8d8810e4345a` (`ClassAField1`, `ClassAField2`, `ClassAField3`),
CONSTRAINT `FK_e6b2926df2c758d8d8810e4345a`
FOREIGN KEY (`ClassAField1`, `ClassAField2`, `ClassAField3`)
REFERENCES `ClassA` (`Field1`, `Field2`, `Field3`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
The difference seems to be the line KEY `FK_e6b2926df2c758d8d8810e4345a` (`ClassAField1`, `ClassAField2`, `ClassAField3`),, which is missing from the generated SQL by TypeORM.
From MySQL documentation, the KEY statement is synonymous to INDEX:
KEY | INDEX
KEY is normally a synonym for INDEX. The key attribute PRIMARY KEY can also be specified as just KEY when given in a column definition. This was implemented for compatibility with other database systems.
So I tried to add the #Index decorator to the column but it did not change anything. I added logging of all executed queries, and it seems that the #Index decorator is not executed, so, maybe the #JoinColumn decorator has priority over it? (Tried to sort decorators different ways just in case).
The only solution that I can think of with TypeORM would be to generate a hash or concatenation of all 3 primary keys, and make it the sole primary key. Two drawbacks: Data duplication and CPU usage when parsing entities before insertion.
If someone has a better suggestion I would like to hear it.
Edit 2 following questions from #RickJames
Update statement:
UPDATE cb
SET cb.SomeField = 'NewValue'
FROM ClassB cb
JOIN ClassA ca
ON cb.Field1 = ca.Field1
AND cb.Field2 = ca.Field2
AND cb.Field3 = ca.Field3
WHERE ca.SomeOtherField LIKE '%partialvalue%'
AND cb.otherPK = 1
That UPDATE might benefit from some of these indexes:
cb: INDEX(otherPK, Field1, Field2, Field3)
ca: INDEX(Field1, Field2, Field3, SomeOtherField)

Prisma - ConnectorError

I'm trying to learn Prisma on my hobby project so I started with basics.
The first thing I tried was a simple GET request which should list all results in a table.
// Using "#prisma/client": "^3.8.1",
const position = await prisma.position.findMany();
It worked fine but obviously, I don't have any data so it return an empty array. If I try to add some items to the Position table I'm getting error (mentioned below). Same error I'm getting also from Prisma Studio so I wonder If I did something wrong or what can I do to fix it.
Prisma schema
model Position {
id Int #id #default(autoincrement())
name String #db.VarChar(255)
externalId String #db.VarChar(255)
benefits String #db.Text()
}
MySQL Schema
CREATE TABLE `Position` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`externalId` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`benefits` text COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Query:
await prisma.position.create({
data: {
name: 'Position Name',
externalId: '321',
benefits: 'My benefits',
},
});
Error:
Error occurred during query execution:
ConnectorError(ConnectorError { user_facing_error: None, kind: QueryError(Server(ServerError { code: 1064, message: "target: referral.-.primary: vttablet: (errno 1064) (sqlstate 42000) (CallerID: unsecure_grpc_client): Sql: \"insert into Position(`name`, externalId,
benefits) values (:v1, :v2, :v3)\", BindVars: {}", state: "42000" })) })
at cb (C:\Develop\referral-nextjs-prisma\node_modules\#prisma\client\runtime\index.js:38695:17)
at async assetHandler (webpack-internal:///./pages/api/position/index.tsx:12:34)
at async Object.apiResolver (C:\Develop\referral-nextjs-prisma\node_modules\next\dist\server\api-utils.js:102:9)
at async DevServer.handleApiRequest (C:\Develop\referral-nextjs-prisma\node_modules\next\dist\server\base-server.js:1076:9)
at async Object.fn (C:\Develop\referral-nextjs-prisma\node_modules\next\dist\server\base-server.js:963:37)
at async Router.execute (C:\Develop\referral-nextjs-prisma\node_modules\next\dist\server\router.js:222:32)
at async DevServer.run (C:\Develop\referral-nextjs-prisma\node_modules\next\dist\server\base-server.js:1103:29)
at async DevServer.run (C:\Develop\referral-nextjs-prisma\node_modules\next\dist\server\dev\next-dev-server.js:444:20)
at async DevServer.handleRequest (C:\Develop\referral-nextjs-prisma\node_modules\next\dist\server\base-server.js:319:20) {
clientVersion: '3.8.1'
}

Problem with migrations using Volo ABP for MySQL

I recently downloaded a project from abp.io using the following configurations:
Project Type: Application
UI Framework: Angular
Database Provider: Entity Framework Core
Separate Identity Server: Yes
I am getting the following error in Package Mangager Console while updating database.
BLOB/TEXT column 'Value' used in key specification without a key length
Below is the migration script:
migrationBuilder.CreateTable(
name: "IdentityServerApiSecrets",
columns: table => new
{
Type = table.Column<string>(maxLength: 250, nullable: false),
Value = table.Column<string>(maxLength: 4000, nullable: false),
ApiResourceId = table.Column<Guid>(nullable: false),
Description = table.Column<string>(maxLength: 2000, nullable: true),
Expiration = table.Column<DateTime>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_IdentityServerApiSecrets", x => new { x.ApiResourceId, x.Type, x.Value });
table.ForeignKey(
name: "FK_IdentityServerApiSecrets_IdentityServerApiResources_ApiResou~",
column: x => x.ApiResourceId,
principalTable: "IdentityServerApiResources",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
I deleted the old migrations and added a new migration which resulted as the above script.
I changed the maxLength of 'Value' property to 1000. And now it shows the following error message:
Specified key was too long; max key length is 3072 bytes
I tried to run the following script [generated in Package Manager Console] in MySql Workbench. This too gives me the same error message.
CREATE TABLE `IdentityServerApiSecrets` (
`Type` varchar(250) CHARACTER SET utf8mb4 NOT NULL,
`Value` varchar(1000) CHARACTER SET utf8mb4 NOT NULL,
`ApiResourceId` varchar(36) CHARACTER SET utf8mb4 NOT NULL,
`Description` varchar(2000) CHARACTER SET utf8mb4 NULL,
`Expiration` datetime(6) NULL,
CONSTRAINT `PK_IdentityServerApiSecrets` PRIMARY KEY (`ApiResourceId`, `Type`, `Value`),
CONSTRAINT `FK_IdentityServerApiSecrets_IdentityServerApiResources_ApiResou~` FOREIGN KEY (`ApiResourceId`) REFERENCES `IdentityServerApiResources` (`Id`) ON DELETE CASCADE
;
Please help.
Do this on your Migration DbContext.
builder.ConfigureIdentityServer(options =>
{
options.DatabaseProvider = EfCoreDatabaseProvider.MySql;
});

TypeORM Synchronize issue with UUID (MySQL)

I am running into issues with synchronize setting on TypeORM with UUID PrimaryGeneratedColumns on MySQL driver. It seems each time application loads it tries to re-index, so it drops the id column and tries to add it again. Issue is that since it's UUID, for MySQL the column type is varchar(36) NOT NULL PRIMARY KEY. So we run into duplicate entry '' for key 'PRIMARY' because UUID is not set.
To recreate issue, you can do it like this:
typeorm init --name MyProject --database mysql
Edit your User.ts entity to change primary key to UUID.
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";
#Entity()
export class User {
#PrimaryGeneratedColumn('uuid')
id: string;
#Column()
firstName: string;
#Column()
lastName: string;
#Column()
age: number;
}
Ensure synchronize is set to true in ormconfig.json.
Start the app and it will insert sample User. Restart app and synchronize will run and try and drop and re-add the column and give error.
message: 'ER_DUP_ENTRY: Duplicate entry \'\' for key \'PRIMARY\'',
code: 'ER_DUP_ENTRY',
errno: 1062,
sqlMessage: 'Duplicate entry \'\' for key \'PRIMARY\'',
sqlState: '23000',
index: 0,
sql:
'ALTER TABLE `user` ADD `id` varchar(36) NOT NULL PRIMARY KEY',
name: 'QueryFailedError',
query:
'ALTER TABLE `user` ADD `id` varchar(36) NOT NULL PRIMARY KEY',
parameters: []

Why Does Sequelize.js in Node.js always return true from a MySQL bit field?

I have a Node.Js Express application and I'm using the Sequelize.js OR/M to query a MySQL 5.6 database. I have a table called homes that contains a couple of bit fields (one of which is called isrental which I have defined as Boolean in the model. When querying the db, these fields always return true even when I have a 0 stored on the record. Here's a quick code example:
var Sequelize = require('sequelize-mysql').sequelize;
var orm = new Sequelize('mysql://procHOAPro:password#NewMasterBedRm/HOAPro'), {
dialect: 'mysql',
language: 'en'
});
var Home = orm.define('homes', {
homeid : Sequelize.INTEGER,
state : Sequelize.STRING,
county : Sequelize.STRING,
city : Sequelize.STRING,
zip : DataTypes.STRING,
isrental : {type: Sequelize.BOOLEAN, allowNull: true, defaultValue: false},
isbuilderowned : {type: Sequelize.BOOLEAN, allowNull: true, defaultValue: false},
mailingaddress : Sequelize.STRING
});
Home.all().success(function(homes) {
console.log(homes[0].isrental);
console.log(homes[1].isrental);
});
table definition:
CREATE TABLE 'homes' (
'homeid' int(11) NOT NULL AUTO_INCREMENT,
'state' varchar(2) NOT NULL,
'county' varchar(100) NOT NULL,
'city' varchar(100) NOT NULL,
'zip' varchar(5) NOT NULL,
'section' varchar(50) NOT NULL,
'township' int(11) NOT NULL,
'townshipdir' varchar(1) NOT NULL,
'range' int(11) NOT NULL,
'rangedir' varchar(1) NOT NULL,
'block' int(11) NOT NULL,
'lot' int(11) NOT NULL,
'physicaladdress' varchar(255) NOT NULL,
'isrental' bit(1) NOT NULL DEFAULT b'0',
'isbuilderowned' int(1) NOT NULL DEFAULT '0',
'mailingaddress' varchar(255) DEFAULT NULL,
PRIMARY KEY ('homeid'),
UNIQUE KEY 'homeid_UNIQUE' ('homeid')
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8;
output:
Executing: SELECT * FROM homes;
true <--this is bs...it's a 0 in the db
true <--this is bs...it's a 0 in the db
As an fyi, I also used the node-orm OR/M and I got the exact same behavior.
Your table definitions seem correct, the error is with sequelize / the way sequelize and node-mysql plays together. Node-mysql returns the bit field as a Buffer instance, but sequelize expects a tinyint. The tinyint can be converted to boolean by double negating it (!!value), whereas double negating a buffer always returns true.
The fix seems pretty easy, just check if the value is a buffer, before trying to convert it to boolean. Could you please create an issue for it on the sequelize github, then I'll hopefully get around to fixing it soon