-- --------------------------------------------------------------------------------
-- _090_attribute Group Routines
-- --------------------------------------------------------------------------------
DELIMITER $$$


create function __attribute_ValidateId(attribute_id_ int unsigned) returns int unsigned
deterministic
begin
    declare attribute_id int unsigned;
    
    select a.id into attribute_id
    from attributeT a
    join tagT t on a.id = t.id
    join nodeT n on a.id = n.id
    join nodeTypeT nt on n.nodeTypeId = nt.id
    where a.id = attribute_id_
        and nt.internalTypeId = 'attribute';
    
    if attribute_id is null then 
        call raiseError1('attribute', 'validate', attribute_id_, 'attribute does not exist');
    end if;

    return 1;
end$$$


create function __attribute_ValidateIdDataType(
    attribute_id_ int unsigned, 
    type_name_ varchar(10) binary
) returns int unsigned
deterministic
begin
    set @1 = __attribute_ValidateId(attribute_id_);
    if (select a.dataTypeId from attributeT a where a.id = attribute_id_) != type_name_ then
        call raiseError2('attribute', 'typecheck', attribute_id_, type_name_, 'attribute mismatching type');
    end if;
    
    return 1;
end$$$


create function attribute_GetByCode(code_ varchar(30) binary) returns int unsigned
deterministic
return (select a.id from attributeT a where a.code = code_)$$$


create function __attribute_ClampValueNumeric(attribute_id_ int unsigned, value_ decimal(22, 6)) returns decimal(22, 6)
deterministic
begin
    declare res decimal(22, 6);
    
    select round(
                greatest(
                    least(
                        value_, 
                        coalesce(a.decimalMax, value_)), 
                    coalesce(a.decimalMin, value_)), 
                a.decimalRound)
    into res
    from attributeNumericT a
    where a.id = attribute_id_;
    
    return res;
end$$$


create function __attribute_ClampValueDatetime(attribute_id_ int unsigned, value_ datetime) returns datetime
deterministic
begin
    declare res datetime;
    
    select cast(date_format(value_, adf.storageFormat) as datetime)
    into res
    from attributeDatetimeT a
    join attributeDatetimeFormatT adf on a.datetimeStorageId = adf.id
    where a.id = attribute_id_;
    
    return res;
end$$$


create function __attribute_UpdateCache(attribute_id_ int unsigned) returns int unsigned
not deterministic modifies sql data
begin
    insert attributeCacheT(id, attributeNames, attributeNamesSearch)
    select 
        al.id, 
        group_concat(concat_ws(0xb, al.languageId, al.uofmPre, al.uofmPost) separator '\0'),
        group_concat(concat_ws(0xb, al.languageId, string_Normalize(al.uofmPre), string_Normalize(al.uofmPost)) separator '\0')
    from attributeTL al
    where al.id = coalesce(attribute_id_, al.id)
    group by al.id
    on duplicate key update
        attributeCacheT.attributeNames = values(attributeNames),
        attributeCacheT.attributeNamesSearch = values(attributeNamesSearch);
    
    return attribute_id_;
end$$$


create function __attribute_Create(
    type_id_ int unsigned,
    type_code_ varchar(30) binary,
    name_ varchar(255),
    sorter_ int,
    can_search_ enum('n', 'y'),
    display_in_object_ enum('n', 'y'),
    display_in_object_list_ enum('n', 'y'),
    factor_ double,
    enabled_ enum('n', 'y'),
    data_type_id_ varchar(10) binary,
    code_ varchar(30) binary,
    required_ enum('n', 'y'),
    sortable_ enum('n', 'y'),
    uofm_pre_ varchar(255),
    uofm_post_ varchar(255)
) returns int unsigned
not deterministic modifies sql data
begin
    declare attribute_id int unsigned;
    
    set attribute_id = __tag_Create(type_id_, type_code_, 'attribute', name_, sorter_, can_search_, display_in_object_, display_in_object_list_, factor_, null, enabled_);
    
    insert attributeT(id, dataTypeId, code, required, sortable)
    values(attribute_id, data_type_id_, code_, required_, sortable_);
        
    insert attributeTL(id, languageId, uofmPre, uofmPost)
    select attribute_id, l.id, coalesce(uofm_pre_, ''), coalesce(uofm_post_, '')
    from languageT l;
    
    set @1 = __attribute_UpdateCache(attribute_id);
    
    return attribute_id;
