File: shell_state.hpp

package info (click to toggle)
duckdb 1.5.1-2
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 299,196 kB
  • sloc: cpp: 865,414; ansic: 57,292; python: 18,871; sql: 12,663; lisp: 11,751; yacc: 7,412; lex: 1,682; sh: 747; makefile: 558
file content (449 lines) | stat: -rw-r--r-- 17,739 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
//===----------------------------------------------------------------------===//
//                         DuckDB
//
// shell_state.hpp
//
//
//===----------------------------------------------------------------------===//

#pragma once

#include <vector>
#include <string>
#include <cstdint>
#include <memory>
#include "duckdb/common/string_util.hpp"
#include "duckdb/common/unique_ptr.hpp"
#include "duckdb/common/optional_ptr.hpp"
#include "duckdb/parser/sql_statement.hpp"
#include "duckdb/main/query_result.hpp"
#include "duckdb/common/atomic.hpp"
#include "duckdb.hpp"

namespace duckdb_shell {
using duckdb::make_uniq;
using duckdb::MaterializedQueryResult;
using duckdb::string;
using duckdb::StringUtil;
using duckdb::unique_ptr;
using duckdb::vector;
struct ColumnarResult;
struct RowResult;
class ShellRenderer;
using duckdb::atomic;
using duckdb::ErrorData;
using duckdb::KeywordHelper;
using duckdb::optional_idx;
using duckdb::optional_ptr;
using duckdb::SQLIdentifier;
using duckdb::SQLString;
using duckdb::unordered_map;
struct ShellState;
using duckdb::InternalException;
using duckdb::InvalidInputException;
using duckdb::to_string;
struct Prompt;
struct ShellProgressBar;
struct PagerState;
struct ShellTableInfo;
struct RenderingQueryResult;
enum class HighlightElementType : uint32_t;

using idx_t = uint64_t;

enum class RenderMode : uint32_t {
	LINE = 0,  /* One column per line.  Blank line between records */
	COLUMN,    /* One record per line in neat columns */
	LIST,      /* One record per line with a separator */
	SEMI,      /* Same as RenderMode::List but append ";" to each line */
	HTML,      /* Generate an XHTML table */
	INSERT,    /* Generate SQL "insert" statements */
	QUOTE,     /* Quote values as for SQL */
	TCL,       /* Generate ANSI-C or TCL quoted elements */
	CSV,       /* Quote strings, numbers are plain */
	EXPLAIN,   /* Like RenderMode::Column, but do not truncate data */
	DESCRIBE,  /* Special DESCRIBE Renderer */
	ASCII,     /* Use ASCII unit and record separators (0x1F/0x1E) */
	PRETTY,    /* Pretty-print schemas */
	EQP,       /* Converts EXPLAIN QUERY PLAN output into a graph */
	JSON,      /* Output JSON */
	MARKDOWN,  /* Markdown formatting */
	TABLE,     /* MySQL-style table formatting */
	BOX,       /* Unicode box-drawing characters */
	LATEX,     /* Latex tabular formatting */
	TRASH,     /* Discard output */
	JSONLINES, /* Output JSON Lines */
	DUCKBOX    /* Unicode box drawing - using DuckDB's own renderer */
};

enum class PrintOutput { STDOUT, STDERR };

enum class InputMode { STANDARD, FILE, DUCKDB_RC };

enum class LargeNumberRendering { NONE = 0, FOOTER = 1, ALL = 2, DEFAULT = 3 };

/*
** These are the allowed shellFlgs values
*/
enum class ShellFlags : uint32_t {
	SHFLG_Newlines = 0x00000010,     /* .dump --newline flag */
	SHFLG_CountChanges = 0x00000020, /* .changes setting */
	SHFLG_Echo = 0x00000040,         /* .echo or --echo setting */
	SHFLG_HeaderSet = 0x00000080     /* .header has been used */
};

enum class ShellOpenFlags { EXIT_ON_FAILURE, KEEP_ALIVE_ON_FAILURE };
enum class SuccessState { SUCCESS, FAILURE };
enum class OptionType { DEFAULT, ON, OFF };
enum class StartupText { ALL, VERSION, NONE };
enum class ReadLineVersion { LINENOISE, FALLBACK };
enum class PagerMode { PAGER_AUTOMATIC, PAGER_ON, PAGER_OFF };

enum class MetadataResult : uint8_t { SUCCESS = 0, FAIL = 1, EXIT = 2, PRINT_USAGE = 3 };
enum class HighlightMode : uint32_t { AUTOMATIC, MIXED_MODE, DARK_MODE, LIGHT_MODE };

enum class ExecuteSQLSingleValueResult {
	SUCCESS,
	EXECUTION_ERROR,
	EMPTY_RESULT,
	MULTIPLE_ROWS,
	MULTIPLE_COLUMNS,
	NULL_RESULT
};

typedef MetadataResult (*metadata_command_t)(ShellState &state, const vector<string> &args);

struct CommandLineOption {
	const char *option;
	idx_t argument_count;
	const char *arguments;
	metadata_command_t pre_init_callback;
	metadata_command_t post_init_callback;
	const char *description;
};

struct MetadataCommand {
	const char *command;
	idx_t argument_count;
	metadata_command_t callback;
	const char *usage;
	const char *description;
	idx_t match_size;
	const char *extra_description;
};

struct ShellColumnInfo {
	string column_name;
	string column_type;
	bool is_primary_key = false;
	bool is_not_null = false;
	bool is_unique = false;
	string default_value;
};

struct ShellTableInfo {
	string database_name;
	string schema_name;
	string table_name;
	optional_idx estimated_size;
	bool is_view = false;
	vector<ShellColumnInfo> columns;
};

enum class BailOnError { AUTOMATIC, BAIL_ON_ERROR, DONT_BAIL_ON_ERROR };

/*
** State information about the database connection is contained in an
** instance of the following structure.
*/
struct ShellState {
public:
	unique_ptr<duckdb::DuckDB> db;            /* The database */
	unique_ptr<duckdb::Connection> conn;      /* The primary connection to the database */
	duckdb::DBConfig config;                  /* Config used for opening the database */
	uint8_t doXdgOpen = 0;                    /* Invoke start/open/xdg-open in output_reset() */
	int outCount = 0;                         /* Revert to stdout when reaching zero */
	int lineno = 0;                           /* Line number of last line read from in */
	FILE *in = nullptr;                       /* Read commands from this stream */
	FILE *out = nullptr;                      /* Write results here */
	int nErr = 0;                             /* Number of errors seen */
	RenderMode mode = RenderMode::LINE;       /* An output mode setting */
	RenderMode modePrior = RenderMode::LINE;  /* Saved mode */
	RenderMode cMode = RenderMode::LINE;      /* temporary output mode for the current query */
	RenderMode normalMode = RenderMode::LINE; /* Output mode before ".explain on" */
	bool showHeader = false;                  /* True to show column names in List or Column mode */
	uint32_t shellFlgs = 0;                   /* Various flags */
	uint32_t priorShFlgs = 0;                 /* Saved copy of flags */
	int64_t szMax = 0;                        /* --maxsize argument to .open */
	string zDestTable;                        /* Name of destination table when RenderMode::Insert */
	string zTempFile;                         /* Temporary file that might need deleting */
	string colSeparator;                      /* Column separator character for several modes */
	string rowSeparator;                      /* Row separator character for RenderMode::Ascii */
	string colSepPrior;                       /* Saved column separator */
	string rowSepPrior;                       /* Saved row separator */
	vector<int> colWidth;                     /* Requested width of each column in columnar modes */
	string nullValue;                         /* The text to print when a NULL comes back from the database */
	int columns = 0;                          /* Column-wise DuckBox rendering */
	string outfile;                           /* Filename for *out */
	string zDbFilename;                       /* name of the database file */
	FILE *pLog = nullptr;                     /* Write log output here */
	size_t max_rows = 0;                      /* The maximum number of rows to render in DuckBox mode */
	size_t max_width = 0; /* The maximum number of characters to render horizontally in DuckBox mode */
	//! The maximum number of rows to analyze in order to determine column widths in DuckBox mode
	idx_t max_analyze_rows = 0;
	//! Decimal separator (if any)
	char decimal_separator = '\0';
	//! Thousand separator (if any)
	char thousand_separator = '\0';
	//! When to use formatting of large numbers (in DuckBox mode)
	LargeNumberRendering large_number_rendering = LargeNumberRendering::DEFAULT;
	//! The command to execute when `-ui` is passed in
	string ui_command = "CALL start_ui()";
	idx_t last_changes = 0;
	idx_t total_changes = 0;
	bool readStdin = true;
	string initFile;
	bool run_init = true;
	unique_ptr<duckdb::MaterializedQueryResult> last_result;
	//! If the following flag is set, then command execution stops at an error
	BailOnError bail = BailOnError::AUTOMATIC;
	//! Table name when rendering a DESCRIBE statement
	string describe_table_name;

