duckdb 0.7.2-dev3666.0 → 0.7.2-dev3710.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/package.json +1 -1
  2. package/src/duckdb/extension/json/json_functions/json_transform.cpp +1 -1
  3. package/src/duckdb/extension/json/json_functions/read_json.cpp +1 -1
  4. package/src/duckdb/extension/json/json_functions/read_json_objects.cpp +1 -1
  5. package/src/duckdb/src/common/exception.cpp +17 -0
  6. package/src/duckdb/src/common/exception_format_value.cpp +14 -0
  7. package/src/duckdb/src/common/file_system.cpp +52 -30
  8. package/src/duckdb/src/common/local_file_system.cpp +5 -3
  9. package/src/duckdb/src/common/types.cpp +1 -1
  10. package/src/duckdb/src/core_functions/scalar/list/list_lambdas.cpp +1 -1
  11. package/src/duckdb/src/execution/operator/persistent/physical_export.cpp +2 -2
  12. package/src/duckdb/src/function/pragma/pragma_queries.cpp +1 -1
  13. package/src/duckdb/src/function/table/read_csv.cpp +3 -0
  14. package/src/duckdb/src/function/table/version/pragma_version.cpp +2 -2
  15. package/src/duckdb/src/include/duckdb/common/exception_format_value.hpp +26 -0
  16. package/src/duckdb/src/include/duckdb/common/file_system.hpp +6 -0
  17. package/src/duckdb/src/include/duckdb/common/string_util.hpp +18 -0
  18. package/src/duckdb/src/include/duckdb/parser/expression/function_expression.hpp +1 -1
  19. package/src/duckdb/src/include/duckdb/parser/expression/operator_expression.hpp +2 -2
  20. package/src/duckdb/src/include/duckdb/parser/keyword_helper.hpp +5 -0
  21. package/src/duckdb/src/main/db_instance_cache.cpp +5 -3
  22. package/src/duckdb/src/main/extension/extension_install.cpp +22 -18
  23. package/src/duckdb/src/parser/expression/collate_expression.cpp +1 -1
  24. package/src/duckdb/src/parser/keyword_helper.cpp +11 -1
  25. package/src/duckdb/src/parser/query_node/select_node.cpp +1 -1
  26. package/src/duckdb/src/parser/statement/copy_statement.cpp +2 -2
  27. package/src/duckdb/src/parser/tableref.cpp +1 -1
  28. package/src/duckdb/src/storage/storage_manager.cpp +3 -0
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "duckdb",
3
3
  "main": "./lib/duckdb.js",
4
4
  "types": "./lib/duckdb.d.ts",
5
- "version": "0.7.2-dev3666.0",
5
+ "version": "0.7.2-dev3710.0",
6
6
  "description": "DuckDB node.js API",
7
7
  "gypfile": true,