end$$$


create function __attribute_SetNumeric(
    attribute_id_ int unsigned,
    decimal_min_ decimal(22, 6), reset_decimal_min_ enum('n', 'y'),
    decimal_max_ decimal(22, 6), reset_decimal_max_ enum('n', 'y'),
    decimal_round_ int
) returns int unsigned
not deterministic modifies sql data
begin
    insert attributeNumericT(
        id, 
        decimalMin, 
        decimalMax, 
        decimalRound)
    values(
        attribute_id_,
        decimal_min_,
        decimal_max_,
        decimal_round_)
    on duplicate key update
        attributeNumericT.decimalMin = if(reset_decimal_min_ = 'y', null, coalesce(decimal_min_, attributeNumericT.decimalMin)),
        attributeNumericT.decimalMax = if(reset_decimal_max_ = 'y', null, coalesce(decimal_max_, attributeNumericT.decimalMax)),
        attributeNumericT.decimalRound = coalesce(decimal_round_, attributeNumericT.decimalRound);

    return attribute_id_;
end$$$


create function __attribute_SetDatetime(
    attribute_id_ int unsigned,
    datetime_storage_id_ varchar(10) binary
) returns int unsigned
not deterministic modifies sql data
begin
    insert attributeDatetimeT(
        id, 
        datetimeStorageId)
    values(
        attribute_id_,
        datetime_storage_id_)
    on duplicate key update
        attributeDatetimeT.datetimeStorageId = coalesce(datetime_storage_id_, attributeDatetimeT.datetimeStorageId);

    return attribute_id_;
end$$$


create function __attribute_SetText(
    attribute_id_ int unsigned,
    text_is_long_ enum('n', 'y')
) returns int unsigned
not deterministic modifies sql data
begin
    insert attributeTextT(
        id, 
        textIsLong)
    values(
        attribute_id_,
        text_is_long_)
    on duplicate key update
        attributeTextT.textIsLong = coalesce(text_is_long_, attributeTextT.textIsLong);

    return attribute_id_;
end$$$


create function __attribute_Duplicate(attribute_id_ int unsigned) returns int unsigned
not deterministic modifies sql data
begin
    declare new_id int unsigned;
    
    set @1 = __attribute_ValidateId(attribute_id_);
    
    set new_id = __tag_DuplicateInternal(attribute_id_);
    
    insert attributeT(id, dataTypeId, code, required, sortable)
    select new_id, a.dataTypeId, null, a.required, a.sortable
    from attributeT a
    where a.id = attribute_id_;
    
    insert attributeTL(id, languageId, uofmPre, uofmPost)
    select new_id, al.languageId, al.uofmPre, al.uofmPost
    from attributeTL al
    where al.id = attribute_id_;
    
    insert attributeCacheT(id, attributeNames, attributeNamesSearch)
    select new_id, ac.attributeNames, ac.attributeNamesSearch
    from attributeCacheT ac
    where ac.id = attribute_id_;
    
    insert attributeNumericT(id, decimalMin, decimalMax, decimalRound)
    select new_id, an.decimalMin, an.decimalMax, an.decimalRound
    from attributeNumericT an
    where an.id = attribute_id_;

    insert attributeDatetimeT(id, datetimeStorageId)
    select new_id, ad.datetimeStorageId
    from attributeDatetimeT ad
    where ad.id = attribute_id_;
    
    insert attributeTextT(id, textIsLong)
    select new_id, ax.textIsLong
    from attributeTextT ax
    where ax.id = attribute_id_;
    
    return new_id;
