0

I have a field called currentItem , this field has a trigger that calls a stored procedure (with transaction) sp_TransferData to perform some transfers of information to a worktable. If the stored procedure fails - I would like to restore the old value of currentItem - as it has not effectively changed.

I am using MySQL and I would like this logic to be in my trigger - obviously we do not want an endless loop so how do I accomplish this ?

Trigger Pseudo Code :

Call sp_TransferData(@ResultCode);
Select @ResultCode into Result;
If Result < 0 THEN
thisField Value = OLD.Value;
End If;

// EDIT 9-16-2016 13:00 // There is only 1 Row in this table never any thing more! Column list edited for brevity.

table global_items

Id INT PK, 
lsize INT, 
wsize INT, 
currentItem INT

Trigger is on currentItem After Update or Before I do not care which as long as it works and does not refire the trigger:

If the value has changed CALL sp_TransferData(@ResultCode);
If (SELECT @ResultCode) < 0 THEN
Reset currentItem to old value but do not cycle the trigger since we are only resetting it.
EndIf;

Just to add this is what I have in my trigger code which is not correct. The Table definitions are supplied.

BEGIN

     IF NEW.currentItem <> OLD.currentItem THEN

     call sp_CurrentItemChanged(NEW.currentItem, @ResultCode, @ResultMsg);

        IF ((Select @ResultCode) < 0) THEN

         NEW.currentItem = OLD.currentItem;

        END IF;

     END IF;

END



    CREATE TABLE working_table (
  Id int(11) NOT NULL AUTO_INCREMENT,
  Mbf float DEFAULT NULL,
  Width float DEFAULT NULL,
  Pulse int(11) DEFAULT NULL,
  PRIMARY KEY (Id)
)
ENGINE = INNODB
AUTO_INCREMENT = 1
CHARACTER SET utf8
COLLATE utf8_general_ci
ROW_FORMAT = DYNAMIC;

CREATE TABLE recipe (
  Id int(11) NOT NULL AUTO_INCREMENT,
  Name varchar(80) NOT NULL DEFAULT 'UnAssigned',
  IsDefault tinyint(1) DEFAULT 0,
  PRIMARY KEY (Id),
  UNIQUE INDEX Id_UNIQUE (Id),
  UNIQUE INDEX Name_UNIQUE (Name)
)
ENGINE = INNODB
AUTO_INCREMENT = 1
CHARACTER SET utf8
COLLATE utf8_general_ci
ROW_FORMAT = DYNAMIC;


CREATE TABLE Packs (
  Id int(11) NOT NULL AUTO_INCREMENT,
  Name varchar(45) NOT NULL DEFAULT 'UNDEFINED',
  Width float NOT NULL DEFAULT 0,
  Pulse int(11) NOT NULL DEFAULT 0,
  Mbf float NOT NULL DEFAULT 0,
  RecipeID int(11) NOT NULL DEFAULT 0,
  SetupID int(11) DEFAULT 1,
  PRIMARY KEY (Id),
  INDEX SetupID_ndx (SetupID),
  INDEX FK_PackRecipeID_Recipe_ID_idx (RecipeID),
  INDEX FK_RecipeID_PackS_idx (RecipeID),
  CONSTRAINT FK_PackRecipeID_Recipe_ID FOREIGN KEY (RecipeID)
  REFERENCES recipe (Id) ON DELETE CASCADE ON UPDATE CASCADE
)
ENGINE = INNODB
AUTO_INCREMENT = 1
CHARACTER SET utf8
COLLATE utf8_general_ci
ROW_FORMAT = DYNAMIC;

CREATE TABLE global_items (
  Id int(11) NOT NULL AUTO_INCREMENT,
  PackSys_Count int(11) DEFAULT NULL,
  Active_Recipe int(11) DEFAULT 1,
  PRIMARY KEY (Id)
)
ENGINE = INNODB
AUTO_INCREMENT = 2
CHARACTER SET utf8
COLLATE utf8_general_ci
ROW_FORMAT = DYNAMIC;

When global_items.Active_recipe changes the trigger fires .. The data that is moved is in packs and its related tables (brevity here) to the working_table. The global_items table is NEVER touched by anything in the lengthy Stored Procedure or any other triggers or any other sql code. It is never modified by anything internal to the SQL storage - it is touched only by an outside applications. I am not sure how I restore the value to the original value on failure of the stored procedure.

enter image description here

