Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/MariaDB/sql/   (MariaDB Server Version 8.1-8.4©)  Datei vom 1.9.2026 mit Größe 14 kB image not shown  

Quelle  sql_path.cc   Sprache: C

 

#include "sql_plugin.h"
#include "sql_class.h"    // class THD
#include "sp_head.h"      // class sp_name
#include "lex_ident.h"
#include "sql_db.h"
#include "sql_path.h"

static constexpr Lex_ident_ci cur_schema= {STRING_WITH_LEN("CURRENT_SCHEMA")};

Sql_path::Sql_path() : m_count(0)
{
}


Sql_path& Sql_path::operator=(const Sql_path &rhs)
{
  set(rhs);
  return *this;
}


Lex_ident_db Sql_path::resolve_current_schema(THD *thd, sp_head *caller,
                                              size_t i) const
{
  /* Resolve CURRENT_SCHEMA to actual database name. */
  if (caller && caller->m_name.str)
    return thd->to_ident_db_normalized_with_error(caller->m_db);
  if (thd->db.str || thd->lex->sphead)
    return thd->copy_db_normalized();
  return {nullptr, 0};
}


bool Sql_path::try_resolve_in_schema(THD *thd, const Lex_ident_db_normalized &schema,
                                      sp_name *name, const Sp_handler **sph,
                                      Database_qualified_name *pkgname,
                                      bool *resolved) const
{
  DBUG_ASSERT(resolved);
  int ret;

  if (check_db_dir_existence(schema.str))
    return false; // Schema doesn't exist

  Database_qualified_name tmp_spname;
  tmp_spname.m_name= name->m_name;

  if (!name->m_explicit_name)
  {
    // Try to find routine directly in schema
    tmp_spname.m_db= schema;
    if (!(ret= (*sph)->sp_routine_exists(thd, &tmp_spname)))
    {
      /*
        [schema] '.' routine_name
      */

      name->m_db= Lex_ident_db_normalized(thd->strmake(schema.str, schema.length),
                                          schema.length);
      *resolved= true;
      return false;
    }
    else if (ret != SP_KEY_NOT_FOUND)
      return true;
  }
  else
  {
    // Try package routine resolution
    if (is_package_public_routine(thd, schema, name->m_db, name->m_name, (*sph)->type()))
    {
      /*
        [schema] '.' pkg_name '.' routine_name routine
      */

      pkgname->m_db= schema;
      pkgname->m_name= Lex_ident_routine(name->m_db);
      *sph= (*sph)->package_routine_handler();
      *resolved= true;
      return name->make_package_routine_name(thd->mem_root, schema,
                                             name->m_db, name->m_name);
    }
  }

  return false;
}


bool Sql_path::resolve(THD *thd, sp_head *caller, sp_name *name,
                       const Sp_handler **sph,
                       Database_qualified_name *pkgname) const
{
  DBUG_ASSERT(name);
  DBUG_ASSERT(name->m_name.str[name->m_name.length] == '\0');

  // Check for fully qualified name schema.pkg.routine and exit early
  if (name->m_explicit_name && strchr(name->m_name.str, '.'))
    return false;

  DBUG_ASSERT(!name->m_explicit_name || name->m_db.str);
  if (caller) // first, search in the current package
  {
    if (!name->m_explicit_name) // name()
    {
      sp_name tmp_name(*name);
      tmp_name.m_db= caller->m_db;
      const Sp_handler *pkg_routine_hndlr= nullptr;
      if ((*sph)->sp_resolve_package_routine_implicit(thd, caller, &tmp_name,
                                                      &pkg_routine_hndlr,
                                                      pkgname))
        return true;

      if (pkg_routine_hndlr)
      {
        *sph= pkg_routine_hndlr;
        *name= tmp_name;
        return false;
      }
    }
    else // name1.name2()
    {
      const Sp_handler *pkg_routine_hndlr= nullptr;
      if ((*sph)->sp_resolve_package_routine_explicit(thd, caller, name,
                                                      &pkg_routine_hndlr,
                                                      pkgname))
        return true;

      if (pkg_routine_hndlr)
      {
        *sph= pkg_routine_hndlr;
        return false;
      }
    }
  }

  bool resolved= false;
  for (size_t i= 0; i < m_count; i++)
  {
    Lex_ident_db schema;
    if (is_cur_schema(i))
    {
      // If PATH contains only CURRENT_SCHEMA (default), skip PATH resolution
      // to avoid extra table operations that affect performance
      if (m_count== 1 && !name->m_explicit_name)
      {
        if (!name->m_db.str && thd->db.str)
          name->m_db= thd->copy_db_normalized();
        return false;
      }

      schema= resolve_current_schema(thd, caller, i);
      if (!schema.str)
        continue; // CURRENT_SCHEMA resolution failed, skip this entry
      /* recursive calls */
      if (caller && name->eq_routine_name(caller))
        return false;
    }
    else
      schema= m_schemas[i];

    /*
      Schemas are already normalized when added to the path, except for
      CURRENT_SCHEMA which was resolved above. We can use them directly.
    */

    const Lex_ident_db_normalized dbn(schema.str, schema.length);

    if (try_resolve_in_schema(thd, dbn, name, sph, pkgname, &resolved))
      return true;

    if (resolved)
      return false;
  }

  return false;
}


