use AnP

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

    if object_id(N'dbo.Exceptions', N'U') is not null drop table dbo.Exceptions
    if object_id(N'dbo.Logs', N'U') is not null drop table dbo.Logs
    if object_id(N'dbo.Errors', N'U') is not null drop table dbo.Errors
    if object_id(N'dbo.Data', N'U') is not null drop table dbo.Data

end
go

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

    if object_id(N'dbo.Data', N'U') is null create table dbo.[Data](
        id integer not null identity(1, 1), 
        [hash] varbinary(32) not null, 
        [data] varchar(max) not null, 
        date_in datetime not null constraint df_parameters_date_in default getdate(), 
        date_out datetime, 
        constraint pk_parameters primary key clustered(id), 
        constraint uq_parameters_name unique (name asc) with (fillfactor = 90), 
        constraint uq_parameters_hash unique (hash asc) with (fillfactor = 90),
        constraint ck_parameters_name check ([name] not like '%[^a-zA-Z0-9_]%' and [name] like '[a-zA-Z_]%')
    )

    if object_id(N'dbo.Errors', N'U') is null create table dbo.Errors(
        id integer not null identity(1, 1), 
        [error] varchar(max) not null, 
        date_in datetime not null constraint df_errors_date_in default getdate(), 
        date_out datetime, 
        constraint pk_errors primary key clustered(id), 
        constraint uq_errors_name unique (name asc) with (fillfactor = 90), 
        constraint ck_errors_name check ([name] not like '%[^a-zA-Z0-9_]%' and [name] like '[a-zA-Z_]%')
    )
    
    if object_id(N'dbo.Logs', N'U') is null create table dbo.Logs(
        id integer not null identity(1, 1), 
        [session] integer not null, 
        [procedure] integer not null, 
        parameters integer not null, 
        error integer not null, 
        date_in datetime not null constraint df_logs_date_in default getdate(), 
        date_out datetime, 
        constraint pk_logs primary key clustered(id), 
        constraint fk_logs_session foreign key([session]) references dbo.Sessions(id) 
            on update no action 
            on delete no action, 
        constraint fk_logs_procedure foreign key([procedure]) references dbo.Procedures(id) 
            on update no action 
            on delete no action, 
        constraint fk_logs_parameters foreign key(parameters) references dbo.[Data](id) 
            on update no action 
            on delete no action, 
        constraint fk_logs_errors foreign key(errors) references dbo.Errors(id) 
            on update no action 
            on delete no action
    )

    if object_id(N'dbo.Exceptions', N'U') is null create table dbo.Exceptions(
        id integer not null identity(1, 1), 
        [session] integer not null, 
        [procedure] integer not null, 
        parameters integer not null, 
        error integer not null, 
        [message] integer not null, 
        [status] varchar(16) not null, 
        code integer not null, 
        date_in datetime not null constraint df_exceptions_date_in default getdate(), 
        date_out datetime, 
        constraint pk_exceptions primary key clustered(id), 
        constraint fk_exceptions_session foreign key([session]) references dbo.Sessions(id) 
            on update no action 
            on delete no action, 
        constraint fk_exceptions_procedure foreign key([procedure]) references dbo.Procedures(id) 
            on update no action 
            on delete no action, 
        constraint fk_exceptions_parameters foreign key(parameters) references dbo.[Data](id) 
            on update no action 
            on delete no action, 
        constraint fk_logs_errors foreign key(errors) references dbo.Errors(id) 
            on update no action 
            on delete no action, 
        constraint fk_logs_message foreign key([message]) references dbo.[Data](id) 
            on update no action 
            on delete no action
    )

end
go

if object_id(N'dbo.data_get_id', N'P') is not null drop procedure dbo.data_get_id
go
create procedure dbo.data_get_id (
    @data varchar(max), 
    @id integer output
) as begin set nocount on 

    declare @hash varbinary(64) = hashbytes(dbo.get_settings('data_hash_algorithm', 'SHA2_512'), @data)
    
    set @id = (select top 1 id from dbo.[Data] where date_out is null and [hash] = @hash)

    if @id is null begin 
        insert into dbo.[Data] ([hash], [data]) values (@hash, @data)
        set @id = scope_identity()
    end

end
go

if object_id(N'dbo.error_get_id', N'P') is not null drop procedure dbo.error_get_id
go
create procedure dbo.error_get_id (
    @error varchar(max), 
    @id integer output
) as begin set nocount on 
    
    set @id = (select top 1 id from dbo.Errors where date_out is null and error = @error)

    if @id is null begin 
        insert into dbo.Errors (error) values (@error)
        set @id = scope_identity()
    end

end
go

if object_id(N'dbo.exceptions_set', N'P') is not null drop procedure dbo.exceptions_set
go
create procedure dbo.exceptions_set(
    @procedure integer, 
    @session integer, 
    @parameters varchar(max),
    @error varchar(512)
) as begin set nocount on 
    
    declare @paramaters_id integer
    declare @error_id integer
    declare @message varchar(max) = error_message()
    declare @state varchar(16) = error_state()
    declare @code integer = error_number()
    declare @message_id integer

    if @parameters is null set @parameters = ''
    if @error is null set @error = ''

    execute dbo.data_get_id @parameters, @paramaters_id output
    execute dbo.error_get_id @error, @error_id output
    execute dbo.data_get_id @message, @message_id output

    insert into dbo.Exceptions([procedure], [session], [parameters], [error], [message], [state], [code]) values 
        (@procedure, @session, @paramaters_id, @error_id, @message_id, @state, @code)

end
go

if object_id(N'dbo.logs_set', N'P') is not null drop procedure dbo.logs_set
go
create procedure dbo.logs_set(
    @procedure integer, 
    @session integer, 
    @parameters varchar(max),
    @error varchar(512), 
    @level integer = 1
) as begin set nocount on 

    declare @levels integer = dbo.settings_get_int('logs_levels', 15)

    if @levels & @level != 0 begin 
        
        declare @paramaters_id integer
        declare @error_id integer

        if @parameters is null set @parameters = ''
        if @error is null set @error = ''

        execute dbo.data_get_id @parameters, @paramaters_id output
        execute dbo.error_get_id @error, @error_id output

        insert into dbo.Logs([procedure], [session], [parameters], error) values 
            (@procedure, @session, @paramaters_id, @error_id)

    end

end
go