use AnP

if object_id(N'dbo.users_tables_remove', N'P') is not null drop procedure dbo.users_tables_remove
go
create procedure dbo.users_tables_remove as begin set nocount on 
    if object_id(N'dbo._Sessions', N'U') is not null drop table dbo._Sessions
    if object_id(N'dbo.SessionsActived', N'U') is not null drop table dbo.SessionsActived
    if object_id(N'dbo.Sessions', N'U') is not null drop table dbo.Sessions
end
go

if object_id(N'dbo.users_tables_create', N'P') is not null drop procedure dbo.users_tables_create
go
create procedure dbo.users_tables_create as begin set nocount on 
    
    if object_id(N'dbo.SessionsActived', N'U') is null create table dbo.SessionsActived(
        id integer not null identity(1, 1), 
        [procedure] integer not null, 
        [user] integer not null, 
        [ip] integer not null, 
        date_last datetime not null constraint df_sessions_actived_date_last default getdate(),
        date_in datetime not null constraint df_sessions_actived_date_in default getdate(), 
        [timeout] integer not null constraint df_sessions_actived_timeout default 300,
        constraint pk_sessions_actived primary key clustered(id), 
        constraint fk_sessions_actived_procedure foreign key([procedure]) references dbo.Procedures(id) 
            on update no action 
            on delete no action, 
        constraint fk_sessions_actived_user foreign key([user]) references dbo.Users(id) 
            on update no action 
            on delete no action, 
        constraint fk_sessions_actived_ip foreign key([ip]) references dbo.Ips(id) 
            on update no action 
            on delete no action
    )

    if object_id(N'dbo.Sessions', N'U') is null create table dbo.Sessions(
        id integer not null, 
        [procedure] integer not null, 
        [user] integer not null, 
        [ip] integer not null, 
        date_last datetime not null constraint df_sessions_date_last default getdate(),
        date_in datetime not null constraint df_sessions_date_in default getdate(), 
        date_out datetime, 
        constraint pk_sessions primary key clustered(id), 
        constraint fk_sessions_procedure foreign key([procedure]) references dbo.Procedures(id) 
            on update no action 
            on delete no action, 
        constraint fk_sessions_user foreign key([user]) references dbo.Users(id) 
            on update no action 
            on delete no action, 
        constraint fk_sessions_ip foreign key([ip]) references dbo.Ips(id) 
            on update no action 
            on delete no action
    )
    
    if object_id(N'dbo._Sessions', N'U') is null create table dbo._Sessions(
        id integer not null identity(1, 1), 
        history_mode integer not null,
        entity integer not null, 
        [procedure] integer not null,
        [user] integer not null, 
        [ip] integer not null, 
        date_last datetime not null,
        date_in datetime not null, 
        date_out datetime, 
        history_date datetime2 not null constraint df__sessions_history_date default sysutcdatetime(), 
        constraint pk__sessions primary key clustered(id), 
        constraint fk__sessions_history_mode foreign key(history_mode) references dbo.HistoryModes(id) 
            on update no action 
            on delete no action,
        constraint fk__sessions_procedure foreign key([procedure]) references dbo.Procedures(id) 
            on update no action 
            on delete no action, 
        constraint fk__sessions_user foreign key([user]) references dbo.Users(id) 
            on update no action 
            on delete no action, 
        constraint fk__sessions_ip foreign key([ip]) references dbo.Ips(id) 
            on update no action 
            on delete no action
    )

end
go

if object_id(N'dbo.users_tables_upgrade', N'P') is not null drop procedure dbo.users_tables_upgrade
go
create procedure dbo.users_tables_upgrade as begin set nocount on 
    execute dbo.users_tables_upgrade
    execute dbo.ips_tables_upgrade
end
go

execute dbo.users_tables_create

