Disable manually setting auto-incremented ids in MySQL - mysql

I want to disallow users from inserting into a table product (which has auto-incremented ids) if they're setting the id manually.
So this would be valid (id generated automatically since it's set as AUTO_INCREMENT):
INSERT INTO product (name) VALUES ("Product1")
But this wouldn't (id being set manually):
INSERT INTO product (id, name) VALUES (10, "Product1")
Is this possible in any way?

Trigger logic may help.
SET SESSION sql_mode := '';
CREATE TABLE test (
id INT AUTO_INCREMENT PRIMARY KEY,
val INT
) AUTO_INCREMENT = 123;
CREATE TRIGGER fail_explicit_id
BEFORE INSERT ON test
FOR EACH ROW
BEGIN
IF NEW.id <> 0 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Explicit ''id'' value is not allowed.';
END IF;
END
INSERT INTO test (val) VALUES (111);
INSERT INTO test VALUES (NULL, 222);
INSERT INTO test VALUES (0, 333);
INSERT INTO test VALUES (DEFAULT, 444);
INSERT INTO test VALUES (456, 555);
Explicit 'id' value is not allowed.
SET SESSION sql_mode := CONCAT_WS(',', ##sql_mode, 'NO_AUTO_VALUE_ON_ZERO');
SELECT ##sql_mode;
INSERT INTO test VALUES (0, 666);
INSERT INTO test VALUES (0, 777);
Duplicate entry '0' for key 'test.PRIMARY'
SELECT * FROM test;
id
val
0
666
123
111
124
222
125
333
126
444
fiddle

Give the user column-level permissions.
grant insert(`name`) on yourdatabase.product to theuser#thehost;
Then the user tries these:
mysql> INSERT INTO product (name) VALUES ("Product1");
Query OK, 1 row affected (0.01 sec)
mysql> INSERT INTO product (id, name) VALUES (10, "Product1");
ERROR 1143 (42000): INSERT command denied to user 'theuser'#'thehost' for column 'id' in table 'product'

Related

Increment a number field via a trigger INSERT in MySQL

I'm building a data versioning system, and I need to increment a version number each time a new row is added to the version table, but it increments once and then stops:
DELIMITER |
CREATE TRIGGER trigger2 AFTER UPDATE ON something
FOR EACH ROW
BEGIN
IF NEW.updated_at <> OLD.updated_at THEN
INSERT INTO versions_something (
`id`,
`some_id`,
`version`,
`title`,
`description`,
`created_at`,
`updated_at`
) VALUES (
null,
NEW.id,
1,
NEW.title,
NEW.description,
NOW(),
NOW()
);
END IF;
UPDATE
versions_something
SET
version = (SELECT MAX(version)) + 1
WHERE versions_something.id = LAST_INSERT_ID();
END;
|
DELIMITER ;
I've tried putting the UPDATE into a separate trigger (AFTER INSERT ON versions_something ...), but MySQL complains that it's clashing with the trigger before it.
I've tried the UPDATE on its own, using the last ID in the table and it works each time, so I have no idea what's happening.

MySQL 1:10 fixed relationship. How to implement it at database level?

Is it possible to implement a 1:N relationship that has 10 rows of the many referencing 1 row from the one table at most?
// ID int
INSERT INTO one VALUES (1);
// ...
INSERT INTO one VALUES (25);
//ID int, one_id int
INSERT INTO many VALUES (1,1);
// ...
INSERT INTO many VALUES (1,10);
INSERT INTO many VALUES (1,11); // ERROR!
INSERT INTO many VALUES (2,11); // working
// ...
INSERT INTO many VALUES (2,20); // working
INSERT INTO many VALUES (2,21); // ERROR!
You can do this with a trigger:
create trigger trg_mytable_max10 before insert on mytable
for each row
begin
declare cnt int;
set cnt = (select count(*) from mytable where col1 = new.col1);
if cnt = 10 then
signal sqlstate '45000' set message_text = 'only 10 records per col1 allowed';
end if;
end;
http://rextester.com/EORH56497

Mysql : How to check uniqueness of pair

CREATE TABLE nodes (
id INTEGER PRIMARY KEY,
name VARCHAR(10) NOT NULL,
feat1 CHAR(1), -- e.g., age
feat2 CHAR(1) -- e.g., school attended or company
);
CREATE TABLE edges (
a INTEGER NOT NULL REFERENCES nodes(id) ON UPDATE CASCADE ON DELETE CASCADE,
b INTEGER NOT NULL REFERENCES nodes(id) ON UPDATE CASCADE ON DELETE CASCADE,
PRIMARY KEY (a, b)
);
CREATE INDEX a_idx ON edges (a);
CREATE INDEX b_idx ON edges (b);
If we want to represent an undirected graph, we need to add a CHECK constraint on the uniqueness of the pair.
Since the SQL standard does not allow a subquery in the CHECK constraint,How can i check uniqueness of the pair?
You could rig up a trigger that fails upon seeing either (A,B) or (B,A) :
Here is the Trigger:
DELIMITER $$
CREATE TRIGGER edges_bi BEFORE INSERT
ON edges FOR EACH ROW
BEGIN
DECLARE found_count,dummy,diff,SomethingsWrong INT DEFAULT 0;
DECLARE errmsg VARCHAR(128);
SET diff = new.a - new.b;
IF diff = 0 THEN
SET errmsg = CONCAT('[',new.a,',',new.b,'] is Vertex, Not Edge');
SET SomethingsWrong = 1;
END IF;
SELECT COUNT(1) INTO found_count FROM edges
WHERE (a=NEW.a AND b=NEW.b) OR (a=NEW.b AND b=NEW.a);
IF found_count = 1 THEN
SET errmsg = CONCAT('[',new.a,',',new.b,'] Already Exists');
SET SomethingsWrong = 1;
END IF;
IF SomethingsWrong = 1 THEN
SELECT errmsg INTO dummy FROM edges WHERE 1=1;
END IF;
END; $$
DELIMITER ;
Here is a sample table:
DROP DATABASE if exists saurabh;
CREATE DATABASE saurabh;
USE saurabh
CREATE TABLE edges
(
a INTEGER NOT NULL,
b INTEGER NOT NULL,
PRIMARY KEY (a,b),
UNIQUE KEY (b,a)
);
Notice that I have a PRIMARY KEY and a UNIQUE KEY with the columns of the PRIMARY KEY reversed
Let's create the table:
mysql> DROP DATABASE if exists saurabh;
Query OK, 1 row affected (0.01 sec)
mysql> CREATE DATABASE saurabh;
Query OK, 1 row affected (0.00 sec)
mysql> USE saurabh
Database changed
mysql> CREATE TABLE edges
-> (
-> a INTEGER NOT NULL,
-> b INTEGER NOT NULL,
-> PRIMARY KEY (a,b),
-> UNIQUE KEY (b,a)
-> );
Query OK, 0 rows affected (0.12 sec)
mysql>
Let's create the Trigger:
mysql> DELIMITER $$
mysql> CREATE TRIGGER edges_bi BEFORE INSERT
-> ON edges FOR EACH ROW
-> BEGIN
-> DECLARE found_count,dummy,diff,SomethingsWrong INT DEFAULT 0;
-> DECLARE errmsg VARCHAR(128);
-> SET diff = new.a - new.b;
-> IF diff = 0 THEN
-> SET errmsg = CONCAT('[',new.a,',',new.b,'] is Vertex, Not Edge');
-> SET SomethingsWrong = 1;
-> END IF;
-> SELECT COUNT(1) INTO found_count FROM edges
-> WHERE (a=NEW.a AND b=NEW.b) OR (a=NEW.b AND b=NEW.a);
-> IF found_count = 1 THEN
-> SET errmsg = CONCAT('[',new.a,',',new.b,'] Already Exists');
-> SET SomethingsWrong = 1;
-> END IF;
-> IF SomethingsWrong = 1 THEN
-> SELECT errmsg INTO dummy FROM edges WHERE 1=1;
-> END IF;
-> END; $$
Query OK, 0 rows affected (0.11 sec)
mysql> DELIMITER ;
Here is some sample data:
INSERT INTO edges (a,b) VALUES (5,3);
INSERT INTO edges (a,b) VALUES (3,3);
INSERT INTO edges (a,b) VALUES (3,5);
INSERT INTO edges (a,b) VALUES (5,5);
SELECT * FROM edges;
Let's try loading these into the edges table:
mysql> INSERT INTO edges (a,b) VALUES (5,3);
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO edges (a,b) VALUES (3,3);
ERROR 1366 (HY000): Incorrect integer value: '[3,3] is Vertex, Not Edge' for column 'dummy' at row 1
mysql> INSERT INTO edges (a,b) VALUES (3,5);
ERROR 1366 (HY000): Incorrect integer value: '[3,5] Already Exists' for column 'dummy' at row 1
mysql> INSERT INTO edges (a,b) VALUES (5,5);
ERROR 1366 (HY000): Incorrect integer value: '[5,5] is Vertex, Not Edge' for column 'dummy' at row 1
mysql> SELECT * FROM edges;
+---+---+
| a | b |
+---+---+
| 5 | 3 |
+---+---+
1 row in set (0.00 sec)
Note that blocking A=B conditions prevents any self-loops
CAVEAT
This trigger does not work if
you start with an empty table
enter (3,3) as the first row
because the BEFORE INSERT trigger does not fire on an empty table.
Once you enter a valid row with A <> B then all checks are performed properly.
Give it a Try !!!
MySQL does not support CHECK constraints.
You can create a BEFORE INSERT and BEFORE UPDATE triggers to check this situation, and throw an error if needed.
Example:
CREATE TABLE edges(
a INT(11) NOT NULL,
b INT(11) NOT NULL
);
DELIMITER $$
CREATE TRIGGER trigger1
BEFORE INSERT
ON edges
FOR EACH ROW
BEGIN
SET #cnt = NULL;
SELECT COUNT(*) INTO #cnt FROM edges
WHERE a = new.a AND b = new.b OR a = new.b AND b = new.a;
IF #cnt > 0 THEN
SIGNAL SQLSTATE '02000' SET MESSAGE_TEXT = 'Error: uniqueness of pair';
END IF;
END
$$
DELIMITER ;
Also, create similar BEFORE UPDATE trigger to avoid NEW wrong values on updating, or just use a stored procedure because the code is the same.
CHECK is not supported on CREATE TABLE by MySQL, as the documentation dictates
The CHECK clause is parsed but ignored by all storage engines
In fact there is an open bug report on that issue since 2004 (!).
The approach I would take is to create a stored procedure trigger on insert and on update that deliberate fails if the pair exists.
I think an answer may depend on how you are populating the edges table, which is not clear from your question. If it is being populated from the nodes table, then you may be able to build a VIEW based on a SELECT query that excludes the mirrored pairs (i.e. 1,2 and 2,1). This might also solve the cascade and delete requirement.

How to use last_insert_id() in MySQL?

My SQL statements like this:
INSERT INTO foo (val1) VALUES('v1'); -- ID in first table
foo_id = SELECT last_insert_id();
INSERT INTO bar (val2) VALUES ('v2'); -- ID in second table
bar_id = SELECT last_insert_id();
INSERT INTO foobar (foo_id, bar_id, val3)
VALUES (foo_id, bar_id, 'text'); -- third table
The above is not working, it doesn't recognize the foo_id = statement.
INSERT INTO foo (val1) VALUES ('v1'); -- ID in first table
SET #foo_id = last_insert_id();
INSERT INTO bar (val2) VALUES ('v2'); -- ID in second table
SET #bar_id = last_insert_id();
INSERT INTO foobar (foo_id, bar_id, val3)
VALUES (#foo_id, #bar_id, 'text'); -- third table

Addition in MySQL trigger not working as expected

I'm doing some addition as part of a mysql trigger. The added value is added a to a column on the table. The code is as follows;
BEGIN
IF NEW.Status = 'processed' AND NEW.Success = 1 THEN
INSERT INTO crm_master
(msisdn, last_action_date, source, contract_type, revenue,inc)
VALUES
(new.msisdn,NOW(), 'INC5', new.Contract_Type, revenue = revenue+5, 1)
ON DUPLICATE KEY UPDATE last_action_date = NOW(),
contract_type = new.Contract_Type,
revenue = revenue+5,
inc = 1;
END IF;
END
The column revenue in the table crm_master is set to default of 0
The problem is that I'm getting unexpected results with incorrect values and in some cases 0 even though there should never be a 0 value.
I don't think it's valid reference to default value revenue = revenue+5 in your insert statement. It should look like
INSERT INTO crm_master
(msisdn, last_action_date, source, contract_type, revenue,inc)
VALUES
(new.msisdn,NOW(), 'INC5', new.Contract_Type, DEFAULT(revenue) +5, 1)
ON DUPLICATE KEY UPDATE ....
Or you can simply do
INSERT INTO ....
VALUES
(new.msisdn,NOW(), 'INC5', new.Contract_Type, 5, 1) ...
*Your update part of INSERT ... ON DUPLICATE KEY UPDATE is ok.
INSERT INTO sometable( column ) VALUES ( column = column + 5 );
is equivalent to
INSERT INTO sometable( column ) VALUES ( 0 );
because (column = column+5) evaluates to 0 as column is never equal to column+5.
This syntax is ok for UPDATE query, but for INSERT you should provide an explicit value, like
INSERT INTO sometable( id, column ) VALUES ( 1, 5 )
ON DUPLICATE KEY UPDATE column = column + 5;
which would insert value 5 if there is no row with given id and add 5 to column if there is one.