Datatype for 1 character only - mysql

I want to insert only one letter into the table field. What will be the data type that will accept only one character?
I don't want to use VARCHAR(1), because it will truncate the remaining characters. I want that if the input is 1 character, it will insert otherwise it will not insert into the table

If you set the column length to be longer (for example, 255), then you can add a trigger which checks the length of the new field. If greater than 1 then you can trigger an error.
For a test example:-
CREATE TABLE IF NOT EXISTS `insert_test` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`sometext` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ;
--
-- Triggers `insert_test`
--
DROP TRIGGER IF EXISTS `length_check_trigger`;
DELIMITER //
CREATE TRIGGER `length_check_trigger` BEFORE INSERT ON `insert_test`
FOR EACH ROW BEGIN
DECLARE msg VARCHAR(255);
IF LENGTH(NEW.sometext) > 1 THEN
SET msg = "DIE: String Too Long.";
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = msg;
END IF;
END
//
DELIMITER ;
You can change the message to what you want. You will need a similar trigger to catch updates as well.

In mysql the data type itself does not control if the attempt to insert / update a field to a longer (invalid) data, than it is allowed by the field definition results in an error or warning.
In mysql you need to set the sql mode to one of the strict sql modes as described by mysql's documentation on sql mode.
If strict mode is not in effect, MySQL inserts adjusted values for
invalid or missing values and produces warnings (see Section
13.7.5.40, “SHOW WARNINGS Syntax”). In strict mode, you can produce this behavior by using INSERT IGNORE or UPDATE IGNORE.
So, both varchar(1) and char(1) are correct definitions, however, you need to enable strict sql mode in order for inserting / updating invalid data to produce an error. Setting sql mode in config file or using set statement is described in the linked documentation:
To set the SQL mode at server startup, use the --sql-mode="modes"
option on the command line, or sql-mode="modes" in an option file such
as my.cnf (Unix operating systems) or my.ini (Windows). modes is a
list of different modes separated by commas. To clear the SQL mode
explicitly, set it to an empty string using --sql-mode="" on the
command line, or sql-mode="" in an option file. ... To change the
SQL mode at runtime, set the global or session sql_mode system
variable using a SET statement:
SET GLOBAL sql_mode = 'modes';
SET SESSION sql_mode = 'modes';

Have you tried the datatype:
TINYTEXT
?
or would it not work on the system you are using?

Related

Timestamp handling function breaks replication

