diff --git a/api/src/main/resources/_core/database/mysql/functions/fn_mamba_get_obs_value_column.sql b/api/src/main/resources/_core/database/mysql/functions/fn_mamba_get_obs_value_column.sql index d848004..24e17ca 100644 --- a/api/src/main/resources/_core/database/mysql/functions/fn_mamba_get_obs_value_column.sql +++ b/api/src/main/resources/_core/database/mysql/functions/fn_mamba_get_obs_value_column.sql @@ -2,32 +2,33 @@ DROP FUNCTION IF EXISTS fn_mamba_get_obs_value_column; DELIMITER // -CREATE FUNCTION fn_mamba_get_obs_value_column(conceptDatatype VARCHAR(20)) RETURNS VARCHAR(20) +CREATE FUNCTION fn_mamba_get_obs_value_column(conceptDatatype VARCHAR(20)) + RETURNS VARCHAR(20) DETERMINISTIC + NO SQL + COMMENT 'Determines the appropriate obs value column based on concept datatype. Returns column name as: obs_value_text, obs_value_boolean, obs_value_datetime, or obs_value_numeric.' + BEGIN DECLARE obsValueColumn VARCHAR(20); - IF conceptDatatype = 'Text' THEN - SET obsValueColumn = 'obs_value_text'; + CASE LOWER(conceptDatatype) + + WHEN 'text' THEN SET obsValueColumn = 'obs_value_text'; -- Free text values + + WHEN 'coded' THEN SET obsValueColumn = 'obs_value_text'; -- Coded concept IDs stored as text - ELSEIF conceptDatatype = 'Coded' - OR conceptDatatype = 'N/A' THEN - SET obsValueColumn = 'obs_value_text'; + WHEN 'n/a' THEN SET obsValueColumn = 'obs_value_text'; -- Handle unspecified/not-applicable types - ELSEIF conceptDatatype = 'Boolean' THEN - SET obsValueColumn = 'obs_value_boolean'; + WHEN 'boolean' THEN SET obsValueColumn = 'obs_value_boolean'; -- True/false values - ELSEIF conceptDatatype = 'Date' - OR conceptDatatype = 'Datetime' THEN - SET obsValueColumn = 'obs_value_datetime'; + WHEN 'date' THEN SET obsValueColumn = 'obs_value_datetime'; -- Date/datetime values - ELSEIF conceptDatatype = 'Numeric' THEN - SET obsValueColumn = 'obs_value_numeric'; + WHEN 'datetime' THEN SET obsValueColumn = 'obs_value_datetime'; -- Explicit datetime handling - ELSE - SET obsValueColumn = 'obs_value_text'; + WHEN 'numeric' THEN SET obsValueColumn = 'obs_value_numeric'; -- Numeric measurements - END IF; + ELSE SET obsValueColumn = 'obs_value_text'; + END CASE; RETURN (obsValueColumn); END // diff --git a/api/src/main/resources/_core/database/mysql/functions/fn_mamba_is_mysql8_or_higher.sql b/api/src/main/resources/_core/database/mysql/functions/fn_mamba_is_mysql8_or_higher.sql new file mode 100644 index 0000000..11c81ac --- /dev/null +++ b/api/src/main/resources/_core/database/mysql/functions/fn_mamba_is_mysql8_or_higher.sql @@ -0,0 +1,21 @@ +DROP FUNCTION IF EXISTS fn_mamba_is_mysql8_or_higher; + +DELIMITER // + +CREATE FUNCTION fn_mamba_is_mysql8_or_higher() + RETURNS BOOLEAN + DETERMINISTIC + READS SQL DATA + COMMENT 'Returns TRUE if MySQL version is 8.0 or higher, FALSE otherwise' + +BEGIN + DECLARE mysql_version VARCHAR(20); + + -- Get MySQL major version number + SET mysql_version = SUBSTRING_INDEX(VERSION(), '.', 1); + + -- Return TRUE if version is 8 or higher + RETURN CAST(mysql_version AS UNSIGNED) >= 8; +END // + +DELIMITER ; \ No newline at end of file diff --git a/api/src/main/resources/_core/database/mysql/functions/fn_mamba_json_array_length.sql b/api/src/main/resources/_core/database/mysql/functions/fn_mamba_json_array_length.sql index 7fda49a..5eff516 100644 --- a/api/src/main/resources/_core/database/mysql/functions/fn_mamba_json_array_length.sql +++ b/api/src/main/resources/_core/database/mysql/functions/fn_mamba_json_array_length.sql @@ -1,33 +1,61 @@ DROP FUNCTION IF EXISTS fn_mamba_json_array_length; + DELIMITER // -CREATE FUNCTION fn_mamba_json_array_length(json_array TEXT) RETURNS INT +CREATE FUNCTION fn_mamba_json_array_length(json_array TEXT) + RETURNS INT DETERMINISTIC + READS SQL DATA + COMMENT 'Returns the number of objects in a JSON array' + BEGIN DECLARE array_length INT DEFAULT 0; DECLARE current_pos INT DEFAULT 1; DECLARE char_val CHAR(1); + DECLARE brace_counter INT DEFAULT 0; + -- Handle NULL input IF json_array IS NULL THEN RETURN 0; END IF; - -- Iterate over the string to count the number of objects based on commas and curly braces - WHILE current_pos <= CHAR_LENGTH(json_array) DO - SET char_val = SUBSTRING(json_array, current_pos, 1); - - -- Check for the start of an object - IF char_val = '{' THEN - SET array_length = array_length + 1; - - -- Move current_pos to the end of this object - SET current_pos = LOCATE('}', json_array, current_pos) + 1; + -- Use native JSON functions for MySQL 8.0+ + IF fn_mamba_is_mysql8_or_higher() THEN + -- Check if input is a valid JSON array + IF JSON_VALID(json_array) AND JSON_TYPE(json_array) = 'ARRAY' THEN + RETURN JSON_LENGTH(json_array); ELSE - SET current_pos = current_pos + 1; + RETURN 0; + END IF; + ELSE + -- Fallback for MySQL 5.7 and earlier - manual counting + + -- Basic validation - check if it looks like an array + IF NOT (json_array LIKE '[%]') THEN + RETURN 0; END IF; - END WHILE; -RETURN array_length; + -- Iterate over the string to count the number of objects based on commas and curly braces + WHILE current_pos <= CHAR_LENGTH(json_array) + DO + SET char_val = SUBSTRING(json_array, current_pos, 1); + + -- Check for the start of an object + IF char_val = '{' THEN + -- Only count top-level objects + IF brace_counter = 0 THEN + SET array_length = array_length + 1; + END IF; + SET brace_counter = brace_counter + 1; + ELSEIF char_val = '}' THEN + SET brace_counter = brace_counter - 1; + END IF; + + SET current_pos = current_pos + 1; + END WHILE; + + RETURN array_length; + END IF; END // DELIMITER ; \ No newline at end of file diff --git a/api/src/main/resources/_core/database/mysql/functions/fn_mamba_json_extract.sql b/api/src/main/resources/_core/database/mysql/functions/fn_mamba_json_extract.sql index 41d6dbe..3532794 100644 --- a/api/src/main/resources/_core/database/mysql/functions/fn_mamba_json_extract.sql +++ b/api/src/main/resources/_core/database/mysql/functions/fn_mamba_json_extract.sql @@ -1,36 +1,63 @@ DROP FUNCTION IF EXISTS fn_mamba_json_extract; + DELIMITER // -CREATE FUNCTION fn_mamba_json_extract(json TEXT, key_name VARCHAR(255)) RETURNS VARCHAR(255) +CREATE FUNCTION fn_mamba_json_extract(json TEXT, key_name VARCHAR(255)) + RETURNS VARCHAR(255) DETERMINISTIC -BEGIN - DECLARE start_index INT; - DECLARE end_index INT; - DECLARE key_length INT; - DECLARE key_index INT; + READS SQL DATA + COMMENT 'Extracts a value from a JSON string by key name' - SET key_name = CONCAT( key_name, '":'); - SET key_length = CHAR_LENGTH(key_name); - SET key_index = LOCATE(key_name, json); +BEGIN + DECLARE start_index INT; + DECLARE end_index INT; + DECLARE key_length INT; + DECLARE key_index INT; - IF key_index = 0 THEN + -- Handle NULL inputs + IF json IS NULL OR key_name IS NULL THEN RETURN NULL; END IF; - SET start_index = key_index + key_length; + -- Use native JSON functions for MySQL 8.0+ + IF fn_mamba_is_mysql8_or_higher() THEN + -- Use built-in JSON functions for better performance and accuracy + RETURN JSON_EXTRACT(json, CONCAT('$.', key_name)); + ELSE + -- Fallback for MySQL 5.7 and earlier - manual JSON parsing + + -- Basic validation - check if it looks like an object + IF NOT (json LIKE '{%}') THEN + RETURN NULL; + END IF; + + -- Add the key structure to search for in the JSON string + SET key_name = CONCAT('"', key_name, '":'); + SET key_length = CHAR_LENGTH(key_name); + SET key_index = LOCATE(key_name, json); - CASE - WHEN SUBSTRING(json, start_index, 1) = '"' THEN - SET start_index = start_index + 1; - SET end_index = LOCATE('"', json, start_index); - ELSE - SET end_index = LOCATE(',', json, start_index); - IF end_index = 0 THEN - SET end_index = LOCATE('}', json, start_index); - END IF; - END CASE; + IF key_index = 0 THEN + RETURN NULL; + END IF; -RETURN SUBSTRING(json, start_index, end_index - start_index); + SET start_index = key_index + key_length; + + CASE + WHEN SUBSTRING(json, start_index, 1) = '"' THEN SET start_index = start_index + 1; + SET end_index = LOCATE('"', json, start_index); + ELSE SET end_index = LOCATE(',', json, start_index); + IF end_index = 0 THEN + SET end_index = LOCATE('}', json, start_index); + END IF; + END CASE; + + -- Handle case where end marker wasn't found + IF end_index = 0 THEN + RETURN NULL; + END IF; + + RETURN SUBSTRING(json, start_index, end_index - start_index); + END IF; END // DELIMITER ; \ No newline at end of file diff --git a/api/src/main/resources/_core/database/mysql/functions/fn_mamba_json_extract_array.sql b/api/src/main/resources/_core/database/mysql/functions/fn_mamba_json_extract_array.sql index 6262c14..1b457d7 100644 --- a/api/src/main/resources/_core/database/mysql/functions/fn_mamba_json_extract_array.sql +++ b/api/src/main/resources/_core/database/mysql/functions/fn_mamba_json_extract_array.sql @@ -1,47 +1,87 @@ DROP FUNCTION IF EXISTS fn_mamba_json_extract_array; + DELIMITER // -CREATE FUNCTION fn_mamba_json_extract_array(json TEXT, key_name VARCHAR(255)) RETURNS TEXT +CREATE FUNCTION fn_mamba_json_extract_array(json TEXT, key_name VARCHAR(255)) + RETURNS TEXT DETERMINISTIC -BEGIN -DECLARE start_index INT; -DECLARE end_index INT; -DECLARE array_text TEXT; + READS SQL DATA + COMMENT 'Extracts an array from a JSON string by key name' - SET key_name = CONCAT('"', key_name, '":'); - SET start_index = LOCATE(key_name, json); +BEGIN + DECLARE start_index INT; + DECLARE end_index INT; + DECLARE array_text TEXT; + DECLARE bracket_counter INT; - IF start_index = 0 THEN + -- Handle NULL inputs + IF json IS NULL OR key_name IS NULL THEN RETURN NULL; END IF; - SET start_index = start_index + CHAR_LENGTH(key_name); + -- Use native JSON functions for MySQL 8.0+ + IF fn_mamba_is_mysql8_or_higher() THEN + -- Use built-in JSON functions for better performance and accuracy + -- Check if the extracted value is an array + IF JSON_VALID(json) AND JSON_TYPE(JSON_EXTRACT(json, CONCAT('$.', key_name))) = 'ARRAY' THEN + -- Return the array without the surrounding quotes + RETURN JSON_EXTRACT(json, CONCAT('$.', key_name)); + ELSE + RETURN NULL; + END IF; + ELSE + -- Fallback for MySQL 5.7 and earlier - manual JSON parsing - IF SUBSTRING(json, start_index, 1) != '[' THEN - RETURN NULL; - END IF; + -- Basic validation - check if it looks like an object + IF NOT (json LIKE '{%}') THEN + RETURN NULL; + END IF; - SET start_index = start_index + 1; -- Start after the '[' - SET end_index = start_index; - - -- Loop to find the matching closing bracket for the array - SET @bracket_counter = 1; - WHILE @bracket_counter > 0 AND end_index <= CHAR_LENGTH(json) DO - SET end_index = end_index + 1; - IF SUBSTRING(json, end_index, 1) = '[' THEN - SET @bracket_counter = @bracket_counter + 1; - ELSEIF SUBSTRING(json, end_index, 1) = ']' THEN - SET @bracket_counter = @bracket_counter - 1; + -- Add the key structure to search for in the JSON string + SET key_name = CONCAT('"', key_name, '":'); + SET start_index = LOCATE(key_name, json); + + IF start_index = 0 THEN + RETURN NULL; END IF; - END WHILE; - IF @bracket_counter != 0 THEN - RETURN NULL; -- The brackets are not balanced, return NULL - END IF; + SET start_index = start_index + CHAR_LENGTH(key_name); -SET array_text = SUBSTRING(json, start_index, end_index - start_index); + -- Skip any whitespace + WHILE SUBSTRING(json, start_index, 1) IN (' ', '\t', '\n', '\r') + DO + SET start_index = start_index + 1; + END WHILE; -RETURN array_text; + -- Check if we're looking at an array + IF SUBSTRING(json, start_index, 1) != '[' THEN + RETURN NULL; + END IF; + + -- Find the matching closing bracket + SET start_index = start_index; -- Keep the opening bracket + SET end_index = start_index; + SET bracket_counter = 1; + + WHILE bracket_counter > 0 AND end_index < CHAR_LENGTH(json) + DO + SET end_index = end_index + 1; + IF SUBSTRING(json, end_index, 1) = '[' THEN + SET bracket_counter = bracket_counter + 1; + ELSEIF SUBSTRING(json, end_index, 1) = ']' THEN + SET bracket_counter = bracket_counter - 1; + END IF; + END WHILE; + + IF bracket_counter != 0 THEN + RETURN NULL; -- The brackets are not balanced, return NULL + END IF; + + -- Include the closing bracket in the result + SET array_text = SUBSTRING(json, start_index, end_index - start_index + 1); + + RETURN array_text; + END IF; END // DELIMITER ; \ No newline at end of file diff --git a/api/src/main/resources/_core/database/mysql/functions/fn_mamba_json_extract_object.sql b/api/src/main/resources/_core/database/mysql/functions/fn_mamba_json_extract_object.sql index 1fe52d7..7c846cb 100644 --- a/api/src/main/resources/_core/database/mysql/functions/fn_mamba_json_extract_object.sql +++ b/api/src/main/resources/_core/database/mysql/functions/fn_mamba_json_extract_object.sql @@ -1,20 +1,24 @@ DROP FUNCTION IF EXISTS fn_mamba_json_extract_object; + DELIMITER // -CREATE FUNCTION fn_mamba_json_extract_object(json_string TEXT, key_name VARCHAR(255)) RETURNS TEXT +CREATE FUNCTION fn_mamba_json_extract_object(json_string TEXT, key_name VARCHAR(255)) + RETURNS TEXT DETERMINISTIC + COMMENT 'Extracts a JSON object from a JSON string by key name' + BEGIN - DECLARE start_index INT; - DECLARE end_index INT; - DECLARE nested_level INT DEFAULT 0; - DECLARE substring_length INT; - DECLARE key_str VARCHAR(255); - DECLARE result TEXT DEFAULT ''; + DECLARE start_index INT; + DECLARE end_index INT; + DECLARE nested_level INT DEFAULT 0; + DECLARE substring_length INT; + DECLARE key_str VARCHAR(255); + DECLARE result TEXT DEFAULT ''; - SET key_str := CONCAT('"', key_name, '": {'); + SET key_str := CONCAT('"', key_name, '": {'); - -- Find the start position of the key - SET start_index := LOCATE(key_str, json_string); + -- Find the start position of the key + SET start_index := LOCATE(key_str, json_string); IF start_index = 0 THEN RETURN NULL; END IF; @@ -26,25 +30,26 @@ BEGIN SET end_index := start_index; -- Find the end of the object - WHILE nested_level >= 0 AND end_index <= CHAR_LENGTH(json_string) DO - SET end_index := end_index + 1; - SET substring_length := end_index - start_index; - - -- Check for nested objects - IF SUBSTRING(json_string, end_index, 1) = '{' THEN - SET nested_level := nested_level + 1; - ELSEIF SUBSTRING(json_string, end_index, 1) = '}' THEN - SET nested_level := nested_level - 1; - END IF; - END WHILE; + WHILE nested_level >= 0 AND end_index <= CHAR_LENGTH(json_string) + DO + SET end_index := end_index + 1; + SET substring_length := end_index - start_index; + + -- Check for nested objects + IF SUBSTRING(json_string, end_index, 1) = '{' THEN + SET nested_level := nested_level + 1; + ELSEIF SUBSTRING(json_string, end_index, 1) = '}' THEN + SET nested_level := nested_level - 1; + END IF; + END WHILE; -- Get the JSON object IF nested_level < 0 THEN - -- We found a matching pair of curly braces + -- We found a matching pair of curly braces SET result := SUBSTRING(json_string, start_index, substring_length); END IF; -RETURN result; + RETURN result; END // DELIMITER ; \ No newline at end of file diff --git a/api/src/main/resources/_core/database/mysql/functions/fn_mamba_json_keys_array.sql b/api/src/main/resources/_core/database/mysql/functions/fn_mamba_json_keys_array.sql index 2997667..a8a403b 100644 --- a/api/src/main/resources/_core/database/mysql/functions/fn_mamba_json_keys_array.sql +++ b/api/src/main/resources/_core/database/mysql/functions/fn_mamba_json_keys_array.sql @@ -1,9 +1,13 @@ - DROP FUNCTION IF EXISTS fn_mamba_json_keys_array; + DELIMITER // -CREATE FUNCTION fn_mamba_json_keys_array(json_object TEXT) RETURNS TEXT +CREATE FUNCTION fn_mamba_json_keys_array(json_object TEXT) + RETURNS TEXT DETERMINISTIC + READS SQL DATA + COMMENT 'Extracts all keys from a JSON object and returns them as a JSON array' + BEGIN DECLARE finished INT DEFAULT 0; DECLARE start_index INT DEFAULT 1; @@ -13,51 +17,85 @@ BEGIN DECLARE json_length INT; DECLARE key_end_index INT; - SET json_length = CHAR_LENGTH(json_object); - - -- Initialize the my_keys string as an empty 'array' - SET my_keys = ''; - - -- This loop goes through the JSON object and extracts the my_keys - WHILE NOT finished DO - -- Find the start of the key - SET start_index = LOCATE('"', json_object, end_index); - IF start_index = 0 OR start_index >= json_length THEN - SET finished = 1; - ELSE - -- Find the end of the key - SET end_index = LOCATE('"', json_object, start_index + 1); - SET key_name = SUBSTRING(json_object, start_index + 1, end_index - start_index - 1); - - -- Append the key to the 'array' of my_keys - IF my_keys = '' - THEN - SET my_keys = CONCAT('["', key_name, '"'); - ELSE - SET my_keys = CONCAT(my_keys, ',"', key_name, '"'); - END IF; + -- Handle NULL input + IF json_object IS NULL THEN + RETURN NULL; + END IF; - -- Move past the current key-value pair - SET key_end_index = LOCATE(',', json_object, end_index); - IF key_end_index = 0 THEN - SET key_end_index = LOCATE('}', json_object, end_index); - END IF; - IF key_end_index = 0 THEN - -- Closing brace not found - malformed JSON + -- Use native JSON functions for MySQL 8.0+ + IF fn_mamba_is_mysql8_or_higher() THEN + -- Check if input is a valid JSON object + IF JSON_VALID(json_object) AND JSON_TYPE(json_object) = 'OBJECT' THEN + RETURN JSON_KEYS(json_object); + ELSE + RETURN NULL; + END IF; + ELSE + -- Fallback for MySQL 5.7 and earlier - manual extraction + + -- Basic validation - check if it looks like an object + IF NOT (json_object LIKE '{%}') THEN + RETURN NULL; + END IF; + + SET json_length = CHAR_LENGTH(json_object); + + -- Initialize the my_keys string as an empty 'array' + SET my_keys = ''; + + -- This loop goes through the JSON object and extracts the keys + WHILE NOT finished + DO + -- Find the start of the key + SET start_index = LOCATE('"', json_object, end_index); + IF start_index = 0 OR start_index >= json_length THEN SET finished = 1; ELSE - -- Prepare for the next iteration - SET end_index = key_end_index + 1; + -- Find the end of the key + SET end_index = LOCATE('"', json_object, start_index + 1); + IF end_index = 0 THEN + -- Malformed JSON - missing closing quote + SET finished = 1; + ELSE + -- Check if this is actually a key (followed by a colon) + IF LOCATE(':', json_object, end_index) = end_index + 1 THEN + SET key_name = SUBSTRING(json_object, start_index + 1, end_index - start_index - 1); + + -- Append the key to the 'array' of keys + IF my_keys = '' THEN + SET my_keys = CONCAT('["', key_name, '"'); + ELSE + SET my_keys = CONCAT(my_keys, ',"', key_name, '"'); + END IF; + + -- Move past the current key-value pair + SET key_end_index = LOCATE(',', json_object, end_index); + IF key_end_index = 0 THEN + SET key_end_index = LOCATE('}', json_object, end_index); + END IF; + IF key_end_index = 0 THEN + -- Closing brace not found - malformed JSON + SET finished = 1; + ELSE + -- Prepare for the next iteration + SET end_index = key_end_index + 1; + END IF; + ELSE + -- This is not a key (no colon after the quote) + -- Move to the next quote + SET end_index = end_index + 1; + END IF; + END IF; END IF; - END IF; - END WHILE; + END WHILE; - -- Close the 'array' of my_keys - IF my_keys != '' THEN - SET my_keys = CONCAT(my_keys, ']'); - END IF; + -- Close the 'array' of keys + IF my_keys != '' THEN + SET my_keys = CONCAT(my_keys, ']'); + END IF; - RETURN my_keys; + RETURN my_keys; + END IF; END // DELIMITER ; \ No newline at end of file diff --git a/api/src/main/resources/_core/database/mysql/functions/fn_mamba_json_length.sql b/api/src/main/resources/_core/database/mysql/functions/fn_mamba_json_length.sql index 5f37e03..17de5a0 100644 --- a/api/src/main/resources/_core/database/mysql/functions/fn_mamba_json_length.sql +++ b/api/src/main/resources/_core/database/mysql/functions/fn_mamba_json_length.sql @@ -1,22 +1,53 @@ DROP FUNCTION IF EXISTS fn_mamba_json_length; + DELIMITER // -CREATE FUNCTION fn_mamba_json_length(json_array TEXT) RETURNS INT +CREATE FUNCTION fn_mamba_json_length(json_array TEXT) + RETURNS INT DETERMINISTIC + READS SQL DATA + COMMENT 'Returns the number of elements in a JSON array' + BEGIN DECLARE element_count INT DEFAULT 0; DECLARE current_position INT DEFAULT 1; - WHILE current_position <= CHAR_LENGTH(json_array) DO - SET element_count = element_count + 1; - SET current_position = LOCATE(',', json_array, current_position) + 1; + -- Handle NULL input + IF json_array IS NULL THEN + RETURN 0; + END IF; + + -- Use native JSON functions for MySQL 8.0+ + IF fn_mamba_is_mysql8_or_higher() THEN + -- Check if input is a valid JSON array + IF JSON_VALID(json_array) AND JSON_TYPE(json_array) = 'ARRAY' THEN + RETURN JSON_LENGTH(json_array); + ELSE + RETURN 0; + END IF; + ELSE + -- Fallback for MySQL 5.7 and earlier - manual counting - IF current_position = 0 THEN - RETURN element_count; + -- Basic validation - check if it looks like an array + IF NOT (json_array LIKE '[%]') THEN + RETURN 0; END IF; - END WHILE; -RETURN element_count; + -- Count elements by counting commas and adding 1 + -- This is a simple approach that works for basic arrays + WHILE current_position <= CHAR_LENGTH(json_array) + DO + SET element_count = element_count + 1; + SET current_position = LOCATE(',', json_array, current_position) + 1; + + IF current_position = 1 THEN + -- No more commas found + RETURN element_count; + END IF; + END WHILE; + + RETURN element_count; + END IF; END // DELIMITER ; \ No newline at end of file diff --git a/api/src/main/resources/_core/database/mysql/functions/fn_mamba_json_object_at_index.sql b/api/src/main/resources/_core/database/mysql/functions/fn_mamba_json_object_at_index.sql index 188c92e..fcde18b 100644 --- a/api/src/main/resources/_core/database/mysql/functions/fn_mamba_json_object_at_index.sql +++ b/api/src/main/resources/_core/database/mysql/functions/fn_mamba_json_object_at_index.sql @@ -1,47 +1,67 @@ DROP FUNCTION IF EXISTS fn_mamba_json_object_at_index; + DELIMITER // -CREATE FUNCTION fn_mamba_json_object_at_index(json_array TEXT, index_pos INT) RETURNS TEXT +CREATE FUNCTION fn_mamba_json_object_at_index(json_array TEXT, index_pos INT) + RETURNS TEXT DETERMINISTIC + READS SQL DATA + COMMENT 'Extracts a JSON object at the specified index position from a JSON array' + BEGIN - DECLARE obj_start INT DEFAULT 1; - DECLARE obj_end INT DEFAULT 1; - DECLARE current_index INT DEFAULT 0; - DECLARE obj_text TEXT; + DECLARE obj_start INT DEFAULT 1; + DECLARE obj_end INT DEFAULT 1; + DECLARE current_index INT DEFAULT 0; + DECLARE obj_text TEXT; -- Handle negative index_pos or json_array being NULL IF index_pos < 1 OR json_array IS NULL THEN RETURN NULL; END IF; - -- Find the start of the requested object - WHILE obj_start < CHAR_LENGTH(json_array) AND current_index < index_pos DO - SET obj_start = LOCATE('{', json_array, obj_end); - - -- If we can't find a new object, return NULL - IF obj_start = 0 THEN - RETURN NULL; + -- Use native JSON functions for MySQL 8.0+ + IF fn_mamba_is_mysql8_or_higher() THEN + -- Check if input is a valid JSON array + IF JSON_TYPE(json_array) = 'ARRAY' THEN + -- Arrays are 0-indexed in JSON_EXTRACT, but 1-indexed in our function + IF index_pos <= JSON_LENGTH(json_array) THEN + RETURN JSON_EXTRACT(json_array, CONCAT('$[', index_pos - 1, ']')); + END IF; END IF; + RETURN NULL; + ELSE + -- Fallback for MySQL 5.7 and earlier - manual JSON parsing - SET current_index = current_index + 1; - -- If this isn't the object we want, find the end and continue - IF current_index < index_pos THEN - SET obj_end = LOCATE('}', json_array, obj_start) + 1; - END IF; - END WHILE; + -- Find the start of the requested object + WHILE obj_start < CHAR_LENGTH(json_array) AND current_index < index_pos + DO + SET obj_start = LOCATE('{', json_array, obj_end); - -- Now obj_start points to the start of the desired object - -- Find the end of it - SET obj_end = LOCATE('}', json_array, obj_start); - IF obj_end = 0 THEN - -- The object is not well-formed - RETURN NULL; - END IF; + -- If we can't find a new object, return NULL + IF obj_start = 0 THEN + RETURN NULL; + END IF; + + SET current_index = current_index + 1; + -- If this isn't the object we want, find the end and continue + IF current_index < index_pos THEN + SET obj_end = LOCATE('}', json_array, obj_start) + 1; + END IF; + END WHILE; - -- Extract the object - SET obj_text = SUBSTRING(json_array, obj_start, obj_end - obj_start + 1); + -- Now obj_start points to the start of the desired object + -- Find the end of it + SET obj_end = LOCATE('}', json_array, obj_start); + IF obj_end = 0 THEN + -- The object is not well-formed + RETURN NULL; + END IF; -RETURN obj_text; + -- Extract the object + SET obj_text = SUBSTRING(json_array, obj_start, obj_end - obj_start + 1); + + RETURN obj_text; + END IF; END // DELIMITER ; \ No newline at end of file diff --git a/api/src/main/resources/_core/database/mysql/functions/fn_mamba_json_value_by_key.sql b/api/src/main/resources/_core/database/mysql/functions/fn_mamba_json_value_by_key.sql index 51d5091..ffb9b9f 100644 --- a/api/src/main/resources/_core/database/mysql/functions/fn_mamba_json_value_by_key.sql +++ b/api/src/main/resources/_core/database/mysql/functions/fn_mamba_json_value_by_key.sql @@ -1,8 +1,13 @@ DROP FUNCTION IF EXISTS fn_mamba_json_value_by_key; + DELIMITER // -CREATE FUNCTION fn_mamba_json_value_by_key(json TEXT, key_name VARCHAR(255)) RETURNS VARCHAR(255) +CREATE FUNCTION fn_mamba_json_value_by_key(json TEXT, key_name VARCHAR(255)) + RETURNS VARCHAR(255) DETERMINISTIC + READS SQL DATA + COMMENT 'Extracts a value from a JSON string by key name' + BEGIN DECLARE start_index INT; DECLARE end_index INT; @@ -11,49 +16,62 @@ BEGIN DECLARE value_length INT; DECLARE extracted_value VARCHAR(255); - -- Add the key structure to search for in the JSON string - SET key_name = CONCAT('"', key_name, '":'); - SET key_length = CHAR_LENGTH(key_name); - - -- Locate the key within the JSON string - SET key_index = LOCATE(key_name, json); - - -- If the key is not found, return NULL - IF key_index = 0 THEN + -- Handle NULL inputs + IF json IS NULL OR key_name IS NULL THEN RETURN NULL; END IF; - -- Set the starting index of the value - SET start_index = key_index + key_length; + -- Use JSON_EXTRACT for MySQL 8.0+ + IF fn_mamba_is_mysql8_or_higher() THEN + -- Use built-in JSON functions for better performance and accuracy + RETURN JSON_UNQUOTE(JSON_EXTRACT(json, CONCAT('$.', key_name))); + ELSE + -- Fallback for MySQL 5.7 and earlier - manual JSON parsing + + -- Add the key structure to search for in the JSON string + SET key_name = CONCAT('"', key_name, '":'); + SET key_length = CHAR_LENGTH(key_name); - -- Check if the value is a string (starts with a quote) - IF SUBSTRING(json, start_index, 1) = '"' THEN - -- Set the start index to the first character of the value (skipping the quote) - SET start_index = start_index + 1; + -- Locate the key within the JSON string + SET key_index = LOCATE(key_name, json); - -- Find the end of the string value (the next quote) - SET end_index = LOCATE('"', json, start_index); - IF end_index = 0 THEN - -- If there's no end quote, the JSON is malformed + -- If the key is not found, return NULL + IF key_index = 0 THEN RETURN NULL; END IF; - ELSE - -- The value is not a string (e.g., a number, boolean, or null) - -- Find the end of the value (either a comma or closing brace) - SET end_index = LOCATE(',', json, start_index); - IF end_index = 0 THEN - SET end_index = LOCATE('}', json, start_index); + + -- Set the starting index of the value + SET start_index = key_index + key_length; + + -- Check if the value is a string (starts with a quote) + IF SUBSTRING(json, start_index, 1) = '"' THEN + -- Set the start index to the first character of the value (skipping the quote) + SET start_index = start_index + 1; + + -- Find the end of the string value (the next quote) + SET end_index = LOCATE('"', json, start_index); + IF end_index = 0 THEN + -- If there's no end quote, the JSON is malformed + RETURN NULL; + END IF; + ELSE + -- The value is not a string (e.g., a number, boolean, or null) + -- Find the end of the value (either a comma or closing brace) + SET end_index = LOCATE(',', json, start_index); + IF end_index = 0 THEN + SET end_index = LOCATE('}', json, start_index); + END IF; END IF; - END IF; - -- Calculate the length of the extracted value - SET value_length = end_index - start_index; + -- Calculate the length of the extracted value + SET value_length = end_index - start_index; - -- Extract the value - SET extracted_value = SUBSTRING(json, start_index, value_length); + -- Extract the value + SET extracted_value = SUBSTRING(json, start_index, value_length); - -- Return the extracted value without leading or trailing quotes -RETURN TRIM(BOTH '"' FROM extracted_value); -END // + -- Return the extracted value without leading or trailing quotes + RETURN TRIM(BOTH '"' FROM extracted_value); + END IF; +END // DELIMITER ; \ No newline at end of file diff --git a/api/src/main/resources/_core/database/mysql/functions/fn_mamba_remove_all_whitespace.sql b/api/src/main/resources/_core/database/mysql/functions/fn_mamba_remove_all_whitespace.sql index 7e9a605..57e010f 100644 --- a/api/src/main/resources/_core/database/mysql/functions/fn_mamba_remove_all_whitespace.sql +++ b/api/src/main/resources/_core/database/mysql/functions/fn_mamba_remove_all_whitespace.sql @@ -1,21 +1,37 @@ DROP FUNCTION IF EXISTS fn_mamba_remove_all_whitespace; + DELIMITER // -CREATE FUNCTION fn_mamba_remove_all_whitespace(input_string TEXT) RETURNS TEXT +CREATE FUNCTION fn_mamba_remove_all_whitespace(input_string TEXT) + RETURNS TEXT DETERMINISTIC + COMMENT 'Removes all whitespace characters from input text' BEGIN - DECLARE cleaned_string TEXT; - SET cleaned_string = input_string; + DECLARE cleaned_string TEXT; + + -- Handle NULL input + IF input_string IS NULL THEN + RETURN NULL; + END IF; + + -- Use REGEXP_REPLACE for MySQL 8.0+ + IF fn_mamba_is_mysql8_or_higher() THEN + -- Remove all whitespace characters using regex + RETURN REGEXP_REPLACE(input_string, '\\s', ''); + ELSE + -- Fallback for MySQL 5.7 and earlier + SET cleaned_string = input_string; - -- Replace common whitespace characters - SET cleaned_string = REPLACE(cleaned_string, CHAR(9), ''); -- Horizontal tab - SET cleaned_string = REPLACE(cleaned_string, CHAR(10), ''); -- Line feed - SET cleaned_string = REPLACE(cleaned_string, CHAR(13), ''); -- Carriage return - SET cleaned_string = REPLACE(cleaned_string, CHAR(32), ''); -- Space - -- SET cleaned_string = REPLACE(cleaned_string, CHAR(160), ''); -- Non-breaking space + -- Replace common whitespace characters + SET cleaned_string = REPLACE(cleaned_string, CHAR(9), ''); -- Horizontal tab + SET cleaned_string = REPLACE(cleaned_string, CHAR(10), ''); -- Line feed + SET cleaned_string = REPLACE(cleaned_string, CHAR(13), ''); -- Carriage return + SET cleaned_string = REPLACE(cleaned_string, CHAR(32), ''); -- Space + SET cleaned_string = REPLACE(cleaned_string, CHAR(160), ''); -- Non-breaking space -RETURN TRIM(cleaned_string); + RETURN cleaned_string; + END IF; END // DELIMITER ; \ No newline at end of file diff --git a/api/src/main/resources/_core/database/mysql/functions/fn_mamba_remove_quotes.sql b/api/src/main/resources/_core/database/mysql/functions/fn_mamba_remove_quotes.sql index 7c534cc..750c76c 100644 --- a/api/src/main/resources/_core/database/mysql/functions/fn_mamba_remove_quotes.sql +++ b/api/src/main/resources/_core/database/mysql/functions/fn_mamba_remove_quotes.sql @@ -1,15 +1,30 @@ DROP FUNCTION IF EXISTS fn_mamba_remove_quotes; + DELIMITER // -CREATE FUNCTION fn_mamba_remove_quotes(original TEXT) RETURNS TEXT +CREATE FUNCTION fn_mamba_remove_quotes(original TEXT) + RETURNS TEXT DETERMINISTIC + READS SQL DATA + COMMENT 'Removes quotes from input text' + BEGIN - DECLARE without_quotes TEXT; + DECLARE without_quotes TEXT; + + IF original IS NULL THEN + RETURN NULL; + END IF; - -- Replace both single and double quotes with nothing - SET without_quotes = REPLACE(REPLACE(original, '"', ''), '''', ''); + -- Use REGEXP_REPLACE for MySQL 8.0+ + IF fn_mamba_is_mysql8_or_higher() THEN + -- Remove single and double quotes using regex + SET without_quotes = REGEXP_REPLACE(original, '[\'"]', ''); + ELSE + -- Replace both single and double quotes with nothing for older MySQL versions + SET without_quotes = REPLACE(REPLACE(original, '"', ''), '''', ''); + END IF; -RETURN fn_mamba_remove_all_whitespace(without_quotes); + RETURN fn_mamba_remove_all_whitespace(without_quotes); END // DELIMITER ; \ No newline at end of file diff --git a/api/src/main/resources/_core/database/mysql/functions/fn_mamba_remove_special_characters.sql b/api/src/main/resources/_core/database/mysql/functions/fn_mamba_remove_special_characters.sql index 6505827..6048b73 100644 --- a/api/src/main/resources/_core/database/mysql/functions/fn_mamba_remove_special_characters.sql +++ b/api/src/main/resources/_core/database/mysql/functions/fn_mamba_remove_special_characters.sql @@ -5,7 +5,7 @@ DELIMITER // CREATE FUNCTION fn_mamba_remove_special_characters(input_text VARCHAR(255)) RETURNS VARCHAR(255) DETERMINISTIC - NO SQL + READS SQL DATA COMMENT 'Removes special characters from input text' BEGIN DECLARE modified_string VARCHAR(255); @@ -17,20 +17,27 @@ BEGIN RETURN NULL; END IF; - SET modified_string = input_text; - - -- Define special characters to remove - SET special_chars = '!@#$%^&*?/,()"-=+£:;><ã\\|[]{}\'~`.'; -- TODO: Added '.' xter as well but Remove after adding backtick support - - -- Remove each special character - WHILE char_index <= CHAR_LENGTH(special_chars) DO - SET current_char = SUBSTRING(special_chars, char_index, 1); - SET modified_string = REPLACE(modified_string, current_char, ''); - SET char_index = char_index + 1; - END WHILE; - - -- Trim any leading or trailing spaces - RETURN TRIM(modified_string); + -- Use REGEXP_REPLACE for MySQL 8.0+ + IF fn_mamba_is_mysql8_or_higher() THEN + RETURN TRIM(REGEXP_REPLACE(input_text, '[^a-zA-Z0-9 ]', '')); + ELSE + -- Fallback for MySQL 5.7 and earlier + SET modified_string = input_text; + + -- Define special characters to remove + SET special_chars = '!@#$%^&*?/,()"-=+£:;><ã\\|[]{}\'~`.'; + + -- Remove each special character + WHILE char_index <= CHAR_LENGTH(special_chars) + DO + SET current_char = SUBSTRING(special_chars, char_index, 1); + SET modified_string = REPLACE(modified_string, current_char, ''); + SET char_index = char_index + 1; + END WHILE; + + -- Trim any leading or trailing spaces + RETURN TRIM(modified_string); + END IF; END // DELIMITER ; \ No newline at end of file diff --git a/api/src/main/resources/_core/database/mysql/sp_makefile b/api/src/main/resources/_core/database/mysql/sp_makefile index 14ba9fd..242d3e2 100644 --- a/api/src/main/resources/_core/database/mysql/sp_makefile +++ b/api/src/main/resources/_core/database/mysql/sp_makefile @@ -21,6 +21,7 @@ functions/fn_mamba_remove_all_whitespace.sql functions/fn_mamba_remove_quotes.sql functions/fn_mamba_remove_special_characters.sql functions/fn_mamba_collapse_spaces.sql +functions/fn_mamba_is_mysql8_or_higher.sql stored-procedures/sp_xf_system_drop_all_functions_in_schema.sql stored-procedures/sp_xf_system_drop_all_stored_procedures_in_schema.sql