 SET NAMES utf8;

/* ************************************************************************************************************************************************************************
    _counters
************************************************************************************************************************************************************************ */    
DELIMITER $$
DROP FUNCTION IF EXISTS `_counters_tools`$$
CREATE DEFINER=`root`@`localhost` FUNCTION `_counters_tools`(
    _MODE VARCHAR(200),
    _Id VARCHAR(250),
    _Counter INTEGER,
    _Param_A VARCHAR(200)
    ) 
RETURNS INTEGER
    NO SQL
    DETERMINISTIC
BEGIN
    IF _MODE IN ('INC', 'SET') THEN
        /*
            DO _counters_tools('SET', 'EVENT.ToDo.10s', '', '');
        */
        INSERT IGNORE INTO `_counters`
        SET
            ID = _Id,
            Counter = 0,
            Last_Clear_At = NOW()
        ;
        UPDATE `_counters`
        SET
            Counter = Counter + IFNULL(NULLIF(_Counter, 0), 1),
            Last_Session = @SYSTEM.Client.Session.GUID,
            Last_con_ID = CONNECTION_ID(),
            Last_Time = (@.COUNTERS.Last_Time := NOW())
        WHERE
            ID = _Id
        ;
        
        SET @.COUNTERS.RETURN = (ROW_COUNT() > 0);
    END IF;
    
    IF _MODE IN ('INC+GET') THEN
        INSERT IGNORE INTO `_counters`
        SET
            ID = _Id
        ;
        UPDATE `_counters`
        SET
            Counter = (@.COUNTERS.RETURN := Counter + IFNULL(NULLIF(_Counter, 0), 1)),
            Last_Session = @SYSTEM.Client.Session.GUID,
            Last_con_ID = CONNECTION_ID(),
            Last_Time = (@.COUNTERS.Last_Time := NOW())
        WHERE
            ID = _Id
        ;
    END IF;
    
    IF _MODE = 'WRITE' THEN
        /*
            DO _counters_tools('WRITE', 'YYYY', '', '');
            DO _counters_tools('WRITE', 'YYYYMM', '', '');
        */
        IF _Id = 'YYYY' THEN 
            SET _Counter = YEAR(CURRENT_DATE());
        ELSE
            IF _Id = 'YYYYMM' THEN 
                SET _Counter = YEAR(CURRENT_DATE())*100 + MONTH(CURRENT_DATE());
            END IF;
        END IF;
        INSERT IGNORE INTO `_counters`
        SET
            ID = _Id,
            Last_Clear_At = NOW()
        ;
        UPDATE `_counters`
        SET
            Counter = _Counter,
            Last_Session = @SYSTEM.Client.Session.GUID,
            Last_con_ID = CONNECTION_ID(),
            Last_Time = (@.COUNTERS.Last_Time := NOW())
        WHERE
            ID = _Id
        ;
        SET @.COUNTERS.RETURN = (ROW_COUNT() > 0);
    END IF;
    
    IF _MODE IN ('GET.Clear', 'GET.Clear.And.Forgotten') THEN
        /*
            DO _counters_tools('GET.Clear', 'EVENT.ToDo.10s', '', '');
        */
        SET @.COUNTERS.Forgotten = FALSE;
        INSERT IGNORE INTO `_counters`
        SET
            ID = _Id
        ;
        UPDATE `_counters`
        SET
            Last_Time = (@.COUNTERS.Last_Time := 
                IF((@.COUNTERS.RETURN := Counter) != 0, 
                --    jest coś w Counter zatem czas ustawiamy na NOW() 
                    NOW(),    --    jest coś w Counter zatem czas ustawiamy na NOW() 
                --    jest zero ale może upłynał czas zwłoki wtedy traktujemy tak jakby cos było o ile jest "GET.Clear.And.Forgotten"
                    IF(@.COUNTERS.Forgotten := (_MODE = 'GET.Clear.And.Forgotten' AND IFNULL(_Counter, 0) > 0 AND Last_Time < NOW() - INTERVAL _Counter SECOND),
                        NOW(),
                        Last_Time
                    )    
                )
            ),    -- czyli zawsze zerujemy ale w @.COUNTERS.RETURN mamy stan z przed zerowania
            Counter = 0,    -- czyli zawsze zerujemy ale w @.COUNTERS.RETURN mamy stan z przed zerowania
            Last_Session = @SYSTEM.Client.Session.GUID,
            Last_con_ID = CONNECTION_ID(),
            Last_Clear_At = NOW()
        WHERE
            ID = _Id
        ;
    --    Po tym update możemy sprawdzać czy @.COUNTERS.RETURN > 0 lub czy @.COUNTERS.Forgotten    
    --    @.COUNTERS.Forgotten nie jest ustawiany gdy _Counter (czas nie używania) jest 0 lub gdy _MODE = 'GET.Clear'
    --    jesli można ustawić @.COUNTERS.Forgotten to dostaje TRUE gdy czas upłynął powyżej _Counter HOUR a FALSE w przeciwnym
    END IF;
    
    IF _MODE = 'GET' THEN
        /*
            SELECT _counters_tools('GET', 'EVENT.ToDo.10s', '', '');
        */
        /*
        było - ale deadlock-i
        INSERT IGNORE INTO `_counters`
        SET
            ID = _Id,
            Counter = (@.COUNTERS.RETURN := 0),
            Last_Time = (@.COUNTERS.Last_Time := NOW()),
            Last_Session = ''
        ON DUPLICATE KEY UPDATE
            Counter = (@.COUNTERS.RETURN := Counter),
            Last_Time = (@.COUNTERS.Last_Time := Last_Time)
        ;
        */
        INSERT IGNORE INTO `_counters`
        SET
            ID = _Id,
            Counter = 0,
            Last_Time = NOW(),
            Last_Clear_At = NOW(),
            Last_Session = ''
        ;
        SELECT 
            Counter, Last_Time 
        INTO 
            @.COUNTERS.RETURN, @.COUNTERS.Last_Time
        FROM `_counters` 
        WHERE 
            ID = _Id
        ;
    END IF;
    
    IF _MODE = 'RESET' THEN
        /*
            DO _counters_tools('RESET', 'EVENT.ToDo.10s', '', '');
        */
        INSERT IGNORE INTO `_counters`
        SET
            ID = _Id
        ;
        UPDATE `_counters`
        SET
            Counter = (@.COUNTERS.RETURN := 0),
            Last_Session = @SYSTEM.Client.Session.GUID,
            Last_con_ID = CONNECTION_ID(),
            Last_Clear_At = NOW(),
            Last_Time = (@.COUNTERS.Last_Time := NOW())
        WHERE
            ID = _Id
        ;
    END IF;
    
    IF _MODE IN ('Anything.To.Do') THEN
        /*
            DO _counters_tools('GET.Clear', 'EVENT.ToDo.10s', '', '');
            UWAGA 
            funkcja tutaj zwraca 
                TRUE gdy jest coś w counterze lub gdy rekord jest zbyt długo nie obsługiwany lub 
                FALSE gdy nie ma niczego w counterze i nie sprawdzamy długości braku obsługi lub gdy czas nie jest zbyd długi
                
            _Counter (podawany w minutach)
                gdy 0/NULL to nie sprawdzamy długo nie obsługiwanego countera
                gdy > 0 to w przypadku gdy counter jest zerowy czyli nie ma czego sprawdzać to sprawdzamy tzw jak długo nie obsługiwany był counter
                i jeśli ten czas jest wiekszy od _Counter to mimo że licznik jest zerowy to zwracamy TRUE
            Mozna to opisać tak
            Gdy parametr _Counter ...
            = 0/NULL
                funkcja zwraca TRUE tylko gdy w liczniku jest wartość Counter > 0 
                funkcja zwraca FALSE tylko gdy w liczniku jest wartość Counter = 0 
            > 0
                funkcja zwraca TRUE tylko gdy w liczniku jest wartość Counter > 0 lub gdy ostatni raz licznik był zmieniany dawniej niz _Counter minut temu
                funkcja zwraca FALSE tylko gdy w liczniku jest wartość Counter = 0 i gdy ostatni raz licznik był zmieniany później niz _Counter minut temu
        */
        SET @.COUNTERS.Forgotten = FALSE;
        INSERT IGNORE INTO `_counters`
        SET
            ID = _Id,
            Counter = (@.COUNTERS.Counter := 0),
            Last_Time = (@.COUNTERS.Last_Time := NOW()),
            Last_Session = ''
        ON DUPLICATE KEY UPDATE
            Last_Time = (@.COUNTERS.Last_Time := 
                IF((@.COUNTERS.Counter := Counter) != 0, 
                --    jest coś w Counter zatem czas ustawiamy na NOW() 
                    NOW(),    --    jest coś w Counter zatem czas ustawiamy na NOW() 
                --    jest zero ale może upłynał czas zwłoki wtedy traktujemy tak jakby cos było o ile jest "GET.Clear.And.Forgotten"
                    IF(@.COUNTERS.Forgotten := (IFNULL(_Counter, 0) > 0 AND Last_Time < NOW() - INTERVAL _Counter SECOND),
                        NOW(),
                        Last_Time
                    )    
                )
            ),    -- czyli zawsze zerujemy ale w @.COUNTERS.RETURN mamy stan z przed zerowania
            Counter = 0    -- czyli zawsze zerujemy ale w @.COUNTERS.RETURN mamy stan z przed zerowania
        ;
    --    Po tym update możemy sprawdzać czy @.COUNTERS.RETURN > 0 lub czy @.COUNTERS.Forgotten    
    --    @.COUNTERS.Forgotten nie jest ustawiany gdy _Counter (czas nie używania) jest 0 lub gdy _MODE = 'GET.Clear'
    --    jesli można ustawić @.COUNTERS.Forgotten to dostaje TRUE gdy czas upłynął powyżej _Counter HOUR a FALSE w przeciwnym
        SET @.COUNTERS.RETURN = (@.COUNTERS.Counter OR @.COUNTERS.Forgotten);
    END IF;
    
    RETURN @.COUNTERS.RETURN;
