diff options
Diffstat (limited to 'sql/sql_error.h')
-rw-r--r-- | sql/sql_error.h | 1336 |
1 files changed, 1336 insertions, 0 deletions
diff --git a/sql/sql_error.h b/sql/sql_error.h new file mode 100644 index 00000000..b5afdf9b --- /dev/null +++ b/sql/sql_error.h @@ -0,0 +1,1336 @@ +/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2017, MariaDB Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef SQL_ERROR_H +#define SQL_ERROR_H + +#include "sql_list.h" /* Sql_alloc, MEM_ROOT, list */ +#include "sql_type_int.h" // Longlong_hybrid +#include "sql_string.h" /* String */ +#include "sql_plist.h" /* I_P_List */ +#include "mysql_com.h" /* MYSQL_ERRMSG_SIZE */ +#include "my_time.h" /* MYSQL_TIME */ +#include "decimal.h" + +class THD; +class my_decimal; +class sp_condition_value; + +/* Types of LOG warnings, used by note_verbosity */ + +#define NOTE_VERBOSITY_NORMAL (1U << 0) +/* Show warnings about keys parts that cannot be used */ +#define NOTE_VERBOSITY_UNUSABLE_KEYS (1U << 1) +/* Show warnings in explain for key parts that cannot be used */ +#define NOTE_VERBOSITY_EXPLAIN (1U << 2) + +/////////////////////////////////////////////////////////////////////////// + +class Sql_state +{ +protected: + /** + This member is always NUL terminated. + */ + char m_sqlstate[SQLSTATE_LENGTH + 1]; +public: + Sql_state() + { + memset(m_sqlstate, 0, sizeof(m_sqlstate)); + } + + Sql_state(const char *sqlstate) + { + set_sqlstate(sqlstate); + } + + const char* get_sqlstate() const + { return m_sqlstate; } + + void set_sqlstate(const Sql_state *other) + { + *this= *other; + } + void set_sqlstate(const char *sqlstate) + { + memcpy(m_sqlstate, sqlstate, SQLSTATE_LENGTH); + m_sqlstate[SQLSTATE_LENGTH]= '\0'; + } + bool eq(const Sql_state *other) const + { + return strcmp(m_sqlstate, other->m_sqlstate) == 0; + } + + bool has_sql_state() const { return m_sqlstate[0] != '\0'; } + + /** + Checks if this SQL state defines a WARNING condition. + Note: m_sqlstate must contain a valid SQL-state. + + @retval true if this SQL state defines a WARNING condition. + @retval false otherwise. + */ + inline bool is_warning() const + { return m_sqlstate[0] == '0' && m_sqlstate[1] == '1'; } + + + /** + Checks if this SQL state defines a NOT FOUND condition. + Note: m_sqlstate must contain a valid SQL-state. + + @retval true if this SQL state defines a NOT FOUND condition. + @retval false otherwise. + */ + inline bool is_not_found() const + { return m_sqlstate[0] == '0' && m_sqlstate[1] == '2'; } + + + /** + Checks if this SQL state defines an EXCEPTION condition. + Note: m_sqlstate must contain a valid SQL-state. + + @retval true if this SQL state defines an EXCEPTION condition. + @retval false otherwise. + */ + inline bool is_exception() const + { return m_sqlstate[0] != '0' || m_sqlstate[1] > '2'; } + +}; + + +class Sql_state_errno: public Sql_state +{ +protected: + /** + MySQL extension, MYSQL_ERRNO condition item. + SQL error number. One of ER_ codes from share/errmsg.txt. + Set by set_error_status. + */ + uint m_sql_errno; + +public: + Sql_state_errno() + :m_sql_errno(0) + { } + Sql_state_errno(uint sql_errno) + :m_sql_errno(sql_errno) + { } + Sql_state_errno(uint sql_errno, const char *sql_state) + :Sql_state(sql_state), + m_sql_errno(sql_errno) + { } + /** + Get the SQL_ERRNO of this condition. + @return the sql error number condition item. + */ + uint get_sql_errno() const + { return m_sql_errno; } + + void set(uint sql_errno, const char *sqlstate) + { + m_sql_errno= sql_errno; + set_sqlstate(sqlstate); + } + void clear() + { + m_sql_errno= 0; + } +}; + + +class Sql_state_errno_level: public Sql_state_errno +{ +public: + /* + Enumeration value describing the severity of the error. + + Note that these enumeration values must correspond to the indices + of the sql_print_message_handlers array. + */ + enum enum_warning_level + { WARN_LEVEL_NOTE, WARN_LEVEL_WARN, WARN_LEVEL_ERROR, WARN_LEVEL_END}; + +protected: + /** Severity (error, warning, note) of this condition. */ + enum_warning_level m_level; + + void assign_defaults(const Sql_state_errno *value); + +public: + /** + Get the error level of this condition. + @return the error level condition item. + */ + enum_warning_level get_level() const + { return m_level; } + + Sql_state_errno_level() + :m_level(WARN_LEVEL_ERROR) + { } + + Sql_state_errno_level(uint sqlerrno, const char* sqlstate, + enum_warning_level level) + :Sql_state_errno(sqlerrno, sqlstate), + m_level(level) + { } + Sql_state_errno_level(const Sql_state_errno &state_errno, + enum_warning_level level) + :Sql_state_errno(state_errno), + m_level(level) + { } + void clear() + { + m_level= WARN_LEVEL_ERROR; + Sql_state_errno::clear(); + } +}; + + +/* + class Sql_user_condition_identity. + Instances of this class uniquely idetify user defined conditions (EXCEPTION). + + SET sql_mode=ORACLE; + CREATE PROCEDURE p1 + AS + a EXCEPTION; + BEGIN + RAISE a; + EXCEPTION + WHEN a THEN NULL; + END; + + Currently a user defined condition is identified by a pointer to + its parse time sp_condition_value instance. This can change when + we add packages. See MDEV-10591. +*/ +class Sql_user_condition_identity +{ +protected: + const sp_condition_value *m_user_condition_value; +public: + Sql_user_condition_identity() + :m_user_condition_value(NULL) + { } + Sql_user_condition_identity(const sp_condition_value *value) + :m_user_condition_value(value) + { } + const sp_condition_value *get_user_condition_value() const + { return m_user_condition_value; } + + void set(const Sql_user_condition_identity &identity) + { + *this= identity; + } + void clear() + { + m_user_condition_value= NULL; + } +}; + + +/** + class Sql_condition_identity. + Instances of this class uniquely identify conditions + (including user-defined exceptions for sql_mode=ORACLE) + and store everything that is needed for handler search + purposes in sp_pcontext::find_handler(). +*/ +class Sql_condition_identity: public Sql_state_errno_level, + public Sql_user_condition_identity +{ +public: + Sql_condition_identity() = default; + Sql_condition_identity(const Sql_state_errno_level &st, + const Sql_user_condition_identity &ucid) + :Sql_state_errno_level(st), + Sql_user_condition_identity(ucid) + { } + Sql_condition_identity(const Sql_state_errno &st, + enum_warning_level level, + const Sql_user_condition_identity &ucid) + :Sql_state_errno_level(st, level), + Sql_user_condition_identity(ucid) + { } + Sql_condition_identity(uint sqlerrno, + const char* sqlstate, + enum_warning_level level, + const Sql_user_condition_identity &ucid) + :Sql_state_errno_level(sqlerrno, sqlstate, level), + Sql_user_condition_identity(ucid) + { } + void clear() + { + Sql_state_errno_level::clear(); + Sql_user_condition_identity::clear(); + } +}; + + +class Sql_condition_items +{ +protected: + /** SQL CLASS_ORIGIN condition item. */ + String m_class_origin; + + /** SQL SUBCLASS_ORIGIN condition item. */ + String m_subclass_origin; + + /** SQL CONSTRAINT_CATALOG condition item. */ + String m_constraint_catalog; + + /** SQL CONSTRAINT_SCHEMA condition item. */ + String m_constraint_schema; + + /** SQL CONSTRAINT_NAME condition item. */ + String m_constraint_name; + + /** SQL CATALOG_NAME condition item. */ + String m_catalog_name; + + /** SQL SCHEMA_NAME condition item. */ + String m_schema_name; + + /** SQL TABLE_NAME condition item. */ + String m_table_name; + + /** SQL COLUMN_NAME condition item. */ + String m_column_name; + + /** SQL CURSOR_NAME condition item. */ + String m_cursor_name; + + /** SQL ROW_NUMBER condition item. */ + ulong m_row_number; + + Sql_condition_items() + :m_class_origin((const char*) NULL, 0, & my_charset_utf8mb3_bin), + m_subclass_origin((const char*) NULL, 0, & my_charset_utf8mb3_bin), + m_constraint_catalog((const char*) NULL, 0, & my_charset_utf8mb3_bin), + m_constraint_schema((const char*) NULL, 0, & my_charset_utf8mb3_bin), + m_constraint_name((const char*) NULL, 0, & my_charset_utf8mb3_bin), + m_catalog_name((const char*) NULL, 0, & my_charset_utf8mb3_bin), + m_schema_name((const char*) NULL, 0, & my_charset_utf8mb3_bin), + m_table_name((const char*) NULL, 0, & my_charset_utf8mb3_bin), + m_column_name((const char*) NULL, 0, & my_charset_utf8mb3_bin), + m_cursor_name((const char*) NULL, 0, & my_charset_utf8mb3_bin), + m_row_number(0) + { } + + void clear() + { + m_class_origin.length(0); + m_subclass_origin.length(0); + m_constraint_catalog.length(0); + m_constraint_schema.length(0); + m_constraint_name.length(0); + m_catalog_name.length(0); + m_schema_name.length(0); + m_table_name.length(0); + m_column_name.length(0); + m_cursor_name.length(0); + m_row_number= 0; + } +}; + + +/** + Representation of a SQL condition. + A SQL condition can be a completion condition (note, warning), + or an exception condition (error, not found). +*/ +class Sql_condition : public Sql_alloc, + public Sql_condition_identity, + public Sql_condition_items +{ +public: + + /** + Convert a bitmask consisting of MYSQL_TIME_{NOTE|WARN}_XXX bits + to WARN_LEVEL_XXX + */ + static enum_warning_level time_warn_level(uint warnings) + { + return MYSQL_TIME_WARN_HAVE_WARNINGS(warnings) ? + WARN_LEVEL_WARN : WARN_LEVEL_NOTE; + } + + /** + Get the MESSAGE_TEXT of this condition. + @return the message text. + */ + const char* get_message_text() const; + + /** + Get the MESSAGE_OCTET_LENGTH of this condition. + @return the length in bytes of the message text. + */ + int get_message_octet_length() const; + +private: + /* + The interface of Sql_condition is mostly private, by design, + so that only the following code: + - various raise_error() or raise_warning() methods in class THD, + - the implementation of SIGNAL / RESIGNAL / GET DIAGNOSTICS + - catch / re-throw of SQL conditions in stored procedures (sp_rcontext) + is allowed to create / modify a SQL condition. + Enforcing this policy prevents confusion, since the only public + interface available to the rest of the server implementation + is the interface offered by the THD methods (THD::raise_error()), + which should be used. + */ + friend class THD; + friend class Warning_info; + friend class Sql_cmd_common_signal; + friend class Sql_cmd_signal; + friend class Sql_cmd_resignal; + friend class sp_rcontext; + friend class Condition_information_item; + + /** + Default constructor. + This constructor is usefull when allocating arrays. + Note that the init() method should be called to complete the Sql_condition. + */ + Sql_condition() + :m_mem_root(NULL) + { } + + /** + Complete the Sql_condition initialisation. + @param mem_root The memory root to use for the condition items + of this condition + */ + void init(MEM_ROOT *mem_root) + { + DBUG_ASSERT(mem_root != NULL); + DBUG_ASSERT(m_mem_root == NULL); + m_mem_root= mem_root; + } + + /** + Constructor. + @param mem_root The memory root to use for the condition items + of this condition + */ + Sql_condition(MEM_ROOT *mem_root) + :m_mem_root(mem_root) + { + DBUG_ASSERT(mem_root != NULL); + } + + Sql_condition(MEM_ROOT *mem_root, const Sql_user_condition_identity &ucid) + :Sql_condition_identity(Sql_state_errno_level(), ucid), + m_mem_root(mem_root) + { + DBUG_ASSERT(mem_root != NULL); + } + /** + Constructor for a fixed message text. + @param mem_root - memory root + @param value - the error number and the sql state for this condition + @param level - the error level for this condition + @param msg - the message text for this condition + */ + Sql_condition(MEM_ROOT *mem_root, const Sql_condition_identity &value, + const char *msg, ulong current_row_for_warning) + : Sql_condition_identity(value), m_mem_root(mem_root) + { + DBUG_ASSERT(value.get_sql_errno() != 0); + DBUG_ASSERT(msg != NULL); + set_builtin_message_text(msg); + m_row_number= current_row_for_warning; + } + + /** Destructor. */ + ~Sql_condition() = default; + + /** + Copy optional condition items attributes. + @param cond the condition to copy. + */ + void copy_opt_attributes(const Sql_condition *cond); + + /** + Set the condition message test. + @param str Message text, expressed in the character set derived from + the server --language option + */ + void set_builtin_message_text(const char* str); + + /** Set the CLASS_ORIGIN of this condition. */ + void set_class_origin(); + + /** Set the SUBCLASS_ORIGIN of this condition. */ + void set_subclass_origin(); + + /** + Assign the condition items 'MYSQL_ERRNO', 'level' and 'MESSAGE_TEXT' + default values of a condition. + @param thd - current thread, to access to localized error messages + @param from - copy condition items from here (can be NULL) + */ + void assign_defaults(THD *thd, const Sql_state_errno *from); + + /** + Clear this SQL condition. + */ + void clear() + { + Sql_condition_identity::clear(); + Sql_condition_items::clear(); + m_message_text.length(0); + } + +private: + /** Message text, expressed in the character set implied by --language. */ + String m_message_text; + + /** Pointers for participating in the list of conditions. */ + Sql_condition *next_in_wi; + Sql_condition **prev_in_wi; + + /** Memory root to use to hold condition item values. */ + MEM_ROOT *m_mem_root; +}; + +/////////////////////////////////////////////////////////////////////////// + +/** + Information about warnings of the current connection. +*/ +class Warning_info +{ + /** The type of the counted and doubly linked list of conditions. */ + typedef I_P_List<Sql_condition, + I_P_List_adapter<Sql_condition, + &Sql_condition::next_in_wi, + &Sql_condition::prev_in_wi>, + I_P_List_counter, + I_P_List_fast_push_back<Sql_condition> > + Sql_condition_list; + + /** A memory root to allocate warnings and errors */ + MEM_ROOT m_warn_root; + + /** List of warnings of all severities (levels). */ + Sql_condition_list m_warn_list; + + /** A break down of the number of warnings per severity (level). */ + uint m_warn_count[(uint) Sql_condition::WARN_LEVEL_END]; + + /** + The number of warnings of the current statement. Warning_info + life cycle differs from statement life cycle -- it may span + multiple statements. In that case we get + m_current_statement_warn_count 0, whereas m_warn_list is not empty. + */ + uint m_current_statement_warn_count; + + /* + Row counter, to print in errors and warnings. Not increased in + create_sort_index(); may differ from examined_row_count. + */ + ulong m_current_row_for_warning; + + /** Used to optionally clear warnings only once per statement. */ + ulonglong m_warn_id; + + /** + A pointer to an element of m_warn_list. It determines SQL-condition + instance which corresponds to the error state in Diagnostics_area. + + This is needed for properly processing SQL-conditions in SQL-handlers. + When an SQL-handler is found for the current error state in Diagnostics_area, + this pointer is needed to remove the corresponding SQL-condition from the + Warning_info list. + + @note m_error_condition might be NULL in the following cases: + - Diagnostics_area set to fatal error state (like OOM); + - Max number of Warning_info elements has been reached (thus, there is + no corresponding SQL-condition object in Warning_info). + */ + const Sql_condition *m_error_condition; + + /** Indicates if push_warning() allows unlimited number of warnings. */ + bool m_allow_unlimited_warnings; + bool initialized; /* Set to 1 if init() has been called */ + + /** Read only status. */ + bool m_read_only; + + /** Pointers for participating in the stack of Warning_info objects. */ + Warning_info *m_next_in_da; + Warning_info **m_prev_in_da; + + List<Sql_condition> m_marked_sql_conditions; + +public: + Warning_info(ulonglong warn_id_arg, bool allow_unlimited_warnings, + bool initialized); + ~Warning_info(); + /* Allocate memory for structures */ + void init(); + void free_memory(); + +private: + Warning_info(const Warning_info &rhs); /* Not implemented */ + Warning_info& operator=(const Warning_info &rhs); /* Not implemented */ + + /** + Checks if Warning_info contains SQL-condition with the given message. + + @param message_str Message string. + @param message_length Length of message string. + + @return true if the Warning_info contains an SQL-condition with the given + message. + */ + bool has_sql_condition(const char *message_str, size_t message_length) const; + + /** + Checks if Warning_info contains SQL-condition with the given error id + + @param sql_errno SQL-condition error number + + @return true if the Warning_info contains an SQL-condition with the given + error id. + */ + bool has_sql_condition(uint sql_errno) const; + + /** + Reset the warning information. Clear all warnings, + the number of warnings, reset current row counter + to point to the first row. + + @param new_id new Warning_info id. + */ + void clear(ulonglong new_id); + + /** + Only clear warning info if haven't yet done that already + for the current query. Allows to be issued at any time + during the query, without risk of clearing some warnings + that have been generated by the current statement. + + @todo: This is a sign of sloppy coding. Instead we need to + designate one place in a statement life cycle where we call + Warning_info::clear(). + + @param query_id Current query id. + */ + void opt_clear(ulonglong query_id) + { + if (query_id != m_warn_id) + clear(query_id); + } + + /** + Concatenate the list of warnings. + + It's considered tolerable to lose an SQL-condition in case of OOM-error, + or if the number of SQL-conditions in the Warning_info reached top limit. + + @param thd Thread context. + @param source Warning_info object to copy SQL-conditions from. + */ + void append_warning_info(THD *thd, const Warning_info *source); + + /** + Reset between two COM_ commands. Warnings are preserved + between commands, but statement_warn_count indicates + the number of warnings of this particular statement only. + */ + void reset_for_next_command() + { m_current_statement_warn_count= 0; } + + /** + Mark active SQL-conditions for later removal. + This is done to simulate stacked DAs for HANDLER statements. + */ + void mark_sql_conditions_for_removal(); + + /** + Unmark SQL-conditions, which were marked for later removal. + This is done to simulate stacked DAs for HANDLER statements. + */ + void unmark_sql_conditions_from_removal() + { m_marked_sql_conditions.empty(); } + + /** + Remove SQL-conditions that are marked for deletion. + This is done to simulate stacked DAs for HANDLER statements. + */ + void remove_marked_sql_conditions(); + + /** + Check if the given SQL-condition is marked for removal in this Warning_info + instance. + + @param cond the SQL-condition. + + @retval true if the given SQL-condition is marked for removal in this + Warning_info instance. + @retval false otherwise. + */ + bool is_marked_for_removal(const Sql_condition *cond) const; + + /** + Mark a single SQL-condition for removal (add the given SQL-condition to the + removal list of this Warning_info instance). + */ + void mark_condition_for_removal(Sql_condition *cond) + { m_marked_sql_conditions.push_back(cond, &m_warn_root); } + + /** + Used for @@warning_count system variable, which prints + the number of rows returned by SHOW WARNINGS. + */ + ulong warn_count() const + { + /* + This may be higher than warn_list.elements() if we have + had more warnings than thd->variables.max_error_count. + */ + return (m_warn_count[(uint) Sql_condition::WARN_LEVEL_NOTE] + + m_warn_count[(uint) Sql_condition::WARN_LEVEL_ERROR] + + m_warn_count[(uint) Sql_condition::WARN_LEVEL_WARN]); + } + + /** + The number of errors, or number of rows returned by SHOW ERRORS, + also the value of session variable @@error_count. + */ + ulong error_count() const + { return m_warn_count[(uint) Sql_condition::WARN_LEVEL_ERROR]; } + + /** + The number of conditions (errors, warnings and notes) in the list. + */ + uint cond_count() const + { + return m_warn_list.elements(); + } + + /** Id of the warning information area. */ + ulonglong id() const { return m_warn_id; } + + /** Set id of the warning information area. */ + void id(ulonglong id_arg) { m_warn_id= id_arg; } + + /** Do we have any errors and warnings that we can *show*? */ + bool is_empty() const { return m_warn_list.is_empty(); } + + /** Increment the current row counter to point at the next row. */ + void inc_current_row_for_warning() { m_current_row_for_warning++; } + + /** Reset the current row counter. Start counting from the first row. */ + void reset_current_row_for_warning(int n) { m_current_row_for_warning= n; } + + ulong set_current_row_for_warning(ulong row) + { + ulong old_row= m_current_row_for_warning; + m_current_row_for_warning= row; + return old_row; + } + + /** Return the current counter value. */ + ulong current_row_for_warning() const { return m_current_row_for_warning; } + + /** Return the number of warnings thrown by the current statement. */ + ulong current_statement_warn_count() const + { return m_current_statement_warn_count; } + + /** Make sure there is room for the given number of conditions. */ + void reserve_space(THD *thd, uint count); + + /** + Add a new SQL-condition to the current list and increment the respective + counters. + + @param thd Thread context. + @param identity SQL-condition identity + @param msg SQL-condition message. + + @return a pointer to the added SQL-condition. + */ + Sql_condition *push_warning(THD *thd, const Sql_condition_identity *identity, + const char* msg, ulong current_row_number); + + /** + Add a new SQL-condition to the current list and increment the respective + counters. + + @param thd Thread context. + @param sql_condition SQL-condition to copy values from. + + @return a pointer to the added SQL-condition. + */ + Sql_condition *push_warning(THD *thd, const Sql_condition *sql_condition); + + /** + Set the read only status for this statement area. + This is a privileged operation, reserved for the implementation of + diagnostics related statements, to enforce that the statement area is + left untouched during execution. + The diagnostics statements are: + - SHOW WARNINGS + - SHOW ERRORS + - GET DIAGNOSTICS + @param read_only the read only property to set. + */ + void set_read_only(bool read_only_arg) + { m_read_only= read_only_arg; } + + /** + Read only status. + @return the read only property. + */ + bool is_read_only() const + { return m_read_only; } + + /** + @return SQL-condition, which corresponds to the error state in + Diagnostics_area. + + @see m_error_condition. + */ + const Sql_condition *get_error_condition() const + { return m_error_condition; } + + /** + Set SQL-condition, which corresponds to the error state in Diagnostics_area. + + @see m_error_condition. + */ + void set_error_condition(const Sql_condition *error_condition) + { m_error_condition= error_condition; } + + /** + Reset SQL-condition, which corresponds to the error state in + Diagnostics_area. + + @see m_error_condition. + */ + void clear_error_condition() + { m_error_condition= NULL; } + + // for: + // - m_next_in_da / m_prev_in_da + // - is_marked_for_removal() + friend class Diagnostics_area; +}; + + +extern size_t err_conv(char *buff, uint to_length, const char *from, + uint from_length, CHARSET_INFO *from_cs); + +class ErrBuff +{ +protected: + mutable char err_buffer[MYSQL_ERRMSG_SIZE]; +public: + ErrBuff() + { + err_buffer[0]= '\0'; + } + const char *ptr() const { return err_buffer; } + LEX_CSTRING set_longlong(const Longlong_hybrid &nr) const + { + int radix= nr.is_unsigned() ? 10 : -10; + const char *end= longlong10_to_str(nr.value(), err_buffer, radix); + DBUG_ASSERT(end >= err_buffer); + return {err_buffer, (size_t) (end - err_buffer)}; + } + LEX_CSTRING set_double(double nr) const + { + size_t length= my_gcvt(nr, MY_GCVT_ARG_DOUBLE, + sizeof(err_buffer), err_buffer, 0); + return {err_buffer, length}; + } + LEX_CSTRING set_decimal(const decimal_t *d) const + { + int length= sizeof(err_buffer); + decimal2string(d, err_buffer, &length, 0, 0, ' '); + DBUG_ASSERT(length >= 0); + return {err_buffer, (size_t) length}; + } + LEX_CSTRING set_str(const char *str, size_t len, CHARSET_INFO *cs) const + { + DBUG_ASSERT(len < UINT_MAX32); + len= err_conv(err_buffer, (uint) sizeof(err_buffer), str, (uint) len, cs); + return {err_buffer, len}; + } + LEX_CSTRING set_mysql_time(const MYSQL_TIME *ltime) const + { + int length= my_TIME_to_str(ltime, err_buffer, AUTO_SEC_PART_DIGITS); + DBUG_ASSERT(length >= 0); + return {err_buffer, (size_t) length}; + } +}; + + +class ErrConv: public ErrBuff +{ +public: + ErrConv() = default; + virtual ~ErrConv() = default; + virtual LEX_CSTRING lex_cstring() const= 0; + inline const char *ptr() const + { + return lex_cstring().str; + } +}; + +class ErrConvString : public ErrConv +{ + const char *str; + size_t len; + CHARSET_INFO *cs; +public: + ErrConvString(const char *str_arg, size_t len_arg, CHARSET_INFO *cs_arg) + : ErrConv(), str(str_arg), len(len_arg), cs(cs_arg) {} + ErrConvString(const char *str_arg, CHARSET_INFO *cs_arg) + : ErrConv(), str(str_arg), len(strlen(str_arg)), cs(cs_arg) {} + ErrConvString(const String *s) + : ErrConv(), str(s->ptr()), len(s->length()), cs(s->charset()) {} + LEX_CSTRING lex_cstring() const override + { + return set_str(str, len, cs); + } +}; + +class ErrConvInteger : public ErrConv, public Longlong_hybrid +{ +public: + ErrConvInteger(const Longlong_hybrid &nr) + : ErrConv(), Longlong_hybrid(nr) { } + LEX_CSTRING lex_cstring() const override + { + return set_longlong(static_cast<Longlong_hybrid>(*this)); + } +}; + +class ErrConvDouble: public ErrConv +{ + double num; +public: + ErrConvDouble(double num_arg) : ErrConv(), num(num_arg) {} + LEX_CSTRING lex_cstring() const override + { + return set_double(num); + } +}; + +class ErrConvTime : public ErrConv +{ + const MYSQL_TIME *ltime; +public: + ErrConvTime(const MYSQL_TIME *ltime_arg) : ErrConv(), ltime(ltime_arg) {} + LEX_CSTRING lex_cstring() const override + { + return set_mysql_time(ltime); + } +}; + +class ErrConvDecimal : public ErrConv +{ + const decimal_t *d; +public: + ErrConvDecimal(const decimal_t *d_arg) : ErrConv(), d(d_arg) {} + LEX_CSTRING lex_cstring() const override + { + return set_decimal(d); + } +}; + +/////////////////////////////////////////////////////////////////////////// + +/** + Stores status of the currently executed statement. + Cleared at the beginning of the statement, and then + can hold either OK, ERROR, or EOF status. + Can not be assigned twice per statement. +*/ + +class Diagnostics_area: public Sql_state_errno, + public Sql_user_condition_identity +{ +private: + /** The type of the counted and doubly linked list of conditions. */ + typedef I_P_List<Warning_info, + I_P_List_adapter<Warning_info, + &Warning_info::m_next_in_da, + &Warning_info::m_prev_in_da>, + I_P_List_counter, + I_P_List_fast_push_back<Warning_info> > + Warning_info_list; + +public: + /** Const iterator used to iterate through the warning list. */ + typedef Warning_info::Sql_condition_list::Const_Iterator + Sql_condition_iterator; + + enum enum_diagnostics_status + { + /** The area is cleared at start of a statement. */ + DA_EMPTY= 0, + /** Set whenever one calls my_ok(). */ + DA_OK, + /** Set whenever one calls my_eof(). */ + DA_EOF, + /** Set whenever one calls my_ok() in PS bulk mode. */ + DA_OK_BULK, + /** Set whenever one calls my_eof() in PS bulk mode. */ + DA_EOF_BULK, + /** Set whenever one calls my_error() or my_message(). */ + DA_ERROR, + /** Set in case of a custom response, such as one from COM_STMT_PREPARE. */ + DA_DISABLED + }; + + void set_overwrite_status(bool can_overwrite_status) + { m_can_overwrite_status= can_overwrite_status; } + + /** True if status information is sent to the client. */ + bool is_sent() const { return m_is_sent; } + + void set_is_sent(bool is_sent_arg) { m_is_sent= is_sent_arg; } + + void set_ok_status(ulonglong affected_rows, + ulonglong last_insert_id, + const char *message); + + void set_eof_status(THD *thd); + + void set_error_status(uint sql_errno); + + void set_error_status(uint sql_errno, + const char *message, + const char *sqlstate, + const Sql_user_condition_identity &ucid, + const Sql_condition *error_condition); + + void set_error_status(uint sql_errno, + const char *message, + const char *sqlstate, + const Sql_condition *error_condition) + { + set_error_status(sql_errno, message, sqlstate, + Sql_user_condition_identity(), + error_condition); + } + + void disable_status(); + + void reset_diagnostics_area(); + + bool is_set() const { return m_status != DA_EMPTY; } + + bool is_error() const { return m_status == DA_ERROR; } + + bool is_eof() const { return m_status == DA_EOF; } + + bool is_ok() const { return m_status == DA_OK; } + + bool is_disabled() const { return m_status == DA_DISABLED; } + + void set_bulk_execution(bool bulk) { is_bulk_execution= bulk; } + + bool is_bulk_op() const { return is_bulk_execution; } + + enum_diagnostics_status status() const { return m_status; } + + const char *message() const + { + DBUG_ASSERT(m_status == DA_ERROR || m_status == DA_OK || + m_status == DA_OK_BULK || m_status == DA_EOF_BULK); + return m_message; + } + + + uint sql_errno() const + { + DBUG_ASSERT(m_status == DA_ERROR); + return Sql_state_errno::get_sql_errno(); + } + + const char* get_sqlstate() const + { DBUG_ASSERT(m_status == DA_ERROR); return Sql_state::get_sqlstate(); } + + ulonglong affected_rows() const + { + DBUG_ASSERT(m_status == DA_OK || m_status == DA_OK_BULK); + return m_affected_rows; + } + + ulonglong last_insert_id() const + { + DBUG_ASSERT(m_status == DA_OK || m_status == DA_OK_BULK); + return m_last_insert_id; + } + + uint statement_warn_count() const + { + DBUG_ASSERT(m_status == DA_OK || m_status == DA_OK_BULK || + m_status == DA_EOF ||m_status == DA_EOF_BULK ); + return m_statement_warn_count; + } + + uint unsafe_statement_warn_count() const + { + return m_statement_warn_count; + } + + /** + Get the current errno, state and id of the user defined condition + and return them as Sql_condition_identity. + */ + Sql_condition_identity get_error_condition_identity() const + { + DBUG_ASSERT(m_status == DA_ERROR); + return Sql_condition_identity(*this /*Sql_state_errno*/, + Sql_condition::WARN_LEVEL_ERROR, + *this /*Sql_user_condition_identity*/); + } + + /* Used to count any warnings pushed after calling set_ok_status(). */ + void increment_warning() + { + if (m_status != DA_EMPTY) + m_statement_warn_count++; + } + + Diagnostics_area(bool initialize); + Diagnostics_area(ulonglong warning_info_id, bool allow_unlimited_warnings, + bool initialize); + void init() { m_main_wi.init() ; } + void free_memory() { m_main_wi.free_memory() ; } + + void push_warning_info(Warning_info *wi) + { m_wi_stack.push_front(wi); } + + void pop_warning_info() + { + DBUG_ASSERT(m_wi_stack.elements() > 0); + m_wi_stack.remove(m_wi_stack.front()); + } + + void set_warning_info_id(ulonglong id) + { get_warning_info()->id(id); } + + ulonglong warning_info_id() const + { return get_warning_info()->id(); } + + /** + Compare given current warning info and current warning info + and see if they are different. They will be different if + warnings have been generated or statements that use tables + have been executed. This is checked by comparing m_warn_id. + + @param wi Warning info to compare with current Warning info. + + @return false if they are equal, true if they are not. + */ + bool warning_info_changed(const Warning_info *wi) const + { return get_warning_info()->id() != wi->id(); } + + bool is_warning_info_empty() const + { return get_warning_info()->is_empty(); } + + ulong current_statement_warn_count() const + { return get_warning_info()->current_statement_warn_count(); } + + bool has_sql_condition(const char *message_str, size_t message_length) const + { return get_warning_info()->has_sql_condition(message_str, message_length); } + + bool has_sql_condition(uint sql_errno) const + { return get_warning_info()->has_sql_condition(sql_errno); } + + void reset_for_next_command() + { get_warning_info()->reset_for_next_command(); } + + void clear_warning_info(ulonglong id) + { get_warning_info()->clear(id); } + + void opt_clear_warning_info(ulonglong query_id) + { get_warning_info()->opt_clear(query_id); } + + long set_current_row_for_warning(long row) + { return get_warning_info()->set_current_row_for_warning(row); } + + ulong current_row_for_warning() const + { return get_warning_info()->current_row_for_warning(); } + + void inc_current_row_for_warning() + { get_warning_info()->inc_current_row_for_warning(); } + + void reset_current_row_for_warning(int n) + { get_warning_info()->reset_current_row_for_warning(n); } + + bool is_warning_info_read_only() const + { return get_warning_info()->is_read_only(); } + + void set_warning_info_read_only(bool read_only_arg) + { get_warning_info()->set_read_only(read_only_arg); } + + ulong error_count() const + { return get_warning_info()->error_count(); } + + ulong warn_count() const + { return get_warning_info()->warn_count(); } + + uint cond_count() const + { return get_warning_info()->cond_count(); } + + Sql_condition_iterator sql_conditions() const + { return get_warning_info()->m_warn_list; } + + void reserve_space(THD *thd, uint count) + { get_warning_info()->reserve_space(thd, count); } + + Sql_condition *push_warning(THD *thd, const Sql_condition *sql_condition) + { return get_warning_info()->push_warning(thd, sql_condition); } + + Sql_condition *push_warning(THD *thd, + uint sql_errno_arg, + const char* sqlstate, + Sql_condition::enum_warning_level level, + const Sql_user_condition_identity &ucid, + const char* msg, + ulong current_row_number) + { + Sql_condition_identity tmp(sql_errno_arg, sqlstate, level, ucid); + return get_warning_info()->push_warning(thd, &tmp, msg, + current_row_number); + } + + Sql_condition *push_warning(THD *thd, + uint sqlerrno, + const char* sqlstate, + Sql_condition::enum_warning_level level, + const char* msg) + { + return push_warning(thd, sqlerrno, sqlstate, level, + Sql_user_condition_identity(), msg, 0); + } + void mark_sql_conditions_for_removal() + { get_warning_info()->mark_sql_conditions_for_removal(); } + + void unmark_sql_conditions_from_removal() + { get_warning_info()->unmark_sql_conditions_from_removal(); } + + void remove_marked_sql_conditions() + { get_warning_info()->remove_marked_sql_conditions(); } + + const Sql_condition *get_error_condition() const + { return get_warning_info()->get_error_condition(); } + + void copy_sql_conditions_to_wi(THD *thd, Warning_info *dst_wi) const + { dst_wi->append_warning_info(thd, get_warning_info()); } + + void copy_sql_conditions_from_wi(THD *thd, const Warning_info *src_wi) + { get_warning_info()->append_warning_info(thd, src_wi); } + + void copy_non_errors_from_wi(THD *thd, const Warning_info *src_wi); + +private: + Warning_info *get_warning_info() { return m_wi_stack.front(); } + + const Warning_info *get_warning_info() const { return m_wi_stack.front(); } + +private: + /** True if status information is sent to the client. */ + bool m_is_sent; + + /** Set to make set_error_status after set_{ok,eof}_status possible. */ + bool m_can_overwrite_status; + + /** Message buffer. Can be used by OK or ERROR status. */ + char m_message[MYSQL_ERRMSG_SIZE]; + + /** + The number of rows affected by the last statement. This is + semantically close to thd->m_row_count_func, but has a different + life cycle. thd->m_row_count_func stores the value returned by + function ROW_COUNT() and is cleared only by statements that + update its value, such as INSERT, UPDATE, DELETE and few others. + This member is cleared at the beginning of the next statement. + + We could possibly merge the two, but life cycle of thd->m_row_count_func + can not be changed. + */ + ulonglong m_affected_rows; + + /** + Similarly to the previous member, this is a replacement of + thd->first_successful_insert_id_in_prev_stmt, which is used + to implement LAST_INSERT_ID(). + */ + + ulonglong m_last_insert_id; + /** + Number of warnings of this last statement. May differ from + the number of warnings returned by SHOW WARNINGS e.g. in case + the statement doesn't clear the warnings, and doesn't generate + them. + */ + uint m_statement_warn_count; + + enum_diagnostics_status m_status; + + my_bool is_bulk_execution; + + Warning_info m_main_wi; + + Warning_info_list m_wi_stack; +}; + +/////////////////////////////////////////////////////////////////////////// + +void convert_error_to_warning(THD *thd); + +void push_warning(THD *thd, Sql_condition::enum_warning_level level, + uint code, const char *msg); + +void push_warning_printf(THD *thd, Sql_condition::enum_warning_level level, + uint code, const char *format, ...); + +bool mysqld_show_warnings(THD *thd, ulong levels_to_show); + +size_t convert_error_message(char *to, size_t to_length, + CHARSET_INFO *to_cs, + const char *from, size_t from_length, + CHARSET_INFO *from_cs, uint *errors); + +extern const LEX_CSTRING warning_level_names[]; + +bool is_sqlstate_valid(const LEX_CSTRING *sqlstate); +/** + Checks if the specified SQL-state-string defines COMPLETION condition. + This function assumes that the given string contains a valid SQL-state. + + @param s the condition SQLSTATE. + + @retval true if the given string defines COMPLETION condition. + @retval false otherwise. +*/ +inline bool is_sqlstate_completion(const char *s) +{ return s[0] == '0' && s[1] == '0'; } + + +#endif // SQL_ERROR_H |