if object_id(N'dbo.sessions_actived_trigger', N'TR') is not null drop trigger dbo.sessions_actived_trigger
go
create trigger dbo.sessions_actived_trigger on dbo.SessionsActived after insert, update, delete as begin set nocount on 

    -- Update.
    update sessions set 
        sessions.[procedure] = inserted.[procedure],
        sessions.[user] = inserted.[user],
        sessions.[ip] = inserted.[ip],
        sessions.date_last = inserted.date_last,
        sessions.date_in = inserted.date_in
    from inserted inserted
    join dbo.Sessions sessions on inserted.id = sessions.id 
    join deleted deleted on inserted.id = deleted.id 

    -- Insert.
    insert into dbo.Sessions(id, [procedure], [user], [ip], date_last, date_in) 
    select inserted.id, inserted.[procedure], inserted.[user], inserted.[ip], inserted.date_last, inserted.date_in 
    from inserted inserted 
    left join dbo.Sessions sessions on inserted.id = sessions.id 
    left join deleted deleted on inserted.id = deleted.id 
    where sessions.id is null and deleted.id is null

    -- Delete.
    update sessions set 
        sessions.[procedure] = deleted.[procedure],
        sessions.[user] = deleted.[user],
        sessions.[ip] = deleted.[ip],
        sessions.date_last = deleted.date_last,
        sessions.date_in = deleted.date_in, 
        sessions.date_out = getdate()
    from deleted deleted
    join dbo.Sessions sessions on deleted.id = sessions.id 
    left join inserted inserted on deleted.id = inserted.id 
    where inserted.id is null

    -- Delete unexistent.
    insert into dbo.Sessions(id, [procedure], [user], [ip], date_last, date_in, date_out) 
    select deleted.id, deleted.[procedure], deleted.[user], deleted.[ip], deleted.date_last, deleted.date_in, getdate() 
    from deleted deleted 
    left join dbo.Sessions sessions on deleted.id = sessions.id 
    left join inserted inserted on deleted.id = inserted.id 
    where sessions.id is null and inserted.id is null

end
go

if object_id(N'dbo.sessions_trigger', N'TR') is not null drop trigger dbo.sessions_trigger
go
create trigger dbo.sessions_trigger on dbo.Sessions after insert, update, delete as begin set nocount on 

    declare @insert integer, @update integer, @delete integer, @unknown integer

    execute dbo.history_mode_get @insert output, @update output, @delete output, @unknown output

    -- Update.
    insert into dbo._Sessions(history_mode, entity, [procedure], [user], [ip], date_last, date_in, date_out) 
    select @update, inserted.id, inserted.[procedure], inserted.[user], inserted.[ip], inserted.date_last, inserted.date_in, null 
    from inserted inserted 
    join deleted deleted on inserted.id = deleted.id 
    where (
        inserted.[procedure] != deleted.[procedure] or 
        inserted.[user] != deleted.[user] or 
        inserted.[ip] != deleted.[ip] or 
        inserted.date_last != deleted.date_last or 
        inserted.date_in != deleted.date_in or 
        isnull(inserted.date_out, '1753-01-01') != isnull(deleted.date_out, '1753-01-01')
    ) and not (
        inserted.[procedure] = deleted.[procedure] and 
        inserted.[user] = deleted.[user] and 
        inserted.[ip] = deleted.[ip] and 
        inserted.date_last != deleted.date_last and 
        inserted.date_in = deleted.date_in and 
        isnull(inserted.date_out, '1753-01-01') = isnull(deleted.date_out, '1753-01-01')
    )

    -- Insert.
    insert into dbo._Sessions(history_mode, entity, [procedure], [user], [ip], date_last, date_in, date_out) 
    select @insert, inserted.id, inserted.[procedure], inserted.[user], inserted.[ip], inserted.date_last, inserted.date_in, null 
    from inserted inserted 
    left join deleted deleted on inserted.id = deleted.id 
    where deleted.id is null

    -- Delete.
    insert into dbo._Sessions(history_mode, entity, [procedure], [user], [ip], date_last, date_in, date_out) 
    select @delete, deleted.id, deleted.[procedure], deleted.[user], deleted.[ip], deleted.date_last, deleted.date_in, getdate()
    from deleted deleted 
    left join inserted inserted on deleted.id = inserted.id 
    where inserted.id is null

end
go

if object_id(N'dbo.machine_session_get', N'FN') is not null drop function dbo.machine_session_get
go
create function machine_session_get() returns integer begin 
    return (
        select top 1 sessions.id 
        from dbo.Sessions sessions 
        join dbo.Users users on users.id = sessions.[user] 
        where 
            sessions.date_out is null and 
            users.date_out is null and 
            users.nick = 'machine'
    )
end
go