END $$


/* ************************************************************************************************************************************************************************
    LOCK MySQL
************************************************************************************************************************************************************************ */    
DROP FUNCTION IF EXISTS `_LOCK`$$
CREATE DEFINER=`root`@`localhost` FUNCTION `_LOCK`(
    _MODE VARCHAR(200),
    _LockKey VARCHAR(250),
    _Timeout INTEGER,
    _Param_A VARCHAR(250)
    ) 
RETURNS INTEGER
    NO SQL
    DETERMINISTIC
BEGIN
    SET @._LockKey = _LockKey, @._Timeout = _Timeout;
    IF _MODE = 'IS_Used' THEN
    --    gdy OK - nie zablokowany to zwraca 0 (NULL)
    --    gdy zablokowany zwraca numer conncection 
        SET @._LOCK = IFNULL(IS_USED_LOCK(@._LockKey), 0);
    END IF;
    
    IF _MODE = 'IS_Used_By_ME' THEN
    --    gdy OK - to zwraca 1 - czyli to moja sesja blokuje
    --    gdy zablokowany przez innego lub gdy wolny!! zwraca 0
        SET @._LOCK = (IFNULL(IS_USED_LOCK(@._LockKey), 0) = CONNECTION_ID());
    END IF;
    
    IF _MODE IN ('IS_FREE') THEN
    --    1 oznacza brak problemów czyli klucz _LockKey jest WOLNY
    --    0 oznacza że klucz _LockKey jest ZAJETY (uzywany)
    --    -1 oznacza inny błąd serwera
        SET @._LOCK = IFNULL(IS_FREE_LOCK(@._LockKey), -1);    
    END IF;
    
    IF _MODE IN ('IS_FREE_OR_MINE') THEN
    --    1 oznacza brak problemów czyli klucz _LockKey jest WOLNY lub jest zajety przeze MNIE
    --    0 oznacza że klucz _LockKey jest ZAJETY (uzywany)
        SET @._LOCK = (IFNULL(IS_USED_LOCK(@._LockKey), CONNECTION_ID()) = CONNECTION_ID());
        -- SET @._LOCK = IFNULL(IS_FREE_LOCK(@._LockKey), -1);    
    END IF;
    
    IF _MODE IN ('LOCK.1', 'Lock') THEN
    --    1 oznacza brak problemów czyli OK gdy udało się zablokować klucz _LockKey
    --    0 oznacza Err gdy nie udało się zablokować klucza _LockKey
    --    -1 oznacza inny błąd serwera
        SET @._LOCK = 
            IF((@._lock_CONNECTION_ID := IS_USED_LOCK(@._LockKey)) = CONNECTION_ID(),
                1,
                IFNULL(GET_LOCK(@._LockKey, @._Timeout), -1)
            )
        ;    
    END IF;
    
    IF _MODE IN ('GET_LOCK', 'Lock.') THEN
    --    1 oznacza brak problemów czyli OK gdy udało się zablokować klucz _LockKey
    --    0 oznacza Err gdy nie udało się zablokować klucza _LockKey
    --    -1 oznacza inny błąd serwera
        SET @._LOCK = IFNULL(GET_LOCK(@._LockKey, @._Timeout), -1);    
    END IF;
    
    IF _MODE IN ('GET_LOCK_OK', 'Lock_OK') THEN
    --    1 oznacza OK brak problemów czyli OK gdy udało się zablokować klucz _LockKey
    --    0 oznacza Err gdy nie udało się zablokować klucza _LockKey
        SET @._LOCK = (IFNULL(GET_LOCK(@._LockKey, @._Timeout), -1) = 1);
    END IF;
    
    IF _MODE IN ('RELEASE_LOCK', 'UnLock') THEN
    --    1 gdy zwolniono klucz _LockKey
    --    0 gdy nie udało sie - gdy ta sesja nie jest włascicielem tego klucza 
    --    NULL nie ma takiego klucza w lockach
        SET @._LOCK := RELEASE_LOCK(@._LockKey);
    END IF;

    IF _MODE IN ('RELEASE_LOCK_OK', 'UnLock_OK') THEN
    --    1 gdy zwolniono klucz _LockKey
    --    0 gdy nie udało sie - gdy ta sesja nie jest włascicielem tego klucza 
    --    NULL nie ma takiego klucza w lockach
        SET @._LOCK := (IFNULL(RELEASE_LOCK(@._LockKey), 1) = 1);
    END IF;
    
    RETURN @._LOCK;
