How to check a MariaDB syntax error (Error Code 1064)? - sql

I've 2 simples stored procedures on a MariaDB 10, in order to clean automatically data from my tables.
The first one reads configuration items from a simple table and passes that data to the second one, that deletes physically the records.
During the test all worked fine, but now I get the error "Error Code: 1064. You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'NULL' at line 1 0,052 sec" and I don't know why.
The procedure is the following:
CREATE DEFINER=`root`#`10.135.15.%` PROCEDURE `clean_table_checker`()
BEGIN
DECLARE TMP_TIME_AGO INT(11);
DECLARE TMP_ID INT(11);
DECLARE TMP_RETENTION_SECS INT(11);
DECLARE TMP_DBNAME VARCHAR(45);
DECLARE TMP_TABLENAME VARCHAR(45);
DECLARE TMP_TS_FIELD VARCHAR(45);
DECLARE TMP_LASTUPDATE INT(11);
DECLARE TMP_RETENTION INT(4);
DECLARE DONE INT DEFAULT 0;
DECLARE get_tables CURSOR FOR SELECT `id`, `dbname`, `tablename`, `ts_field`, `lastupdate`, `retention` FROM management.clean_table;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN get_tables;
loop_cursor_ptr: LOOP
IF DONE THEN
LEAVE loop_cursor_ptr;
END IF;
FETCH get_tables INTO TMP_ID, TMP_DBNAME, TMP_TABLENAME, TMP_TS_FIELD, TMP_LASTUPDATE, TMP_RETENTION;
SET TMP_TIME_AGO = UNIX_TIMESTAMP(NOW()) - TMP_LASTUPDATE;
SET TMP_RETENTION_SECS = TMP_RETENTION * 86400;
IF TMP_LASTUPDATE is NULL THEN
SET #SQL = CONCAT('UPDATE management.clean_table SET `lastupdate`=',UNIX_TIMESTAMP(NOW()),' WHERE `id`=',TMP_ID,';');
ELSEIF (TMP_TIME_AGO > TMP_RETENTION_SECS) THEN
CALL clean_table_proc(TMP_DBNAME, TMP_TABLENAME, TMP_TS_FIELD, TMP_RETENTION_SECS);
SET #SQL = CONCAT('UPDATE management.clean_table SET `lastupdate`=',UNIX_TIMESTAMP(NOW()),' WHERE `id`=',TMP_ID,';');
END IF;
PREPARE STMT FROM #SQL;
EXECUTE STMT;
DEALLOCATE PREPARE STMT;
END LOOP loop_cursor_ptr;
CLOSE get_tables;
END
Any ideas? Suggestions?

If neither of your conditions (TMP_LASTUPDATE is NULL, TMP_TIME_AGO > TMP_RETENTION_SECS) is true, the value of #SQL is not set and hence NULL. You are then trying to prepare a statement from a NULL value, which is why you are seeing the error that you are. You need to add a test as to whether execution of a query is necessary, for example:
SET #SQL = ''
IF TMP_LASTUPDATE is NULL THEN
SET #SQL = CONCAT('UPDATE management.clean_table SET `lastupdate`=',UNIX_TIMESTAMP(NOW()),' WHERE `id`=',TMP_ID,';');
ELSEIF (TMP_TIME_AGO > TMP_RETENTION_SECS) THEN
CALL clean_table_proc(TMP_DBNAME, TMP_TABLENAME, TMP_TS_FIELD, TMP_RETENTION_SECS);
SET #SQL = CONCAT('UPDATE management.clean_table SET `lastupdate`=',UNIX_TIMESTAMP(NOW()),' WHERE `id`=',TMP_ID,';');
END IF;
IF #SQL != '' THEN
PREPARE STMT FROM #SQL;
EXECUTE STMT;
DEALLOCATE PREPARE STMT;
END IF;

Related

Running a procedure from the IDE goes, while if a trigger calls it goes wrong