/*
  Iterate through all schemas in @@path and find a package "package" with the
  type "type"

  @param thd     - The THD
  @param OUT db  - The found database which contains a type package.type
                   If db->str is nullptr the nothing was found.
  @param package - The package to find.
  @param type    - The type to find.
  @retval true   - If error happened during resolution.
  @retval false  - If no errors happened during resolution.
                   Note, if nothing was found and no errors happened,
                   the return value is false.
*/

bool Sql_path::find_package_spec_type(THD *thd,
                                      Lex_ident_db_normalized *db,
                                      const Lex_ident_sys_st &package,
                                      const Lex_ident_sys_st &type)
{
  DBUG_ASSERT(!thd->is_fatal_error);
  *db= Lex_ident_db_normalized();
  for (size_t i= 0; i < m_count; i++)
  {
    sp_package *pkg;
    Lex_ident_db_normalized ncdb;
    if (is_cur_schema(i))
    {
      if (!thd->db.str)
        continue;
      if (!(ncdb= thd->copy_db_normalized()).str)
        return true; // EOM
    }
    else
    {
      /*
        Conversion to Lex_ident_db_normalized is safe because
        database names get into Sql_path in normalized form.
      */

      ncdb= Lex_ident_db_normalized(m_schemas[i].str, m_schemas[i].length);
    }
    if (!(pkg= Sp_handler::find_package_spec(thd, ncdb, package)))
    {
      if (thd->is_fatal_error)
        return true; // E.g. ER_STACK_OVERRUN_NEED_MORE raised
      continue;
    }
    sp_type_def *tdef= pkg->find_type_def(type);
    if (tdef)
    {
      *db= ncdb;
      return false; // Found a TYPE declaration in a CREATE PACKAGE
    }
  }
  return false; // Nothing found
}


void Sql_path::free()
{
  if (m_count)
    my_free(const_cast<char*>(m_schemas[0].str));
  m_count= 0;
}


bool Sql_path::init()
{
  free();
  char *buf= my_strndup(key_memory_Sys_var_charptr_value, "", 1, MYF(MY_WME));
  m_schemas[m_count++]= {buf, 0};
  return false;
}


bool Sql_path::is_cur_schema(size_t i) const
{
  return !m_schemas[i].length;
}


bool Sql_path::add_schema(char **to, bool is_quoted)
{
  const Lex_ident_db &dbn= m_schemas[m_count];
  m_schemas[m_count].length= *to - m_schemas[m_count].str;
  *(*to)++= 0;

  // Validate
  if (Lex_ident_db::check_name_with_error(dbn))
    goto err;

  if (!is_quoted && cur_schema.streq(dbn))
  {
    for (size_t i = 0; i < m_count; i++) // Check for duplicates
      if (is_cur_schema(i))
        goto err;
    m_schemas[m_count].length= 0;
  }
  else
  {
    for (size_t i = 0; i < m_count; i++)
      if (!is_cur_schema(i) && dbn.streq(m_schemas[i]))
        goto err;
  }

  m_count++;
  return false;
err:
  return true;
}