END $$

/* ************************************************************************************************************************************************************************
    LOCK z tabeli `_locks`
************************************************************************************************************************************************************************ */    
        -- DROP FUNCTION IF EXISTS `_Is_same_User_in_2_sessions`$$
        DROP FUNCTION IF EXISTS `_Lck_Is_same_User_in_2_sessions`$$
        CREATE DEFINER=`root`@`localhost` FUNCTION `_Lck_Is_same_User_in_2_sessions`(
            _GUID1 VARCHAR(38),
            _GUID2 VARCHAR(38)
        ) RETURNS BOOLEAN
            NO SQL
            DETERMINISTIC
        BEGIN
            RETURN 
                (
                    (SELECT l1.log_USER_GUID FROM `_sys_db_logons` l1 WHERE l1.GUID = _GUID1)
                     <=> 
                    (SELECT l2.log_USER_GUID FROM `_sys_db_logons` l2 WHERE l2.GUID = _GUID2)
                ) 
            ;
        END $$

        DROP FUNCTION IF EXISTS `_Lck_Exists_conncection`$$
        CREATE DEFINER=`root`@`localhost` FUNCTION `_Lck_Exists_conncection`(
            _con_ID VARCHAR(38)
        ) RETURNS BOOLEAN
            NO SQL
            DETERMINISTIC
        BEGIN
            RETURN 
                EXISTS(
                        SELECT
                            p.ID
                        FROM `information_schema`.`PROCESSLIST` p
                        WHERE 
                            p.ID = _con_ID
                            AND 
                            p.DB = DATABASE()
                )
            ;
        END $$

DROP FUNCTION IF EXISTS `_lck`$$
CREATE DEFINER=`root`@`localhost` FUNCTION `_lck`(
    _MODE VARCHAR(250), 
    _IN_Session VARCHAR(250),
    _Id VARCHAR(250),
    _Ctx VARCHAR(250),
    _TableName VARCHAR(150),
    _TimeOut INTEGER,    -- w sekundach
    _LockProc VARCHAR(200),
    _Param_A VARCHAR(200),
    _Params VARCHAR(200)
    ) 
RETURNS BOOLEAN
    NO SQL
    DETERMINISTIC
