1#include "duckdb/catalog/catalog_search_path.hpp"
2
3#include "duckdb/common/constants.hpp"
4#include "duckdb/common/exception.hpp"
5#include "duckdb/common/string_util.hpp"
6#include "duckdb/main/client_context.hpp"
7#include "duckdb/catalog/catalog.hpp"
8#include "duckdb/main/database_manager.hpp"
9
10namespace duckdb {
11
12CatalogSearchEntry::CatalogSearchEntry(string catalog_p, string schema_p)
13 : catalog(std::move(catalog_p)), schema(std::move(schema_p)) {
14}
15
16string CatalogSearchEntry::ToString() const {
17 if (catalog.empty()) {
18 return WriteOptionallyQuoted(input: schema);
19 } else {
20 return WriteOptionallyQuoted(input: catalog) + "." + WriteOptionallyQuoted(input: schema);
21 }
22}
23
24string CatalogSearchEntry::WriteOptionallyQuoted(const string &input) {
25 for (idx_t i = 0; i < input.size(); i++) {
26 if (input[i] == '.' || input[i] == ',') {
27 return "\"" + input + "\"";
28 }
29 }
30 return input;
31}
32
33string CatalogSearchEntry::ListToString(const vector<CatalogSearchEntry> &input) {
34 string result;
35 for (auto &entry : input) {
36 if (!result.empty()) {
37 result += ",";
38 }
39 result += entry.ToString();
40 }
41 return result;
42}
43
44CatalogSearchEntry CatalogSearchEntry::ParseInternal(const string &input, idx_t &idx) {
45 string catalog;
46 string schema;
47 string entry;
48 bool finished = false;
49normal:
50 for (; idx < input.size(); idx++) {
51 if (input[idx] == '"') {
52 idx++;
53 goto quoted;
54 } else if (input[idx] == '.') {
55 goto separator;
56 } else if (input[idx] == ',') {
57 finished = true;
58 goto separator;
59 }
60 entry += input[idx];
61 }
62 finished = true;
63 goto separator;
64quoted:
65 //! look for another quote
66 for (; idx < input.size(); idx++) {
67 if (input[idx] == '"') {
68 //! unquote
69 idx++;
70 goto normal;
71 }
72 entry += input[idx];
73 }
74 throw ParserException("Unterminated quote in qualified name!");
75separator:
76 if (entry.empty()) {
77 throw ParserException("Unexpected dot - empty CatalogSearchEntry");
78 }
79 if (schema.empty()) {
80 // if we parse one entry it is the schema
81 schema = std::move(entry);
82 } else if (catalog.empty()) {
83 // if we parse two entries it is [catalog.schema]
84 catalog = std::move(schema);
85 schema = std::move(entry);
86 } else {
87 throw ParserException("Too many dots - expected [schema] or [catalog.schema] for CatalogSearchEntry");
88 }
89 entry = "";
90 idx++;
91 if (finished) {
92 goto final;
93 }
94 goto normal;
95final:
96 if (schema.empty()) {
97 throw ParserException("Unexpected end of entry - empty CatalogSearchEntry");
98 }
99 return CatalogSearchEntry(std::move(catalog), std::move(schema));
100}
101
102CatalogSearchEntry CatalogSearchEntry::Parse(const string &input) {
103 idx_t pos = 0;
104 auto result = ParseInternal(input, idx&: pos);
105 if (pos < input.size()) {
106 throw ParserException("Failed to convert entry \"%s\" to CatalogSearchEntry - expected a single entry", input);
107 }
108 return result;
109}
110
111vector<CatalogSearchEntry> CatalogSearchEntry::ParseList(const string &input) {
112 idx_t pos = 0;
113 vector<CatalogSearchEntry> result;
114 while (pos < input.size()) {
115 auto entry = ParseInternal(input, idx&: pos);
116 result.push_back(x: entry);
117 }
118 return result;
119}
120
121CatalogSearchPath::CatalogSearchPath(ClientContext &context_p) : context(context_p) {
122 Reset();
123}
124
125void CatalogSearchPath::Reset() {
126 vector<CatalogSearchEntry> empty;
127 SetPaths(empty);
128}
129
130string CatalogSearchPath::GetSetName(CatalogSetPathType set_type) {
131 switch (set_type) {
132 case CatalogSetPathType::SET_SCHEMA:
133 return "SET schema";
134 case CatalogSetPathType::SET_SCHEMAS:
135 return "SET search_path";
136 default:
137 throw InternalException("Unrecognized CatalogSetPathType");
138 }
139}
140
141void CatalogSearchPath::Set(vector<CatalogSearchEntry> new_paths, CatalogSetPathType set_type) {
142 if (set_type != CatalogSetPathType::SET_SCHEMAS && new_paths.size() != 1) {
143 throw CatalogException("%s can set only 1 schema. This has %d", GetSetName(set_type), new_paths.size());
144 }
145 for (auto &path : new_paths) {
146 auto schema_entry = Catalog::GetSchema(context, catalog_name: path.catalog, schema_name: path.schema, if_not_found: OnEntryNotFound::RETURN_NULL);
147 if (schema_entry) {
148 // we are setting a schema - update the catalog and schema
149 if (path.catalog.empty()) {
150 path.catalog = GetDefault().catalog;
151 }
152 continue;
153 }
154 // only schema supplied - check if this is a catalog instead
155 if (path.catalog.empty()) {
156 auto catalog = Catalog::GetCatalogEntry(context, catalog_name: path.schema);
157 if (catalog) {
158 auto schema = catalog->GetSchema(context, DEFAULT_SCHEMA, if_not_found: OnEntryNotFound::RETURN_NULL);
159 if (schema) {
160 path.catalog = std::move(path.schema);
161 path.schema = schema->name;
162 continue;
163 }
164 }
165 }
166 throw CatalogException("%s: No catalog + schema named \"%s\" found.", GetSetName(set_type), path.ToString());
167 }
168 if (set_type == CatalogSetPathType::SET_SCHEMA) {
169 if (new_paths[0].catalog == TEMP_CATALOG || new_paths[0].catalog == SYSTEM_CATALOG) {
170 throw CatalogException("%s cannot be set to internal schema \"%s\"", GetSetName(set_type),
171 new_paths[0].catalog);
172 }
173 }
174 this->set_paths = std::move(new_paths);
175 SetPaths(set_paths);
176}
177
178void CatalogSearchPath::Set(CatalogSearchEntry new_value, CatalogSetPathType set_type) {
179 vector<CatalogSearchEntry> new_paths {std::move(new_value)};
180 Set(new_paths: std::move(new_paths), set_type);
181}
182
183const vector<CatalogSearchEntry> &CatalogSearchPath::Get() {
184 return paths;
185}
186
187string CatalogSearchPath::GetDefaultSchema(const string &catalog) {
188 for (auto &path : paths) {
189 if (path.catalog == TEMP_CATALOG) {
190 continue;
191 }
192 if (StringUtil::CIEquals(l1: path.catalog, l2: catalog)) {
193 return path.schema;
194 }
195 }
196 return DEFAULT_SCHEMA;
197}
198
199string CatalogSearchPath::GetDefaultCatalog(const string &schema) {
200 for (auto &path : paths) {
201 if (path.catalog == TEMP_CATALOG) {
202 continue;
203 }
204 if (StringUtil::CIEquals(l1: path.schema, l2: schema)) {
205 return path.catalog;
206 }
207 }
208 return INVALID_CATALOG;
209}
210
211vector<string> CatalogSearchPath::GetCatalogsForSchema(const string &schema) {
212 vector<string> schemas;
213 for (auto &path : paths) {
214 if (StringUtil::CIEquals(l1: path.schema, l2: schema)) {
215 schemas.push_back(x: path.catalog);
216 }
217 }
218 return schemas;
219}
220
221vector<string> CatalogSearchPath::GetSchemasForCatalog(const string &catalog) {
222 vector<string> schemas;
223 for (auto &path : paths) {
224 if (StringUtil::CIEquals(l1: path.catalog, l2: catalog)) {
225 schemas.push_back(x: path.schema);
226 }
227 }
228 return schemas;
229}
230
231const CatalogSearchEntry &CatalogSearchPath::GetDefault() {
232 const auto &paths = Get();
233 D_ASSERT(paths.size() >= 2);
234 return paths[1];
235}
236
237void CatalogSearchPath::SetPaths(vector<CatalogSearchEntry> new_paths) {
238 paths.clear();
239 paths.reserve(n: new_paths.size() + 3);
240 paths.emplace_back(TEMP_CATALOG, DEFAULT_SCHEMA);
241 for (auto &path : new_paths) {
242 paths.push_back(x: std::move(path));
243 }
244 paths.emplace_back(INVALID_CATALOG, DEFAULT_SCHEMA);
245 paths.emplace_back(SYSTEM_CATALOG, DEFAULT_SCHEMA);
246 paths.emplace_back(SYSTEM_CATALOG, args: "pg_catalog");
247}
248
249bool CatalogSearchPath::SchemaInSearchPath(ClientContext &context, const string &catalog_name,
250 const string &schema_name) {
251 for (auto &path : paths) {
252 if (path.schema != schema_name) {
253 continue;
254 }
255 if (path.catalog == catalog_name) {
256 return true;
257 }
258 if (IsInvalidCatalog(str: path.catalog) && catalog_name == DatabaseManager::GetDefaultDatabase(context)) {
259 return true;
260 }
261 }
262 return false;
263}
264
265} // namespace duckdb
266