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.
Related
What i want thing is, i want to set the sql_mode variable as a local variable, not as a session or global variable. Reason to do that is i want to disapear the change of sql mode variable after one of query was executed. Below session and global are worked well, but this is not the what i want. Global one is kept the sql mode as a empty one forever. Session one is kept the sql mode as a empty one until connection close. I want thing is, keep the sql mode until a quarry is executed only.
mysql> set global sql_mode='';
mysql> set session sql_mode='';
mysql query :-
SELECT tc_exe_grp_num,tcs.tc_tc_id,tcs.tcs_id
FROM tc_exe_res tcer
INNER JOIN tcs tcs
ON tcs.tcs_id = tcer.tcs_tcs_id
WHERE tcs.tc_tc_id='1'
AND tcs.tc_tc_id='1'
GROUP BY tc_exe_grp_num
ORDER BY tc_exe_grp_num ;
got the idea from this article
please help me.
##sql_mode is session variable, not a local variable.
It is possible to retrieve the current setting of sql_mode, and save it in a user-defined variable, and then later set sql_mode back to the original setting.
For example:
-- save current setting of sql_mode in user defined variable
-- change sql_mode to desired setting
SET #SAVE_sql_mode = ##sql_mode ;
SET ##sql_mode = 'NO_ENGINE_SUBSTITUTION' ;
-- subsequent operations run under new setting of sql_mode
SELECT '...';
-- set sql_mode back to saved setting
SET ##sql_mode = #SAVE_sql_mode ;
I couldn't find a direct answer for this, but there is a solution,
First set the "sql mode" as a empty one and after quarry was executed set the "sql mode" with what previously had values, try it in below way,
set session sql_mode='';
SELECT tc_exe_grp_num,tcs.tc_tc_id,tcs.tcs_id FROM tc_exe_res tcer INNER JOIN tcs tcs ON tcs.tcs_id = tcer.tcs_tcs_id WHERE tcs.tc_tc_id='1' AND tcs.tc_tc_id='1' group by tc_exe_grp_num ORDER BY tc_exe_grp_num ;
set session sql_mode='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';
I got a project, which make insertions which inserting no values(not empty values) to the columns with NOT NULL and NO DEFAULT values.
I believed that is impossible to make insertion with missing required values, and it always throws an error: Field 'xxxx' doesn't have a default value. But as I see here mysql can be set to
sql-mode="NO_ENGINE_SUBSTITUTION"
I am confused, cause I think it is dangerous. And if I switch it OFF it will apply to all projects and it could be really bad. So what should I do? Is it possible to set the mode only for one mysql database while other databases will be on STRICT mode? What do you think about it? Is it an issue or not?
The sql-mode system variable is available at both global and session level. Which means either you have to set this for entire server or particular connection. So there is no way to configure this for subset of DBs at server level. However you can specify the sql mode when you are making the connection. So those connections will run in strict mode.
The solution that i propose is to made a trigger so when there is a no value it will insert a null value to that column
this is an example :
CREATE TRIGGER upd_check BEFORE UPDATE ON account
FOR EACH ROW
BEGIN
IF NEW.amount < 0 THEN
SET NEW.amount = 0;
END IF;
END;
So if somebody wants to know how it looks like in PHP/PDO for one concrete session:
$pdo = new PDO(
$dsn,
$username,
$password,
array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET sql_mode="NO_ENGINE_SUBSTITUTION"')
);
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?
I have the following function:
CREATE FUNCTION test_rule (value TEXT, greater_than DECIMAL(10,4))
RETURNS BOOL
RETURN greater_than IS NULL OR CAST(value AS DECIMAL(10,4)) > greater_than;
If I use this function with an empty string vor value I receive an error:
SELECT test_rule('',10)
(1366): Incorrect decimal value: '' for column '' at row -1
But the same expression works fine without a function:
SELECT '' IS NULL OR CAST('' AS DECIMAL(10,4)) > 10
0
Why is this?
I'm using MySQL version 5.6.22
The way mysql handles certain data conversions is also affected by the sql mode settings (see Strict SQL Mode section in particular) and CAST functions.
The documentation is a bit hazy on how these restrictions apply in an expression evaluation, however the CAST function documentation does mention that
The SQL mode affects the result of conversion operations.
The OP mentioned in a comment that sql_mode is set to STRICT_TRANS_TABLES, NO_ENGINE_SUBSTITUTION. STRICT_TRANS_TABLES means that strict sql mode is enabled for transactional tables and probably for cast operations as well.
'' (empty string) cannot be properly casted to a number. If strict sql mode is not enabled, then mysql generates a warning and evaluates '' with the default value for the given numeric type - 0 that is. However, if strict sql mode is enabled, such conversion results in an error.
You can change the sql mode on global or session level by setting the appropriate value to sql_mode variable.
There is one more complication with stored programs. Stored programs run with the sql mode setting valid at compile time:
MySQL stores the sql_mode system variable setting in effect when a
routine is created or altered, and always executes the routine with
this setting in force, regardless of the current server SQL mode when
the routine begins executing.
If you want different stored procedures to run under different sql mode settings, then
drop the existing stored routine
change the sql mode to the desired setting
re-create the stored program
You can query the sql mode setting for any stored routine by listing INFORMATION_SCHEMA.ROUTINES table (sql_mode column).
However, I would consider re-writing the code in this particular instance so that it checks if the value parameter equals with '' empty string, rather than starting to play with the sql mode settings. The problem is that these special sql mode setting requirements can be easily forgotten or overlooked during system change or migration and the existing code may start throwing errors around and it will be difficult to re-trace the source of the issues.
I am currently using the CONTEXT_INFO property of the Master database for storing the logged in username to use it later in Table Triggers for auditing.
While migrating to SQL Azure, the issue of Cross-Database connections popped and I couldn't find direct solutions to this issue.
Following are the Issue Details:
I call Stored Procedure XXX from Data Access Layer and pass the Username as Parameter
The username is used to set the CONTEXT_INFO value in XXX
The CONTEXT_INFO value is then used in Tables Insert/Update/Delete Triggers to Store Username for Application Auditing
Solutions that I found so far:
Create Table In Database to work as CONTEXT_INFO
Use 2 Connection Strings in Data Access Layer, one for Master Database (to set CONTEXT_INFO) and the other is for the application and execute the SET CONTEXT_INFO each time before opening the connection to my application
But I find both solutions risky, specially when expanding the Database over multiple SQL Azure Databases in the future.
Appreciate your support.
The approach I took is shown below. On trick was to check to see running not running on SQL Azure, then we would need to call 'SET CONTEXT_INFO ...'. This allows the same code to be execute on local SQL Server Express and Azure without changes.
Create a table to store the context info (not in master but in the same database)
CREATE TABLE [dbo].[ContextInfo] (
[ContextInfo] varbinary(128) not null,
[ApplicationUsername] nvarchar(128) not null,
[UpdatedAt] datetime NOT NULL,
CONSTRAINT [PK_UserContextInfo] PRIMARY KEY CLUSTERED ([ContextInfo] ASC)
)
Create a stored procedure to 'Set Context Info' which is called from application
CREATE PROCEDURE [dbo].[SetContextInfo]
#ApplicationUsername nvarchar(128)
AS
SET NOCOUNT ON
-- Remove all context items older than an 5 minutes ago
DELETE
FROM [dbo].[ContextInfo]
WHERE [UpdatedAt] < DATEADD(mi, -5, GETUTCDATE())
--
-- Use the MERGE command to do an update/insert
-- See: http://technet.microsoft.com/en-us/library/bb510625.aspx
--
IF SERVERPROPERTY('edition') <> 'SQL Azure'
BEGIN
DECLARE #b varbinary(128)
SET #b = CONVERT(varbinary(128),newid())
EXEC sp_executesql #statement=N'SET CONTEXT_INFO #b',#params=N'#b varbinary(128)',#b=#b
END
DECLARE #ContextInfo varbinary(128)
SELECT #ContextInfo = CONTEXT_INFO()
MERGE [dbo].[ContextInfo] AS target
USING (SELECT #ContextInfo, #ApplicationUsername) AS source ([ContextInfo], [ApplicationUsername])
ON (target.[ContextInfo] = source.[ContextInfo])
WHEN MATCHED THEN
UPDATE SET [ApplicationUsername] = source.[ApplicationUsername], [UpdatedAt] = GETUTCDATE()
WHEN NOT MATCHED THEN
INSERT ([ContextInfo], [ApplicationUsername], [UpdatedAt])
VALUES (source.[ContextInfo], source.[ApplicationUsername], GETUTCDATE());
Create a stored procedure to 'Get Context Info'
CREATE PROCEDURE [dbo].[GetContextInfo]
AS
SET NOCOUNT ON
DECLARE #ContextInfo varbinary(128)
SELECT #ContextInfo = CONTEXT_INFO()
SELECT [ApplicationUsername]
FROM [dbo].[ContextInfo]
WHERE [ContextInfo] = #ContextInfo
GO
In trigger source, use:
DECLARE #UserContext TABLE ([Username] VARCHAR(128))
INSERT INTO #UserContext (Username)
EXEC [dbo].[GetContextInfo]
Now you have the username stored in the table variable. In case changes are applied by an administrator outside of your application, you may also want to check if the username was not set and default to something like *SYSTEM_USER*.