/*
    NAJNOWSZA V 2021 
    MOŻE BYC uzyta w Triggerach
    Nie ma Wait w LOCK czyli oczekiwania na zwolnienie - do tego uzyj procedury 
        CALL _lcks('LOCK', @LR, '', 'X1', 'opng_e', '', 180, '', 2, '');
*/    
BEGIN
    
    SET
        _IN_Session = IFNULL(NULLIF(_IN_Session, ''), IFNULL(@SYSTEM.Client.Session.GUID, Get_Session_GUID('', NULL, '', ''))),
        _TimeOut = IFNULL(_TimeOut, -1),
        _LockProc = NULLIF(_LockProc, ''),
        @.LCK.Result.Msg := '',
        @.LCK.Result.ReadOnly := 0,
        @.LCK.Result.Msg.MsgDlg := NULL,    --    do komunikatów 'Warning'/'Error'
        @.LCK.Result.Msg.Effect := NULL    -- 'ReadOnly'/NULL, NULL ozn.'Stop'
    ;    
    IF _MODE  IN ('GET', 'LOCK', 'GET.CON', 'LOCK.CON') THEN
    /******************************************************************************************************************************************************
        BLOKOWANIE zwraca: 1 - OK lub 0 - zabronione
        _lck('LOCK', '', 'B', 'WYJ', 'wyjazdy', 60, '', '', '');
    ******************************************************************************************************************************************************/
        SET @.Prev.LockSession :=  NULL;
        INSERT IGNORE INTO `_locks` 
        SET
            LockLevel = IF(_MODE IN ('GET', 'LOCK'), 'SESSION', 'CONNECTION'),
            Id = _Id,
            TableName = _TableName,
            Ctx = _Ctx,
            LockSession = IF((@.INS.OK := TRUE), _IN_Session, NULL),    -- zosta te konstrukcje - nie ruszaj!!!
            con_ID = CONNECTION_ID(),
            LockProc = _LockProc,
            LockAt = NOW(),
            TimeOut = _TimeOut,
            ReleaseAt = NOW() + INTERVAL IFNULL(NULLIF(_TimeOut, -1), 3600) SECOND
        ON DUPLICATE KEY UPDATE
            LockSession = 
                IF(@.INS.OK :=
                    IF(_MODE  IN ('GET', 'LOCK'),
                        (
                            -- (@.CUR.LockSession := LockSession) = _IN_Session
                            LockSession = _IN_Session
                            OR
                            _Lck_Is_same_User_in_2_sessions((@.Prev.LockSession := LockSession), _IN_Session)
                        ),
                        (
                            con_ID = CONNECTION_ID()
                            OR
                            NOT _Lck_Exists_conncection(con_ID)
                        )
                    ),
                    _IN_Session,
                    LockSession
                ),
            con_ID = IF(@.INS.OK, CONNECTION_ID(), con_ID),
            Number_of_Trials = Number_of_Trials + (NOT(@.INS.OK))
        ;
        SET @.INS.ROW_COUNT = ROW_COUNT();
        -- IF ROW_COUNT() = 1 THEN
        IF @.INS.OK THEN
        --    Licznik wszystkich lock-ów
            UPDATE `_locks` lck
            SET
                lck.ID = lck.ID + IF(@.INS.ROW_COUNT = 1, 1, 0),
                lck.Number_of_Trials = lck.Number_of_Trials + 1
            WHERE
                LockLevel = 'COUNT'
                -- AND lck.Id = '0' 
                AND lck.Ctx = '0'
            ;
        END IF;
    --    Zwracam:
    --    1 (TRUE) gdy OK oraz     
    --    0 (FALSE) gdy ERR
        SET @.LCK.RETURN = @.INS.OK;
    END IF;
    -- k.bach@ncm.com.pl
    IF _MODE  IN ('FREE', 'UNLOCK', 'RELEASE') THEN
    /******************************************************************************************************************************************************
        USUWANIE zwraca: 1 - OK lub 0 - zabronione
        _lck('FREE', '', 'B', 'WYJ', 'wyjazdy', 60, '', '', '');
    ******************************************************************************************************************************************************/
        DELETE FROM `_locks`
        WHERE
            LockLevel = 'SESSION'
            AND Id = _Id
            AND Ctx = _Ctx
            AND TableName = IFNULL(_TableName, '')
            -- bylo AND LockSession = _IN_Session
            AND
            (
                LockSession = _IN_Session
                OR
                (_Params LIKE '%<Is_same_User>%' AND _Lck_Is_same_User_in_2_sessions(LockSession, _IN_Session))
            )
            
        ;
        SET @.LCK.RETURN = (ROW_COUNT() > 0);
        IF _Params LIKE '%<SELECT>%' OR _Params LIKE '%<MSG>%' THEN
            SET @.LCK.Result.Status = IF(@.LCK.RETURN, 'OK', 'FAIL');
        END IF;
    END IF;
    
    IF _MODE IN ('FREE.CON', 'UNLOCK.CON', 'RELEASE.CON') THEN
    /******************************************************************************************************************************************************
        USUWANIE zwraca: 1 - OK lub 0 - zabronione
        _lck('FREE.CON', '', 'B', 'WYJ', 'wyjazdy', '', '', '', '');
    ******************************************************************************************************************************************************/
        DELETE FROM `_locks`
        WHERE
            LockLevel = 'CONNECTION'
            AND Id = _Id
            AND Ctx = _Ctx
            AND TableName = IFNULL(_TableName, '')
            AND con_ID = CONNECTION_ID()
        ;
        SET @.LCK.RETURN = (ROW_COUNT() > 0);
        IF _Params LIKE '%<SELECT>%' OR _Params LIKE '%<MSG>%' THEN
            SET @.LCK.Result.Status = IF(@.LCK.RETURN, 'OK', 'FAIL');
        END IF;
    END IF;
    
    IF _MODE IN ('CHECK', 'LOCK.Exists', 'LOCK.is.FREE', 'LOCK.OTHER', 'LOCK.is.MINE', 'LOCK.is.FREE.or.MINE', 'LOCK.is.STRANGER') THEN
    /******************************************************************************************************************************************************
        jest lock ? EXISTS 0/1
            gdy jest 0    FREE
            gdy jest 1    
                czy to moja sesja ? MY_LOCK 0/1 czyli OTHER_LOCK 1/0
                    gdy jest 1    MY_LOCK = 1, OTHER_LOCK = 0 
                    gdy jest 0    MY_LOCK = 0, OTHER_LOCK = 1
                        czy sesja jest też moja własnością czyli ma mnie za usera ? MY_OTHER_LOCK 0/1
                            gdy jest 1    MINE
                            gdy jest 0    STRANGER
                        
        FREE        mogę wejśc 
        MINE        mogę wejśc 
        STRANGER    nie mogę wejśc 
        SELECT _lck('CHECK', '', '{3959DF44-D64F-4981-9AEE-7FB5804DB695}', 'EDIT', 'opng_e', '', '', '', '');
        _lck('LOCK.OTHER', '', '{3959DF44-D64F-4981-9AEE-7FB5804DB695}', 'EDIT', 'opng_e', '', '', '', '');
        _lck('LOCK.is.STRANGER', '', 'B', 'WYJ', 'wyjazdy', 60, '', '', '');
    ******************************************************************************************************************************************************/
    
            SET
            @.WE.lck.Id = _Id,
            @.WE.lck.Ctx = _Ctx,
            @.WE.lck.TableName = IFNULL(_TableName, ''),
            @X.C = @X.C + 1
            ;
    
        SET @.LCK.Current.Session = NULL, @.LCK.Current.Session.IS_ACTIVE = NULL, @.LCK.Current.Session.IS_INACTIVE = NULL, @.LCK.Current.Session.Msg = NULL;
        SELECT
            lck.LockSession, NOT lck.IS_INACTIVE, lck.Msg
        INTO 
            @.LCK.Current.Session, @.LCK.Current.Session.IS_ACTIVE, @.LCK.Current.Session.Msg
        FROM `_locks_inf` lck
        WHERE
            lck.Id = _Id
            AND lck.Ctx = _Ctx
            AND lck.TableName = IFNULL(_TableName, '')
        LIMIT 1
        ;
        /*
        SET
            @.LCK.Current.Session = 
            (
                SELECT
                    lck.LockSession
                FROM `_locks` lck
                WHERE
                    lck.Id = _Id
                    AND lck.Ctx = _Ctx
                    AND lck.TableName = IFNULL(_TableName, '')
            )
        ;    --    zwróci NULL lub sesję
        */
        SET @.LCK.EXISTS = @.LCK.Current.Session IS NOT NULL;
        SET @.LCK.MY_LOCK = @.LCK.EXISTS AND (@.LCK.Current.Session = _IN_Session);
        SET @.LCK.OTHER_LOCK = @.LCK.EXISTS AND (@.LCK.Current.Session != _IN_Session);
        SET @.LCK.MY_OTHER_LOCK = @.LCK.EXISTS AND NOT @.LCK.MY_LOCK AND _Lck_Is_same_User_in_2_sessions(@.LCK.Current.Session, _IN_Session);
        SET @.LCK.STRANGER = @.LCK.EXISTS AND NOT @.LCK.MY_LOCK AND NOT @.LCK.MY_OTHER_LOCK;
        SET @.LCK.Current.Session.IS_ACTIVE = IF(@.LCK.EXISTS, @.LCK.Current.Session.IS_ACTIVE, FALSE);
        SET @.LCK.Current.Session.IS_INACTIVE = NOT @.LCK.Current.Session.IS_ACTIVE;
    --    'LOCK.Exists', 'LOCK.is.FREE', 'LOCK.is.FREE.or.MINE', 'LOCK.is.STRANGER'        
        IF _MODE  = 'LOCK.Exists' THEN
            SET @.LCK.RETURN = @.LCK.EXISTS;
        END IF;
        IF _MODE  = 'LOCK.is.FREE' THEN
        --    gdy nie ma blokady żadnej!!!
            SET 
                @.LCK.RETURN = NOT @.LCK.EXISTS
            ;
        END IF;
        IF _MODE  = 'LOCK.OTHER' THEN
        --    gdy jest jaka kolwiek blokada z inna sesja (nie moją - nawet jesli ta sesja tez ma mnie za usera)
            SET 
                @.LCK.RETURN = @.LCK.OTHER_LOCK
            ;
        END IF;
        IF _MODE  = 'LOCK.is.FREE.or.MINE' THEN
        --    gdy nie ma blokady albo gdy jest "moja"
            SET 
                @.LCK.RETURN = NOT @.LCK.EXISTS OR @.LCK.MY_LOCK OR @.LCK.MY_OTHER_LOCK
            ;
        END IF;
        IF _MODE  = 'LOCK.is.MINE' THEN
        --    blokada istnieje i jest "moja"
            SET 
                @.LCK.RETURN = @.LCK.EXISTS AND @.LCK.MY_LOCK
            ;
        END IF;
        IF _MODE  = 'LOCK.is.STRANGER' THEN
        --    gdy jest blokada obcej sesji
            SET 
                @.LCK.RETURN = @.LCK.STRANGER
            ;
        END IF;
        IF _MODE  = 'CHECK' THEN
            SET 
                @.LCK.RETURN = 1
            ;
        END IF;
        IF _Params LIKE '%<SELECT>%' OR _Params LIKE '%<MSG>%' THEN
            SET @.LCK.Result.Status = 'OK';
        END IF;
    END IF;
    
    IF _MODE IN ('Is.FREE.or.MINE', 'Is.LOCK') THEN
    /******************************************************************************************************************************************************
        Zwraca czy rekord jest "mój" tzn rekord mojej sesji lub takiej samej sesji (maja identycznego usera)
        _lck('Is.FREE.or.MINE', '', 'B', 'WYJ', 'wyjazdy', 60, '', '', '');
        _lck('Is.LOCK', '', 'B', 'WYJ', 'wyjazdy', 60, '', '', '');
    ******************************************************************************************************************************************************/
        SET
            @.LCK.Obcy.EXISTS = 
            EXISTS(
            --    select na rekord z sesja różna od mojej
                SELECT
                    lck.*
                FROM `_locks` lck
                WHERE
                    lck.Id = _Id
                    AND lck.Ctx = _Ctx
                    AND lck.TableName = IFNULL(_TableName, '')
                    AND lck.LockSession != _IN_Session  -- ... ma to byc sesja inna niz moja
                    AND NOT _Lck_Is_same_User_in_2_sessions(lck.LockSession, _IN_Session) -- ... i ma innego usera
            )
        ;
        IF _MODE  = 'Is.LOCK' THEN
            SET @.LCK.RETURN = @.LCK.Obcy.EXISTS;
        ELSE
            SET @.LCK.RETURN = NOT @.LCK.Obcy.EXISTS;
        END IF;
        IF _Params LIKE '%<SELECT>%' OR _Params LIKE '%<MSG>%' THEN
            SET @.LCK.Result.Status = IF(@.LCK.RETURN, 'OK', 'FAIL');
        END IF;
    END IF;
    
    IF _MODE IN ('INCREASE.ReleaseAt') THEN
    /******************************************************************************************************************************************************
        Zwieksza ReleaseAt o kolejny TimeOut
        Np. po APPLY w świecie E aby przedluzyc życie lock-a (ale trzeba w E robić FREE przy END/CANCEL !!!)
        Zwraca 1 gdy to jest mój rekord
        _lck('INCREASE.ReleaseAt', '', 'B', 'WYJ', 'wyjazdy', 60, '', '', '');
    ******************************************************************************************************************************************************/
        UPDATE `_locks` lck
        SET
            lck.ReleaseAt = lck.ReleaseAt + INTERVAL IFNULL(NULLIF(_TimeOut, -1), 3600) SECOND
        WHERE
            LockLevel = 'SESSION'
            AND lck.Id = _Id
            AND lck.Ctx = _Ctx
            AND lck.TableName = IFNULL(_TableName, '')
            AND
            (
                lck.LockSession = _IN_Session
                OR
                _Is_same_User_in_2_sessions(lck.LockSession, _IN_Session)
            )
        ;
        SET @.LCK.RETURN = (ROW_COUNT() > 0);
        IF _Params LIKE '%<SELECT>%' OR _Params LIKE '%<MSG>%' THEN
            SET @.LCK.Result.Status = IF(@.LCK.RETURN, 'OK', 'FAIL');
        END IF;
    END IF;

    IF _MODE IN ('DELETE.TimeOut') THEN
    /******************************************************************************************************************************************************
        Usuwa te z przekroczonym TimeOutem czyli te dla których upłynął ReleaseAt
        Zwraca 1 gdy było co usuwać
        SELECT _lck('DELETE.TimeOut', '', '', '', '', '', '', '', '');
        DO _lck('DELETE.TimeOut', '', '', '', '', '', '', '', '');
    ******************************************************************************************************************************************************/
        DELETE FROM `_locks`
        WHERE
            LockLevel != 'COUNT'
            -- jednak dla wszystkich AND LockLevel = 'SESSION'
            AND ReleaseAt <> '0000-00-00 00:00:00'
            AND ReleaseAt < NOW()
        ;
        DELETE FROM `_locks`
        WHERE
            LockLevel = 'CONNECTION'
            AND NOT _Lck_Exists_conncection(con_ID)
        ;
        SET @.LCK.RETURN = (ROW_COUNT() > 0);
        IF _Params LIKE '%<SELECT>%' OR _Params LIKE '%<MSG>%' THEN
            SET @.LCK.Result.Status = IF(@.LCK.RETURN, 'OK', 'OK');
        END IF;
    END IF;
    
    IF _MODE IN ('DELETE') THEN
    /******************************************************************************************************************************************************
        Usuwa bez gadania
        Zwraca 1 gdy było co usuwać
        _lck('DELETE', '', 'B', 'WYJ', 'wyjazdy', 60, '', '', '');
    ******************************************************************************************************************************************************/
        DELETE FROM `_locks`
        WHERE
            LockLevel != 'COUNT'
            AND Id = _Id
            AND Ctx = _Ctx
            AND TableName = IFNULL(_TableName, '')
        ;
        SET @.LCK.RETURN = (ROW_COUNT() > 0);
        IF _Params LIKE '%<SELECT>%' OR _Params LIKE '%<MSG>%' THEN
            SET @.LCK.Result.Status = IF(@.LCK.RETURN, 'OK', 'OK');
        END IF;
    END IF;
    
    IF _MODE IN ('Check.and.Update.LockSession') THEN
    /******************************************************************************************************************************************************
        Sprawdza czy aktywna jest sesja blokady i woła dla niej check bo może juz czas na CLose.TimeOUT
        Zwraca 1 gdy było co usuwać
        _lck('Check.and.Update.LockSession', '', 'B', 'WYJ', 'wyjazdy', 60, '', '', '');
    ******************************************************************************************************************************************************/
        DELETE lck FROM `_locks` lck
        WHERE
            lck.LockLevel != 'COUNT'
            AND lck.Id = _Id
            AND lck.Ctx = _Ctx
            AND lck.TableName = IFNULL(_TableName, '')
        --    zapamietaj technicznie sesję    
            AND lck.LockSession = IFNULL((@.lck.LockSession := lck.LockSession), '')
        --    gdy czas minął
            AND lck.ReleaseAt <> '0000-00-00 00:00:00'
            AND lck.ReleaseAt < NOW()
        ;
        SET @.LCK.RETURN = (ROW_COUNT() > 0);
    --    Jeśli usunieto blokadę to @.LCK.RETURN > 0 i juz nic nie robimy ale zawyczaj <= 0 i wtedy
    --    trzeba wywołac 'CHECK.STATE' dla zesji blokady bo być może bedzie CLOSE.TimeOut ale
    --    UWAGA w triggerze `_sys_db_logons` gdy CLOSE... tio usuwane są blokady dla takiej sesji zatem po CHECK.STATE wychodzimy
        IF NOT @.LCK.RETURN AND @.lck.LockSession IS NOT NULL THEN
            UPDATE `_sys_db_logons` sdl
            SET 
                sdl.do_info = 'CHECK.STATE' 
            WHERE 
                sdl.GUID = @.lck.LockSession
                AND NOT sdl.INACTIVE    -- dla aktywnych jeszcze sesji
            ;
        END IF;
    END IF;
    
    RETURN @.LCK.RETURN;