We need an increasing microseconds timestamp that is NEVER ever allowed to decrease, especially if the time is altered (eg ntp) or on restarts etc. I'm currently forced to use MariaDB 5.5.68 (on CentOS).
Basically it is an implementation of https://en.wikipedia.org/wiki/Lamport_timestamp
Currently it works like this:
Table creation:
CREATE TABLE `tblboxmicro` (
`microTime` BIGINT UNSIGNED NOT NULL -- the highest microtime used
) ENGINE=MYISAM DEFAULT CHARSET=utf8;
INSERT INTO `tblboxmicro` (`microTime`) VALUES (0);
We create this function in order to use that easily:
delimiter //
create function getLamportMicros()
returns bigint
reads sql data
begin
declare ret bigint;
UPDATE tblboxmicro SET microTime = GREATEST(round(##SESSION.timestamp * 1000000, 0), microTime+1);
SELECT microTime into ret FROM tblboxmicro;
return ret;
end
//
delimiter ;
It is used this way (a real life example query from PHP with PDO):
INSERT INTO tblboxusers
(microTime, roleNodeId,
userNodeId, boxId, roleId, email, name, notes, meta, cipher, accesscode)
VALUES (getLamportMicros(),0,?,?,?,?,?,?,?,?,?)
The problem
Now we want to establish a cross-master replication and this function is always breaking the replication. It says
Slave SQL: Could not execute Update_rows event on table box.tblboxmicro; Can't find record in 'tblboxmicro', Error_code: 1032; handler error HA_ERR_END_OF_FILE;
Is there a more clever way to reach our goal in a way that does not break replication? It has to be fast, of course...
Edit: We use MIXED binlog format.
We finally found the issue. Mariadb was not able to correctly replicate the #SESSION timestamp. We changed the getLamportMicros() function like this for making it work:
delimiter //
create function getLamportMicros()
returns bigint
reads sql data
begin
declare ret bigint;
select GREATEST(CAST(1000000*UNIX_TIMESTAMP(current_timestamp(6)) AS UNSIGNED), microTime+1) into ret from tblboxmicro;
update tblboxmicro set microTime = GREATEST(ret, microTime);
return ret;
end
//
delimiter ;
This is what I found: https://dev.mysql.com/doc/refman/5.7/en/replication-features-variables.html
In statement-based replication, session variables are not replicated
properly when used in statements that update tables. For example, the
following sequence of statements do not insert the same data on the
source and the replica:
SET max_join_size=1000; INSERT INTO mytable VALUES(##max_join_size);
Thus, by replacing ##SESSION.timestamp with the UNIX_TIMESTAMP(current_timestamp(6)) function, it seems to work now. The casting was simply to make it usable for our specific case.

the dbms assumes that a column name is a system variable inside the trigger

I have a table with three variables. one is the amout, the other is a restricting. Strict is the name of a column, but the system views it as a variable. It is not. It is not a reserved word either.
As this is an update, New is forbidden
Operation failed: There was an error while applying the SQL script to the database.
ERROR 1193: Unknown system variable 'strict'
SQL Statement:
CREATE DEFINER = CURRENT_USER TRIGGER `bibliotech`.`patronus_AFTER_UPDATE` AFTER UPDATE ON `patronus` FOR EACH ROW
BEGIN
if amount > 5.65
then set strict =1;
elseif amount <5.65
then set strict =0;
end if;
ND

MySQL - Field <myfield> doesn't have a default value

I am aware that the certain field doesn't have a default value.
I've been using MySQL 5.5.28 and it does work whenever I insert without specifying a value on that field. The field is TINYINT and by default, without specifying any value AND without declaring a default value during creation of the table, a value of 0 will be inserted in that field during INSERT statement.
However, after updating to MySQL 5.5.30, the query doesn't work anymore and is returning Field doesn't have a default value.
I've been looking through the changelogs and didn't find any clue that something has changed with regards to the default values of Integer.
MySQL 5.5.29 : http://dev.mysql.com/doc/relnotes/mysql/5.5/en/news-5-5-29.html#mysqld-5-5-29-feature
MySQL 5.5.30 : http://dev.mysql.com/doc/relnotes/mysql/5.5/en/news-5-5-30.html
Test queries:
MyTable has the Fields MyField1 and MyField2
INSERT INTO MyTable(MyField2)VALUES('MICHAEL');
Result on MySQL 5.5.28:
MyField1 | MyField2
0 | MICHAEL
With warning: 1 row(s) affected, 1 warning(s): 1364 Field 'MyField1' doesn't have a default value
Result on MySQL 5.5.30:
No changes on data and throws an error
Error Code: 1364. Field 'MyField1' doesn't have a default value
INSERT INTO MyTable(MyField1, MyField2)VALUES(0, 'MICHAEL');
The above query will work though.
In the 1st server strict sql mode was not enabled, while in the 2nd one it was. Read more about strict mode in the mysql documentation.
Specifically:
If strict mode is not in effect, MySQL inserts adjusted values for invalid or missing values and produces warnings (see Section 13.7.5.40, “SHOW WARNINGS Syntax”). In strict mode, you can produce this behavior by using INSERT IGNORE or UPDATE IGNORE.

Disable strict sql mode for a inline query in mysql

Can some how i could Disable strict sql mode for a stored procedure or inline query in mysql?
I just wanted to either disable for single sp/query or a single database.
What i have tried is
set sql_mode='' ;INSERT INTO system_log(appname, action, level, thread_id, context,
context_id,market_id, message,transaction_id,primary_msisdn,primary_issuer
,secondary_msisdn,secondary_issuer, merchant_id,acquirer_id)
VALUES(
#appname, #action,#level, #thread_id, #context
, #context_id
,#market_id
,CASE WHEN #message = 'NULL' THEN NULL ELSE #message END
,#transaction_id
,#primary_msisdn
,#primary_issuer
,#secondary_msisdn,#secondary_issuer, #merchant_id,#acquirer_id
);
You have to set the session version of the sql_mode server system variable:
SET SESSION sql_mode = ''; --no mode set
After that you can restore sql_mode by setting it to the appropriate value.
However, I would rather consider rewriting the stored procedure so that you do not have to change the sql mode.

How do I require a mysql field?

I just discovered NOT NULL does not make a field required.
When creating a mysql table, how do I create a field that cannot contain null or blank (must have something in it)?
By default, MySQL accepts invalid values. You can set MySQL to strict mode to force valid values. This will reject a query that does not provide a value for a NOT NULL column as well as enforce integrity on all types of columns.
Update: MySQL 5.7 and above now have strict mode on by default. So it would not accept invalid values by default like previous versions.
http://dev.mysql.com/doc/refman/5.0/en/sql-mode.html#sql-mode-important
http://dev.mysql.com/doc/refman/5.0/en/sql-mode.html#sqlmode_strict_all_tables
Edit:
#Barranka and #RocketHazmat made good points in the comments. '' is not the same as null, so MySQL will allow that in a NOT NULL column. In that instance, you would have to resort to your code or a trigger.
In the code (PHP for example), this could be easy enough, running something like:
if (!strlen($value)) {
// Exclude value or use NULL in query
}
I think you should do two things:
Set the column to NOT NULL to force the input of a value
Use a trigger to validate the values.
Within the trigger you can cancel the operation if the desired column does not fulfill a required condition (for example, having zero-length).
This question and its answers address this second thing, and here is an example:
delimiter $$
CREATE TRIGGER `cancel_insert_if_empty`
BEFORE INSERT ON `your_table`
FOR EACH ROW
BEGIN
declare msg varchar(255);
if NEW.your_column is null or length(NEW.your_column) = 0 then
set msg = "You're doing something wrong! Now suffer the consequences";
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = msg;
end if;
END$$
delimiter ;
In this example, if you try to insert a null value or a zero-length string in your_column an error will rise and the insert will be canceled. Quoting from the reference manual:
MySQL handles errors during trigger execution as follows:
If a BEFORE trigger fails, the operation on the corresponding row is not performed.
A BEFORE trigger is activated by the attempt to insert or modify the row, regardless of whether the attempt subsequently succeeds.
An error during either a BEFORE or AFTER trigger results in failure of the entire statement that caused trigger invocation.
Of course, you can write a trigger to check the updates too.
Hope this helps.
You can set default value for that field: City varchar(40) DEFAULT 'Sandnes'