void Sql_path::set(const Sql_path &rhs)
{
  free();

  if ((m_count= rhs.m_count))
  {
    auto rbuf= rhs.m_schemas[0].str;
    auto rend= rhs.m_schemas[m_count-1].str + rhs.m_schemas[m_count-1].length;
    auto buf= (const char*)my_memdup(key_memory_Sys_var_charptr_value, rbuf,
                                     rend - rbuf + 1, MYF(MY_WME));
    for (size_t i= 0; i < rhs.m_count; i++)
      m_schemas[i]= { rhs.m_schemas[i].str - rbuf + buf, rhs.m_schemas[i].length };
  }
}


void Sql_path::set(Sql_path &&rhs)
{
  size_t count= std::max(m_count, rhs.m_count);
  for (size_t i= 0; i < count; i++)
    std::swap(m_schemas[i], rhs.m_schemas[i]);
  std::swap(m_count, rhs.m_count);
  rhs.free();
}


/*
  Set the PATH value from a string str0.
  The string can contain:
  - A character string in an arbitrary character set,
    including tricky ones such as swe7, ucs2, utf32.
    It is expected to convert to utf8mb3 without data loss.
  - A binary string.
    It is expected to re-enterpret to utf8mb3 without errors
    (i.e. be a well-formed utf8mb3)

  If the string does not convert or re-enterpret to utf8mb3 without
  errors or data loss, then an SQL error is raised.
*/

bool Sql_path::from_text(const system_variables &sv, String *str0)
{
  enum tokenize_state
  {
    START, QUOTED_TOKEN_DOUBLE='"', QUOTED_TOKEN_BACKTICK='`', UNQUOTED_TOKEN, END
  } state= START;

  const bool ansi_quotes= sv.sql_mode & MODE_ANSI_QUOTES;

  CHARSET_INFO *cs= &my_charset_utf8mb3_general_ci; // as in make_ident_casedn()
  DBUG_ASSERT(cs->cset->casedn_multiply(cs) == 1);
  DBUG_ASSERT(str0->ptr());

  // Convert convert/re-intepret str0 to utf8mb3
  String_copier copier;
  StringBuffer<128> str;
  if (str.copy(cs, str0->charset(), str0->ptr(), str0->length(),
               str0->length()/*nchars*/, &copier) ||
      copier.most_important_error_pos())
  {
    my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "PATH", ErrConvString(str0).ptr());
    return true;
  }

  /*
    Now we have a well-formed utf8mb3 string in `str`.
    Remember and parse it.
  */

  char *buf= (char*)my_malloc(key_memory_Sys_var_charptr_value,
                              str.length() + 1, MYF(MY_WME));
  if (!buf)
    return true;

  char *curr= buf, *to= buf, *end;

  if (lower_case_table_names > 0)
    end= buf + cs->cset->casedn(cs, str.ptr(), str.length(), buf, str.length());
  else
  {
    memcpy(buf, str.ptr(), str.length());
    end= buf + str.length();
  }

  free();
  while (curr < end)
  {
    auto len = cs->charlen(curr, end);
    DBUG_ASSERT(len > 0); // We have a well-formed utf8mb3

    switch (state)
    {
      case START:
        if (*curr == ',' || my_isspace(cs, (uchar) *curr))
          curr++;
        else
        {
          if (m_count >= array_elements(m_schemas))
          {
            my_error(ER_VALUE_TOO_LONG, MYF(0), "PATH");
            goto err;
          }
          m_schemas[m_count].str= to;
          if (*curr == '`' || (*curr == '"' && ansi_quotes))
            state= (tokenize_state) *curr++;
          else if (*curr == '"')
            goto err_bad_val;
          else
          {
            state= UNQUOTED_TOKEN;
            while (len--)
              *to++= *curr++;
          }
        }
        break;
      case QUOTED_TOKEN_BACKTICK:
      case QUOTED_TOKEN_DOUBLE:
        if (*curr == state)
        {
          curr++;
          if (curr >= end || *curr != state)
          {
            state= END;
            if (add_schema(&to, true))
              goto err_bad_val;
            break;
          }
        }
        while (len--)
          *to++= *curr++;
        break;
      case UNQUOTED_TOKEN:
        if (*curr == ',' || my_isspace(cs, (uchar) *curr))
        {
          state= *curr++ == ',' ? START : END;
          if (add_schema(&to, false))
            goto err_bad_val;
          break;
        }
        else if (*curr == '`' || *curr == '"')
          goto err_bad_val;
        else
          while (len--)
            *to++= *curr++;
        break;
      case END:
        if (*curr == ',')
          state= START;
        else if (!my_isspace(cs, (uchar) *curr))
          goto err_bad_val;
        curr++;
        break;
    }
  }

  switch (state)
  {
    case START:
    case END:
      break;
    case QUOTED_TOKEN_BACKTICK:
    case QUOTED_TOKEN_DOUBLE:
      goto err_bad_val;
    case UNQUOTED_TOKEN:
      if (add_schema(&to, false))
        goto err_bad_val;
      break;
  }
  if (!m_count)
    my_free(buf);
  return false;