	/*
	** Treat stdin as an interactive input if the following variable
	** is true.  Otherwise, assume stdin is connected to a file or pipe.
	*/
	bool stdin_is_interactive = true;

	/*
	** On Windows systems we have to know if standard output is a console
	** in order to translate UTF-8 into MBCS.  The following variable is
	** true if translation is required.
	*/
	bool stdout_is_console = true;
	bool stderr_is_console = true;

	//! True if an interrupt (Control-C) has been received.
	atomic<idx_t> seenInterrupt;
	//! Name of our program
	const char *program_name;

	//! Whether or not syntax highlighting is enabled
	bool highlighting_enabled = true;
	//! Whether or not we are running in safe mode
	bool safe_mode = false;
	//! Whether or not we are highlighting errors
	OptionType highlight_errors = OptionType::DEFAULT;
	//! Whether or not we are highlighting results
	OptionType highlight_results = OptionType::DEFAULT;
	//! Path to .duckdbrc file
	string duckdb_rc_path;
	//! Startup text to display
	StartupText startup_text = StartupText::ALL;
	//! Whether or not the loading resources message was displayed
	bool displayed_loading_resources_message = false;

	/*
	** Prompt strings. Initialized in main. Settable with
	**   .prompt main continue
	*/
	static constexpr idx_t MAX_PROMPT_SIZE = 20;
	unique_ptr<Prompt> main_prompt;
	//! Continuation prompt
	char continuePrompt[MAX_PROMPT_SIZE];
	//! Selected continuation prompt
	char continuePromptSelected[MAX_PROMPT_SIZE];
	//! Prompt showing there is more text available up
	char scrollUpPrompt[MAX_PROMPT_SIZE];
	//! Prompt showing there is more text available down
	char scrollDownPrompt[MAX_PROMPT_SIZE];
	//! Progress bar used to render the components that are displayed when query status / progress is rendered
	unique_ptr<ShellProgressBar> progress_bar;
	//! User-configured highlight elements
	duckdb::unordered_set<HighlightElementType> user_configured_elements;

#ifdef HAVE_LINENOISE
	ReadLineVersion rl_version = ReadLineVersion::LINENOISE;
#else
	ReadLineVersion rl_version = ReadLineVersion::FALLBACK;
#endif

