//===----------------------------------------------------------------------===// // DuckDB // // duckdb/main/client_context.hpp // // //===----------------------------------------------------------------------===// #pragma once #include "duckdb/catalog/catalog_entry/schema_catalog_entry.hpp" #include "duckdb/catalog/catalog_set.hpp" #include "duckdb/common/enums/pending_execution_result.hpp" #include "duckdb/common/deque.hpp" #include "duckdb/common/pair.hpp" #include "duckdb/common/unordered_set.hpp" #include "duckdb/common/winapi.hpp" #include "duckdb/main/prepared_statement.hpp" #include "duckdb/main/stream_query_result.hpp" #include "duckdb/main/table_description.hpp" #include "duckdb/transaction/transaction_context.hpp" #include "duckdb/main/pending_query_result.hpp" #include "duckdb/common/atomic.hpp" #include "duckdb/main/client_config.hpp" #include "duckdb/main/external_dependencies.hpp" #include "duckdb/common/preserved_error.hpp" namespace duckdb { class Appender; class Catalog; class CatalogSearchPath; class ColumnDataCollection; class DatabaseInstance; class FileOpener; class LogicalOperator; class PreparedStatementData; class Relation; class BufferedFileWriter; class QueryProfiler; class ClientContextLock; struct CreateScalarFunctionInfo; class ScalarFunctionCatalogEntry; struct ActiveQueryContext; struct ParserOptions; struct ClientData; struct PendingQueryParameters { //! Prepared statement parameters (if any) vector *parameters = nullptr; //! Whether or not a stream result should be allowed bool allow_stream_result = false; }; //! ClientContextState is virtual base class for ClientContext-local (or Query-Local, using QueryEnd callback) state //! e.g. caches that need to live as long as a ClientContext or Query. class ClientContextState { public: virtual ~ClientContextState() {}; virtual void QueryEnd() = 0; }; //! The ClientContext holds information relevant to the current client session //! during execution class ClientContext : public std::enable_shared_from_this { friend class PendingQueryResult; friend class StreamQueryResult; friend class DuckTransactionManager; public: DUCKDB_API explicit ClientContext(shared_ptr db); DUCKDB_API ~ClientContext(); //! The database that this client is connected to shared_ptr db; //! Whether or not the query is interrupted atomic interrupted; //! External Objects (e.g., Python objects) that views depend of unordered_map>> external_dependencies; //! Set of optional states (e.g. Caches) that can be held by the ClientContext unordered_map> registered_state; //! The client configuration ClientConfig config; //! The set of client-specific data unique_ptr client_data; //! Data for the currently running transaction TransactionContext transaction; public: MetaTransaction &ActiveTransaction() { return transaction.ActiveTransaction(); } //! Interrupt execution of a query DUCKDB_API void Interrupt(); //! Enable query profiling DUCKDB_API void EnableProfiling(); //! Disable query profiling DUCKDB_API void DisableProfiling(); //! Issue a query, returning a QueryResult. The QueryResult can be either a StreamQueryResult or a //! MaterializedQueryResult. The StreamQueryResult will only be returned in the case of a successful SELECT //! statement. DUCKDB_API unique_ptr Query(const string &query, bool allow_stream_result); DUCKDB_API unique_ptr Query(unique_ptr statement, bool allow_stream_result); //! Issues a query to the database and returns a Pending Query Result. Note that "query" may only contain //! a single statement. DUCKDB_API unique_ptr PendingQuery(const string &query, bool allow_stream_result); //! Issues a query to the database and returns a Pending Query Result DUCKDB_API unique_ptr PendingQuery(unique_ptr statement, bool allow_stream_result); //! Destroy the client context DUCKDB_API void Destroy(); //! Get the table info of a specific table, or nullptr if it cannot be found DUCKDB_API unique_ptr TableInfo(const string &schema_name, const string &table_name); //! Appends a DataChunk to the specified table. Returns whether or not the append was successful. DUCKDB_API void Append(TableDescription &description, ColumnDataCollection &collection); //! Try to bind a relation in the current client context; either throws an exception or fills the result_columns //! list with the set of returned columns DUCKDB_API void TryBindRelation(Relation &relation, vector &result_columns); //! Execute a relation DUCKDB_API unique_ptr PendingQuery(const shared_ptr &relation, bool allow_stream_result); DUCKDB_API unique_ptr Execute(const shared_ptr &relation); //! Prepare a query DUCKDB_API unique_ptr Prepare(const string &query); //! Directly prepare a SQL statement DUCKDB_API unique_ptr Prepare(unique_ptr statement); //! Create a pending query result from a prepared statement with the given name and set of parameters //! It is possible that the prepared statement will be re-bound. This will generally happen if the catalog is //! modified in between the prepared statement being bound and the prepared statement being run. DUCKDB_API unique_ptr PendingQuery(const string &query, shared_ptr &prepared, PendingQueryParameters parameters); //! Execute a prepared statement with the given name and set of parameters //! It is possible that the prepared statement will be re-bound. This will generally happen if the catalog is //! modified in between the prepared statement being bound and the prepared statement being run. DUCKDB_API unique_ptr Execute(const string &query, shared_ptr &prepared, vector &values, bool allow_stream_result = true); DUCKDB_API unique_ptr Execute(const string &query, shared_ptr &prepared, PendingQueryParameters parameters); //! Gets current percentage of the query's progress, returns 0 in case the progress bar is disabled. DUCKDB_API double GetProgress(); //! Register function in the temporary schema DUCKDB_API void RegisterFunction(CreateFunctionInfo &info); //! Parse statements from a query DUCKDB_API vector> ParseStatements(const string &query); //! Extract the logical plan of a query DUCKDB_API unique_ptr ExtractPlan(const string &query); DUCKDB_API void HandlePragmaStatements(vector> &statements); //! Runs a function with a valid transaction context, potentially starting a transaction if the context is in auto //! commit mode. DUCKDB_API void RunFunctionInTransaction(const std::function &fun, bool requires_valid_transaction = true); //! Same as RunFunctionInTransaction, but does not obtain a lock on the client context or check for validation DUCKDB_API void RunFunctionInTransactionInternal(ClientContextLock &lock, const std::function &fun, bool requires_valid_transaction = true); //! Equivalent to CURRENT_SETTING(key) SQL function. DUCKDB_API bool TryGetCurrentSetting(const std::string &key, Value &result); //! Returns the parser options for this client context DUCKDB_API ParserOptions GetParserOptions() const; DUCKDB_API unique_ptr Fetch(ClientContextLock &lock, StreamQueryResult &result); //! Whether or not the given result object (streaming query result or pending query result) is active DUCKDB_API bool IsActiveResult(ClientContextLock &lock, BaseQueryResult *result); //! Returns the current executor Executor &GetExecutor(); //! Returns the current query string (if any) const string &GetCurrentQuery(); //! Fetch a list of table names that are required for a given query DUCKDB_API unordered_set GetTableNames(const string &query); DUCKDB_API ClientProperties GetClientProperties() const; //! Returns true if execution of the current query is finished DUCKDB_API bool ExecutionIsFinished(); private: //! Parse statements and resolve pragmas from a query bool ParseStatements(ClientContextLock &lock, const string &query, vector> &result, PreservedError &error); //! Issues a query to the database and returns a Pending Query Result unique_ptr PendingQueryInternal(ClientContextLock &lock, unique_ptr statement, PendingQueryParameters parameters, bool verify = true); unique_ptr ExecutePendingQueryInternal(ClientContextLock &lock, PendingQueryResult &query); //! Parse statements from a query vector> ParseStatementsInternal(ClientContextLock &lock, const string &query); //! Perform aggressive query verification of a SELECT statement. Only called when query_verification_enabled is //! true. PreservedError VerifyQuery(ClientContextLock &lock, const string &query, unique_ptr statement); void InitialCleanup(ClientContextLock &lock); //! Internal clean up, does not lock. Caller must hold the context_lock. void CleanupInternal(ClientContextLock &lock, BaseQueryResult *result = nullptr, bool invalidate_transaction = false); unique_ptr PendingStatementOrPreparedStatement(ClientContextLock &lock, const string &query, unique_ptr statement, shared_ptr &prepared, PendingQueryParameters parameters); unique_ptr PendingPreparedStatement(ClientContextLock &lock, shared_ptr statement_p, PendingQueryParameters parameters); //! Internally prepare a SQL statement. Caller must hold the context_lock. shared_ptr CreatePreparedStatement(ClientContextLock &lock, const string &query, unique_ptr statement, vector *values = nullptr); unique_ptr PendingStatementInternal(ClientContextLock &lock, const string &query, unique_ptr statement, PendingQueryParameters parameters); unique_ptr RunStatementInternal(ClientContextLock &lock, const string &query, unique_ptr statement, bool allow_stream_result, bool verify = true); unique_ptr PrepareInternal(ClientContextLock &lock, unique_ptr statement); void LogQueryInternal(ClientContextLock &lock, const string &query); unique_ptr FetchResultInternal(ClientContextLock &lock, PendingQueryResult &pending); unique_ptr FetchInternal(ClientContextLock &lock, Executor &executor, BaseQueryResult &result); unique_ptr LockContext(); void BeginTransactionInternal(ClientContextLock &lock, bool requires_valid_transaction); void BeginQueryInternal(ClientContextLock &lock, const string &query); PreservedError EndQueryInternal(ClientContextLock &lock, bool success, bool invalidate_transaction); PendingExecutionResult ExecuteTaskInternal(ClientContextLock &lock, PendingQueryResult &result); unique_ptr PendingStatementOrPreparedStatementInternal( ClientContextLock &lock, const string &query, unique_ptr statement, shared_ptr &prepared, PendingQueryParameters parameters); unique_ptr PendingQueryPreparedInternal(ClientContextLock &lock, const string &query, shared_ptr &prepared, PendingQueryParameters parameters); unique_ptr PendingQueryInternal(ClientContextLock &, const shared_ptr &relation, bool allow_stream_result); private: //! Lock on using the ClientContext in parallel mutex context_lock; //! The currently active query context unique_ptr active_query; //! The current query progress atomic query_progress; }; class ClientContextLock { public: explicit ClientContextLock(mutex &context_lock) : client_guard(context_lock) { } ~ClientContextLock() { } private: lock_guard client_guard; }; class ClientContextWrapper { public: explicit ClientContextWrapper(const shared_ptr &context) : client_context(context) { }; shared_ptr GetContext() { auto actual_context = client_context.lock(); if (!actual_context) { throw ConnectionException("Connection has already been closed"); } return actual_context; } private: std::weak_ptr client_context; }; } // namespace duckdb