err_bad_val:
  my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "PATH", ErrConvString(&str).ptr());
err:
  m_count= 0;
  my_free(buf);
  return true;
}


LEX_CSTRING Sql_path::get_schema_for_print(size_t num) const
{
  if (is_cur_schema(num))
    return cur_schema;
  return m_schemas[num];
}


size_t Sql_path::text_format_nbytes_needed() const
{
  size_t nbytes= 0;

  for (size_t i= 0; i < m_count; i++)
  {
    LEX_CSTRING schema= get_schema_for_print(i);
    size_t len= schema.length;
    for (size_t j= 0; j < schema.length; j++)
    {
      if (schema.str[j] == '`')
        len++;
    }

    nbytes+= len + 2 + 1;
  }

  if (nbytes)
    nbytes--;

  return nbytes + 1;
}


size_t Sql_path::print(char *dst, size_t nbytes_available) const
{
  char *start= dst;

  for (size_t i= 0; i < m_count; i++)
  {
    LEX_CSTRING schema= get_schema_for_print(i);

    if (dst - start + schema.length + 3 > nbytes_available)
      break;

    if (schema.str == cur_schema.str)
    {
      memcpy(dst, schema.str, schema.length);
      dst+= schema.length;
      *dst++= ',';
      continue;
    }

    *dst++= '`';
    for (size_t j= 0; j < schema.length; j++)
    {
      *dst++= schema.str[j];
      if (schema.str[j] == '`')
        *dst++= '`';
    }
    *dst++= '`';
    *dst++= ',';
  }

  if (dst > start)
    dst--;

  if (dst < start + nbytes_available)
    *dst= '\0';

  return dst - start;
}


LEX_CSTRING Sql_path::lex_cstring(MEM_ROOT *mem_root) const
{
  LEX_CSTRING res;
  size_t nbytes_needed= text_format_nbytes_needed();
  char *ptr= (char *) alloc_root(mem_root, nbytes_needed);
  if (ptr)
  {
    res.length= print(ptr, nbytes_needed);
    res.str= ptr;
    DBUG_ASSERT(res.length < nbytes_needed);
  }
  else
  {
    res.str= nullptr;
    res.length= 0;
  }

  return res;
}

Sql_path_instant_set::Sql_path_instant_set(THD *thd, const LEX_CSTRING &str)
  : m_thd(thd), m_path(std::move(thd->variables.path))
{
  String path(str.str, str.length, system_charset_info);
  if (thd->variables.path.from_text(thd->variables, &path))
  {
    thd->variables.path.set(std::move(m_path));
    m_thd= NULL;
  }
}

Sql_path_instant_set::Sql_path_instant_set(THD *thd, const Sql_path &new_path)
  : m_thd(thd), m_path(std::move(thd->variables.path))
{
  thd->variables.path.set(new_path);
}

Sql_path_instant_set::~Sql_path_instant_set()
{
  m_thd->variables.path.set(std::move(m_path));
}

Messung V0.5 in Prozent
C=95 H=80 G=87

¤ Dauer der Verarbeitung: 0.14 Sekunden  (vorverarbeitet am  2026-10-08) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.