Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  duckdb_query.cc   Sprache: C

 

/*
  Copyright (c) 2026, MariaDB Foundation.
  Copyright (c) 2026, Roman Nozdrin <drrtuy@gmail.com>
  Copyright (c) 2026, Leonid Fedorov.

  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 St, Fifth Floor, Boston, MA 02110-1335 USA
*/


#define MYSQL_SERVER 1
#include <my_global.h>
#include "sql_class.h"
#include "log.h"

#undef UNKNOWN

#include "duckdb_query.h"
#include "duckdb/common/exception.hpp"
#include "duckdb/main/pending_query_result.hpp"
#include "duckdb_context.h"
#include "duckdb_manager.h"
#include "duckdb_log.h"

#include <cctype>

extern handlerton *duckdb_hton;

namespace myduck
{

SqlRegionType scan_sql_region(const std::string &sql, size_t start,
                              bool backslash_escapes, size_t &end)
{
  end= start;
  if (start >= sql.size())
    return SqlRegionType::NONE;

  char c= sql[start];
  if (c == '/' && start + 1 < sql.size() && sql[start + 1] == '*')
  {
    size_t close= sql.find("*/", start + 2);
    if (close == std::string::npos)
    {
      end= sql.size();
      return SqlRegionType::UNTERMINATED;
    }
    end= close + 2;
    return SqlRegionType::COMMENT;
  }

  if (c == '#' ||
      (c == '-' && start + 1 < sql.size() && sql[start + 1] == '-' &&
       (start + 2 == sql.size() ||
        isspace(static_cast<unsigned char>(sql[start + 2])))))
  {
    size_t newline= sql.find('\n', start + (c == '#' ? 1 : 2));
    end= newline == std::string::npos ? sql.size() : newline + 1;
    return SqlRegionType::COMMENT;
  }

  if (c != '\'' && c != '"' && c != '`')
    return SqlRegionType::NONE;

  for (size_t i= start + 1; i < sql.size(); i++)
  {
    if (sql[i] == '\\' && backslash_escapes && i + 1 < sql.size())
    {
      i++;
      continue;
    }
    if (sql[i] != c)
      continue;
    if (i + 1 < sql.size() && sql[i + 1] == c)
    {
      i++;
      continue;
    }
    end= i + 1;
    return SqlRegionType::QUOTED;
  }

  end= sql.size();
  return SqlRegionType::UNTERMINATED;
}

bool mariadb_query_has_unsafe_quote_escape(THD *thd, const char *query,
                                            size_t length)
{
  if (!thd->backslash_escapes() || length == 0)
    return false;

  const std::string sql(query, length);
  const bool ansi_quotes= thd->variables.sql_mode & MODE_ANSI_QUOTES;
  for (size_t i= 0; i < sql.size();)
  {
    size_t duckdb_end;
    SqlRegionType duckdb_region= scan_sql_region(sql, i, false, duckdb_end);
    if (duckdb_region == SqlRegionType::COMMENT)
    {
      i= duckdb_end;
      continue;
    }

    const bool string_literal=
        sql[i] == '\'' || (sql[i] == '"' && !ansi_quotes);
    if (string_literal)
    {
      size_t mariadb_end;
      SqlRegionType mariadb_region=
          scan_sql_region(sql, i, true, mariadb_end);
      if (mariadb_region != duckdb_region || mariadb_end != duckdb_end)
        return true;
      i= mariadb_end;
      continue;
    }

    if (duckdb_region == SqlRegionType::QUOTED)
      i= duckdb_end;
    else if (duckdb_region == SqlRegionType::UNTERMINATED)
      return true;
    else
      i++;
  }
  return false;
}

/*
  Convert forwarded MariaDB SQL (the raw thd->query() text, plus any
  Item::print() fragments) from backtick-quoted identifiers into DuckDB SQL
  (double-quoted identifiers).

  MariaDB delimits identifiers with backticks and doubles an embedded backtick;
  DuckDB delimits with double quotes and doubles an embedded double quote. A
  naive character-by-character swap breaks identifiers that contain a double
  quote (MDEV-40653) and also corrupts backticks that appear inside string
  literals. Walk the string instead: copy string literals and already
  double-quoted identifiers verbatim, and rewrite only backtick-delimited
  identifiers, escaping any embedded double quote.
*/

static std::string backticks_to_double_quotes(const std::string &sql)
{
  std::string out;
  out.reserve(sql.size());
  const size_t n= sql.size();
  size_t i= 0;

  while (i < n)
  {
    char c= sql[i];
    size_t end;
    SqlRegionType region= scan_sql_region(sql, i, false, end);

    if (region == SqlRegionType::COMMENT ||
        region == SqlRegionType::UNTERMINATED)
    {
      out.append(sql, i, end - i);
      i= end;
      continue;
    }

    /* Single-quoted string literal: copy verbatim ('' and \' escapes). */
    if (region == SqlRegionType::QUOTED && c == '\'')
    {
      out.append(sql, i, end - i);
      i= end;
      continue;
    }

    /* Already double-quoted identifier: copy verbatim ("" escape). */
    if (region == SqlRegionType::QUOTED && c == '"')
    {
      out.append(sql, i, end - i);
      i= end;
      continue;
    }

    /* Backtick identifier: rewrite as a double-quoted identifier. */
    if (region == SqlRegionType::QUOTED && c == '`')
    {
      out.push_back('"');
      for (i++; i + 1 < end; i++)
      {
        char d= sql[i];
        if (d == '`' && i + 2 < end && sql[i + 1] == '`')
        {
          out.push_back('`');
          i++;
          continue;
        }
        if (d == '"')
          out.push_back('"'); /* escape " inside a DuckDB identifier */
        out.push_back(d);
      }
      out.push_back('"');
      i= end;
      continue;
    }

    out.push_back(c);
    i++;
  }

  return out;
}

duckdb::unique_ptr<duckdb::MaterializedQueryResult>
duckdb_query(duckdb::Connection &connection, const std::string &query)
{
  const std::string q= backticks_to_double_quotes(query);

  if (myduck::duckdb_log_options & LOG_DUCKDB_QUERY)
    sql_print_information("DuckDB query: %s", q.c_str());

  try
  {
    auto res= connection.Query(q);

    if (myduck::duckdb_log_options & LOG_DUCKDB_QUERY_RESULT)
    {
      if (res->HasError())
        sql_print_information("DuckDB error: %s", res->GetError().c_str());
    }
    return res;
  }
  catch (duckdb::Exception &e)
  {
    auto result= duckdb::make_uniq<duckdb::MaterializedQueryResult>(
        duckdb::ErrorData(e.what()));
    return result;
  }
  catch (std::exception &e)
  {
    auto result= duckdb::make_uniq<duckdb::MaterializedQueryResult>(
        duckdb::ErrorData(e.what()));
    return result;
  }
}

static duckdb::unique_ptr<duckdb::QueryResult>
duckdb_pending_query(duckdb::Connection &connection, const std::string &query,
                     duckdb::QueryResultOutputType output_type)
{
  const std::string q= backticks_to_double_quotes(query);

  if (myduck::duckdb_log_options & LOG_DUCKDB_QUERY)
    sql_print_information("DuckDB query: %s", q.c_str());

  try
  {
    auto pending= connection.PendingQuery(q, output_type);
    duckdb::unique_ptr<duckdb::QueryResult> res;
    if (pending->HasError())
      res= duckdb::make_uniq<duckdb::MaterializedQueryResult>(
          pending->GetErrorObject());
    else
      res= pending->Execute();

    if ((myduck::duckdb_log_options & LOG_DUCKDB_QUERY_RESULT) &&
        res->HasError())
      sql_print_information("DuckDB error: %s", res->GetError().c_str());
    return res;
  }
  catch (duckdb::Exception &e)
  {
    return duckdb::make_uniq<duckdb::MaterializedQueryResult>(
        duckdb::ErrorData(e.what()));
  }
  catch (std::exception &e)
  {
    return duckdb::make_uniq<duckdb::MaterializedQueryResult>(
        duckdb::ErrorData(e.what()));
  }
}

static duckdb::unique_ptr<duckdb::MaterializedQueryResult>
duckdb_query_single(duckdb::Connection &connection, const std::string &query)
{
  auto res= duckdb_pending_query(
      connection, query, duckdb::QueryResultOutputType::FORCE_MATERIALIZED);
  DBUG_ASSERT(res->type == duckdb::QueryResultType::MATERIALIZED_RESULT);
  return duckdb::unique_ptr_cast<duckdb::QueryResult,
                                 duckdb::MaterializedQueryResult>(
      std::move(res));
}

duckdb::unique_ptr<duckdb::QueryResult>
duckdb_stream_query(duckdb::Connection &connection, const std::string &query)
{
  return duckdb_pending_query(
      connection, query, duckdb::QueryResultOutputType::ALLOW_STREAMING);
}

static std::string get_thd_schema(THD *thd)
{
  if (thd->db.str && thd->db.length > 0)
    return std::string(thd->db.str, thd->db.length);
  return {};
}

duckdb::unique_ptr<duckdb::MaterializedQueryResult>
duckdb_query(THD *thd, const std::string &query, bool need_config)
{
  if (mariadb_query_has_unsafe_quote_escape(thd, query.data(), query.size()))
    return duckdb::make_uniq<duckdb::MaterializedQueryResult>(
        duckdb::ErrorData("Unsafe MariaDB backslash quote escape in forwarded SQL"));

  auto *ctx=
      static_cast<DuckdbThdContext *>(thd_get_ha_data(thd, duckdb_hton));
  if (!ctx)
  {
    ctx= new DuckdbThdContext();
    thd_set_ha_data(thd, duckdb_hton, ctx);
  }

  if (need_config)
  {
    ctx->config_duckdb_env(get_thd_schema(thd));
    ctx->config_duckdb_session(thd);
  }

  return duckdb_query_single(ctx->get_connection(), query);
}

duckdb::unique_ptr<duckdb::QueryResult>
duckdb_stream_query(THD *thd, const std::string &query, bool need_config)
{
  if (mariadb_query_has_unsafe_quote_escape(thd, query.data(), query.size()))
    return duckdb::make_uniq<duckdb::MaterializedQueryResult>(
        duckdb::ErrorData("Unsafe MariaDB backslash quote escape in forwarded SQL"));

  auto *ctx=
      static_cast<DuckdbThdContext *>(thd_get_ha_data(thd, duckdb_hton));
  if (!ctx)
  {
    ctx= new DuckdbThdContext();
    thd_set_ha_data(thd, duckdb_hton, ctx);
  }

  if (need_config)
  {
    ctx->config_duckdb_env(get_thd_schema(thd));
    ctx->config_duckdb_session(thd);
  }

  return duckdb_stream_query(ctx->get_connection(), query);
}

duckdb::unique_ptr<duckdb::MaterializedQueryResult>
duckdb_query(const std::string &query)
{
  auto connection= DuckdbManager::CreateConnection();
  return duckdb_query(*connection, query);
}

} // namespace myduck

Messung V0.5 in Prozent
C=99 H=95 G=96

¤ Dauer der Verarbeitung: 0.1 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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1126438
#Domains=1867298