END $$

DROP PROCEDURE IF EXISTS `_lcks`$$
CREATE DEFINER=`root`@`localhost` PROCEDURE `_lcks`(
    _MODE VARCHAR(250), 
    INOUT _Result BOOLEAN,
    _IN_Session VARCHAR(250),
    _Id VARCHAR(250),
    _Ctx VARCHAR(250),
    _TableName VARCHAR(150),
    _TimeOut INTEGER,    -- w sekundach
    _LockProc VARCHAR(200),
    _Wait INTEGER,    -- w sekundach
    _Params VARCHAR(200)
    ) 
/*
    NAJNOWSZA V 2021 
    NIE!!! MOŻE BYC uzyta w Triggerach!!!!!!!!!
    Za to ma Wait w LOCK czyli oczekiwanie na zwolnienie 
        CALL _lcks('LOCK', @LR, '', 'X1', 'opng_e', '', 180, '', 20, ''); czyli czekaj 20s na zwolnienie
*/    
BEGIN
    
    SET
        _IN_Session = IFNULL(NULLIF(_IN_Session, ''), @SYSTEM.Client.Session.GUID),
        _TimeOut = IFNULL(_TimeOut, -1),
        _LockProc = NULLIF(_LockProc, ''),
        _Wait = IFNULL(_Wait, 0)
    ;    -- set @._Wait = _Wait;
    
    IF _MODE LIKE 'GET%' OR _MODE LIKE 'LOCK%' OR _MODE LIKE 'Is.FREE%' OR _MODE LIKE 'CHECK%' THEN
        SET 
            @.Wait.SLEEP = 1,
            @.Wait.Start = @@TimeStamp, 
            -- @.Repeat.Until = NOW() + INTERVAL _Wait SECOND, 
            @.Wait.trial = 0
        ;
        L_GET: LOOP
        /******************************************************************************************************************************************************
            BLOKOWANIE zwraca: 1 - OK lub 0 - zabronione
            _lck('LOCK', '', 'B', 'WYJ', 'wyjazdy', 60, '', '', '');
        ******************************************************************************************************************************************************/
            SET @.Wait.trial = @.Wait.trial + 1;
        --    1,2,..n-ta próba pobrania blokady    
            SET @.LCK.Result = _lck(_MODE, _IN_Session, _Id, _Ctx, _TableName, _TimeOut, _LockProc, '', _Params);
        --    Gdy niepowodzenie sprawdz czy zamek nie przekroczyl czasu albo czy sesja do zamka nie jest juz martwa
        --    ale tylko 1 raz
            IF NOT @.LCK.Result AND @.Wait.trial = 1 THEN
                DO _lck('Check.and.Update.LockSession', '', _Id, _Ctx, _TableName, _TimeOut, _LockProc, '', _Params);
            --    1 tylko próba pobrania blokady    
                SET @.LCK.Result = _lck(_MODE, _IN_Session, _Id, _Ctx, _TableName, _TimeOut, _LockProc, '', _Params);
            END IF;
        --    Gdy niepowodzenie i Wait > 0 to poczekaj i spróbuj ponownie
            IF NOT @.LCK.Result AND _Wait > 0 THEN
                SET @.Wait.Next = @@TimeStamp;
                SET @.Wait.Passed = (@.Wait.Next - @.Wait.Start);
            --    Gdy jeszcze jet przed czasem to kontynuuj ale gdy czas przekroczono to żegnamy
                IF ROUND(@.Wait.Passed) + @.Wait.SLEEP <= _Wait THEN
                --    poczekaj ...
                    DO SLEEP(@.Wait.SLEEP);
                --    wróć w petli do kolejnych prób
                    ITERATE L_GET;
                END IF;
            END IF;
        --    Zwracam:
        --    1 (TRUE) gdy OK oraz     
        --    0 (FALSE) gdy ERR
            SET _Result = @.LCK.Result;
            LEAVE L_GET;
        END LOOP L_GET;
        
        IF _Params LIKE '%<SELECT>%' OR _Params LIKE '%<MSG>%' THEN
            IF NOT @.LCK.Result OR _MODE = 'CHECK' THEN
                SET 
                    @.LCK.Result.Msg := (
                        SELECT 
                            lck.Msg 
                        FROM `_locks_inf` lck    -- uwaga `_locks_inf` to VIEW !!!!
                        WHERE
                            LockLevel != 'COUNT'
                            AND lck.Id = _Id
                            AND lck.Ctx = _Ctx
                            AND lck.TableName = IFNULL(_TableName, '')
                    ),
                    @.LCK.Result.ReadOnly := _Params LIKE '%<ReadOnly>%'
                ;
            END IF;
            SET 
                @.LCK.Result.Status := IF(@.LCK.Result, 'OK', IF(_Wait > 0, 'LOCK.TimeOut', 'LOCK')),
                @.LCK.Result.Msg.MsgDlg := IF(@.LCK.Result, NULL, IF(@.LCK.Result.ReadOnly, 'Warning', 'Error')),    --    do komunikatów 'Warning'/'Error'
                @.LCK.Result.Msg.Effect := IF(@.LCK.Result, NULL, IF(@.LCK.Result.ReadOnly, 'ReadOnly', 'Stop'))        -- 'ReadOnly'/NULL, NULL ozn.'Stop'
            ;
        END IF;
    ELSE    
    --    Może byc wołana z EVENTu CLEAR.from.EVENT !!!
        IF _MODE = 'CLEAR.from.EVENT' THEN
        --     Czyści zaległe w EVENTs co 1 min
        --    CALL _lcks('CLEAR.from.EVENT', @LR, '', '', '', '', '', '', '', '');
        --    Najpierw zaraportuj
            INSERT INTO `_db_app_errors`
            (`Session`, `Process`, `Type`, `Statement`, `Err` , `Err_MSG`)
            SELECT
                l.LockSession AS `Session`,
                CONCAT('LOCK TimeOut: "', l.Ctx, '"') AS `Process`, 
                'LOCK TimeOut' AS `Type`, 
                CONCAT('ID/CTX/Table: ', CONCAT_WS(' / ', l.ID, l.Ctx, NULLIF(l.TableName, ''))) AS `Statement`, 
                NULL AS Err, 
                CONCAT_WS('\n', 
                    CONCAT('Bloka z dnia ', l.LockAt, _DisplayDurationH('D/H/M/S', l.TimeOut/3600, ' na czas', '')), 
                    _DisplayDurationH('D/H/M/S', TIMESTAMPDIFF(SECOND, l.LockAt, CURRENT_TIMESTAMP())/3600, 'Czas trwania:', ''),
                    _DisplayDurationH('D/H/M/S', TIMESTAMPDIFF(SECOND, l.ReleaseAt, CURRENT_TIMESTAMP())/3600, 'Przekroczono o:', ''),
                    CONCAT('Połączenie nr: ', l.Con_ID),
                    NULL
                ) AS Err_MSG
                -- ,l.*
            FROM 
            (
                SELECT
                    l1.*
                FROM `_locks` l1
                WHERE
                    l1.LockLevel != 'COUNT'
                    AND l1.ReleaseAt <> '0000-00-00 00:00:00'
                    AND l1.ReleaseAt < NOW()
                UNION
                SELECT
                    l2.*
                FROM `_locks` l2
                WHERE
                    l2.LockLevel = 'CONNECTION'
                    AND NOT _Lck_Exists_conncection(l2.con_ID)
            ) l    
            ;
        --    Teraz USUN czyli de facto wołamy DO _lck('DELETE.TimeOut', '', '', '', '', '', '', '', ''); 
        --    to tak jabysmy wołali  ...
        --    DO _lck('DELETE.TimeOut', '', '', '', '', '', '', '', '');
            SET _MODE = 'DELETE.TimeOut';
        END IF;
    --    Teraz juz tylko inne _MODE-y - zatem obsluguje je za pomocą funkcji
        SET @.LCK.Result = _lck(_MODE, _IN_Session, _Id, _Ctx, _TableName, _TimeOut, _LockProc, '', _Params);
        SET _Result = @.LCK.Result;
    END IF;
    
