Hi all, I am having a problem where an MJoin on a link table is not allowing the insert to take place and throws a foreign key constraint error.
The tables I have are:
CREATE TABLE users
(
uuid
char(36) COLLATE utf8mb4_bin NOT NULL,
name
varchar(255) COLLATE utf8mb4_bin NOT NULL,
email
varchar(255) COLLATE utf8mb4_bin NOT NULL,
password
varchar(255) COLLATE utf8mb4_bin NOT NULL,
reset_password
tinyint(1) NOT NULL,
PRIMARY KEY (uuid
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
CREATE TABLE roles
(
uuid
char(36) COLLATE utf8mb4_bin NOT NULL,
name
varchar(255) COLLATE utf8mb4_bin NOT NULL,
description
varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
PRIMARY KEY (uuid
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
CREATE TABLE user_roles
(
role_uuid
char(36) COLLATE utf8mb4_bin NOT NULL,
user_uuid
char(36) COLLATE utf8mb4_bin NOT NULL,
PRIMARY KEY (role_uuid
,user_uuid
),
KEY user_uuid
(user_uuid
),
CONSTRAINT user_roles_ibfk_1
FOREIGN KEY (role_uuid
) REFERENCES roles
(uuid
) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT user_roles_ibfk_2
FOREIGN KEY (user_uuid
) REFERENCES users
(uuid
) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
I use NodeJS and this is my user-api.js file
//-------------------------------------------------------------
'use strict';
const
{
Editor,
Field,
Validate,
Format,
Options,
Mjoin
} = require('datatables.net-editor-server'),
db = require('../db'),
bcrypt = require('bcryptjs'),
uuidv4 = require('uuid/v4'),
{User} = require('../models');
exports.UserAPI = (req, res, next) => {
const editor = new Editor(db, 'users', 'uuid')
.debug(true)
.fields(
new Field('users.uuid')
.set(Field.SetType.Create)
.setValue(uuidv4()),
new Field('users.name')
.validator(Validate.required(new Validate.Options(
{message: 'The user name is required'}
))),
new Field('users.email')
.validator(Validate.required(new Validate.Options(
{message: 'The user email is required'}
)))
.validator(Validate.dbUnique(new Validate.Options(
{message: 'The user email must be unique'}
)))
.validator(Validate.email(new Validate.Options(
{message: 'The user email must be a valid email'}
))),
new Field('users.password')
.validator(Validate.required(new Validate.Options(
{message: 'The user password is required'}
))),
new Field("users.reset_password")
.getFormatter( (val, data) => val === 1)
)
.join(
new Mjoin('roles')
.link('users.uuid', 'user_roles.user_uuid')
.link('roles.uuid', 'user_roles.role_uuid')
.fields(
new Field('uuid')
.validator(Validate.required(new Validate.Options(
{message: 'You must assign a role to the user'}
)))
.options(
new Options().table('roles').value('uuid').label('name')
),
new Field('name')
.options(
new Options()
.table('roles')
.value('roles.uuid')
.label('name')
)
)
);
editor.on('preCreate', (editor, values) => {
if (values.users.password) {
const hashedPassword = bcrypt.hashSync(values.users.password, 12);
values.users.password = hashedPassword;
values.users.reset_password = true;
}
});
editor.on('preEdit', (editor, id, values) => {
User.findByPk(values.users.uuid)
.then(user => {
if (user.password !== values.users.password) {
const hashedPassword = bcrypt.hashSync(values.users.password, 12);
values.users.password = hashedPassword;
}
})
.catch(err => {
console.log(err);
})
});
editor.process(req.body)
.then(function () {
res.send(JSON.stringify(editor.data()));
})
.catch(err => {
console.log(err);
});
};
//-------------------------------------------------------------
and finally my page javascript
//-------------------------------------------------------------
editor = new $.fn.dataTable.Editor({
ajax: {
url: "/api/user",
headers: {
'CSRFToken': "<%= csrfToken %>",
},
data: function (d) {
d._csrf = "<%= csrfToken %>"
}
},
// template: "#customForm",
fields: [
{
label: "uuid",
name: "users.uuid",
type: "hidden"
},
{
label: "Name",
name: "users.name",
fieldInfo: "The users full name."
},
{
label: "Email",
name: "users.email",
fieldInfo: "The users email address. This must be unique."
},
{
label: "Password",
name: "users.password",
type: "password",
fieldInfo: "The users password."
},
{
// label: "",
name: "users.reset_password",
type: "checkbox",
options: [
{label: "Reset Password", value: true}
],
separator: '',
unselectedValue: false,
fieldInfo: "User must reset password on next login."
},
{
label: "Roles",
type: "checkbox",
name: "roles[].uuid",
// separator: ', ',
fieldInfo: "Roles assigned to the user"
}
// {
// label: "Email",
// name: "users.email",
// type: "email",
// fieldInfo: 'The users email address. This must be unique.'
// },
]
});
//-------------------------------------------------------------
here is a debug from Knex:
*{ method: 'select',
options: {},
timeout: false,
cancelOnTimeout: false,
bindings: [ 'd@d.com' ],
__knexQueryUid: 'cbcbaf42-824a-4a1c-be62-04718cf751fb',
sql:
'select users
.email
from users
where users
.email
= ?' }
{ method: 'insert',
options: {},
timeout: false,
cancelOnTimeout: false,
bindings:
[ 'd@d.com',
'd',
'$2a$12$7pztTvnfygH24oLmScDr4enYq22hny2wNYSJPBKdG1hzTZi1WII4.',
true,
'c1396b1b-6eb8-48ca-a8f5-e8d763db0b43' ],
__knexQueryUid: '87c2ff5d-9589-40e0-ba3c-6c3c0526f808',
sql:
'insert into users
(email
, name
, password
, reset_password
, uuid
) values (?, ?, ?, ?, ?)' }
{ method: 'insert',
options: {},
timeout: false,
cancelOnTimeout: false,
bindings: [ '49107101-42e3-4220-b16c-0de862706254', '0' ],
__knexQueryUid: '75a6cb0c-bf91-4e5f-a5c2-7f697b6af474',
sql:
'insert into user_roles
(role_uuid
, user_uuid
) values (?, ?)' }
{ Error: Cannot add or update a child row: a foreign key constraint fails
at Packet.asError (/Users/charlesdejager/dev/gdskills/node_modules/mysql2/lib/packets/packet.js:684:17)
at Query.execute (/Users/charlesdejager/dev/gdskills/node_modules/mysql2/lib/commands/command.js:28:26)
at Connection.handlePacket (/Users/charlesdejager/dev/gdskills/node_modules/mysql2/lib/connection.js:449:32)
at PacketParser.Connection.packetParser.p [as onPacket] (/Users/charlesdejager/dev/gdskills/node_modules/mysql2/lib/connection.js:72:12)
at PacketParser.executeStart (/Users/charlesdejager/dev/gdskills/node_modules/mysql2/lib/packet_parser.js:75:16)
at Socket.Connection.stream.on.data (/Users/charlesdejager/dev/gdskills/node_modules/mysql2/lib/connection.js:79:25)
at Socket.emit (events.js:188:13)
at addChunk (_stream_readable.js:288:12)
at readableAddChunk (_stream_readable.js:269:11)
at Socket.Readable.push (_stream_readable.js:224:10)
at TCP.onStreamRead [as onread] (internal/stream_base_commons.js:145:17)
code: 'ER_NO_REFERENCED_ROW',
errno: 1216,
sqlState: '23000',
sqlMessage:
'Cannot add or update a child row: a foreign key constraint fails' }*
The funny thing is that if I use Postgres this works perfectly. With MySQL this fails.
Any help will be appreciated.