package sa import ( "fmt" "regexp" ) var ( checkStringQuoteRE = regexp.MustCompile(`^'[0-9A-Za-z_\-=:]+'$`) checkIntRE = regexp.MustCompile(`^\d+$`) checkImproperIntRE = regexp.MustCompile(`^'\d+'$`) checkNumericRE = regexp.MustCompile(`^\d+(\.\d+)?$`) checkBooleanRE = regexp.MustCompile(`^([0-1])|(?i)(true|false)|(?i)(on|off)`) ) // checkMariaDBSystemVariables validates a MariaDB config passed in via SA // setDefault or DSN. This manually curated list of system variables was // partially generated by a tool in issue #6687. An overview of the validations // performed are: // // - Correct quoting for strings and string enums prevent future // problems such as PR #6683 from occurring. // // - Regex validation is performed for the various booleans, floats, integers, and strings. // // Only session scoped variables should be included. A session variable is one // that affects the current session only. Passing a session variable that only // works in the global scope causes database connection error 1045. // https://mariadb.com/kb/en/set/#global-session func checkMariaDBSystemVariables(name string, value string) error { // System variable names will be indexed into the appropriate hash sets // below and can possibly exist in several sets. // Check the list of currently known MariaDB string type system variables // and determine if the value is a properly formatted string e.g. // sql_mode='STRICT_TABLES' mariaDBStringTypes := map[string]struct{}{ "character_set_client": {}, "character_set_connection": {}, "character_set_database": {}, "character_set_filesystem": {}, "character_set_results": {}, "character_set_server": {}, "collation_connection": {}, "collation_database": {}, "collation_server": {}, "debug/debug_dbug": {}, "debug_sync": {}, "enforce_storage_engine": {}, "external_user": {}, "lc_messages": {}, "lc_time_names": {}, "old_alter_table": {}, "old_mode": {}, "optimizer_switch": {}, "proxy_user": {}, "session_track_system_variables": {}, "sql_mode": {}, "time_zone": {}, } if _, found := mariaDBStringTypes[name]; found { if checkStringQuoteRE.FindString(value) != value { return fmt.Errorf("%s=%s string is not properly quoted", name, value) } return nil } // MariaDB numerics which may either be integers or floats. // https://mariadb.com/kb/en/numeric-data-type-overview/ mariaDBNumericTypes := map[string]struct{}{ "bulk_insert_buffer_size": {}, "default_week_format": {}, "eq_range_index_dive_limit": {}, "error_count": {}, "expensive_subquery_limit": {}, "group_concat_max_len": {}, "histogram_size": {}, "idle_readonly_transaction_timeout": {}, "idle_transaction_timeout": {}, "idle_write_transaction_timeout": {}, "in_predicate_conversion_threshold": {}, "insert_id": {}, "interactive_timeout": {}, "join_buffer_size": {}, "join_buffer_space_limit": {}, "join_cache_level": {}, "last_insert_id": {}, "lock_wait_timeout": {}, "log_slow_min_examined_row_limit": {}, "log_slow_query_time": {}, "log_slow_rate_limit": {}, "long_query_time": {}, "max_allowed_packet": {}, "max_delayed_threads": {}, "max_digest_length": {}, "max_error_count": {}, "max_heap_table_size": {}, "max_join_size": {}, "max_length_for_sort_data": {}, "max_recursive_iterations": {}, "max_rowid_filter_size": {}, "max_seeks_for_key": {}, "max_session_mem_used": {}, "max_sort_length": {}, "max_sp_recursion_depth": {}, "max_statement_time": {}, "max_user_connections": {}, "min_examined_row_limit": {}, "mrr_buffer_size": {}, "net_buffer_length": {}, "net_read_timeout": {}, "net_retry_count": {}, "net_write_timeout": {}, "optimizer_extra_pruning_depth": {}, "optimizer_max_sel_arg_weight": {}, "optimizer_prune_level": {}, "optimizer_search_depth": {}, "optimizer_selectivity_sampling_limit": {}, "optimizer_trace_max_mem_size": {}, "optimizer_use_condition_selectivity": {}, "preload_buffer_size": {}, "profiling_history_size": {}, "progress_report_time": {}, "pseudo_slave_mode": {}, "pseudo_thread_id": {}, "query_alloc_block_size": {}, "query_prealloc_size": {}, "rand_seed1": {}, "range_alloc_block_size": {}, "read_rnd_buffer_size": {}, "rowid_merge_buff_size": {}, "sql_select_limit": {}, "tmp_disk_table_size": {}, "tmp_table_size": {}, "transaction_alloc_block_size": {}, "transaction_prealloc_size": {}, "wait_timeout": {}, "warning_count": {}, } if _, found := mariaDBNumericTypes[name]; found { if checkNumericRE.FindString(value) != value { return fmt.Errorf("%s=%s requires a numeric value, but is not formatted like a number", name, value) } return nil } // Certain MariaDB enums can have both string and integer values. mariaDBIntEnumTypes := map[string]struct{}{ "completion_type": {}, "query_cache_type": {}, } mariaDBStringEnumTypes := map[string]struct{}{ "completion_type": {}, "default_regex_flags": {}, "default_storage_engine": {}, "default_tmp_storage_engine": {}, "histogram_type": {}, "log_slow_filter": {}, "log_slow_verbosity": {}, "optimizer_trace": {}, "query_cache_type": {}, "session_track_transaction_info": {}, "transaction_isolation": {}, "tx_isolation": {}, "use_stat_tables": {}, } // Check the list of currently known MariaDB enumeration type system // variables and determine if the value is either: // 1) A properly formatted integer e.g. completion_type=1 if _, found := mariaDBIntEnumTypes[name]; found { if checkIntRE.FindString(value) == value { return nil } if checkImproperIntRE.FindString(value) == value { return fmt.Errorf("%s=%s integer enum is quoted, but should not be", name, value) } } // 2) A properly formatted string e.g. completion_type='CHAIN' if _, found := mariaDBStringEnumTypes[name]; found { if checkStringQuoteRE.FindString(value) != value { return fmt.Errorf("%s=%s string enum is not properly quoted", name, value) } return nil } // MariaDB booleans can be (0, false) or (1, true). // https://mariadb.com/kb/en/boolean/ mariaDBBooleanTypes := map[string]struct{}{ "autocommit": {}, "big_tables": {}, "check_constraint_checks": {}, "foreign_key_checks": {}, "in_transaction": {}, "keep_files_on_create": {}, "log_slow_query": {}, "low_priority_updates": {}, "old": {}, "old_passwords": {}, "profiling": {}, "query_cache_strip_comments": {}, "query_cache_wlock_invalidate": {}, "session_track_schema": {}, "session_track_state_change": {}, "slow_query_log": {}, "sql_auto_is_null": {}, "sql_big_selects": {}, "sql_buffer_result": {}, "sql_if_exists": {}, "sql_log_off": {}, "sql_notes": {}, "sql_quote_show_create": {}, "sql_safe_updates": {}, "sql_warnings": {}, "standard_compliant_cte": {}, "tcp_nodelay": {}, "transaction_read_only": {}, "tx_read_only": {}, "unique_checks": {}, "updatable_views_with_limit": {}, } if _, found := mariaDBBooleanTypes[name]; found { if checkBooleanRE.FindString(value) != value { return fmt.Errorf("%s=%s expected boolean value", name, value) } return nil } return fmt.Errorf("%s=%s was unexpected", name, value) }