end$$$


create function attribute_CreateNumeric(
    type_id_ int unsigned,
    type_code_ varchar(30) binary,
    name_ varchar(255),
    sorter_ int,
    can_search_ enum('n', 'y'),
    display_in_object_ enum('n', 'y'),
    display_in_object_list_ enum('n', 'y'),
    factor_ double,
    enabled_ enum('n', 'y'),
    code_ varchar(30) binary,
    required_ enum('n', 'y'),
    sortable_ enum('n', 'y'),
    uofm_pre_ varchar(255),
    uofm_post_ varchar(255),
    decimal_min_ decimal(22, 6),
    decimal_max_ decimal(22, 6),
    decimal_round_ int
) returns int unsigned
not deterministic modifies sql data
begin
    declare attribute_id int unsigned;
    
    set attribute_id = __attribute_Create(
        type_id_, 
        type_code_, 
        name_, 
        sorter_, 
        can_search_, 
        display_in_object_,
        display_in_object_list_,
        factor_,
        enabled_, 
        'numeric', 
        code_,
        required_,
        sortable_,
        uofm_pre_, 
        uofm_post_);
    
    set @1 = __attribute_SetNumeric(attribute_id, decimal_min_, null, decimal_max_, null, decimal_round_);
    
    return attribute_id;
end$$$


create function attribute_CreateDatetime(
    type_id_ int unsigned,
    type_code_ varchar(30) binary,
    name_ varchar(255),
    sorter_ int,
    can_search_ enum('n', 'y'),
    display_in_object_ enum('n', 'y'),
    display_in_object_list_ enum('n', 'y'),
    factor_ double,
    enabled_ enum('n', 'y'),
    code_ varchar(30) binary,
    required_ enum('n', 'y'),
    sortable_ enum('n', 'y'),
    uofm_pre_ varchar(255),
    uofm_post_ varchar(255),
    datetime_storage_id_ varchar(10) binary
) returns int unsigned
not deterministic modifies sql data
begin
    declare attribute_id int unsigned;
    
    set attribute_id = __attribute_Create(
        type_id_, 
        type_code_, 
        name_, 
        sorter_, 
        can_search_, 
        display_in_object_,
        display_in_object_list_,
        factor_,
        enabled_, 
        'datetime', 
        code_,
        required_,
        sortable_,
        uofm_pre_, 
        uofm_post_);
    
    set @1 = __attribute_SetDatetime(attribute_id, datetime_storage_id_);
    
    return attribute_id;
end$$$


create function attribute_CreateText(
    type_id_ int unsigned,
    type_code_ varchar(30) binary,
    name_ varchar(255),
    sorter_ int,
    can_search_ enum('n', 'y'),
    display_in_object_ enum('n', 'y'),
    display_in_object_list_ enum('n', 'y'),
    factor_ double,
    enabled_ enum('n', 'y'),
    code_ varchar(30) binary,
    required_ enum('n', 'y'),
    sortable_ enum('n', 'y'),
    uofm_pre_ varchar(255),
    uofm_post_ varchar(255),
    text_is_long_ enum('n', 'y')
) returns int unsigned
not deterministic modifies sql data
begin
    declare attribute_id int unsigned;
    
    set attribute_id = __attribute_Create(
        type_id_, 
        type_code_, 
        name_, 
        sorter_, 
        can_search_, 
        display_in_object_,
        display_in_object_list_,
        factor_,
        enabled_, 
        'text', 
        code_,
        required_,
        sortable_,
        uofm_pre_, 
        uofm_post_);
    
    set @1 = __attribute_SetText(attribute_id, text_is_long_);
    
    return attribute_id;
end$$$


create function attribute_Delete(attribute_id_ int unsigned) returns int unsigned
not deterministic modifies sql data
begin
    set @1 = __attribute_ValidateId(attribute_id_);
    return __tag_Delete(attribute_id_); -- call ancestor function