	//! Whether or not to run the pager
	PagerMode pager_mode = PagerMode::PAGER_AUTOMATIC;
	//! The command to run when running the pager
	string pager_command;
	// In automatic mode, only show a pager when this row count is exceeded
	idx_t pager_min_rows = 50;
	//! Whether or not the pager is currently active
	bool pager_is_active = false;
	//! Shell highlighting mode
	HighlightMode highlight_mode = HighlightMode::AUTOMATIC;

public:
	ShellState();
	~ShellState();

	static ShellState &Get();
	static ShellState *&GetReference();

	void Initialize();
	void Destroy();
	void PushOutputMode();
	void PopOutputMode();
	void OutputCSV(const char *z, int bSep);
	string EscapeCString(const string &z);
	string GetSchemaLine(const string &str, const string &tail);
	string GetSchemaLineN(const string &str, idx_t n, const string &tail);
	bool SetOutputMode(const string &mode, const char *tbl_name);
	bool ImportData(const vector<string> &args);
	bool OpenDatabase(const vector<string> &args);
	bool SetOutputFile(const vector<string> &args, char output_mode);
	bool ReadFromFile(const string &file);
	bool DisplaySchemas(const vector<string> &args);
	MetadataResult DisplayEntries(const vector<string> &args, char type);
	MetadataResult DisplayTables(const vector<string> &args);
	void ShowConfiguration();
	void ClearInterrupt();

