Uploaded image for project: 'MariaDB Server'
  1. MariaDB Server
  2. MDEV-27159

Re-design the upper level of handling DML commands

    XMLWordPrintable

Details

    Description

      The main goal of this task is to re-design the top level for UPDATE/DELETE/INSERT statements to follow the same approach of handling these statements as MySQL 8.0. With this approach DML statements are processed by the methods 'prepare' and execute of the class Sql_cmd_dml derived from the class Sql_com that already exists
      in MariaDB.
      Here's the declaration of the class Sql_cmd_dml:

       class Sql_cmd_dml : public Sql_cmd
      {
      public:
        /// @return true if data change statement, false if not (SELECT statement)
        virtual bool is_data_change_stmt() const { return true; }
       
        /**
          Command-specific resolving (doesn't include LEX::prepare())
       
          @param thd  Current THD.
          @returns false on success, true on error
        */
        virtual bool prepare(THD *thd);
       
        /**
          Execute this query once
       
          @param thd Thread handler
          @returns false on success, true on error
        */
        virtual bool execute(THD *thd);
       
        virtual bool is_dml() const { return true; }
       
      protected:
        Sql_cmd_dml()
            : Sql_cmd(), lex(nullptr), result(nullptr), m_empty_query(false) {}
       
        /// @return true if query is guaranteed to return no data
        /**
          @todo Also check this for the following cases:
                - Empty source for multi-table UPDATE and DELETE.
                - Check empty query expression for INSERT
        */
        bool is_empty_query() const
        {
          DBUG_ASSERT(is_prepared());
          return m_empty_query;
        }
       
        /// Set statement as returning no data
        void set_empty_query() { m_empty_query = true; }
       
        /**
          Perform a precheck of table privileges for the specific operation.
       
          @details
          Check that user has some relevant privileges for all tables involved in
          the statement, e.g. SELECT privileges for tables selected from, INSERT
          privileges for tables inserted into, etc. This function will also populate
          TABLE_LIST::grant with all privileges the user has for each table, which
          is later used during checking of column privileges.
          Note that at preparation time, views are not expanded yet. Privilege
          checking is thus rudimentary and must be complemented with later calls to
          SELECT_LEX::check_view_privileges().
          The reason to call this function at such an early stage is to be able to
          quickly reject statements for which the user obviously has insufficient
          privileges.
       
          @param thd thread handler
          @returns false if success, true if false
        */
        virtual bool precheck(THD *thd) = 0;
       
        /**
          Perform the command-specific parts of DML command preparation,
          to be called from prepare()
       
          @param thd the current thread
          @returns false if success, true if error
        */
        virtual bool prepare_inner(THD *thd) = 0;
       
        /**
          The inner parts of query optimization and execution.
          Single-table DML operations needs to reimplement this.
       
          @param thd Thread handler
          @returns false on success, true on error
        */
        virtual bool execute_inner(THD *thd);
       
        virtual DML_prelocking_strategy *get_dml_prelocking_strategy() = 0;
       
         uint table_count;
       
       protected:
        LEX *lex;              ///< Pointer to LEX for this statement
        select_result *result; ///< Pointer to object for handling of the result
        bool m_empty_query;    ///< True if query will produce no rows
      };
      

      The implementations of the virtual functions

       virtual bool precheck(THD *thd) = 0;
       virtual bool prepare_inner(THD *thd) = 0;
       virtual bool execute_inner(THD *thd);
      

      will be different for different types of statements.

      The method Sql_cmd_dml::execute execute a DML statement.

      bool Sql_cmd_dml::execute(THD *thd)
      {
        lex = thd->lex;
        bool res;
       
        SELECT_LEX_UNIT *unit = &lex->unit;
        SELECT_LEX *select_lex= lex->first_select_lex();
       
        if (!is_prepared())
        {
          if (prepare(thd))
             goto err;
        }
        else
        {
          if (precheck(thd))
            goto err;
          if (open_tables_for_query(thd, lex->query_tables, &table_count, 0,
                                    get_dml_prelocking_strategy()))
            goto err;
        }
       
        THD_STAGE_INFO(thd, stage_init);
       
        DBUG_ASSERT(!lex->is_query_tables_locked());
        /*
          Locking of tables is done after preparation but before optimization.
          This allows to do better partition pruning and avoid locking unused
          partitions. As a consequence, in such a case, prepare stage can rely only
          on metadata about tables used and not data from them.
        */
        if (!is_empty_query())
        {
          if (lock_tables(thd, lex->query_tables, table_count, 0))
            goto err;
        }
       
        unit->set_limit(select_lex);
       
        // Perform statement-specific execution
        res = execute_inner(thd);
       
        if (res)
          goto err;
       
        // "unprepare" this object since unit->cleanup actually unprepares
        unprepare(thd);
       
        THD_STAGE_INFO(thd, stage_end);
       
        return res;
       
      err:
        DBUG_ASSERT(thd->is_error() || thd->killed);
        THD_STAGE_INFO(thd, stage_end);
        (void)unit->cleanup();
       
        return thd->is_error();
      }
      

      The method Sql_cmd_dml::prepare performs the prepare phase.

      bool Sql_cmd_dml::prepare(THD *thd)
      {
        lex= thd->lex;
        SELECT_LEX_UNIT *unit= &lex->unit;
       
        DBUG_ASSERT(!is_prepared());
       
        // Perform a coarse statement-specific privilege check.
        if (precheck(thd))
           goto err;
       
        lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_DERIVED;
       
        if (open_tables_for_query(thd, lex->query_tables, &table_count, 0,
                                  get_dml_prelocking_strategy()))
        {
          if (thd->is_error())
            goto err;
          (void)unit->cleanup();
          return true;
        }
       
        if (prepare_inner(thd))
          goto err;
       
        lex->context_analysis_only&= ~CONTEXT_ANALYSIS_ONLY_DERIVED;
       
        set_prepared();
        unit->set_prepared();
       
        return false;
       
      err:
        DBUG_ASSERT(thd->is_error());
        DBUG_PRINT("info", ("report_error: %d", thd->is_error()));
       
        (void)unit->cleanup();
       
        return true;
      }
      

      Attachments

        Activity

          People

            igor Igor Babaev
            igor Igor Babaev
            Votes:
            1 Vote for this issue
            Watchers:
            8 Start watching this issue

            Dates

              Created:
              Updated:

              Git Integration

                Error rendering 'com.xiplink.jira.git.jira_git_plugin:git-issue-webpanel'. Please contact your Jira administrators.