1#include "duckdb/planner/pragma_handler.hpp"
2#include "duckdb/planner/binder.hpp"
3#include "duckdb/parser/parser.hpp"
4
5#include "duckdb/catalog/catalog.hpp"
6#include "duckdb/catalog/catalog_entry/pragma_function_catalog_entry.hpp"
7#include "duckdb/parser/statement/multi_statement.hpp"
8#include "duckdb/parser/parsed_data/pragma_info.hpp"
9#include "duckdb/function/function.hpp"
10
11#include "duckdb/main/client_context.hpp"
12
13#include "duckdb/common/string_util.hpp"
14#include "duckdb/common/file_system.hpp"
15#include "duckdb/function/function_binder.hpp"
16
17namespace duckdb {
18
19PragmaHandler::PragmaHandler(ClientContext &context) : context(context) {
20}
21
22void PragmaHandler::HandlePragmaStatementsInternal(vector<unique_ptr<SQLStatement>> &statements) {
23 vector<unique_ptr<SQLStatement>> new_statements;
24 for (idx_t i = 0; i < statements.size(); i++) {
25 if (statements[i]->type == StatementType::MULTI_STATEMENT) {
26 auto &multi_statement = statements[i]->Cast<MultiStatement>();
27 for (auto &stmt : multi_statement.statements) {
28 statements.push_back(x: std::move(stmt));
29 }
30 continue;
31 }
32 if (statements[i]->type == StatementType::PRAGMA_STATEMENT) {
33 // PRAGMA statement: check if we need to replace it by a new set of statements
34 PragmaHandler handler(context);
35 string new_query;
36 bool expanded = handler.HandlePragma(statement: statements[i].get(), resulting_query&: new_query);
37 if (expanded) {
38 // this PRAGMA statement gets replaced by a new query string
39 // push the new query string through the parser again and add it to the transformer
40 Parser parser(context.GetParserOptions());
41 parser.ParseQuery(query: new_query);
42 // insert the new statements and remove the old statement
43 for (idx_t j = 0; j < parser.statements.size(); j++) {
44 new_statements.push_back(x: std::move(parser.statements[j]));
45 }
46 continue;
47 }
48 }
49 new_statements.push_back(x: std::move(statements[i]));
50 }
51 statements = std::move(new_statements);
52}
53
54void PragmaHandler::HandlePragmaStatements(ClientContextLock &lock, vector<unique_ptr<SQLStatement>> &statements) {
55 // first check if there are any pragma statements
56 bool found_pragma = false;
57 for (idx_t i = 0; i < statements.size(); i++) {
58 if (statements[i]->type == StatementType::PRAGMA_STATEMENT ||
59 statements[i]->type == StatementType::MULTI_STATEMENT) {
60 found_pragma = true;
61 break;
62 }
63 }
64 if (!found_pragma) {
65 // no pragmas: skip this step
66 return;
67 }
68 context.RunFunctionInTransactionInternal(lock, fun: [&]() { HandlePragmaStatementsInternal(statements); });
69}
70
71bool PragmaHandler::HandlePragma(SQLStatement *statement, string &resulting_query) { // PragmaInfo &info
72 auto info = *(statement->Cast<PragmaStatement>()).info;
73 auto &entry = Catalog::GetEntry<PragmaFunctionCatalogEntry>(context, INVALID_CATALOG, DEFAULT_SCHEMA, name: info.name);
74 string error;
75
76 FunctionBinder function_binder(context);
77 idx_t bound_idx = function_binder.BindFunction(name: entry.name, functions&: entry.functions, info, error);
78 if (bound_idx == DConstants::INVALID_INDEX) {
79 throw BinderException(error);
80 }
81 auto bound_function = entry.functions.GetFunctionByOffset(offset: bound_idx);
82 if (bound_function.query) {
83 QueryErrorContext error_context(statement, statement->stmt_location);
84 Binder::BindNamedParameters(types&: bound_function.named_parameters, values&: info.named_parameters, error_context,
85 func_name&: bound_function.name);
86 FunctionParameters parameters {.values: info.parameters, .named_parameters: info.named_parameters};
87 resulting_query = bound_function.query(context, parameters);
88 return true;
89 }
90 return false;
91}
92
93} // namespace duckdb
94