end$$$


create function __attribute_Update(
    attribute_id_ int unsigned,
    sorter_ int,
    can_search_ enum('n', 'y'), reset_can_search_ enum('n', 'y'), 
    display_in_object_ enum('n', 'y'), reset_display_in_object_ enum('n', 'y'),
    display_in_object_list_ enum('n', 'y'), reset_display_in_object_list_ enum('n', 'y'),
    factor_ double,
    enabled_ enum('n', 'y'),
    code_ varchar(30) binary,
    required_ enum('n', 'y'),
    sortable_ enum('n', 'y')
) returns int unsigned
not deterministic modifies sql data
begin
    set @1 = __tag_Update(
                    attribute_id_, 
                    sorter_, 
                    can_search_, reset_can_search_, 
                    display_in_object_, reset_display_in_object_,
                    display_in_object_list_, reset_display_in_object_list_,
                    factor_, 
                    null,
                    enabled_);
    
    update attributeT set
        code = if(code_ = '', null, coalesce(code_, code)),
        required = coalesce(required_, required),
        sortable = coalesce(sortable_, sortable)
    where id = attribute_id_;

    -- as of now - no __attribute_UpdateCache here because cache does not depend on attributeT

    return attribute_id_;
end$$$


create function attribute_Update(
    attribute_id_ int unsigned,
    sorter_ int,
    can_search_ enum('n', 'y'), reset_can_search_ enum('n', 'y'), 
    display_in_object_ enum('n', 'y'), reset_display_in_object_ enum('n', 'y'),
    display_in_object_list_ enum('n', 'y'), reset_display_in_object_list_ enum('n', 'y'),
    factor_ double,
    enabled_ enum('n', 'y'),
    code_ varchar(30) binary,
    required_ enum('n', 'y'),
    sortable_ enum('n', 'y')
) returns int unsigned
not deterministic modifies sql data
begin
    set @1 = __attribute_ValidateId(attribute_id_);

    set @2 = __attribute_Update(
                        attribute_id_, 
                        sorter_, 
                        can_search_, reset_can_search_, 
                        display_in_object_, reset_display_in_object_,
                        display_in_object_list_, reset_display_in_object_list_,
                        factor_, 
                        enabled_, 
                        code_,
                        required_,
                        sortable_);

    set @3 = __object_UpdateSortCache(null, attribute_id_, null);

    return attribute_id_;
end$$$


create function __attribute_UpdateNumericValues(attribute_id_ int unsigned) returns int unsigned
not deterministic modifies sql data
begin
    update attributeValueNumericT set
        value = __attribute_ClampValueNumeric(attribute_id_, valueRaw)
    where attributeId = attribute_id_;

    return attribute_id_;
end$$$


create function attribute_UpdateNumeric(
    attribute_id_ int unsigned,
    sorter_ int,
    can_search_ enum('n', 'y'), reset_can_search_ enum('n', 'y'), 
    display_in_object_ enum('n', 'y'), reset_display_in_object_ enum('n', 'y'),
    display_in_object_list_ enum('n', 'y'), reset_display_in_object_list_ enum('n', 'y'),
    factor_ double,
    enabled_ enum('n', 'y'),
    code_ varchar(30) binary,
    required_ enum('n', 'y'),
    sortable_ enum('n', 'y'),
    decimal_min_ decimal(22, 6), reset_decimal_min_ enum('n', 'y'),
    decimal_max_ decimal(22, 6), reset_decimal_max_ enum('n', 'y'),
    decimal_round_ int
) returns int unsigned
not deterministic modifies sql data
begin
    set @1 = __attribute_ValidateIdDataType(attribute_id_, 'numeric');
    
    set @2 = __attribute_Update(
                        attribute_id_, 
                        sorter_, 
                        can_search_, reset_can_search_, 
                        display_in_object_, reset_display_in_object_,
                        display_in_object_list_, reset_display_in_object_list_,
                        factor_, 
                        enabled_, 
                        code_,
                        required_,
                        sortable_);
    
    set @3 = __attribute_SetNumeric(attribute_id_, decimal_min_, reset_decimal_min_, decimal_max_, reset_decimal_max_, decimal_round_);
    
    set @4 = __attribute_UpdateNumericValues(attribute_id_);

    set @5 = __object_UpdateSortCache(null, attribute_id_, null);

    return attribute_id_;
