1//===----------------------------------------------------------------------===//
2// DuckDB
3//
4// duckdb/main/client_context.hpp
5//
6//
7//===----------------------------------------------------------------------===//
8
9#pragma once
10
11#include "duckdb/catalog/catalog_entry/schema_catalog_entry.hpp"
12#include "duckdb/catalog/catalog_set.hpp"
13#include "duckdb/common/enums/pending_execution_result.hpp"
14#include "duckdb/common/deque.hpp"
15#include "duckdb/common/pair.hpp"
16#include "duckdb/common/unordered_set.hpp"
17#include "duckdb/common/winapi.hpp"
18#include "duckdb/main/prepared_statement.hpp"
19#include "duckdb/main/stream_query_result.hpp"
20#include "duckdb/main/table_description.hpp"
21#include "duckdb/transaction/transaction_context.hpp"
22#include "duckdb/main/pending_query_result.hpp"
23#include "duckdb/common/atomic.hpp"
24#include "duckdb/main/client_config.hpp"
25#include "duckdb/main/external_dependencies.hpp"
26#include "duckdb/common/preserved_error.hpp"
27
28namespace duckdb {
29class Appender;
30class Catalog;
31class CatalogSearchPath;
32class ColumnDataCollection;
33class DatabaseInstance;
34class FileOpener;
35class LogicalOperator;
36class PreparedStatementData;
37class Relation;
38class BufferedFileWriter;
39class QueryProfiler;
40class ClientContextLock;
41struct CreateScalarFunctionInfo;
42class ScalarFunctionCatalogEntry;
43struct ActiveQueryContext;
44struct ParserOptions;
45struct ClientData;
46
47struct PendingQueryParameters {
48 //! Prepared statement parameters (if any)
49 vector<Value> *parameters = nullptr;
50 //! Whether or not a stream result should be allowed
51 bool allow_stream_result = false;
52};
53
54//! ClientContextState is virtual base class for ClientContext-local (or Query-Local, using QueryEnd callback) state
55//! e.g. caches that need to live as long as a ClientContext or Query.
56class ClientContextState {
57public:
58 virtual ~ClientContextState() {};
59 virtual void QueryEnd() = 0;
60};
61
62//! The ClientContext holds information relevant to the current client session
63//! during execution
64class ClientContext : public std::enable_shared_from_this<ClientContext> {
65 friend class PendingQueryResult;
66 friend class StreamQueryResult;
67 friend class DuckTransactionManager;
68
69public:
70 DUCKDB_API explicit ClientContext(shared_ptr<DatabaseInstance> db);
71 DUCKDB_API ~ClientContext();
72
73 //! The database that this client is connected to
74 shared_ptr<DatabaseInstance> db;
75 //! Whether or not the query is interrupted
76 atomic<bool> interrupted;
77 //! External Objects (e.g., Python objects) that views depend of
78 unordered_map<string, vector<shared_ptr<ExternalDependency>>> external_dependencies;
79 //! Set of optional states (e.g. Caches) that can be held by the ClientContext
80 unordered_map<string, shared_ptr<ClientContextState>> registered_state;
81 //! The client configuration
82 ClientConfig config;
83 //! The set of client-specific data
84 unique_ptr<ClientData> client_data;
85 //! Data for the currently running transaction
86 TransactionContext transaction;
87
88public:
89 MetaTransaction &ActiveTransaction() {
90 return transaction.ActiveTransaction();
91 }
92
93 //! Interrupt execution of a query
94 DUCKDB_API void Interrupt();
95 //! Enable query profiling
96 DUCKDB_API void EnableProfiling();
97 //! Disable query profiling
98 DUCKDB_API void DisableProfiling();
99
100 //! Issue a query, returning a QueryResult. The QueryResult can be either a StreamQueryResult or a
101 //! MaterializedQueryResult. The StreamQueryResult will only be returned in the case of a successful SELECT
102 //! statement.
103 DUCKDB_API unique_ptr<QueryResult> Query(const string &query, bool allow_stream_result);
104 DUCKDB_API unique_ptr<QueryResult> Query(unique_ptr<SQLStatement> statement, bool allow_stream_result);
105
106 //! Issues a query to the database and returns a Pending Query Result. Note that "query" may only contain
107 //! a single statement.
108 DUCKDB_API unique_ptr<PendingQueryResult> PendingQuery(const string &query, bool allow_stream_result);
109 //! Issues a query to the database and returns a Pending Query Result
110 DUCKDB_API unique_ptr<PendingQueryResult> PendingQuery(unique_ptr<SQLStatement> statement,
111 bool allow_stream_result);
112
113 //! Destroy the client context
114 DUCKDB_API void Destroy();
115
116 //! Get the table info of a specific table, or nullptr if it cannot be found
117 DUCKDB_API unique_ptr<TableDescription> TableInfo(const string &schema_name, const string &table_name);
118 //! Appends a DataChunk to the specified table. Returns whether or not the append was successful.
119 DUCKDB_API void Append(TableDescription &description, ColumnDataCollection &collection);
120 //! Try to bind a relation in the current client context; either throws an exception or fills the result_columns
121 //! list with the set of returned columns
122 DUCKDB_API void TryBindRelation(Relation &relation, vector<ColumnDefinition> &result_columns);
123
124 //! Execute a relation
125 DUCKDB_API unique_ptr<PendingQueryResult> PendingQuery(const shared_ptr<Relation> &relation,
126 bool allow_stream_result);
127 DUCKDB_API unique_ptr<QueryResult> Execute(const shared_ptr<Relation> &relation);
128
129 //! Prepare a query
130 DUCKDB_API unique_ptr<PreparedStatement> Prepare(const string &query);
131 //! Directly prepare a SQL statement
132 DUCKDB_API unique_ptr<PreparedStatement> Prepare(unique_ptr<SQLStatement> statement);
133
134 //! Create a pending query result from a prepared statement with the given name and set of parameters
135 //! It is possible that the prepared statement will be re-bound. This will generally happen if the catalog is
136 //! modified in between the prepared statement being bound and the prepared statement being run.
137 DUCKDB_API unique_ptr<PendingQueryResult>
138 PendingQuery(const string &query, shared_ptr<PreparedStatementData> &prepared, PendingQueryParameters parameters);
139
140 //! Execute a prepared statement with the given name and set of parameters
141 //! It is possible that the prepared statement will be re-bound. This will generally happen if the catalog is
142 //! modified in between the prepared statement being bound and the prepared statement being run.
143 DUCKDB_API unique_ptr<QueryResult> Execute(const string &query, shared_ptr<PreparedStatementData> &prepared,
144 vector<Value> &values, bool allow_stream_result = true);
145 DUCKDB_API unique_ptr<QueryResult> Execute(const string &query, shared_ptr<PreparedStatementData> &prepared,
146 PendingQueryParameters parameters);
147
148 //! Gets current percentage of the query's progress, returns 0 in case the progress bar is disabled.
149 DUCKDB_API double GetProgress();
150
151 //! Register function in the temporary schema
152 DUCKDB_API void RegisterFunction(CreateFunctionInfo &info);
153
154 //! Parse statements from a query
155 DUCKDB_API vector<unique_ptr<SQLStatement>> ParseStatements(const string &query);
156
157 //! Extract the logical plan of a query
158 DUCKDB_API unique_ptr<LogicalOperator> ExtractPlan(const string &query);
159 DUCKDB_API void HandlePragmaStatements(vector<unique_ptr<SQLStatement>> &statements);
160
161 //! Runs a function with a valid transaction context, potentially starting a transaction if the context is in auto
162 //! commit mode.
163 DUCKDB_API void RunFunctionInTransaction(const std::function<void(void)> &fun,
164 bool requires_valid_transaction = true);
165 //! Same as RunFunctionInTransaction, but does not obtain a lock on the client context or check for validation
166 DUCKDB_API void RunFunctionInTransactionInternal(ClientContextLock &lock, const std::function<void(void)> &fun,
167 bool requires_valid_transaction = true);
168
169 //! Equivalent to CURRENT_SETTING(key) SQL function.
170 DUCKDB_API bool TryGetCurrentSetting(const std::string &key, Value &result);
171
172 //! Returns the parser options for this client context
173 DUCKDB_API ParserOptions GetParserOptions() const;
174
175 DUCKDB_API unique_ptr<DataChunk> Fetch(ClientContextLock &lock, StreamQueryResult &result);
176
177 //! Whether or not the given result object (streaming query result or pending query result) is active
178 DUCKDB_API bool IsActiveResult(ClientContextLock &lock, BaseQueryResult *result);
179
180 //! Returns the current executor
181 Executor &GetExecutor();
182
183 //! Returns the current query string (if any)
184 const string &GetCurrentQuery();
185
186 //! Fetch a list of table names that are required for a given query
187 DUCKDB_API unordered_set<string> GetTableNames(const string &query);
188
189 DUCKDB_API ClientProperties GetClientProperties() const;
190
191 //! Returns true if execution of the current query is finished
192 DUCKDB_API bool ExecutionIsFinished();
193
194private:
195 //! Parse statements and resolve pragmas from a query
196 bool ParseStatements(ClientContextLock &lock, const string &query, vector<unique_ptr<SQLStatement>> &result,
197 PreservedError &error);
198 //! Issues a query to the database and returns a Pending Query Result
199 unique_ptr<PendingQueryResult> PendingQueryInternal(ClientContextLock &lock, unique_ptr<SQLStatement> statement,
200 PendingQueryParameters parameters, bool verify = true);
201 unique_ptr<QueryResult> ExecutePendingQueryInternal(ClientContextLock &lock, PendingQueryResult &query);
202
203 //! Parse statements from a query
204 vector<unique_ptr<SQLStatement>> ParseStatementsInternal(ClientContextLock &lock, const string &query);
205 //! Perform aggressive query verification of a SELECT statement. Only called when query_verification_enabled is
206 //! true.
207 PreservedError VerifyQuery(ClientContextLock &lock, const string &query, unique_ptr<SQLStatement> statement);
208
209 void InitialCleanup(ClientContextLock &lock);
210 //! Internal clean up, does not lock. Caller must hold the context_lock.
211 void CleanupInternal(ClientContextLock &lock, BaseQueryResult *result = nullptr,
212 bool invalidate_transaction = false);
213 unique_ptr<PendingQueryResult> PendingStatementOrPreparedStatement(ClientContextLock &lock, const string &query,
214 unique_ptr<SQLStatement> statement,
215 shared_ptr<PreparedStatementData> &prepared,
216 PendingQueryParameters parameters);
217 unique_ptr<PendingQueryResult> PendingPreparedStatement(ClientContextLock &lock,
218 shared_ptr<PreparedStatementData> statement_p,
219 PendingQueryParameters parameters);
220
221 //! Internally prepare a SQL statement. Caller must hold the context_lock.
222 shared_ptr<PreparedStatementData> CreatePreparedStatement(ClientContextLock &lock, const string &query,
223 unique_ptr<SQLStatement> statement,
224 vector<Value> *values = nullptr);
225 unique_ptr<PendingQueryResult> PendingStatementInternal(ClientContextLock &lock, const string &query,
226 unique_ptr<SQLStatement> statement,
227 PendingQueryParameters parameters);
228 unique_ptr<QueryResult> RunStatementInternal(ClientContextLock &lock, const string &query,
229 unique_ptr<SQLStatement> statement, bool allow_stream_result,
230 bool verify = true);
231 unique_ptr<PreparedStatement> PrepareInternal(ClientContextLock &lock, unique_ptr<SQLStatement> statement);
232 void LogQueryInternal(ClientContextLock &lock, const string &query);
233
234 unique_ptr<QueryResult> FetchResultInternal(ClientContextLock &lock, PendingQueryResult &pending);
235 unique_ptr<DataChunk> FetchInternal(ClientContextLock &lock, Executor &executor, BaseQueryResult &result);
236
237 unique_ptr<ClientContextLock> LockContext();
238
239 void BeginTransactionInternal(ClientContextLock &lock, bool requires_valid_transaction);
240 void BeginQueryInternal(ClientContextLock &lock, const string &query);
241 PreservedError EndQueryInternal(ClientContextLock &lock, bool success, bool invalidate_transaction);
242
243 PendingExecutionResult ExecuteTaskInternal(ClientContextLock &lock, PendingQueryResult &result);
244
245 unique_ptr<PendingQueryResult> PendingStatementOrPreparedStatementInternal(
246 ClientContextLock &lock, const string &query, unique_ptr<SQLStatement> statement,
247 shared_ptr<PreparedStatementData> &prepared, PendingQueryParameters parameters);
248
249 unique_ptr<PendingQueryResult> PendingQueryPreparedInternal(ClientContextLock &lock, const string &query,
250 shared_ptr<PreparedStatementData> &prepared,
251 PendingQueryParameters parameters);
252
253 unique_ptr<PendingQueryResult> PendingQueryInternal(ClientContextLock &, const shared_ptr<Relation> &relation,
254 bool allow_stream_result);
255
256private:
257 //! Lock on using the ClientContext in parallel
258 mutex context_lock;
259 //! The currently active query context
260 unique_ptr<ActiveQueryContext> active_query;
261 //! The current query progress
262 atomic<double> query_progress;
263};
264
265class ClientContextLock {
266public:
267 explicit ClientContextLock(mutex &context_lock) : client_guard(context_lock) {
268 }
269
270 ~ClientContextLock() {
271 }
272
273private:
274 lock_guard<mutex> client_guard;
275};
276
277class ClientContextWrapper {
278public:
279 explicit ClientContextWrapper(const shared_ptr<ClientContext> &context)
280 : client_context(context) {
281
282 };
283 shared_ptr<ClientContext> GetContext() {
284 auto actual_context = client_context.lock();
285 if (!actual_context) {
286 throw ConnectionException("Connection has already been closed");
287 }
288 return actual_context;
289 }
290
291private:
292 std::weak_ptr<ClientContext> client_context;
293};
294
295} // namespace duckdb
296