8
8
  "dependencies": {
@@ -659,7 +659,7 @@ static bool TransformObjectToMap(yyjson_val *objects[], yyjson_alc *alc, Vector
659
659
  // Transform keys
660
660
  if (!JSONTransform::Transform(keys, alc, MapVector::GetKeys(result), list_size, options)) {
661
661
  throw ConversionException(
662
- StringUtil::Format(options.error_message, ". Cannot default to NULL, because map keys cannot be NULL"));
662
+ StringUtil::Format(options.error_message + ". Cannot default to NULL, because map keys cannot be NULL"));
663
663
  }
664
664
 
665
665
  // Transform values
@@ -225,7 +225,7 @@ unique_ptr<FunctionData> ReadJSONBind(ClientContext &context, TableFunctionBindI
225
225
  transform_options.error_unknown_key = bind_data->auto_detect && !bind_data->ignore_errors;
226
226
  transform_options.delay_error = true;
227
227
 
228
- return bind_data;
228
+ return std::move(bind_data);
229
229
  }
230
230
 
231
231
  static void ReadJSONFunction(ClientContext &context, TableFunctionInput &data_p, DataChunk &output) {
@@ -16,7 +16,7 @@ unique_ptr<FunctionData> ReadJSONObjectsBind(ClientContext &context, TableFuncti
16
16
  bind_data->reader_bind =
17
17
  MultiFileReader::BindOptions(bind_data->options.file_options, bind_data->files, return_types, names);
18
18
 
19
- return bind_data;
19
+ return std::move(bind_data);
20
20
  }
21
21
 
22
22
  static void ReadJSONObjectsFunction(ClientContext &context, TableFunctionInput &data_p, DataChunk &output) {
@@ -59,6 +59,23 @@ string Exception::GetStackTrace(int max_depth) {
59
59
  }
60
60
 
61
61
  string Exception::ConstructMessageRecursive(const string &msg, std::vector<ExceptionFormatValue> &values) {
62
+ #ifdef DEBUG
63
+ // Verify that we have the required amount of values for the message
64
+ idx_t parameter_count = 0;
65
+ for (idx_t i = 0; i < msg.size(); i++) {
66
+ if (msg[i] != '%') {
67
+ continue;
68
+ }
69
+ if (i < msg.size() && msg[i + 1] == '%') {
70
+ i++;
71
+ continue;
72
+ }
73
+ parameter_count++;
74
+ }
75
+ if (parameter_count != values.size()) {
76
+ throw InternalException("Expected %d parameters, received %d", parameter_count, values.size());
77
+ }
78
+ #endif
62
79
  return ExceptionFormatValue::Format(msg, values);
63
80
  }
64
81
 
@@ -3,6 +3,7 @@
3
3
  #include "fmt/format.h"
4
4
  #include "fmt/printf.h"
5
5
  #include "duckdb/common/types/hugeint.hpp"
6
+ #include "duckdb/parser/keyword_helper.hpp"
6
7
 
7
8
  namespace duckdb {
8
9
 
@@ -40,6 +41,19 @@ template <>
40
41
  ExceptionFormatValue ExceptionFormatValue::CreateFormatValue(string value) {
41
42
  return ExceptionFormatValue(std::move(value));
42
43
  }
44
+
45
+ template <>
46
+ ExceptionFormatValue
47
+ ExceptionFormatValue::CreateFormatValue(SQLString value) { // NOLINT: templating requires us to copy value here
48
+ return KeywordHelper::WriteQuoted(value.raw_string, '\'');
49
+ }
50
+
51
+ template <>
52
+ ExceptionFormatValue
53
+ ExceptionFormatValue::CreateFormatValue(SQLIdentifier value) { // NOLINT: templating requires us to copy value here
54
+ return KeywordHelper::WriteOptionallyQuoted(value.raw_string, '"');
55
+ }
56
+
43
57
  template <>
44
58
  ExceptionFormatValue ExceptionFormatValue::CreateFormatValue(const char *value) {
45
59
  return ExceptionFormatValue(string(value));
@@ -11,6 +11,7 @@
11
11
  #include "duckdb/main/client_data.hpp"
12
12
  #include "duckdb/main/database.hpp"
13
13
  #include "duckdb/main/extension_helper.hpp"
14
+ #include "duckdb/common/windows_util.hpp"
14
15
 
15
16
  #include <cstdint>
16
17
  #include <cstdio>
@@ -53,6 +54,14 @@ bool PathMatched(const string &path, const string &sub_path) {
53
54
 
54
55
  #ifndef _WIN32
55
56
 
57
+ string FileSystem::GetEnvVariable(const string &name) {
58
+ const char *env = getenv(name.c_str());
59
+ if (!env) {
60
+ return string();
61
+ }
62
+ return env;
63
+ }
64
+
56
65
  bool FileSystem::IsPathAbsolute(const string &path) {
57
66
  auto path_separator = FileSystem::PathSeparator();
58
67
  return PathMatched(path, path_separator);
@@ -85,36 +94,59 @@ string FileSystem::GetWorkingDirectory() {
85
94
  }
86
95
  return string(buffer.get());
87
96
  }
97
+
98
+ string FileSystem::NormalizeAbsolutePath(const string &path) {
99
+ D_ASSERT(IsPathAbsolute(path));
100
+ return path;
101
+ }
102
+
88
103
  #else
89
104
 
90
- bool FileSystem::IsPathAbsolute(const string &path) {
91
- // 1) A single backslash
92
- auto sub_path = FileSystem::PathSeparator();
93
- if (PathMatched(path, sub_path)) {
94
- return true;
105
+ string FileSystem::GetEnvVariable(const string &env) {
106
+ // first convert the environment variable name to the correct encoding
107
+ auto env_w = WindowsUtil::UTF8ToUnicode(env.c_str());
108
+ // use _wgetenv to get the value
109
+ auto res_w = _wgetenv(env_w.c_str());
110
+ if (!res_w) {
111
+ // no environment variable of this name found
112
+ return string();
95
113
  }
96
- // 2) check if starts with a double-backslash (i.e., \\)
97
- sub_path += FileSystem::PathSeparator();
98
- if (PathMatched(path, sub_path)) {
114
+ return WindowsUtil::UnicodeToUTF8(res_w);
115
+ }
116
+
117
+ bool FileSystem::IsPathAbsolute(const string &path) {
118
+ // 1) A single backslash or forward-slash
119
+ if (PathMatched(path, "\\") || PathMatched(path, "/")) {
99
120
  return true;
100
121
  }
101
- // 3) A disk designator with a backslash (e.g., C:\)
122
+ // 2) A disk designator with a backslash (e.g., C:\ or C:/)
102
123
  auto path_aux = path;
103
124
  path_aux.erase(0, 1);
104
- sub_path = ":" + FileSystem::PathSeparator();
105
- if (PathMatched(path_aux, sub_path)) {
125
+ if (PathMatched(path_aux, ":\\") || PathMatched(path_aux, ":/")) {
106
126
  return true;
107
127
  }
108
128
  return false;
109
129
  }
110
130
 
131
+ string FileSystem::NormalizeAbsolutePath(const string &path) {
132
+ D_ASSERT(IsPathAbsolute(path));
133
+ auto result = StringUtil::Lower(FileSystem::ConvertSeparators(path));
134
+ if (PathMatched(result, "\\")) {
135
+ // Path starts with a single backslash or forward slash
136
+ // prepend drive letter
137
+ return GetWorkingDirectory().substr(0, 2) + result;
138
+ }
139
+ return result;
140
+ }
141
+
111
142
  string FileSystem::PathSeparator() {
112
143
  return "\\";
113
144
  }
114
145
 
115
146
  void FileSystem::SetWorkingDirectory(const string &path) {
116
- if (!SetCurrentDirectory(path.c_str())) {
117
- throw IOException("Could not change working directory!");
147
+ auto unicode_path = WindowsUtil::UTF8ToUnicode(path.c_str());
148
+ if (!SetCurrentDirectoryW(unicode_path.c_str())) {
149
+ throw IOException("Could not change working directory to \"%s\"", path);
118
150
  }
119
151
  }
120
152
 
@@ -134,16 +166,16 @@ idx_t FileSystem::GetAvailableMemory() {
134
166
  }
135
167
 
136
168
  string FileSystem::GetWorkingDirectory() {
137
- idx_t count = GetCurrentDirectory(0, nullptr);
169
+ idx_t count = GetCurrentDirectoryW(0, nullptr);
138
170
  if (count == 0) {
139
171
  throw IOException("Could not get working directory!");
140
172
  }
141
- auto buffer = make_unsafe_array<char>(count);
142
- idx_t ret = GetCurrentDirectory(count, buffer.get());
173
+ auto buffer = make_unsafe_array<wchar_t>(count);
174
+ idx_t ret = GetCurrentDirectoryW(count, buffer.get());
143
175
  if (count != ret + 1) {
144
176
  throw IOException("Could not get working directory!");
145
177
  }
146
- return string(buffer.get(), ret);
178
+ return WindowsUtil::UnicodeToUTF8(buffer.get());
147
179
  }
148
180
 
149
181
  #endif
@@ -161,13 +193,7 @@ string FileSystem::ConvertSeparators(const string &path) {
161
193
  return path;
162
194
  }
163
195
  // on windows-based systems we accept both
164
- string result = path;
165
- for (idx_t i = 0; i < result.size(); i++) {
166
- if (result[i] == '/') {
167
- result[i] = separator;
168
- }
169
- }
170
- return result;
196
+ return StringUtil::Replace(path, "/", separator_str);
171
197
  }
172
198
 
173
199
  string FileSystem::ExtractName(const string &path) {
@@ -202,14 +228,10 @@ string FileSystem::GetHomeDirectory(optional_ptr<FileOpener> opener) {
202
228
  }
203
229
  // fallback to the default home directories for the specified system
204
230
  #ifdef DUCKDB_WINDOWS
205
- const char *homedir = getenv("USERPROFILE");
231
+ return FileSystem::GetEnvVariable("USERPROFILE");
206
232
  #else
207
- const char *homedir = getenv("HOME");
233
+ return FileSystem::GetEnvVariable("HOME");
208
234
  #endif
209
- if (homedir) {
210
- return homedir;
211
- }
212
- return string();
213
235
  }
214
236
 
215
237
  string FileSystem::GetHomeDirectory() {
@@ -162,8 +162,9 @@ static FileType GetFileTypeInternal(int fd) { // LCOV_EXCL_START
162
162
  }
163
163
  } // LCOV_EXCL_STOP
164
164
 
165
- unique_ptr<FileHandle> LocalFileSystem::OpenFile(const string &path, uint8_t flags, FileLockType lock_type,
165
+ unique_ptr<FileHandle> LocalFileSystem::OpenFile(const string &path_p, uint8_t flags, FileLockType lock_type,
166
166
  FileCompressionType compression, FileOpener *opener) {
167
+ auto path = FileSystem::ExpandPath(path_p, opener);
167
168
  if (compression != FileCompressionType::UNCOMPRESSED) {
168
169
  throw NotImplementedException("Unsupported compression type for default file system");
169
170
  }
@@ -506,8 +507,9 @@ public:
506
507
  };
507
508
  };
508
509
 
509
- unique_ptr<FileHandle> LocalFileSystem::OpenFile(const string &path, uint8_t flags, FileLockType lock_type,
510
+ unique_ptr<FileHandle> LocalFileSystem::OpenFile(const string &path_p, uint8_t flags, FileLockType lock_type,
510
511
  FileCompressionType compression, FileOpener *opener) {
512
+ auto path = FileSystem::ExpandPath(path_p, opener);
511
513
  if (compression != FileCompressionType::UNCOMPRESSED) {
512
514
  throw NotImplementedException("Unsupported compression type for default file system");
513
515
  }
@@ -775,7 +777,7 @@ void LocalFileSystem::MoveFile(const string &source, const string &target) {
775
777
  auto source_unicode = WindowsUtil::UTF8ToUnicode(source.c_str());
776
778
  auto target_unicode = WindowsUtil::UTF8ToUnicode(target.c_str());
777
779
  if (!MoveFileW(source_unicode.c_str(), target_unicode.c_str())) {
778
- throw IOException("Could not move file");
780
+ throw IOException("Could not move file: %s", GetLastErrorAsString());
779
781
  }
780
782
  }
781
783
 
@@ -349,7 +349,7 @@ string LogicalType::ToString() const {
349
349
  auto &child_types = StructType::GetChildTypes(*this);
350
350
  string ret = "STRUCT(";
351
351
  for (size_t i = 0; i < child_types.size(); i++) {
352
- ret += KeywordHelper::WriteOptionallyQuoted(child_types[i].first) + " " + child_types[i].second.ToString();
352
+ ret += StringUtil::Format("%s %s", SQLIdentifier(child_types[i].first), child_types[i].second);
353
353
  if (i < child_types.size() - 1) {
354
354
  ret += ", ";
355
355
  }
@@ -77,7 +77,7 @@ static void AppendFilteredToResult(Vector &lambda_vector, list_entry_t *result_e
77
77
  }
78
78
 
79
79
  // found a true value
80
- if (lambda_validity.RowIsValid(entry) && lambda_values[entry] > 0) {
80
+ if (lambda_validity.RowIsValid(entry) && lambda_values[entry]) {
81
81
  true_sel.set_index(true_count++, i);
82
82
  curr_list_len++;
83
83
  }
@@ -51,8 +51,8 @@ static void WriteCopyStatement(FileSystem &fs, stringstream &ss, CopyInfo &info,
51
51
  ss << KeywordHelper::WriteOptionallyQuoted(exported_table.schema_name) << ".";
52
52
  }
53
53
 
54
- ss << KeywordHelper::WriteOptionallyQuoted(exported_table.table_name) << " FROM '" << exported_table.file_path
55
- << "' (";
54
+ ss << StringUtil::Format("%s FROM %s (", SQLIdentifier(exported_table.table_name),
55
+ SQLString(exported_table.file_path));
56
56
 
57
57
  // write the copy options
58
58
  ss << "FORMAT '" << info.format << "'";
@@ -41,7 +41,7 @@ string PragmaShowTables(ClientContext &context, const FunctionParameters &parame
41
41
  )
42
42
  SELECT "name"
43
43
  FROM db_objects
44
- ORDER BY "name";)EOF", where_clause, where_clause, where_clause);
44
+ ORDER BY "name";)EOF", where_clause, where_clause);
45
45
  // clang-format on
46
46
 
47
47
  return pragma_query;
@@ -586,6 +586,9 @@ bool LineInfo::CanItGetLine(idx_t file_idx, idx_t batch_idx) {
586
586
  if (current_batches.empty() || done) {
587
587
  return true;
588
588
  }
589
+ if (file_idx >= current_batches.size() || current_batches[file_idx].empty()) {
590
+ return true;
591
+ }
589
592
  auto min_value = *current_batches[file_idx].begin();
590
593
  if (min_value >= batch_idx) {
591
594
  return true;
@@ -1,8 +1,8 @@
1
1
  #ifndef DUCKDB_VERSION
2
- #define DUCKDB_VERSION "0.7.2-dev3666"
2
+ #define DUCKDB_VERSION "0.7.2-dev3710"
3
3
  #endif
4
4
  #ifndef DUCKDB_SOURCE_ID
5
- #define DUCKDB_SOURCE_ID "eae707d54c"
5
+ #define DUCKDB_SOURCE_ID "59a4ec3adc"
6
6
  #endif
7
7
  #include "duckdb/function/table/system_functions.hpp"
8
8
  #include "duckdb/main/database.hpp"
@@ -15,6 +15,28 @@
15
15
 
16
16
  namespace duckdb {
17
17
 
18
+ // Helper class to support custom overloading
19
+ // Escaping " and quoting the value with "
20
+ class SQLIdentifier {
21
+ public:
22
+ SQLIdentifier(const string &raw_string) : raw_string(raw_string) {
23
+ }
24
+
25
+ public:
26
+ string raw_string;
27
+ };
28
+
29
+ // Helper class to support custom overloading
30
+ // Escaping ' and quoting the value with '
31
+ class SQLString {
32
+ public:
33
+ SQLString(const string &raw_string) : raw_string(raw_string) {
34
+ }
35
+
36
+ public:
37
+ string raw_string;
38
+ };
39
+
18
40
  enum class PhysicalType : uint8_t;
19
41
  struct LogicalType;
20
42
 
@@ -47,6 +69,10 @@ public:
47
69
  template <>
48
70
  DUCKDB_API ExceptionFormatValue ExceptionFormatValue::CreateFormatValue(PhysicalType value);
49
71
  template <>
72
+ DUCKDB_API ExceptionFormatValue ExceptionFormatValue::CreateFormatValue(SQLString value);
73
+ template <>
74
+ DUCKDB_API ExceptionFormatValue ExceptionFormatValue::CreateFormatValue(SQLIdentifier value);
75
+ template <>
50
76
  DUCKDB_API ExceptionFormatValue ExceptionFormatValue::CreateFormatValue(LogicalType value);
51
77
  template <>
52
78
  DUCKDB_API ExceptionFormatValue ExceptionFormatValue::CreateFormatValue(float value);
@@ -178,6 +178,9 @@ public:
178
178
  DUCKDB_API static string PathSeparator();
179
179
  //! Checks if path is starts with separator (i.e., '/' on UNIX '\\' on Windows)
180
180
  DUCKDB_API static bool IsPathAbsolute(const string &path);
181
+ //! Normalize an absolute path - the goal of normalizing is converting "\test.db" and "C:/test.db" into "C:\test.db"
182
+ //! so that the database system cache can correctly
183
+ DUCKDB_API static string NormalizeAbsolutePath(const string &path);
181
184
  //! Join two paths together
182
185
  DUCKDB_API static string JoinPath(const string &a, const string &path);
183
186
  //! Convert separators in a path to the local separators (e.g. convert "/" into \\ on windows)
@@ -187,6 +190,9 @@ public:
187
190
  //! Extract the name of a file (e.g if the input is lib/example.dll the name is 'example.dll')
188
191
  DUCKDB_API static string ExtractName(const string &path);
189
192
 
193
+ //! Returns the value of an environment variable - or the empty string if it is not set
194
+ DUCKDB_API static string GetEnvVariable(const string &name);
195
+
190
196
  //! Whether there is a glob in the string
191
197
  DUCKDB_API static bool HasGlob(const string &str);
192
198
  //! Runs a glob on the file system, returning a list of matching files
@@ -15,6 +15,7 @@
15
15
  #include <cstring>
16
16
 
17
17
  namespace duckdb {
18
+
18
19
  /**
19
20
  * String Utility Functions
20
21
  * Note that these are not the most efficient implementations (i.e., they copy
@@ -85,6 +86,23 @@ public:
85
86
  return false;
86
87
  }
87
88
 
89
+ template <class TO>
90
+ static vector<TO> ConvertStrings(const vector<string> &strings) {
91
+ vector<TO> result;
92
+ for (auto &string : strings) {
93
+ result.emplace_back(string);
94
+ }
95
+ return result;
96
+ }
97
+
98
+ static vector<SQLIdentifier> ConvertToSQLIdentifiers(const vector<string> &strings) {
99
+ return ConvertStrings<SQLIdentifier>(strings);
100
+ }
101
+
102
+ static vector<SQLString> ConvertToSQLStrings(const vector<string> &strings) {
103
+ return ConvertStrings<SQLString>(strings);
104
+ }
105
+
88
106
  //! Returns true if the needle string exists in the haystack
89
107
  DUCKDB_API static bool Contains(const string &haystack, const string &needle);
90
108
 
@@ -92,7 +92,7 @@ public:
92
92
  result += StringUtil::Join(entry.children, entry.children.size(), ", ", [&](const unique_ptr<BASE> &child) {
93
93
  return child->alias.empty() || !add_alias
94
94
  ? child->ToString()
95
- : KeywordHelper::WriteOptionallyQuoted(child->alias) + " := " + child->ToString();
95
+ : StringUtil::Format("%s := %s", SQLIdentifier(child->alias), child->ToString());
96
96
  });
97
97
  // ordered aggregate
98
98
  if (order_bys && !order_bys->orders.empty()) {
@@ -96,8 +96,8 @@ public:
96
96
  auto child_string = entry.children[1]->ToString();
97
97
  D_ASSERT(child_string.size() >= 3);
98
98
  D_ASSERT(child_string[0] == '\'' && child_string[child_string.size() - 1] == '\'');
99
- return "(" + entry.children[0]->ToString() + ")." +
100
- KeywordHelper::WriteOptionallyQuoted(child_string.substr(1, child_string.size() - 2));
99
+ return StringUtil::Format("(%s).%s", entry.children[0]->ToString(),
100
+ SQLIdentifier(child_string.substr(1, child_string.size() - 2)));
101
101
  }
102
102
  case ExpressionType::ARRAY_CONSTRUCTOR: {
103
103
  string result = "(ARRAY[";
@@ -17,9 +17,14 @@ public:
17
17
  //! Returns true if the given text matches a keyword of the parser
18
18
  static bool IsKeyword(const string &text);
19
19
 
20
+ static string EscapeQuotes(const string &text, char quote = '"');
21
+
20
22
  //! Returns true if the given string needs to be quoted when written as an identifier
21
23
  static bool RequiresQuotes(const string &text, bool allow_caps = true);
22
24
 
25
+ //! Writes a string that is quoted
26
+ static string WriteQuoted(const string &text, char quote = '\'');
27
+
23
28
  //! Writes a string that is optionally quoted + escaped so it can be used as an identifier
24
29
  static string WriteOptionallyQuoted(const string &text, char quote = '"', bool allow_caps = true);
25
30
  };
@@ -1,8 +1,10 @@
1
1
  #include "duckdb/main/db_instance_cache.hpp"
2
2
  #include "duckdb/main/extension_helper.hpp"
3
+
3
4
  namespace duckdb {
4
5
 
5
- string GetDBAbsolutePath(const string &database) {
6
+ string GetDBAbsolutePath(const string &database_p) {
7
+ auto database = FileSystem::ExpandPath(database_p, nullptr);
6
8
  if (database.empty()) {
7
9
  return ":memory:";
8
10
  }
@@ -15,9 +17,9 @@ string GetDBAbsolutePath(const string &database) {
15
17
  return database;
16
18
  }
17
19
  if (FileSystem::IsPathAbsolute(database)) {
18
- return database;
20
+ return FileSystem::NormalizeAbsolutePath(database);
19
21
  }
20
- return FileSystem::JoinPath(FileSystem::GetWorkingDirectory(), database);
22
+ return FileSystem::NormalizeAbsolutePath(FileSystem::JoinPath(FileSystem::GetWorkingDirectory(), database));
21
23
  }
22
24
 
23
25
  shared_ptr<DuckDB> DBInstanceCache::GetInstanceInternal(const string &database, const DBConfig &config) {
@@ -133,6 +133,23 @@ void ExtensionHelper::InstallExtension(ClientContext &context, const string &ext
133
133
  InstallExtensionInternal(config, &client_config, fs, local_path, extension, force_install);
134
134
  }
135
135
 
136
+ unsafe_array_ptr<data_t> ReadExtensionFileFromDisk(FileSystem &fs, const string &path, idx_t &file_size) {
137
+ auto source_file = fs.OpenFile(path, FileFlags::FILE_FLAGS_READ);
138
+ file_size = source_file->GetFileSize();
139
+ auto in_buffer = make_unsafe_array<data_t>(file_size);
140
+ source_file->Read(in_buffer.get(), file_size);
141
+ source_file->Close();
142
+ return in_buffer;
143
+ }
144
+
145
+ void WriteExtensionFileToDisk(FileSystem &fs, const string &path, void *data, idx_t data_size) {
146
+ auto target_file = fs.OpenFile(path, FileFlags::FILE_FLAGS_WRITE | FileFlags::FILE_FLAGS_APPEND |
147
+ FileFlags::FILE_FLAGS_FILE_CREATE_NEW);
148
+ target_file->Write(data, data_size);
149
+ target_file->Close();
150
+ target_file.reset();
151
+ }
152
+
136
153
  void ExtensionHelper::InstallExtensionInternal(DBConfig &config, ClientConfig *client_config, FileSystem &fs,
137
154
  const string &local_path, const string &extension, bool force_install) {
138
155
  if (!config.options.enable_external_access) {
@@ -152,18 +169,9 @@ void ExtensionHelper::InstallExtensionInternal(DBConfig &config, ClientConfig *c
152
169
  }
153
170
  auto is_http_url = StringUtil::Contains(extension, "http://");
154
171
  if (fs.FileExists(extension)) {
155
-
156
- std::ifstream in(extension, std::ios::binary);
157
- if (in.bad()) {
158
- throw IOException("Failed to read extension from \"%s\"", extension);
159
- }
160
- std::ofstream out(temp_path, std::ios::binary);
161
- out << in.rdbuf();
162
- if (out.bad()) {
163
- throw IOException("Failed to write extension to \"%s\"", temp_path);
164
- }
165
- in.close();
166
- out.close();
172
+ idx_t file_size;
173
+ auto in_buffer = ReadExtensionFileFromDisk(fs, extension, file_size);
174
+ WriteExtensionFileToDisk(fs, temp_path, in_buffer.get(), file_size);
167
175
 
168
176
  fs.MoveFile(temp_path, local_extension_path);
169
177
  return;
@@ -225,12 +233,8 @@ void ExtensionHelper::InstallExtensionInternal(DBConfig &config, ClientConfig *c
225
233
  }
226
234
  }
227
235
  auto decompressed_body = GZipFileSystem::UncompressGZIPString(res->body);
228
- std::ofstream out(temp_path, std::ios::binary);
229
- out.write(decompressed_body.data(), decompressed_body.size());
230
- if (out.bad()) {
231
- throw IOException("Failed to write extension to %s", temp_path);
232
- }
233
- out.close();
236
+
237
+ WriteExtensionFileToDisk(fs, temp_path, (void *)decompressed_body.data(), decompressed_body.size());
234
238
  fs.MoveFile(temp_path, local_extension_path);
235
239
  #endif
236
240
  }
@@ -15,7 +15,7 @@ CollateExpression::CollateExpression(string collation_p, unique_ptr<ParsedExpres
15
15
  }
16
16
 
17
17
  string CollateExpression::ToString() const {
18
- return child->ToString() + " COLLATE " + KeywordHelper::WriteOptionallyQuoted(collation);
18
+ return StringUtil::Format("%s COLLATE %s", child->ToString(), SQLIdentifier(collation));
19
19
  }
20
20
 
21
21
  bool CollateExpression::Equal(const CollateExpression *a, const CollateExpression *b) {
@@ -29,11 +29,21 @@ bool KeywordHelper::RequiresQuotes(const string &text, bool allow_caps) {
29
29
  return IsKeyword(text);
30
30
  }
31
31
 
32
+ string KeywordHelper::EscapeQuotes(const string &text, char quote) {
33
+ return StringUtil::Replace(text, string(1, quote), string(2, quote));
34
+ }
35
+
36
+ string KeywordHelper::WriteQuoted(const string &text, char quote) {
37
+ // 1. Escapes all occurences of 'quote' by doubling them (escape in SQL)
38
+ // 2. Adds quotes around the string
39
+ return string(1, quote) + EscapeQuotes(text) + string(1, quote);
40
+ }
41
+
32
42
  string KeywordHelper::WriteOptionallyQuoted(const string &text, char quote, bool allow_caps) {
33
43
  if (!RequiresQuotes(text, allow_caps)) {
34
44
  return text;
35
45
  }
36
- return string(1, quote) + StringUtil::Replace(text, string(1, quote), string(2, quote)) + string(1, quote);
46
+ return WriteQuoted(text, quote);
37
47
  }
38
48
 
39
49
  } // namespace duckdb
@@ -39,7 +39,7 @@ string SelectNode::ToString() const {
39
39
  }
40
40
  result += select_list[i]->ToString();
41
41
  if (!select_list[i]->alias.empty()) {
42
- result += " AS " + KeywordHelper::WriteOptionallyQuoted(select_list[i]->alias);
42
+ result += StringUtil::Format(" AS %s", SQLIdentifier(select_list[i]->alias));
43
43
  }
44
44
  }
45
45
  if (from_table && from_table->type != TableReferenceType::EMPTY) {
@@ -86,7 +86,7 @@ string CopyStatement::ToString() const {
86
86
  D_ASSERT(!select_statement);
87
87
  result += TablePart(*info);
88
88
  result += " FROM";
89
- result += StringUtil::Format(" '%s'", info->file_path);
89
+ result += StringUtil::Format(" %s", SQLString(info->file_path));
90
90
  result += CopyOptionsToString(info->format, info->options);
91
91
  } else {
92
92
  if (select_statement) {
@@ -96,7 +96,7 @@ string CopyStatement::ToString() const {
96
96
  result += TablePart(*info);
97
97
  }
98
98
  result += " TO ";
99
- result += StringUtil::Format("'%s'", info->file_path);
99
+ result += StringUtil::Format("%s", SQLString(info->file_path));
100
100
  result += CopyOptionsToString(info->format, info->options);
101
101
  }
102
102
  return result;
@@ -16,7 +16,7 @@ string TableRef::BaseToString(string result) const {
16
16
 
17
17
  string TableRef::BaseToString(string result, const vector<string> &column_name_alias) const {
18
18
  if (!alias.empty()) {
19
- result += " AS " + KeywordHelper::WriteOptionallyQuoted(alias);
19
+ result += StringUtil::Format(" AS %s", SQLIdentifier(alias));
20
20
  }
21
21
  if (!column_name_alias.empty()) {
22
22
  D_ASSERT(!alias.empty());
@@ -19,6 +19,9 @@ StorageManager::StorageManager(AttachedDatabase &db, string path_p, bool read_on
19
19
  : db(db), path(std::move(path_p)), read_only(read_only) {
20
20
  if (path.empty()) {
21
21
  path = ":memory:";
22
+ } else {
23
+ auto &fs = FileSystem::Get(db);
24
+ this->path = fs.ExpandPath(path);
22
25
  }
23
26
  }
24
27