--    Chemy select - proszę bardzo...
    IF _Params LIKE '%<SELECT>%' THEN
        SELECT 
            @.LCK.Result.Status AS Status, 
            @.LCK.Result.Msg AS Msg
        ;
    END IF;
END $$


/* ************************************************************************************************************************************************************************
    I jeszcze VIEW na `_locks` -> view nazywa się `_locks_inf`  a nie v_xxx !!!!!!!!!
************************************************************************************************************************************************************************ */    
CREATE OR REPLACE DEFINER=`root`@`localhost` VIEW `_locks_inf` AS
    SELECT
        -- _DisplayDurationH('D/H/M/S', TIMESTAMPDIFF(SECOND, l.LockAt, CURRENT_TIMESTAMP())/3600, 'od', '') AS Kiedy,
        -- _VarStr(IF(TIMESTAMPDIFF(HOUR, l.LockAt, CURRENT_TIMESTAMP()) > 1, CONCAT(TIMESTAMPDIFF(HOUR, l.LockAt, CURRENT_TIMESTAMP()), ' godz. (', TIMESTAMPDIFF(MINUTE, l.LockAt, CURRENT_TIMESTAMP()), ' min.)'), CONCAT(TIMESTAMPDIFF(MINUTE, l.LockAt, CURRENT_TIMESTAMP()), ' min.'))) AS Kiedy,
        -- l.ReleaseAt,
        CONCAT_WS('\n',
            CONCAT(
                IF(l.LockProc = 'EDIT', 'Rekord/dokument używany przez: ', IF(l.LockProc = 'PROC', 'Proces zajęty przez: ',  'Rekord/dokument używany przez: ')),
                _VarStr(o.Imie1), ' ', _VarStr(o.Nazwisko)
            ),
            CONCAT(
                'Blokada założona: ', _Format_DT(l.LockAt, '%dd %d %M %YC[DW][DZ]'), ' o ', _Format_DT(l.LockAt, '%HH:%NN:%SS,')
            ),
            -- _DisplayDurationH('D/H/M', TIMESTAMPDIFF(SECOND, l.LockAt, CURRENT_TIMESTAMP())/3600, 'Trwa ponad ', ''),
            _DisplayDurationH('D/H/M/S', TIMESTAMPDIFF(SECOND, l.LockAt, CURRENT_TIMESTAMP())/3600, 'trwa od', ''),
            CONCAT(
                _VarStr('Połączenie nr: '), _VarStr(l.Con_ID), _VarStr(' (prób przejęcia: '), _VarStr(l.Number_of_Trials), ')', ', sesja nr: "', _VarStr(l.LockSession), '"'
            ),
            IF((NOW() > l.ReleaseAt) AND l.ReleaseAt <> '0000-00-00 00:00:00',
                CONCAT_WS('\n',
                -- '********************************************************',
                'UWAGA', 'SESJA JUŻ NIEAKTYWNA!', 'skontaktuj się z administratorem w celu', 'zdjęcia blokady!!!',
                -- '********************************************************',
                NULL
                ),
                NULL
            ),
            NULL
                -- #LockTime#\nTrwa ponad #Trwa#\nSesja nr: #LockSession#\n#Msg#',
        ) AS Msg,
        -- l.LockSession,
        (NOW() > l.ReleaseAt) AND l.ReleaseAt <> '0000-00-00 00:00:00' AS IS_Time_OUT,
        -- (IFNULL(ss.INACTIVE, 1) = 0 AND TIMESTAMPDIFF(HOUR, l.LockAt, CURRENT_TIMESTAMP()) > 12) IS_Time_OUT,
        (IFNULL(ss.INACTIVE, 1) = 1) IS_INACTIVE,
        l.*,
        ss.log_USER_GUID
    FROM `_locks` l
    LEFT JOIN `_sys_db_logons` ss ON ss.GUID = l.LockSession
    LEFT JOIN `osoby` o ON o.GUID = ss.log_USER_GUID