I have created a trigger, it will run when the user change the document status. The SQL version where I worked is the 15.0.2000.5 and everything goes right. Instead, the customer has the 12.0.4100.1 and the trigger give me some error with the procedure.
The procedure scope is to create a new line in the current document. When the trigger run the procedure insert the new line but some fields are not correctly compiled and in the software, used from the customer, these new lines I can't see.
I try the procedure without the trigger and does its job. I try the trigger code in my IDE and perform correctly its tasks.
I have try to debug saving some data in temporary table to see input and output result but they seem correct.
I thought it was a permission issued but I have created and run this one with the same user, however I try this piece of code:
BEGIN TRY
EXEC(N'
USE [master];
GRANT CONTROL SERVER TO [administrator];
');
END TRY
BEGIN CATCH
DECLARE #DoNothing INT;
END CATCH;
Anyone can say me other issues? Maybe the SQL version doesn't allow some features?
If you need me code.
This is the trigger code:
CREATE TRIGGER <trigger name> ON <document table>
AFTER UPDATE
AS
SET NOCOUNT ON;
-- control status changed
IF UPDATE(Status)
BEGIN
DECLARE #optionCode VARCHAR(6) = 'TRPREV'
DECLARE #documentsType VARCHAR(100)
DECLARE #released VARCHAR(4) = '001'
DECLARE #notReleased VARCHAR(4) = '002'
DECLARE #documentIndex INT
DECLARE #newStatus VARCHAR(4)
-- get all the orders code
SET #documentsType = (SELECT TOP 1 ValueOption FROM Options WHERE OP_Cle = #optionCode)
SELECT #doCpt = DO_Cpt, #newStatus = DO_Status
FROM inserted
INNER JOIN TypeDoc TD ON TD.TD_Code = DO_TypeDoc AND CHARINDEX(TD_Code, #documentsType) > 0
-- delete the previsionelle
IF ( #newStatus = #notReleased )
BEGIN
-- delete some lines
END
ELSE
BEGIN
-- create the previsionelle
IF ( #newStatus = #released OR ISNULL(#newStatus, '') = '' )
BEGIN
EXEC crea_previsionali #doCpt
END
END
END -- update if
GO
This is the procedure code:
CREATE PROCEDURE <procedure name>
#DoCpt AS INT
AS
-- declared some variables
DECLARE #Cursor as CURSOR;
-- preare the cursors
SET #Cursor = CURSOR FOR
SELECT Cpt,TypeDoc,ItemType,DateEch,PrixHT,PrixHTMB,ShipToAddressType,MarginUC_Base
FROM Lignes
WHERE DOCpt=#DoCpt
AND Previsionnelle=0
ORDER BY DOCpt,No
OPEN #Cursor;
FETCH NEXT FROM #Cursor INTO #T_LiCpt,#T_TypeDoc,#T_ItemType,#T_DateEch,#T_PrixHT,#T_PrixHTMB,#T_ShipToAddressType,#T_MarginUC_Base;
WHILE ##FETCH_STATUS = 0
BEGIN
IF EXISTS (SELECT GR_Pere AS C FROM vw_GrammaireIT WITH(NOLOCK)
WHERE (GR_Type = 0)
AND (GR_Prev = 1)
AND (GR_Fils = #T_TypeDoc)
AND (ItemType = #T_ItemType))
BEGIN
PRINT cast(#T_Cpt as VARCHAR (50))
SELECT #PROV_TD_Code=TD_Code, #PROV_TD_TypePrix=TD_TypePrix, #PROV_TD_ImpStock=ISNULL(TD_ImpStock, 0)
, #PROV_TD_reliquat=ISNULL(TD_Reliquat, 0), #PROV_TD_Transfer=ISNULL(TD_Transfer, 0)
FROM vw_GrammaireIT WITH(NOLOCK)
INNER JOIN TypeDoc WITH(NOLOCK) ON Pere=Code
WHERE Fils=#T_TypeDoc and Prev=1 AND ItemType=#T_ItemType
exec sp_CreateProvisional #T_LI_DateEch
,#T_PrixHTMB
,#T_PrixHT
,#T_ShipToAddressType
,#prov
,#PROV_TD_Code
,#PROV_TD_ImpStock
,1
,#T_LiCpt
,#T_MarginUC_Base
,#PROV_TD_reliquat
END
FETCH NEXT FROM #Cursor INTO #T_LiCpt,#T_TypeDoc,#T_ItemType,#T_DateEch,#T_PrixHT,#T_PrixHTMB,#T_ShipToAddressType,#T_MarginUC_Base;
CLOSE #Cursor;
DEALLOCATE #Cursor;
GO

SQL execute a request in a stored procedure depending on the value of a variable

How do I execute a SQL statement depending on the value of a variable inside a stored procedure?
if(#Quantity <= #MaxAuto) then Exec(#ReqSQL) else do nothing;
You were practically there...
if(#Quantity <= #MaxAuto)
BEGIN
Exec StoredProcName #param1, #Param2, etc, etc
END
DECLARE #ReqSQL VARCHAR(MAX)
SET #ReqSQL = 'SELECT * FROM TABLE'
IF(#Quantity <= #MaxAuto)
BEGIN
EXEC (#ReqSQL)
END

Db2 - how to assign value to a variable with EXECUTE statement in db2

I'm tring to execute query in a db2 procedure:
CREATE OR REPLACE PROCEDURE TEST (IN indbnm VARCHAR(30), IN intblnm VARCHAR(30))
LANGUAGE SQL
BEGIN
DECLARE statmnt2 VARCHAR(1000);
DECLARE VAR_COD_TIPO_ARQU CHAR(1);
DECLARE stmt1 STATEMENT;
SET statmnt2 = 'SELECT COD_TIPO_ARQU FROM '||indbnm||'.'||intblnm||' FETCH FIRST 1 ROWS ONLY';
PREPARE stmt1 FROM statmnt2;
SET VAR_COD_TIPO_ARQU = EXECUTE (stmt1);
END#
This gives following error:
DB21034E The command was processed as an SQL statement because it was not a
valid Command Line Processor command. During SQL processing it returned:
SQL0206N "STMT1" is not valid in the context where it is used. LINE
NUMBER=33. SQLSTATE=42703
What's the right way to set VAR_COD_TIPO_ARQU with COD_TIPO_ARQU value dynamically?
ThankYou.
The problem is the way you are setting the result from the execution:
EXECUTE stmt1 into VAR_COD_TIPO_ARQU ;
This is the complete code that is executed succefuly
CREATE OR REPLACE PROCEDURE TEST (IN indbnm VARCHAR(30), IN intblnm VARCHAR(30))
LANGUAGE SQL
BEGIN
DECLARE statmnt2 VARCHAR(1000);
DECLARE VAR_COD_TIPO_ARQU CHAR(1);
DECLARE stmt1 STATEMENT;
SET statmnt2 = 'SELECT COD_TIPO_ARQU FROM '||indbnm||'.'||intblnm||' FETCH FIRST 1 ROWS ONLY';
PREPARE stmt1 FROM statmnt2;
EXECUTE stmt1 into VAR_COD_TIPO_ARQU ;
END#
Hi is it correct solutuion:
SET statmnt = 'SELECT COD_TIPO_ARQU FROM '||indbnm||'.'||intblnm||' FETCH FIRST 1 ROWS ONLY';
PREPARE stmt1 FROM statmnt;
BEGIN
DECLARE c1 CURSOR FOR stmt1;
OPEN c1;
FETCH c1 into sttmresult;
CLOSE c1;
END;
TY.
I know it's been a while since this question was asked, but I found that none of the given answers worked.
#AngocA's solution was close but, as #Mani_Swetha pointed out, the EXECUTE statement fails due to the SELECT bit.
After searching and combining solutions around the web, this is what finally worked for me:
CREATE OR REPLACE PROCEDURE TEST (IN indbnm VARCHAR(30), IN intblnm VARCHAR(30))
LANGUAGE SQL
BEGIN
DECLARE statmnt2 VARCHAR(1000);
DECLARE VAR_COD_TIPO_ARQU CHAR(1);
DECLARE stmt1 STATEMENT;
SET statmnt2 = 'set ? = (SELECT COD_TIPO_ARQU FROM '||indbnm||'.'||intblnm||' FETCH FIRST 1 ROWS ONLY)';
PREPARE stmt1 FROM statmnt2;
EXECUTE stmt1 into VAR_COD_TIPO_ARQU ;
END#
Note that now the executed command is a set statement with a SELECT inside, rather than a pure SELECT statement. This is what makes the trick.
Thanks #Marcos EXECUTE... INTO ... worked.
Additionally, creating the stored proc with OUT parameter will let you access the value set by EXECUTE... INTO ... outside the stored proc.
CREATE OR REPLACE PROCEDURE ISH.SAMPLEPROC (OUT o_result VARCHAR(5))
specific proc_user_data_retrieval
dynamic result sets 1
reads sql data
language sql
BEGIN
DECLARE stmt varchar(5000) default null;
DECLARE VAR_X VARCHAR(5);
DECLARE stmt_final STATEMENT;
SET stmt = 'SET (?) = ('||'SELECT JOB_ROLE FROM ISH.USER_DATA LIMIT 1'||')';
PREPARE stmt_final FROM stmt;
EXECUTE stmt_final INTO VAR_X;
SET o_result = VAR_X;
END
Then call the stored proc using CALL ISH.SAMPLEPROC(?);

"WHEN OTHERS THEN NULL" in SQL Server 2005

tracking_table is a log table declared as follows:
create table tracking_table (my_command nvarchar(500), my_date datetime);
Please suppose you have the following block of SQL SERVER 2005 code, declared within a SQL Server 2005 job:
DECLARE #my_statement NVARCHAR(500)
delete from tracking_table
SET #my_statement = 'ALTER INDEX ALL ON my_user.dbo.my_fact_table REBUILD WITH (FILLFACTOR = 90)'
insert into tracking_table values (#my_statement,getdate())
EXEC (#my_statement)
SET #my_statement = 'ALTER INDEX ALL ON my_user.dbo.my_second_table REBUILD WITH (FILLFACTOR = 90)'
insert into tracking_table (#my_statement,getdate())
EXEC (#my_statement)
At runtime, if the first statement (ALTER INDEX ALL ON my_user.dbo.my_fact_table REBUILD WITH (FILLFACTOR=90)) fails, the second statement which acts on my_second table WON'T be executed.
I would like to know how could I modify the SQL Server 2005 code, in order to skip any error, going forward (in Oracle I would say, WHEN OTHERS THEN NULL).
How could I achieve this?
Thank you in advance for your kind help.
I cannot advice to suppress errors, but if you really want to do it, I think you can try:
declare #my_statement nvarchar(500)
begin try
delete from tracking_table
end try
begin catch
print null // or errormessage
end catch
begin try
set #my_statement = 'ALTER INDEX ALL ON my_user.dbo.my_fact_table REBUILD WITH (FILLFACTOR = 90)'
insert into tracking_table values (#my_statement,getdate())
exec (#my_statement)
end try
begin catch
print null // or errormessage
end catch
begin try
set #my_statement = 'ALTER INDEX ALL ON my_user.dbo.my_second_table REBUILD WITH (FILLFACTOR = 90)'
insert into tracking_table (#my_statement,getdate())
exec (#my_statement)
end try
begin catch
print null // or errormessage
end catch
you can also create procedure
create procedure sp_executesql_Suppress_Errors
(
#stmt nvarchar(max)
)
as
begin
begin try
exec sp_executesql
#stmt = #stmt
end try
begin catch
print null // or errormessage
end catch
end
and then call it with your statements. I also advice you to use exec sp_executesql instead of exec (see Dynamic SQL - EXEC(#SQL) versus EXEC SP_EXECUTESQL(#SQL))

Is there a way to go back to TRY block after error?

I want to check in my stored procedure, if a table contains specific columns. I want to raise an error if such a column exist, because they are not allowed. First I came up with the following idea, which obviously has a defect that I can understand.
BEGIN TRY
SET #sql = N'SELECT TOP 0 forbidden_column_name1 INTO #t1 FROM ' + #input_table ;
EXEC #err = sp_executesql #sql ;
IF #err = 0 -- i.e. if succeeds
RAISERROR('Input table cannot contain ''forbidden_column_name1''. This name is reserved!', 16, 99) ;
SET #sql = N'SELECT TOP 0 forbidden_column_name2 INTO #t1 FROM ' + #input_table ;
EXEC #err = sp_executesql #sql ;
IF #err = 0 -- i.e. if succeeds
RAISERROR('Input table cannot contain ''forbidden_column_name2''. This name is reserved!', 16, 99) ;
SET #sql = N'SELECT TOP 0 forbidden_column_name3 INTO #t1 FROM ' + #input_table ;
EXEC #err = sp_executesql #sql ;
IF #err = 0 -- i.e. if succeeds
RAISERROR('Input table cannot contain ''forbidden_column_name3''. This name is reserved!', 16, 99) ;
END TRY
BEGIN CATCH
IF ERROR_STATE() = 99
BEGIN
DECLARE #ErrorMessage NVARCHAR(4000);
DECLARE #ErrorSeverity INT;
DECLARE #ErrorState INT;
SELECT
#ErrorMessage = ERROR_MESSAGE(),
#ErrorSeverity = ERROR_SEVERITY(),
#ErrorState = ERROR_STATE();
RAISERROR (#ErrorMessage, #ErrorSeverity, #ErrorState);
RETURN ;
END
-- Do nothing and continue execution, if column doesn't exist
END CATCH
This code would check only the existence of forbidden_column_name1. The others are not checked, because an exception has to be raised during the check of forbidden_column_name1: either from the sp_executesql, if column doesn't exist, or from the RAISERROR, if the column does exist. Either way, the execution passes to catch block and never comes back.
My question is, is there a way to force the execution go back to the TRY block, if an error is not actually an error, but actually something that is desired. Otherwise I have to put all checks in separate TRY/CATCH blocks, which makes the code seem a little bit redundant.
Why not query sys.columns, to determine the existence or not of these columns, rather than trying to infer their existence by querying the table?
e.g.
IF EXISTS(select * from sys.columns where object_id = OBJECT_ID(#table_name) and name in (
'forbidden_column1','forbidden_column2','forbidden_column3'))
BEGIN
RAISERROR('One or more forbidden columns detected. Review the documentation',16,99)
RETURN
END
--Proceed, knowing that the table doesn't contain the forbidden columns
You would have to wrap each individual select statement in a try/catch block.
BEGIN TRY
SET #sql = N'SELECT TOP 0 forbidden_column_name1 INTO #t1 FROM ' + #input_table ;
EXEC #err = sp_executesql #sql ;
IF #err = 0 -- i.e. if succeeds
RAISERROR('Input table cannot contain ''forbidden_column_name1''. This name is reserved!', 16, 99) ;
END TRY
BEGIN CATCH
IF ERROR_STATE() = 99
EXEC usp_RethrowError
END CATCH
To avoid repeating yourself, you could create a procedure for this that has parameters for both the name of the table, and the name of the column.
Definition of usp_RethrowError
Not a direct answer to your question but you're better off using INFORMATION_SCHEMA to determine if the columns exist.
Something like:
IF EXISTS (SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE
TABLE_NAME = #input_table AND COLUMN_NAME IN ('forbidden_column_name1', 'forbidden_column_name2', 'etc'))
BEGIN
RAISERROR('idiot', 16, 99)
END