end$$$


create function __attribute_UpdateDatetimeValues(attribute_id_ int unsigned) returns int unsigned
not deterministic modifies sql data
begin
    update attributeValueDatetimeT set
        value = __attribute_ClampValueDatetime(attribute_id_, valueRaw)
    where attributeId = attribute_id_;

    return attribute_id_;
end$$$


create function attribute_UpdateDatetime(
    attribute_id_ int unsigned,
    sorter_ int,
    can_search_ enum('n', 'y'), reset_can_search_ enum('n', 'y'), 
    display_in_object_ enum('n', 'y'), reset_display_in_object_ enum('n', 'y'),
    display_in_object_list_ enum('n', 'y'), reset_display_in_object_list_ enum('n', 'y'),
    factor_ double,
    enabled_ enum('n', 'y'),
    code_ varchar(30) binary,
    required_ enum('n', 'y'),
    sortable_ enum('n', 'y'),
    datetime_storage_id_ varchar(10) binary
) returns int unsigned
not deterministic modifies sql data
begin
    set @1 = __attribute_ValidateIdDataType(attribute_id_, 'datetime');
    
    set @2 = __attribute_Update(
                        attribute_id_, 
                        sorter_, 
                        can_search_, reset_can_search_, 
                        display_in_object_, reset_display_in_object_,
                        display_in_object_list_, reset_display_in_object_list_,
                        factor_, 
                        enabled_, 
                        code_,
                        required_,
                        sortable_);
    
    set @3 = __attribute_SetDatetime(attribute_id_, datetime_storage_id_);
    
    set @4 = __attribute_UpdateDatetimeValues(attribute_id_);
    
    set @5 = __object_UpdateSortCache(null, attribute_id_, null);
    
    return attribute_id_;
end$$$


create function attribute_UpdateText(
    attribute_id_ int unsigned,
    sorter_ int,
    can_search_ enum('n', 'y'), reset_can_search_ enum('n', 'y'), 
    display_in_object_ enum('n', 'y'), reset_display_in_object_ enum('n', 'y'),
    display_in_object_list_ enum('n', 'y'), reset_display_in_object_list_ enum('n', 'y'),
    factor_ double,
    enabled_ enum('n', 'y'),
    code_ varchar(30) binary,
    required_ enum('n', 'y'),
    sortable_ enum('n', 'y'),
    text_is_long_ enum('n', 'y')
) returns int unsigned
not deterministic modifies sql data
begin
    set @1 = __attribute_ValidateIdDataType(attribute_id_, 'text');
    
    set @2 = __attribute_Update(
                        attribute_id_, 
                        sorter_, 
                        can_search_, reset_can_search_, 
                        display_in_object_, reset_display_in_object_,
                        display_in_object_list_, reset_display_in_object_list_,
                        factor_, 
                        enabled_, 
                        code_,
                        required_,
                        sortable_);
    
    set @3 = __attribute_SetText(attribute_id_, text_is_long_);
    
    return attribute_id_;
end$$$