;
$$


DELIMITER ;

/*
====================================================================================================================================================================================
    NOTATNIK
====================================================================================================================================================================================

CREATE TABLE `_locks` (
  `LockLevel` VARCHAR(150) DEFAULT 'SESSION' COMMENT '"SESSION"/"CONNECTION" dwa tryby blokady na poziomie tylko sesji (jedna trwa caly czas ĹĽycia sesji lub poĹ‚aczenia gdy ĹĽyje poĹ‚Ä…czenie',
  `Id` VARCHAR(250) NOT NULL DEFAULT '' COMMENT 'ID rekordu',
  `Ctx` VARCHAR(150) NOT NULL DEFAULT '' COMMENT 'kontekst przetwarzania',
  `TableName` VARCHAR(100) NOT NULL DEFAULT '' COMMENT 'ewntualna Tabela - gdy NULL nie ma znaczenia w unikalnosci rekordu',
  `LockAt` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'kiedy zablokowano',
  `TimeOut` INT(11) NOT NULL DEFAULT '-1' COMMENT 'czas w sec. ile ma twa blokada -1 to wiecznosc',
  `ReleaseAt` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT 'kiedy zablokowano',
  `LockSession` VARCHAR(38) NOT NULL DEFAULT '' COMMENT 'id sesji blokujacej',
  `con_ID` INT(11) DEFAULT NULL COMMENT 'ID poloczenia DB',
  `LockProc` VARCHAR(150) NOT NULL DEFAULT 'PROC' COMMENT 'co sie dzieje podczas blokady np. "PROC"/"EDIT"/etc',
  `Number_of_Trials` INT(11) NOT NULL DEFAULT '0' COMMENT 'Liczba prob',
  `LastTime` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE CURRENT_TIMESTAMP COMMENT 'ostatnia zmiana',
  PRIMARY KEY (`Id`,`Ctx`,`TableName`),
  KEY `LockAt` (`LockAt`),
  KEY `ReleaseAt` (`ReleaseAt`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
;
ALTER TABLE `_locks`   
  CHANGE `Id` `Id` VARCHAR(150) NOT NULL DEFAULT '' COMMENT 'ID rekordu',
  CHANGE `Ctx` `Ctx` VARCHAR(100) NOT NULL DEFAULT '' COMMENT 'kontekst przetwarzania',
  CHANGE `TableName` `TableName` VARCHAR(50) NOT NULL DEFAULT '' COMMENT 'ewentualna Tabela - gdy NULL nie ma znaczenia w unikalnosci rekordu'
;

CREATE TABLE `_counters` (
  `Id` VARCHAR(250) NOT NULL DEFAULT '' COMMENT 'ID licznika',
  `Counter` INT(11) DEFAULT NULL COMMENT 'licznik',
  `Last_Time` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE CURRENT_TIMESTAMP COMMENT 'ostatnia zmiana',
  `Last_Session` VARCHAR(38) NOT NULL DEFAULT '' COMMENT 'id sesji',
  `Last_con_ID` INT(11) DEFAULT NULL COMMENT 'ID poloczenia DB',
  PRIMARY KEY (`Id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
;

ALTER TABLE `_locks`   
  CHANGE `Ctx` `Ctx` VARCHAR(150) CHARSET utf8 COLLATE utf8_general_ci DEFAULT '' NOT NULL COMMENT 'kontekst przetwarzania',
  CHANGE `TableName` `TableName` VARCHAR(100) CHARSET utf8 COLLATE utf8_general_ci DEFAULT '' NOT NULL COMMENT 'ewntualna Tabela - gdy NULL nie ma znaczenia w unikalnosci rekordu'
;
*/