	static void Exit(int exit_code);
	static idx_t RenderLength(const char *str, idx_t str_len);
	static idx_t RenderLength(duckdb::string_t str);
	static idx_t RenderLength(const string &str);
	static bool IsCharacter(char c);
	void SetBinaryMode();
	void SetTextMode();
	static idx_t StringLength(const char *z);
	void SetTableName(const char *zName);
	static void StaticPrint(PrintOutput output, const char *str, idx_t len);
	void Print(PrintOutput output, const char *str, idx_t len);
	void Print(PrintOutput output, const char *str);
	void Print(PrintOutput output, duckdb::string_t str);
	void Print(PrintOutput output, const string &str);
	void Print(const char *str, idx_t len);
	void Print(duckdb::string_t str);
	void Print(const char *str);
	void Print(const string &str);
	template <typename... ARGS>
	void PrintF(PrintOutput stream, const string &str, ARGS... params) {
		Print(stream, StringUtil::Format(str, params...));
	}
	template <typename... ARGS>
	void PrintF(const string &str, ARGS... params) {
		PrintF(PrintOutput::STDOUT, str, std::forward<ARGS>(params)...);
	}
	bool ColumnTypeIsInteger(const char *type);
	unique_ptr<ShellRenderer> GetRenderer();
	unique_ptr<ShellRenderer> GetRenderer(RenderMode mode);
	vector<string> TableColumnList(const char *zTab);
	SuccessState ExecuteStatement(unique_ptr<duckdb::SQLStatement> statement);
	static bool UseDescribeRenderMode(const duckdb::SQLStatement &stmt, string &describe_table_name);
	void RenderTableMetadata(vector<ShellTableInfo> &result);

	void PrintDatabaseError(const string &zErr);
	int RunInitialCommand(const char *sql, bool bail);
	void AddError();

	SuccessState ExecuteSQL(const string &zSql);
	void RunSchemaDumpQuery(const string &zQuery);
	void RunTableDumpQuery(const string &zSelect);
	void OpenDB(ShellOpenFlags open_flags = ShellOpenFlags::EXIT_ON_FAILURE);

	void SetOrClearFlag(ShellFlags mFlag, const string &zArg);
	bool ShellHasFlag(ShellFlags flag) {
		return (shellFlgs & static_cast<uint32_t>(flag)) != 0;
	}

	void ShellSetFlag(ShellFlags flag) {
		shellFlgs |= static_cast<uint32_t>(flag);
	}

	void ShellClearFlag(ShellFlags flag) {
		shellFlgs &= ~static_cast<uint32_t>(flag);
	}
	void ResetOutput();
	bool ShouldUsePager(ShellRenderer &renderer, RenderingQueryResult &result);
	bool ShouldUsePager();
	bool ShouldUsePager(idx_t line_count);
	idx_t GetMaxRenderWidth() const;
	string GetSystemPager();
	unique_ptr<PagerState> SetupPager();
	static void StartPagerDisplay();
	static void FinishPagerDisplay();
	void ClearTempFile();
	void NewTempFile(const char *zSuffix);
	int DoMetaCommand(const string &zLine);
	idx_t PrintHelp(const char *zPattern);