create function attribute_UpdateLocal(
    attribute_id_ int unsigned,
    lang_id_ char(3),
    name_ varchar(255),
    uofm_pre_ varchar(255),
    uofm_post_ varchar(255)
) returns int unsigned
not deterministic modifies sql data
begin
    set @1 = __attribute_ValidateId(attribute_id_);
    
    set @2 = __tag_UpdateLocal(attribute_id_, lang_id_, name_);
    
    insert attributeTL(id, languageId, uofmPre, uofmPost)
    select attribute_id_, l.id, coalesce(uofm_pre_, ''), coalesce(uofm_post_, '')
    from languageT l
    where l.id = coalesce(lang_id_, l.id)
    on duplicate key update 
        attributeTL.uofmPre = coalesce(uofm_pre_, attributeTL.uofmPre),
        attributeTL.uofmPost = coalesce(uofm_post_, attributeTL.uofmPost);
        
    set @3 = __attribute_UpdateCache(attribute_id_);

    return attribute_id_;
end$$$


-- --------------------------------------------------------------------------------
-- attribute values
-- --------------------------------------------------------------------------------

create function __attribute_ValidateValueByType(
    data_type_ varchar(10) binary,
    value_numeric_ decimal(22, 6),
    value_datetime_ datetime,
    value_text_ text
) returns int unsigned
deterministic
begin
    if (data_type_ = 'numeric' and value_numeric_ is null) then
        call raiseError2('attribute', 'validateValue', attribute_id_, 'numeric', 'no numeric value');
    elseif (data_type_ = 'datetime' and value_datetime_ is null) then
        call raiseError2('attribute', 'validateValue', attribute_id_, 'datetime', 'no datetime value');
    elseif (data_type_ = 'text' and value_text_ is null) then
        call raiseError2('attribute', 'validateValue', attribute_id_, 'text', 'no text value');
    end if;

    return 1;
end$$$


create function __attribute_SetValueNumeric(
    attribute_id_ int unsigned,
    object_id_ int unsigned,
    value_numeric_ decimal(22, 6)
) returns int unsigned
not deterministic modifies sql data
begin
    if value_numeric_ is null then
        delete from attributeValueNumericT
        where attributeId = attribute_id_
            and objectId = object_id_;
    else
        insert attributeValueNumericT(attributeId, objectId, value, valueRaw)
        values(attribute_id_, object_id_, __attribute_ClampValueNumeric(attribute_id_, value_numeric_), value_numeric_)
        on duplicate key update 
            attributeValueNumericT.value = __attribute_ClampValueNumeric(attribute_id_, value_numeric_),
            attributeValueNumericT.valueRaw = value_numeric_;
    end if;
    
    return attribute_id_;
end$$$


create function __attribute_SetValueDatetime(
    attribute_id_ int unsigned,
    object_id_ int unsigned,
    value_datetime_ datetime
) returns int unsigned
not deterministic modifies sql data
begin
    if value_datetime_ is null then
        delete from attributeValueDatetimeT
        where attributeId = attribute_id_
            and objectId = object_id_;
    else
        insert attributeValueDatetimeT(attributeId, objectId, value, valueRaw)
        values(attribute_id_, object_id_, __attribute_ClampValueDatetime(attribute_id_, value_datetime_), value_datetime_)
        on duplicate key update 
            attributeValueDatetimeT.value = __attribute_ClampValueDatetime(attribute_id_, value_datetime_),
            attributeValueDatetimeT.valueRaw = value_datetime_;
    end if;
        
    return attribute_id_;
end$$$


create function __attribute_SetValueText(
    attribute_id_ int unsigned,
    object_id_ int unsigned,
    lang_id_ char(3),
    value_text_ text
) returns int unsigned
not deterministic modifies sql data
begin
    if value_text_ is null then
        delete from attributeValueTextT
        where attributeId = attribute_id_
            and objectId = object_id_;
    else
        insert attributeValueTextT(attributeId, objectId, languageId, value)
        select attribute_id_, object_id_, l.id, value_text_
        from languageT l
        where l.id = coalesce(lang_id_, l.id)
        on duplicate key update 
            attributeValueTextT.value = value_text_;
    end if;
        
    return attribute_id_;
end$$$