Ken
  • 2,518
  • 2
  • 27
  • 35
  • I am sure this can be answered but it is vague. What is the tableName? What is the update trigger type (and on what tableName?) . Also a data visual from you would make it clear. That is, if you seriously want to attract an answer – Drew Sep 16 '16 at 17:21
  • @Drew the trigger is on a column not the table. I posted the trigger settings, the tablename with columns (not sure why the tablename is important) , the trigger is on the column currentItem its an INT. I simply want on fail to reset the value back to what it was . – Ken Sep 16 '16 at 18:06
  • Triggers are on tables. http://dev.mysql.com/doc/refman/5.7/en/trigger-syntax.html . Being explicit if the trigger (on a `tableName`) is also impacting that `tableName` is crucial to know as self-modification of same table can be an Error Condition. That is, if we are even talking about the commonly understood concept from the url shown here – Drew Sep 16 '16 at 18:35
  • @Drew - In the Stored Procedure I am not updating anything in this table. This global_items table is a configuration persistence. Any change is then propagated to the appropriate systems that need changing - a config recipe if you will. If it fails I do not want the wrong info in this table as it is accessed by more than 1 component. – Ken Sep 16 '16 at 19:32
  • It seems you know how to do all this, or you are not explaining what the problem is (closes for no MCVE). I would love to answer this, and I pointed 2 other people at your question. It is still not depicted with table schemas and before and afters, and what you mean by *obviously we do not want an endless loop so how do I accomplish this ?* so that may explain why it is not answered – Drew Sep 17 '16 at 00:27
  • It is not always enjoyable to take the time to write up posts well (it is also not always enjoyable to read them). See this answer [Here](http://stackoverflow.com/a/37472510) where the OP depicts some tables. You would need to do some sort of before and after (Expected Results) or something to help someone understand this before they embark on 30 minutes of their time, creating their own schema, because you did not `show create table myTableName` for both tables – Drew Sep 17 '16 at 00:30
  • Isn't the the Trigger's change part of the transaction, hence subject to `ROLLBACK`? – Rick James Sep 17 '16 at 05:09
  • @RickJames the trigger field is in the global_items - it never gets touched by any internal sql code. The stored Procedure just transfers several tables (not related to global_items) but these tables are related to each other and are then transferred into the working_table. The sp never touches the globals_items (outside application) run the procedure if successful allow its active_recipe (currentItem) value to change and if not restore it to original value , I do not want the trigger to fire again when I restore the value or is there a way to prevent the value change until after success? – Ken Sep 17 '16 at 05:31
  • @Drew I added the table creation - the global_items table is unrelated to any other table in db. It has ONLY 1 record. It gets modified by an outside application. No SQL procedures change any of it's values. The stored procedure is Lengthy. It simply takes a group of tables and makes one table out of that group with a lot of joins and selects etc.. into (working_table). The Trigger code posted does not work. Is there a method in trigger to allow value to revert to original on CALL stored procedure result = fail? can I use a rollback in the begin end block of trigger - to roll it back ? – Ken Sep 17 '16 at 05:39

1 Answers1

1

I think I might be understanding what you are getting at. But since you did not show your trigger entirely I guess, and not the Stored Proc, I just winged it and investigated.

I think the problem is that your stored proc does not have an OUT qualifier to it to make it write-able. The below works fine and I think it captures how to solve your issue.

Schema:

-- drop table global_items;
create table global_items
(   Id INT primary key, 
    lsize INT not null, 
    wsize INT not null, 
    currentItem INT not null,
    theCount int not null
);
insert global_items(Id,lsize,wsize,currentItem,theCount) VALUES
(1,1,1,100,0);

select * from global_items;

Trigger:

DROP TRIGGER IF EXISTS giBeforeUpdate;
DELIMITER $$
CREATE TRIGGER giBeforeUpdate
BEFORE UPDATE
   ON global_items FOR EACH ROW
BEGIN
    DECLARE tRet INT;
    SET tRet=0;
    SET NEW.theCount=OLD.theCount+1;
    CALL uspDoSomething7(tRet);
    IF tRet=1 THEN
        -- stored proc said FAILURE
        SET NEW.currentItem=OLD.currentItem;
    END IF;
END;$$
DELIMITER ;

Stored Procedure:

DROP PROCEDURE IF EXISTS uspDoSomething7;
DELIMITER $$
CREATE PROCEDURE uspDoSomething7(OUT retVal INT)
BEGIN
    DECLARE rndNum INT;
    SET rndNum=FLOOR(RAND()*2)+1; -- random number 1 or 2

    -- sets retVal to 1 on FAILURE
    IF rndNum=2 THEN
        SET retVal=1; -- FAIL
    ELSE
        SET retVal=0; -- SUCCESS
    END IF;
END$$
DELIMITER ;

Test:

Repeatedly call this confirm that it fails roughly half the time

that is, currentItem retains its old value

but chg the update stmt below each time for the currentItem= part of it

update global_items set currentItem=410,lsize=2 where Id=1;
select * from global_items;
Drew
  • 24,851
  • 10
  • 43
  • 78
  • I am new to using triggers some some things are hazy for me - not your example though - just me implementing triggers; in the statement here of the trigger IF tRet=1 THEN -- stored proc said FAILURE SET NEW.currentItem=OLD.currentItem; END IF; this is done before update so the value has yet to change ?? and because of this - I can reset the value to original and the trigger will not endlessly loop. This is the answer for what I was trying to figure out. thx.. – Ken Sep 19 '16 at 04:45
  • Feel free to come to [Campaigns](http://chat.stackoverflow.com/rooms/95290) chat and ask away. Just ping RIck or myself with an `@` – Drew Sep 19 '16 at 04:49