	void ShellAddHistory(const char *line);
	int ShellLoadHistory(const char *path);
	int ShellSaveHistory(const char *path);
	int ShellSetHistoryMaxLength(idx_t max_length);
	char *OneInputLine(FILE *in, char *zPrior, int isContinuation);

	int RunOneSqlLine(InputMode mode, char *zSql);
	string GetDefaultDuckDBRC();
	bool ProcessDuckDBRC(const char *file);
	bool ProcessFile(const string &file, InputMode input_mode = InputMode::FILE, bool default_duckdb_rc = false);
	int ProcessInput(InputMode mode);
	bool GetBailOnError(InputMode mode);
	static bool SQLIsComplete(const char *zSql);
	static bool IsSpace(char c);
	static bool IsDigit(char c);
	static int64_t StringToInt(const string &arg);
	bool StringToBool(const string &zArg);
	static void GenerateRandomBytes(int N, void *pBuf);
	static bool StringGlob(const char *zGlobPattern, const char *zString);
	static bool StringLike(const char *zPattern, const char *zStr, unsigned int esc);
	static void Sleep(idx_t ms);
	void PrintUsage();
	void DetectDarkLightMode();
#if defined(_WIN32) || defined(WIN32)
	static std::wstring Win32Utf8ToUnicode(const string &zText);
	static std::wstring Win32Utf8ToUnicode(const char *) = delete;
	static std::wstring Win32Utf8ToUnicode(char *) = delete;
	static string Win32UnicodeToUtf8(const std::wstring &zWideText);
	static string Win32MbcsToUtf8(const string &zText, bool useAnsi);
	static string Win32Utf8ToMbcs(const string &zText, bool useAnsi);
#endif
	optional_ptr<const CommandLineOption> FindCommandLineOption(const string &option, string &error_msg) const;
	optional_ptr<const MetadataCommand> FindMetadataCommand(const string &option, string &error_msg) const;
	static vector<string> GetMetadataCompletions(const char *zLine, idx_t nLine);

	//! Execute a SQL query
	// On fail - print the error and returns FAILURE
	SuccessState ExecuteQuery(const string &query);
	//! Execute a SQL query and extracts a single string value
	ExecuteSQLSingleValueResult ExecuteSQLSingleValue(const string &sql, string &result);
	ExecuteSQLSingleValueResult ExecuteSQLSingleValue(duckdb::Connection &con, const string &sql, string &result_value);
	//! Execute a SQL query and renders the result using the given renderer.
	//! On fail - prints the error and returns FAILURE
	SuccessState RenderQuery(ShellRenderer &renderer, const string &query, PagerMode pager_overwrite);
	SuccessState RenderQueryResult(ShellRenderer &renderer, duckdb::QueryResult &result,
	                               PagerMode pager_overwrite = PagerMode::PAGER_AUTOMATIC);
	bool HighlightErrors() const;
	bool HighlightResults() const;

	static MetadataResult SetNullValue(ShellState &state, const vector<string> &args);
	static MetadataResult SetSeparator(ShellState &state, const vector<string> &args);
	static MetadataResult EnableSafeMode(ShellState &state, const vector<string> &args);
	static MetadataResult ToggleTimer(ShellState &state, const vector<string> &args);
	SuccessState ChangeDirectory(const string &path);
	SuccessState ShowDatabases();
	void CloseOutputFile(FILE *file);
	FILE *OpenOutputFile(const char *zFile, int bTextMode);
	static void SetPrompt(char prompt[], const string &new_value);
	static string ModeToString(RenderMode mode);
};

struct PagerState {
	explicit PagerState(ShellState &state, uint32_t win_console_cp_before_pager_p = 0)
	    : state(state), win_console_cp_before_pager(win_console_cp_before_pager_p) {
	}
	~PagerState();

	optional_ptr<ShellState> state;
	uint32_t win_console_cp_before_pager = 0;
};

} // namespace duckdb_shell