-- -------------------------------------------------------------------------------- -- _010_utils Group Routines -- @__sorterValue - current sorter value -- @__sorterIncrement - the amount the sorter will be incremented by -- @__stringQuoteOutHelperPrevC - previous char value in character filtering function -- -------------------------------------------------------------------------------- DELIMITER $$$ create procedure raiseError(msg_ varchar(128)) begin declare msg varchar(128) default substring(msg_, 1, 128); signal sqlstate '45000' set message_text = msg; end$$$ create procedure raiseError1( object_ varchar(30), action_ varchar(30), subject_ varchar(30), msg_ varchar(65)) begin call raiseError(concat_ws('|', coalesce(object_, ''), coalesce(action_, ''), coalesce(subject_, ''), coalesce(msg_, ''))); end$$$ create procedure raiseError2( object_ varchar(30), action_ varchar(30), subject1_ varchar(30), subject2_ varchar(30), msg_ varchar(65)) begin call raiseError(concat_ws('|', coalesce(object_, ''), coalesce(action_, ''), coalesce(subject1_, ''), coalesce(subject2_, ''), coalesce(msg_, ''))); end$$$ create function testOddRaiseError(n int) returns int not deterministic begin if (n % 2 = 1) then call raiseError1('testObject', 'testAction', n, '<-- this is odd!'); end if; return n; end$$$ -- dynamic sql wrapper create procedure __executeSQL(sql_query_ mediumtext) modifies sql data begin set @executeSQL_q = sql_query_; prepare executeSQL_stmt from @executeSQL_q; execute executeSQL_stmt; deallocate prepare executeSQL_stmt; end$$$ -- maintenance create procedure analyzeAll(db_name_ varchar(100), write_log_ int) modifies sql data begin declare q mediumtext; declare db_name__ varchar(100) default coalesce(db_name_, database()); declare sess_id bigint unsigned default uuid_short(); if coalesce(write_log_, 1) then insert _maintenanceLogT(sessId, msg) values (sess_id, concat('analyze all for ', db_name__,' started')); end if; set @@group_concat_max_len = 262144; select concat('analyze table ', group_concat(t.TABLE_NAME)) into q from information_schema.TABLES t where t.TABLE_SCHEMA = db_name__ and t.TABLE_TYPE = 'BASE TABLE'; call __executeSQL(q); if coalesce(write_log_, 1) then insert _maintenanceLogT(sessId, msg) values (sess_id, concat('analyze all for ', db_name__,' finished')); end if; end$$$ create procedure optimizeAll(db_name_ varchar(100), write_log_ int) modifies sql data begin declare q mediumtext; declare db_name__ varchar(100) default coalesce(db_name_, database()); declare sess_id bigint unsigned default uuid_short(); if coalesce(write_log_, 1) then insert _maintenanceLogT(sessId, msg) values (sess_id, concat('optimize all for ', db_name__,' started')); end if; set @@group_concat_max_len = 262144; select concat('optimize table ', group_concat(t.TABLE_NAME)) into q from information_schema.TABLES t where t.TABLE_SCHEMA = db_name__ and t.TABLE_TYPE = 'BASE TABLE' and t.`ENGINE` = 'InnoDB' and t.TABLE_NAME != '_maintenanceLogT'; call __executeSQL(q); if coalesce(write_log_, 1) then insert _maintenanceLogT(sessId, msg) values (sess_id, concat('optimize all for ', db_name__,' finished')); end if; end$$$ create function maintenance_GetLockName() returns varchar(50) deterministic return concat(database(), '.maintenance')$$$ create function __maintenance_DataStart(queue_limit bigint unsigned) returns bigint unsigned not deterministic modifies sql data begin declare max_id bigint unsigned default coalesce( (select id from zzzNodeQueueT order by id limit queue_limit, 1), (select max(id) from zzzNodeQueueT)); drop temporary table if exists __nodeQueueT; create temporary table __nodeQueueT( flag char(10) binary not null, nodeId int unsigned not null, primary key(flag, nodeId) ); insert __nodeQueueT(flag, nodeId) select flag, nodeId from zzzNodeQueueT where id <= max_id group by flag, nodeId; return max_id; end$$$ create function __maintenance_DataEnd(n bigint unsigned) returns bigint unsigned not deterministic modifies sql data begin drop temporary table if exists __nodeQueueT; delete from zzzNodeQueueT where id <= n; return 1; end$$$ create procedure maintenance_5minute(write_log_ int) modifies sql data begin declare lock_name varchar(50) default maintenance_GetLockName(); declare sess_id bigint unsigned default uuid_short(); declare write_log int default coalesce(write_log_, 1); declare maintenance_number bigint unsigned; declare id_limit int unsigned default greatest(coalesce(constI('maintenance.limit.node'), 2000) - 1, 1); -- CONST: node limit if get_lock(lock_name, 1) then if write_log then insert _maintenanceLogT(sessId, msg) values (sess_id, concat('maintenance_5minute started')); end if; set maintenance_number = __maintenance_DataStart(id_limit); set @1 = __nodeAncestry_Update(); -- FORWARD: __object_UpdateRatings is defined in _080_object set @2 = __object_UpdateRatings(); -- FORWARD: __object_UpdateAddresses is defined in _080_object set @3 = __object_UpdateAddresses(); -- FORWARD: __tag_UpdateCache is defined in _070_tag set @4 = __tag_UpdateCache(); -- FORWARD: __nodeLink_UpdateRecommendCacheQueue is defined in _020_nodeLink set @5 = __nodeLink_UpdateRecommendCacheQueue(); set @6 = __currency_Calculate(); set @7 = __maintenance_DataEnd(maintenance_number); -- FORWARD: viewLog_UpdateStat is defined in _030_node set @8 = viewLog_UpdateStat(null); if coalesce(constI('search.cache.update'), 1) then -- FORWARD: __search_UpdateResultCache is defined in _110_search set @1 = __search_UpdateResultCache() and const_SetI('search.cache.update', 0); end if; if write_log then insert _maintenanceLogT(sessId, msg) values (sess_id, concat('maintenance_5minute finished')); end if; set @0 = release_lock(lock_name); else if write_log then insert _maintenanceLogT(sessId, msg) values (sess_id, concat('maintenance_5minute could not start - another instance running')); end if; end if; end$$$ -- workset functions create function __nodeWorkset_Cleanup() returns int unsigned not deterministic begin delete from zzzNodeWorksetT where added < date_sub(now(), interval 1 day); return 1; end$$$ create function __nodeWorkset_GetLevel() returns int unsigned return coalesce(@__nodeWorksetLevel, 0)$$$ create function __nodeWorkset_Start() returns int unsigned not deterministic modifies sql data begin set @__nodeWorksetLevel = coalesce(@__nodeWorksetLevel, 0) + 1; if __nodeWorkset_GetLevel() = 1 then delete from zzzNodeWorksetT where connId = connection_id(); end if; return 1; end$$$ create function __nodeWorkset_Update(node_id int unsigned, direction int, only_direction int, linked int) returns int unsigned comment 'direction=-1 - downwards; direction=1 - upwards; direction=0 - both; others - undefined; only_direction is the same, but controls if records added at all' not deterministic modifies sql data begin if __nodeWorkset_GetLevel() > 0 then insert zzzNodeWorksetT(nodeId, connId, isMain, isExact, isLinked) values (node_id, connection_id(), 1, if(direction < 0, 2, 1), linked) on duplicate key update isMain = 1, isExact = if(isExact = 1, isExact, values(isExact)), isLinked = isLinked or linked; -- downwards from source node insert zzzNodeWorksetT(nodeId, connId, isMain, isLinked) select l.srcNodeId, connection_id(), direction < 1, linked and direction < 1 from nodeLinkT l where l.dstNodeId = node_id and metric > 0 and only_direction < 1 on duplicate key update isMain = isMain or (direction < 1), isLinked = isLinked or values(isLinked); -- upwards from source node insert zzzNodeWorksetT(nodeId, connId, isMain, isLinked) select l.dstNodeId, connection_id(), direction > -1, linked and direction > -1 from nodeLinkT l where l.srcNodeId = node_id and metric > 0 and only_direction > -1 on duplicate key update isMain = isMain or (direction > -1), isLinked = isLinked or values(isLinked); end if; return node_id; end$$$ create function __nodeWorkset_End() returns int unsigned not deterministic modifies sql data begin if __nodeWorkset_GetLevel() = 1 then set @1 = __nodeWorkset_Cleanup(); delete from zzzNodeWorksetT where connId = connection_id(); end if; set @__nodeWorksetLevel = greatest(coalesce(@__nodeWorksetLevel, 0) - 1, 0); return 1; end$$$ -- node ancestry support, based on workset create function __nodeAncestry_Update() returns int unsigned not deterministic modifies sql data begin insert nodeAncestryT(id, languageId, tagNames) select nad.dstNodeId, nad.languageId, group_concat( concat_ws( 0xb, nad.srcTypeId, nad.srcTypeName, string_ConcatSmart( ' ', nad.srcUofmPre, if( nad.srcInternalTypeId = 'attribute', coalesce( nad.srcNumericValue, date_format(nad.srcDatetimeValue, nad.srcDatetimeOutputFormat), string_Replace0xbZero(nad.srcTextValue), ''), nad.srcName), nad.srcUofmPost)) order by nad.ancestrySorter, nad.srcTypeName, nad.srcName separator '\0') tag_names from nodeAncestryDataV nad join __nodeQueueT nq on nad.dstNodeId = nq.nodeId where nq.flag = ' ' group by nad.dstNodeId, nad.languageId on duplicate key update nodeAncestryT.tagNames = values(tagNames); insert nodeAncestryAllT(id, tagNames) select na.id, group_concat(concat_ws(0x1e, na.languageId, na.tagNames) separator '\Z') from nodeAncestryT na join __nodeQueueT nq on na.id = nq.nodeId where nq.flag = ' ' group by na.id on duplicate key update nodeAncestryAllT.tagNames = values(tagNames); return 1; end$$$ create function __nodeAncestry_UpdateAll(reset_data_ int) returns int unsigned not deterministic modifies sql data comment 'updates all nodes ancestry; to be called by hand when needed' begin delete from nodeAncestryT where coalesce(reset_data_, 0); delete from nodeAncestryAllT where coalesce(reset_data_, 0); insert nodeAncestryT(id, languageId, tagNames) select nad.dstNodeId, nad.languageId, group_concat( concat_ws( 0xb, nad.srcTypeId, nad.srcTypeName, string_ConcatSmart( ' ', nad.srcUofmPre, if( nad.srcInternalTypeId = 'attribute', coalesce( nad.srcNumericValue, date_format(nad.srcDatetimeValue, nad.srcDatetimeOutputFormat), string_Replace0xbZero(nad.srcTextValue), ''), nad.srcName), nad.srcUofmPost)) order by nad.ancestrySorter, nad.srcTypeName, nad.srcName separator '\0') tag_names from nodeAncestryDataV nad group by nad.dstNodeId, nad.languageId on duplicate key update nodeAncestryT.tagNames = values(tagNames); insert nodeAncestryAllT(id, tagNames) select na.id, group_concat(concat_ws(0x1e, na.languageId, na.tagNames) separator '\Z') from nodeAncestryT na group by na.id on duplicate key update nodeAncestryAllT.tagNames = values(tagNames); return 1; end$$$ -- automatic sorter numbering create function sorter_Set(sorter_value_ int, sorter_increment_ int) returns int not deterministic begin set @__sorterValue = coalesce(sorter_value_, @__sorterValue); set @__sorterIncrement = coalesce(sorter_increment_, @__sorterIncrement); return @__sorterValue; end$$$ create function sorter_Get() returns int not deterministic begin declare result int default @__sorterValue; set @__sorterValue = @__sorterValue + @__sorterIncrement; return result; end$$$ create function sorter_GetIncrement() returns int deterministic return @__sorterIncrement$$$ -- string manipulation create function __string_QuoteOutHelper(c_ varchar(1) binary, single_ int) returns varchar(1) not deterministic begin if single_ then if coalesce(@__stringQuoteOutHelperPrevC, '') = c_ then return ''; else set @__stringQuoteOutHelperPrevC = c_; return c_; end if; else set @__stringQuoteOutHelperPrevC = c_; return c_; end if; end$$$ create function __string_QuoteOut( s varchar(2048), purp varchar(10), rpl varchar(1) ) returns varchar(2048) deterministic begin declare l int default char_length(s); set @__stringQuoteOutHelperPrevC = null; -- init return ( select group_concat( __string_QuoteOutHelper( coalesce(vc.replacement, vc.id, rpl), coalesce(vc.forceSingle, 'y') = 'y') order by i.n separator '') from zzzInts10000V i left join zzzValidCharT vc on substring(s, i.n + 1, 1) = vc.id and vc.purpose = purp where i.n < l); end$$$ create function string_Alphanumeric(s varchar(2048)) returns varchar(2048) deterministic return __string_QuoteOut(s, 'alias', '-')$$$ create function string_Normalize(s varchar(2048)) returns varchar(2048) deterministic return __string_QuoteOut(s, 'norm', '')$$$ create function __string_ConcatSmart(s1 varchar(255), s varchar(255), s2 varchar(255)) returns varchar(1024) deterministic return if(s != '', concat(coalesce(s1, ''), s, coalesce(s2, '')), '')$$$ create function string_ConcatSmart(sep varchar(50), s1 varchar(255), s varchar(255), s2 varchar(255)) returns varchar(1024) deterministic return __string_ConcatSmart(__string_ConcatSmart('', s1, sep), s, __string_ConcatSmart(sep, s2, ''))$$$ create function string_Replace0xbZero(v varchar(2048)) returns varchar(2048) deterministic return replace(replace(v, 0xb, ''), '\0', '')$$$ -- datetime manipulation create function datetime_ToDouble(t datetime) returns double deterministic return to_days(t) + (time_to_sec(t) + 0.0) / 86400.0$$$ -- consts -- const get functions break from naming rules (would be const_GetS/GetI/GetF) for brevity create function constS(name_ varchar(60)) returns varchar(255) deterministic return (select valueString from constT where id = name_)$$$ create function constI(name_ varchar(60)) returns int deterministic return (select valueInt from constT where id = name_)$$$ create function constF(name_ varchar(60)) returns double deterministic return (select valueFloat from constT where id = name_)$$$ create function const_SetS(name_ varchar(60), value_ varchar(255)) returns varchar(255) not deterministic modifies sql data begin declare data_type ENUM('string', 'int', 'float') default (select dataType from constT where id = name_); if (data_type is null) or (data_type = 'string') then insert constT(id, dataType, valueString) values (name_, 'string', value_) on duplicate key update dataType = 'string', valueString = value_; else call raiseError2('const', 'update', substring(name_, 1, 30), 'string', 'cannot update const - mismatching type'); end if; return value_; end$$$ create function const_SetI(name_ varchar(60), value_ int) returns int not deterministic modifies sql data begin declare data_type ENUM('string', 'int', 'float') default (select dataType from constT where id = name_); if (data_type is null) or (data_type = 'int') then insert constT(id, dataType, valueInt) values (name_, 'int', value_) on duplicate key update dataType = 'int', valueInt = value_; else call raiseError2('const', 'update', substring(name_, 1, 30), 'int', 'cannot update const - mismatching type'); end if; return value_; end$$$ create function const_SetF(name_ varchar(60), value_ double) returns double not deterministic modifies sql data begin declare data_type ENUM('string', 'int', 'float') default (select dataType from constT where id = name_); if (data_type is null) or (data_type = 'float') then insert constT(id, dataType, valueFloat) values (name_, 'float', value_) on duplicate key update dataType = 'float', valueFloat = value_; else call raiseError2('const', 'update', substring(name_, 1, 30), 'float', 'cannot update const - mismatching type'); end if; return value_; end$$$ -- --------------------------------- -- currency -- --------------------------------- create function currency_SetExchangeRate(currency_id_ varchar(4) binary, effective_date_ datetime, rate_ decimal(22, 7)) returns int unsigned not deterministic modifies sql data begin insert currencyExchangeRateT(currencyId, effectiveDate, rate) values (currency_id_, coalesce(effective_date_, now()), rate_); return 1; end$$$ create function currency_EnqueueAll() returns int unsigned not deterministic modifies sql data begin -- FORWARD: __attribute_EnqueueObjects is defined in _090_attribute insert zzzDevNullObjectT(n) select __attribute_EnqueueObjects(ap.id) from attributePriceT ap; return 1; end$$$ -- to be called from maintenance routine create function __currency_Calculate() returns int unsigned not deterministic modifies sql data begin set @1 = intlist_Start1(); insert intlist1T(n) select nq.nodeId from __nodeQueueT nq where nq.flag = 'currency'; -- FORWARD: __attribute_PriceCalculateList is defined in _090_attribute set @2 = __attribute_PriceCalculateList(null); -- FORWARD: __attribute_PriceUpdateCacheList is defined in _090_attribute set @3 = __attribute_PriceUpdateCacheList(null); set @4 = intlist_End1(); return 1; end$$$ create function __currency_CalculateAll() returns int unsigned comment 'to be called manually' not deterministic modifies sql data begin set @1 = intlist_Start1(); insert intlist1T(n) select o.id from objectT o; -- FORWARD: __attribute_PriceCalculateList is defined in _090_attribute set @2 = __attribute_PriceCalculateList(null); -- FORWARD: __attribute_PriceUpdateCacheList is defined in _090_attribute set @3 = __attribute_PriceUpdateCacheList(null); set @4 = intlist_End1(); return 1; end$$$ -- --------------------------------- -- node queue -- --------------------------------- create function nodeQueue_Add(node_id_ int unsigned, flag_ char(10) binary) returns bigint unsigned not deterministic modifies sql data begin insert zzzNodeQueueT(nodeId, flag) values (node_id_, coalesce(flag_, ' ')); return last_insert_id(); end$$$ -- --------------------------------- -- int list temp tables -- -- as we cannot use the same temp table for 2 times in 1 query, nor we can -- use the same table in re-entrant manner, we create independent exact copies -- of the same table, hence several copies of the same routines -- --------------------------------- create function intlist_Start1() returns int unsigned not deterministic modifies sql data begin drop temporary table if exists intlist1T; create temporary table intlist1T(n int unsigned not null primary key); return 1; end$$$ create function intlist_StartNoReset1() returns int unsigned not deterministic modifies sql data begin create temporary table if not exists intlist1T(n int unsigned not null primary key); return 1; end$$$ create function intlist_Add1(n_ int unsigned) returns int unsigned not deterministic modifies sql data begin insert ignore intlist1T(n) values (n_); return n_; end$$$ create function intlist_End1() returns int unsigned not deterministic modifies sql data begin drop temporary table if exists intlist1T; return 1; end$$$ -- create function intlist_Start2() returns int unsigned not deterministic modifies sql data begin drop temporary table if exists intlist2T; create temporary table intlist2T(n int unsigned not null primary key); return 1; end$$$ create function intlist_StartNoReset2() returns int unsigned not deterministic modifies sql data begin create temporary table if not exists intlist2T(n int unsigned not null primary key); return 1; end$$$ create function intlist_Add2(n_ int unsigned) returns int unsigned not deterministic modifies sql data begin insert ignore intlist2T(n) values (n_); return n_; end$$$ create function intlist_End2() returns int unsigned not deterministic modifies sql data begin drop temporary table if exists intlist2T; return 1; end$$$ -- and so on - create intlist3T routines if necessary