create function __attribute_SetLink(
    attribute_id_ int unsigned,
    object_id_ int unsigned,
    do_link_setup_ int unsigned
) returns int unsigned
not deterministic modifies sql data
begin
    if do_link_setup_ then
        set @1 = nodeLink_UpdateStart();

        set @2 = nodeLink_Relink(null, attribute_id_, object_id_);

        set @3 = __nodeLink_UpdateWorkSet(attribute_id_, -1) and __nodeLink_UpdateWorkSet(object_id_, 1);

        set @4 = nodeLink_UpdateEnd();
    else
        set @1 = nodeLink_Unlink(null, attribute_id_, object_id_);
    end if;
    
    return attribute_id_;
end$$$


create function attribute_SetValueNumeric(
    attribute_id_ int unsigned,
    object_id_ int unsigned,
    value_numeric_ decimal(22, 6) 
) returns int unsigned
not deterministic modifies sql data
begin
    set @1 = __attribute_ValidateIdDataType(attribute_id_, 'numeric') and __object_ValidateId(object_id_);
    
    set @3 = __attribute_SetValueNumeric(attribute_id_, object_id_, value_numeric_);

    set @2 = __attribute_SetLink(attribute_id_, object_id_, value_numeric_ is not null);
    
    set @4 = __object_UpdateSortCache(object_id_, attribute_id_, value_numeric_);

    return attribute_id_;
end$$$


create function attribute_SetValueDatetime(
    attribute_id_ int unsigned,
    object_id_ int unsigned,
    value_datetime_ datetime
) returns int unsigned
not deterministic modifies sql data
begin
    set @1 = __attribute_ValidateIdDataType(attribute_id_, 'datetime') and __object_ValidateId(object_id_);
    
    set @3 = __attribute_SetValueDatetime(attribute_id_, object_id_, value_datetime_);

    set @2 = __attribute_SetLink(attribute_id_, object_id_, value_datetime_ is not null);

    set @4 = __object_UpdateSortCache(object_id_, attribute_id_, datetime_ToDouble(value_datetime_));

    return attribute_id_;
end$$$


create function attribute_SetValueText(
    attribute_id_ int unsigned,
    object_id_ int unsigned,
    lang_id_ char(3),
    value_text_ text
) returns int unsigned
not deterministic modifies sql data
begin
    declare val text default if(value_text_ = '', null, value_text_);
    
    set @1 = __attribute_ValidateIdDataType(attribute_id_, 'text') and __object_ValidateId(object_id_);
    
    set @3 = __attribute_SetValueText(attribute_id_, object_id_, lang_id_, val);

    set @2 = __attribute_SetLink(attribute_id_, object_id_, val is not null);

    return attribute_id_;
end$$$


create function attribute_SetValue(
    attribute_id_ int unsigned,
    object_id_ int unsigned,
    lang_id_ char(3),
    value_ text
) returns int unsigned
not deterministic modifies sql data
begin
    declare data_type varchar(10) default (select dataTypeId from attributeT where id = attribute_id_);
    declare val text default if(value_ = '', null, value_);
    
    set @1 = __attribute_ValidateId(attribute_id_) and __object_ValidateId(object_id_);
    
    case data_type
        when 'numeric' then
            set @3 = __attribute_SetValueNumeric(attribute_id_, object_id_, cast(val as decimal(22, 6)));
            set @4 = __object_UpdateSortCache(object_id_, attribute_id_, cast(val as decimal(22, 6)));
        when 'datetime' then
            set @3 = __attribute_SetValueDatetime(attribute_id_, object_id_, cast(val as datetime));
            set @4 = __object_UpdateSortCache(object_id_, attribute_id_, datetime_ToDouble(cast(val as datetime)));
        else
            set @3 = __attribute_SetValueText(attribute_id_, object_id_, lang_id_, val);
    end case;

    set @2 = __attribute_SetLink(attribute_id_, object_id_, val is not null);

    return attribute_id_;
end$$$

