Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: Arrow Database Connectivity driver
5 : * Author: Even Rouault, <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
9 : * Copyright (c) 2024, Dewey Dunnington <dewey@voltrondata.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "ogr_adbc.h"
15 : #include "ogradbcdrivercore.h"
16 : #include "memdataset.h"
17 : #include "ogr_p.h"
18 : #include "cpl_error.h"
19 : #include "cpl_json.h"
20 : #include "gdal_adbc.h"
21 :
22 : #include <algorithm>
23 :
24 : #if defined(OGR_ADBC_HAS_DRIVER_MANAGER)
25 : #ifdef __clang__
26 : #pragma clang diagnostic push
27 : #pragma clang diagnostic ignored "-Wdocumentation"
28 : #endif
29 : #include <arrow-adbc/adbc_driver_manager.h>
30 : #ifdef __clang__
31 : #pragma clang diagnostic pop
32 : #endif
33 : #endif
34 :
35 : // #define DEBUG_VERBOSE
36 :
37 : #define OGR_ADBC_VERSION ADBC_VERSION_1_1_0
38 : static_assert(sizeof(AdbcDriver) == ADBC_DRIVER_1_1_0_SIZE);
39 :
40 : namespace
41 : {
42 :
43 : #if !defined(OGR_ADBC_HAS_DRIVER_MANAGER)
44 0 : AdbcStatusCode OGRDuckDBLoadDriver(const char *driver_name, void *driver,
45 : struct AdbcError *error)
46 : {
47 0 : void *load_handle = CPLGetSymbol(driver_name, "duckdb_adbc_init");
48 0 : if (!load_handle)
49 : {
50 0 : return ADBC_STATUS_INTERNAL;
51 : }
52 :
53 0 : AdbcDriverInitFunc init_func =
54 : reinterpret_cast<AdbcDriverInitFunc>(load_handle);
55 0 : return init_func(OGR_ADBC_VERSION, driver, error);
56 : }
57 : #endif
58 :
59 1 : AdbcStatusCode OGRADBCLoadDriver(const char *driver_name,
60 : const char *entrypoint, void *driver,
61 : struct AdbcError *error)
62 : {
63 : GDALAdbcLoadDriverFunc load_driver_override =
64 1 : GDALGetAdbcLoadDriverOverride();
65 1 : if (load_driver_override)
66 : {
67 0 : return load_driver_override(driver_name, entrypoint, OGR_ADBC_VERSION,
68 0 : driver, error);
69 : }
70 : else
71 : {
72 : #if defined(OGR_ADBC_HAS_DRIVER_MANAGER)
73 : return AdbcLoadDriver(driver_name, entrypoint, OGR_ADBC_VERSION, driver,
74 : error);
75 : #else
76 : // If the driver is for DuckDB, use a minimal loading function, which
77 : // doesn't rely on the ADBC driver manager.
78 1 : if (strstr(driver_name, "duckdb"))
79 : {
80 0 : return OGRDuckDBLoadDriver(driver_name, driver, error);
81 : }
82 1 : return ADBC_STATUS_NOT_IMPLEMENTED;
83 : #endif
84 : }
85 : }
86 :
87 : } // namespace
88 :
89 : // Helper to wrap driver callbacks
90 : #define ADBC_CALL(func, ...) m_driver.func(__VA_ARGS__)
91 :
92 : /************************************************************************/
93 : /* ~OGRADBCDataset() */
94 : /************************************************************************/
95 :
96 2 : OGRADBCDataset::~OGRADBCDataset()
97 : {
98 1 : OGRADBCDataset::FlushCache(true);
99 :
100 : // Layers must be closed before the connection
101 1 : m_apoLayers.clear();
102 1 : OGRADBCError error;
103 1 : if (m_connection)
104 0 : ADBC_CALL(ConnectionRelease, m_connection.get(), error);
105 1 : error.clear();
106 1 : if (m_driver.release)
107 : {
108 0 : ADBC_CALL(DatabaseRelease, &m_database, error);
109 0 : m_driver.release(&m_driver, error);
110 : }
111 2 : }
112 :
113 : /************************************************************************/
114 : /* FlushCache() */
115 : /************************************************************************/
116 :
117 1 : CPLErr OGRADBCDataset::FlushCache(bool /* bAtClosing */)
118 : {
119 1 : CPLErr eErr = CE_None;
120 1 : for (auto &poLayer : m_apoLayers)
121 : {
122 0 : auto poADBCLayer = dynamic_cast<OGRADBCLayer *>(poLayer.get());
123 0 : if (poADBCLayer)
124 : {
125 0 : if (!poADBCLayer->RunDeferredCreation())
126 0 : eErr = CE_Failure;
127 : }
128 : }
129 :
130 1 : return eErr;
131 : }
132 :
133 : /************************************************************************/
134 : /* CreateLayer() */
135 : /************************************************************************/
136 :
137 : std::unique_ptr<OGRADBCLayer>
138 0 : OGRADBCDataset::CreateLayer(const char *pszStatement, const char *pszLayerName,
139 : bool bInternalUse)
140 : {
141 :
142 0 : CPLString osStatement(pszStatement);
143 0 : if (!m_osParquetFilename.empty())
144 : {
145 0 : const char *pszSrcLayerName = m_apoLayers.size() == 1
146 0 : ? m_apoLayers[0]->GetDescription()
147 0 : : pszLayerName;
148 : // Substitute the OGR layer name with the DuckDB expected filename,
149 : // single-quoted
150 : const std::string osFrom =
151 0 : std::string(" FROM ").append(pszSrcLayerName);
152 0 : const auto nPos = osStatement.ifind(osFrom);
153 0 : if (nPos != std::string::npos)
154 : {
155 : osStatement =
156 0 : osStatement.substr(0, nPos)
157 0 : .append(" FROM '")
158 0 : .append(OGRDuplicateCharacter(m_osParquetFilename, '\''))
159 0 : .append("'")
160 0 : .append(osStatement.substr(nPos + osFrom.size()));
161 : }
162 : else
163 : {
164 : const std::string osFrom2 =
165 0 : std::string(" FROM \"")
166 0 : .append(OGRDuplicateCharacter(pszSrcLayerName, '"'))
167 0 : .append("\"");
168 0 : const auto nPos2 = osStatement.ifind(osFrom2);
169 0 : if (nPos2 != std::string::npos)
170 : {
171 : osStatement =
172 0 : osStatement.substr(0, nPos2)
173 0 : .append(" FROM '")
174 : .append(
175 0 : OGRDuplicateCharacter(m_osParquetFilename, '\''))
176 0 : .append("'")
177 0 : .append(osStatement.substr(nPos2 + osFrom2.size()));
178 : }
179 : }
180 : }
181 :
182 : return std::make_unique<OGRADBCLayer>(this, pszLayerName, osStatement,
183 0 : bInternalUse);
184 : }
185 :
186 : /************************************************************************/
187 : /* CreateInternalLayer() */
188 : /************************************************************************/
189 :
190 : std::unique_ptr<OGRADBCLayer>
191 0 : OGRADBCDataset::CreateInternalLayer(const char *pszStatement)
192 : {
193 : #ifdef DEBUG_VERBOSE
194 : CPLDebug("ADBC", "%s", pszStatement);
195 : #endif
196 0 : return CreateLayer(pszStatement, "temp", true);
197 : }
198 :
199 : /************************************************************************/
200 : /* ExecuteSQL() */
201 : /************************************************************************/
202 :
203 0 : OGRLayer *OGRADBCDataset::ExecuteSQL(const char *pszStatement,
204 : OGRGeometry *poSpatialFilter,
205 : const char *pszDialect)
206 : {
207 0 : for (auto &poLayer : m_apoLayers)
208 : {
209 0 : auto poADBCLayer = dynamic_cast<OGRADBCLayer *>(poLayer.get());
210 0 : if (poADBCLayer)
211 0 : poADBCLayer->RunDeferredCreation();
212 : }
213 :
214 0 : if (pszDialect && pszDialect[0] != 0 && !EQUAL(pszDialect, "NATIVE"))
215 : {
216 0 : return GDALDataset::ExecuteSQL(pszStatement, poSpatialFilter,
217 0 : pszDialect);
218 : }
219 :
220 0 : std::string osStatement(pszStatement);
221 0 : for (const char *pszPrefix : {"SELECT * FROM ", "SELECT COUNT(*) FROM "})
222 : {
223 0 : if (m_bIsBigQuery && STARTS_WITH_CI(pszStatement, pszPrefix))
224 : {
225 0 : const auto nPos = osStatement.find(' ', strlen(pszPrefix));
226 : const std::string osTableName = osStatement.substr(
227 : strlen(pszPrefix),
228 0 : nPos == std::string::npos ? nPos : nPos - strlen(pszPrefix));
229 0 : auto poADBCLayer = dynamic_cast<OGRADBCBigQueryLayer *>(
230 0 : GetLayerByName(osTableName.c_str()));
231 0 : if (poADBCLayer)
232 : {
233 0 : std::string osDatasetId;
234 0 : std::string osTableId;
235 0 : if (poADBCLayer->GetBigQueryDatasetAndTableId(osDatasetId,
236 : osTableId))
237 : {
238 0 : std::string osNewStatement = pszPrefix;
239 0 : osNewStatement += '`';
240 0 : osNewStatement += OGRDuplicateCharacter(osDatasetId, '`');
241 0 : osNewStatement += "`.`";
242 0 : osNewStatement += OGRDuplicateCharacter(osTableId, '`');
243 0 : osNewStatement += '`';
244 0 : if (nPos != std::string::npos)
245 0 : osNewStatement += osStatement.substr(nPos);
246 0 : osStatement = std::move(osNewStatement);
247 : }
248 : }
249 0 : break;
250 : }
251 : }
252 :
253 0 : const char *pszLayerName = "RESULTSET";
254 0 : std::unique_ptr<OGRADBCLayer> poLayer;
255 0 : if (m_bIsBigQuery)
256 0 : poLayer = std::make_unique<OGRADBCBigQueryLayer>(
257 : this, pszLayerName, osStatement,
258 0 : /* bInternalUse = */ false);
259 : else
260 0 : poLayer = CreateLayer(osStatement.c_str(), pszLayerName, false);
261 0 : if (poLayer->GotError())
262 0 : return nullptr;
263 0 : if (poSpatialFilter)
264 : {
265 0 : if (poLayer->GetGeomType() == wkbNone)
266 0 : return nullptr;
267 0 : poLayer->SetSpatialFilter(poSpatialFilter);
268 : }
269 0 : return poLayer.release();
270 : }
271 :
272 : /************************************************************************/
273 : /* IsParquetExtension() */
274 : /************************************************************************/
275 :
276 1 : static bool IsParquetExtension(const char *pszStr)
277 : {
278 1 : const std::string osExt = CPLGetExtensionSafe(pszStr);
279 2 : return EQUAL(osExt.c_str(), "parquet") || EQUAL(osExt.c_str(), "parq");
280 : }
281 :
282 : /************************************************************************/
283 : /* Open() */
284 : /************************************************************************/
285 :
286 1 : bool OGRADBCDataset::Open(const GDALOpenInfo *poOpenInfo)
287 : {
288 1 : OGRADBCError error;
289 :
290 1 : const char *pszFilename = poOpenInfo->pszFilename;
291 1 : std::unique_ptr<GDALOpenInfo> poTmpOpenInfo;
292 1 : if (STARTS_WITH(pszFilename, "ADBC:"))
293 : {
294 1 : pszFilename += strlen("ADBC:");
295 1 : if (pszFilename[0])
296 : {
297 0 : poTmpOpenInfo = std::make_unique<GDALOpenInfo>(pszFilename,
298 0 : poOpenInfo->eAccess);
299 0 : poTmpOpenInfo->papszOpenOptions = poOpenInfo->papszOpenOptions;
300 0 : poOpenInfo = poTmpOpenInfo.get();
301 : }
302 : }
303 : const char *pszADBCDriverName =
304 1 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "ADBC_DRIVER");
305 1 : m_bIsDuckDBDataset = OGRADBCDriverIsDuckDB(poOpenInfo);
306 : const bool bIsSQLite3 =
307 2 : (pszADBCDriverName && EQUAL(pszADBCDriverName, "adbc_driver_sqlite")) ||
308 1 : OGRADBCDriverIsSQLite3(poOpenInfo);
309 : bool bIsParquet =
310 1 : OGRADBCDriverIsParquet(poOpenInfo) || IsParquetExtension(pszFilename);
311 1 : m_bIsBigQuery =
312 1 : pszADBCDriverName && strstr(pszADBCDriverName, "adbc_driver_bigquery");
313 1 : const char *pszSQL = CSLFetchNameValue(poOpenInfo->papszOpenOptions, "SQL");
314 1 : if (!bIsParquet && pszSQL)
315 : {
316 0 : CPLString osSQL(pszSQL);
317 0 : auto iPos = osSQL.find("FROM '");
318 0 : if (iPos != std::string::npos)
319 : {
320 0 : iPos += strlen("FROM '");
321 0 : const auto iPos2 = osSQL.find("'", iPos);
322 0 : if (iPos2 != std::string::npos)
323 : {
324 0 : std::string osFilename = osSQL.substr(iPos, iPos2 - iPos);
325 0 : if (IsParquetExtension(osFilename.c_str()))
326 : {
327 0 : m_osParquetFilename = std::move(osFilename);
328 0 : bIsParquet = true;
329 : }
330 : }
331 : }
332 : }
333 1 : const bool bIsPostgreSQL = STARTS_WITH(pszFilename, "postgresql://");
334 :
335 1 : if (!pszADBCDriverName)
336 : {
337 0 : if (m_bIsDuckDBDataset || bIsParquet)
338 : {
339 0 : pszADBCDriverName =
340 : #ifdef _WIN32
341 : "duckdb.dll"
342 : #elif defined(__MACH__) && defined(__APPLE__)
343 : "libduckdb.dylib"
344 : #else
345 : "libduckdb.so"
346 : #endif
347 : ;
348 : }
349 0 : else if (bIsPostgreSQL)
350 0 : pszADBCDriverName = "adbc_driver_postgresql";
351 0 : else if (bIsSQLite3)
352 : {
353 0 : pszADBCDriverName = "adbc_driver_sqlite";
354 : }
355 0 : else if (CSLFetchNameValue(poOpenInfo->papszOpenOptions,
356 0 : "BIGQUERY_PROJECT_ID") ||
357 0 : CSLFetchNameValue(poOpenInfo->papszOpenOptions,
358 0 : "BIGQUERY_DATASET_ID") ||
359 0 : CSLFetchNameValue(poOpenInfo->papszOpenOptions,
360 0 : "BIGQUERY_JSON_CREDENTIAL_STRING") ||
361 0 : CSLFetchNameValue(poOpenInfo->papszOpenOptions,
362 : "BIGQUERY_JSON_CREDENTIAL_FILE"))
363 : {
364 0 : m_bIsBigQuery = true;
365 0 : pszADBCDriverName = "adbc_driver_bigquery";
366 : }
367 : else
368 : {
369 0 : CPLError(CE_Failure, CPLE_AppDefined,
370 : "Open option ADBC_DRIVER must be specified");
371 0 : return false;
372 : }
373 : }
374 :
375 1 : if (poOpenInfo->eAccess == GA_Update && !m_bIsBigQuery)
376 : {
377 0 : return false;
378 : }
379 :
380 1 : eAccess = poOpenInfo->eAccess;
381 :
382 1 : m_bIsDuckDBDriver =
383 2 : m_bIsDuckDBDataset || bIsParquet ||
384 1 : (pszADBCDriverName && strstr(pszADBCDriverName, "duckdb"));
385 :
386 : // Load the driver
387 1 : if (m_bIsDuckDBDriver)
388 : {
389 0 : if (OGRADBCLoadDriver(pszADBCDriverName, "duckdb_adbc_init", &m_driver,
390 0 : error) != ADBC_STATUS_OK)
391 : {
392 0 : CPLError(CE_Failure, CPLE_AppDefined, "AdbcLoadDriver() failed: %s",
393 : error.message());
394 0 : return false;
395 : }
396 : }
397 : else
398 : {
399 1 : if (OGRADBCLoadDriver(pszADBCDriverName, nullptr, &m_driver, error) !=
400 : ADBC_STATUS_OK)
401 : {
402 1 : CPLError(CE_Failure, CPLE_AppDefined, "AdbcLoadDriver() failed: %s",
403 : error.message());
404 1 : return false;
405 : }
406 : }
407 :
408 : // Allocate the database
409 0 : if (ADBC_CALL(DatabaseNew, &m_database, error) != ADBC_STATUS_OK)
410 : {
411 0 : CPLError(CE_Failure, CPLE_AppDefined, "AdbcDatabaseNew() failed: %s",
412 : error.message());
413 0 : return false;
414 : }
415 :
416 : // Set options
417 0 : if (m_bIsDuckDBDriver && pszFilename[0])
418 : {
419 : VSIStatBuf sStatBuf;
420 0 : if (!bIsParquet && VSIStat(pszFilename, &sStatBuf) != 0 &&
421 0 : strcmp(pszFilename, ":memory:") != 0)
422 : {
423 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s does not exist",
424 : pszFilename);
425 0 : return false;
426 : }
427 0 : if (ADBC_CALL(DatabaseSetOption, &m_database, "path",
428 : bIsParquet ? ":memory:" : pszFilename,
429 0 : error) != ADBC_STATUS_OK)
430 : {
431 0 : CPLError(CE_Failure, CPLE_AppDefined,
432 : "AdbcDatabaseSetOption() failed: %s", error.message());
433 0 : return false;
434 0 : }
435 : }
436 0 : else if (pszFilename[0])
437 : {
438 0 : if (ADBC_CALL(DatabaseSetOption, &m_database, "uri", pszFilename,
439 0 : error) != ADBC_STATUS_OK)
440 : {
441 0 : CPLError(CE_Failure, CPLE_AppDefined,
442 : "AdbcDatabaseSetOption() failed: %s", error.message());
443 0 : return false;
444 : }
445 : }
446 :
447 0 : const auto GetAsOpenOptionOrConfigOption = [poOpenInfo](const char *pszKey)
448 : {
449 : const char *pszVal =
450 0 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, pszKey);
451 0 : if (pszVal)
452 0 : return pszVal;
453 : // Below comments are for scripts/collect_config_options.py
454 : // CPLGetConfigOption("BIGQUERY_PROJECT_ID", nullptr);
455 : // CPLGetConfigOption("BIGQUERY_DATASET_ID", nullptr);
456 : // CPLGetConfigOption("BIGQUERY_JSON_CREDENTIAL_STRING", nullptr);
457 : // CPLGetConfigOption("BIGQUERY_JSON_CREDENTIAL_FILE", nullptr);
458 0 : return CPLGetConfigOption(pszKey, nullptr);
459 0 : };
460 :
461 : const char *pszBIGQUERY_PROJECT_ID =
462 0 : GetAsOpenOptionOrConfigOption("BIGQUERY_PROJECT_ID");
463 0 : if (pszBIGQUERY_PROJECT_ID && pszBIGQUERY_PROJECT_ID[0])
464 : {
465 0 : if (ADBC_CALL(DatabaseSetOption, &m_database,
466 : "adbc.bigquery.sql.project_id", pszBIGQUERY_PROJECT_ID,
467 0 : error) != ADBC_STATUS_OK)
468 : {
469 0 : CPLError(CE_Failure, CPLE_AppDefined,
470 : "AdbcDatabaseSetOption() failed: %s", error.message());
471 0 : return false;
472 : }
473 : }
474 :
475 : const char *pszBIGQUERY_DATASET_ID =
476 0 : GetAsOpenOptionOrConfigOption("BIGQUERY_DATASET_ID");
477 0 : if (pszBIGQUERY_DATASET_ID && pszBIGQUERY_DATASET_ID[0])
478 : {
479 0 : m_osBigQueryDatasetId = pszBIGQUERY_DATASET_ID;
480 0 : if (ADBC_CALL(DatabaseSetOption, &m_database,
481 : "adbc.bigquery.sql.dataset_id", pszBIGQUERY_DATASET_ID,
482 0 : error) != ADBC_STATUS_OK)
483 : {
484 0 : CPLError(CE_Failure, CPLE_AppDefined,
485 : "AdbcDatabaseSetOption() failed: %s", error.message());
486 0 : return false;
487 : }
488 : }
489 :
490 : const char *pszBIGQUERY_JSON_CREDENTIAL_STRING =
491 0 : GetAsOpenOptionOrConfigOption("BIGQUERY_JSON_CREDENTIAL_STRING");
492 : const char *pszBIGQUERY_JSON_CREDENTIAL_FILE =
493 0 : GetAsOpenOptionOrConfigOption("BIGQUERY_JSON_CREDENTIAL_FILE");
494 0 : if (pszBIGQUERY_JSON_CREDENTIAL_STRING &&
495 0 : pszBIGQUERY_JSON_CREDENTIAL_STRING[0])
496 : {
497 0 : if (pszBIGQUERY_JSON_CREDENTIAL_FILE &&
498 0 : pszBIGQUERY_JSON_CREDENTIAL_FILE[0])
499 : {
500 0 : CPLError(CE_Warning, CPLE_AppDefined,
501 : "BIGQUERY_JSON_CREDENTIAL_FILE ignored when "
502 : "BIGQUERY_JSON_CREDENTIAL_STRING is set");
503 : }
504 :
505 0 : if (ADBC_CALL(DatabaseSetOption, &m_database,
506 : "adbc.bigquery.sql.auth_credentials",
507 : "adbc.bigquery.sql.auth_type.json_credential_string",
508 0 : error) != ADBC_STATUS_OK)
509 : {
510 0 : CPLError(CE_Failure, CPLE_AppDefined,
511 : "AdbcDatabaseSetOption() failed: %s", error.message());
512 0 : return false;
513 : }
514 :
515 0 : if (ADBC_CALL(DatabaseSetOption, &m_database,
516 : "adbc.bigquery.sql.auth_credentials",
517 : pszBIGQUERY_JSON_CREDENTIAL_STRING,
518 0 : error) != ADBC_STATUS_OK)
519 : {
520 0 : CPLError(CE_Failure, CPLE_AppDefined,
521 : "AdbcDatabaseSetOption() failed: %s", error.message());
522 0 : return false;
523 : }
524 : }
525 0 : else if (pszBIGQUERY_JSON_CREDENTIAL_FILE &&
526 0 : pszBIGQUERY_JSON_CREDENTIAL_FILE[0])
527 : {
528 0 : if (ADBC_CALL(DatabaseSetOption, &m_database,
529 : "adbc.bigquery.sql.auth_credentials",
530 : "adbc.bigquery.sql.auth_type.json_credential_file",
531 0 : error) != ADBC_STATUS_OK)
532 : {
533 0 : CPLError(CE_Failure, CPLE_AppDefined,
534 : "AdbcDatabaseSetOption() failed: %s", error.message());
535 0 : return false;
536 : }
537 :
538 0 : if (ADBC_CALL(DatabaseSetOption, &m_database,
539 : "adbc.bigquery.sql.auth_credentials",
540 : pszBIGQUERY_JSON_CREDENTIAL_FILE,
541 0 : error) != ADBC_STATUS_OK)
542 : {
543 0 : CPLError(CE_Failure, CPLE_AppDefined,
544 : "AdbcDatabaseSetOption() failed: %s", error.message());
545 0 : return false;
546 : }
547 : }
548 :
549 0 : if (m_bIsBigQuery)
550 : {
551 0 : if (!(pszBIGQUERY_PROJECT_ID && pszBIGQUERY_PROJECT_ID[0]))
552 : {
553 0 : CPLError(CE_Failure, CPLE_AppDefined,
554 : "Required BIGQUERY_PROJECT_ID open option not provided");
555 0 : return false;
556 : }
557 0 : if (!(pszBIGQUERY_JSON_CREDENTIAL_STRING &&
558 0 : pszBIGQUERY_JSON_CREDENTIAL_STRING[0]) &&
559 0 : !(pszBIGQUERY_JSON_CREDENTIAL_FILE &&
560 0 : pszBIGQUERY_JSON_CREDENTIAL_FILE[0]))
561 : {
562 0 : CPLError(CE_Failure, CPLE_AppDefined,
563 : "Required BIGQUERY_JSON_CREDENTIAL_STRING or "
564 : "BIGQUERY_JSON_CREDENTIAL_FILE open option not provided");
565 0 : return false;
566 : }
567 : }
568 :
569 0 : for (const auto &[pszKey, pszValue] : cpl::IterateNameValue(
570 0 : static_cast<CSLConstList>(poOpenInfo->papszOpenOptions)))
571 : {
572 0 : if (STARTS_WITH_CI(pszKey, "ADBC_OPTION_"))
573 : {
574 0 : if (ADBC_CALL(DatabaseSetOption, &m_database,
575 : pszKey + strlen("ADBC_OPTION_"), pszValue,
576 0 : error) != ADBC_STATUS_OK)
577 : {
578 0 : CPLError(CE_Failure, CPLE_AppDefined,
579 : "AdbcDatabaseSetOption() failed: %s", error.message());
580 0 : return false;
581 : }
582 : }
583 : }
584 :
585 0 : if (ADBC_CALL(DatabaseInit, &m_database, error) != ADBC_STATUS_OK)
586 : {
587 0 : CPLError(CE_Failure, CPLE_AppDefined, "AdbcDatabaseInit() failed: %s",
588 : error.message());
589 0 : return false;
590 : }
591 :
592 0 : m_connection = std::make_unique<AdbcConnection>();
593 0 : if (ADBC_CALL(ConnectionNew, m_connection.get(), error) != ADBC_STATUS_OK)
594 : {
595 0 : CPLError(CE_Failure, CPLE_AppDefined, "AdbcConnectionNew() failed: %s",
596 : error.message());
597 0 : return false;
598 : }
599 :
600 0 : if (ADBC_CALL(ConnectionInit, m_connection.get(), &m_database, error) !=
601 : ADBC_STATUS_OK)
602 : {
603 0 : CPLError(CE_Failure, CPLE_AppDefined, "AdbcConnectionInit() failed: %s",
604 : error.message());
605 0 : return false;
606 : }
607 :
608 0 : char **papszPreludeStatements = CSLFetchNameValueMultiple(
609 0 : poOpenInfo->papszOpenOptions, "PRELUDE_STATEMENTS");
610 0 : for (const char *pszStatement :
611 0 : cpl::Iterate(CSLConstList(papszPreludeStatements)))
612 : {
613 0 : CPL_IGNORE_RET_VAL(CreateInternalLayer(pszStatement)->GotError());
614 : }
615 0 : CSLDestroy(papszPreludeStatements);
616 0 : if (m_bIsDuckDBDriver && CPLTestBool(CPLGetConfigOption(
617 : "OGR_ADBC_AUTO_LOAD_DUCKDB_SPATIAL", "ON")))
618 : {
619 : auto poTmpLayer =
620 : CreateInternalLayer("SELECT 1 FROM duckdb_extensions() WHERE "
621 0 : "extension_name='spatial' AND loaded = false");
622 0 : if (!poTmpLayer->GotError() &&
623 0 : std::unique_ptr<OGRFeature>(poTmpLayer->GetNextFeature()) !=
624 : nullptr)
625 : {
626 0 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
627 0 : CPL_IGNORE_RET_VAL(CreateInternalLayer("LOAD spatial")->GotError());
628 : }
629 :
630 : poTmpLayer =
631 0 : CreateInternalLayer("SELECT 1 FROM duckdb_extensions() WHERE "
632 0 : "extension_name='spatial' AND loaded = true");
633 0 : m_bSpatialLoaded = !poTmpLayer->GotError() &&
634 0 : std::unique_ptr<OGRFeature>(
635 0 : poTmpLayer->GetNextFeature()) != nullptr;
636 : }
637 :
638 0 : std::string osLayerName = "RESULTSET";
639 0 : std::string osSQL;
640 0 : bool bIsParquetLayer = false;
641 0 : if (bIsParquet)
642 : {
643 0 : if (m_osParquetFilename.empty())
644 0 : m_osParquetFilename = pszFilename;
645 0 : osLayerName = CPLGetBasenameSafe(m_osParquetFilename.c_str());
646 0 : if (osLayerName == "*")
647 0 : osLayerName = CPLGetBasenameSafe(
648 0 : CPLGetDirnameSafe(m_osParquetFilename.c_str()).c_str());
649 0 : if (!pszSQL)
650 : {
651 : osSQL =
652 : CPLSPrintf("SELECT * FROM read_parquet('%s')",
653 0 : OGRDuplicateCharacter(pszFilename, '\'').c_str());
654 0 : pszSQL = osSQL.c_str();
655 0 : bIsParquetLayer = true;
656 : }
657 : }
658 :
659 0 : if (pszSQL)
660 : {
661 0 : if (pszSQL[0])
662 : {
663 0 : std::unique_ptr<OGRADBCLayer> poLayer;
664 0 : if ((bIsParquet || m_bIsDuckDBDataset) && m_bSpatialLoaded)
665 : {
666 0 : std::string osErrorMsg;
667 : {
668 0 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
669 0 : poLayer = CreateLayer(pszSQL, osLayerName.c_str(), false);
670 0 : if (poLayer->GotError())
671 0 : osErrorMsg = CPLGetLastErrorMsg();
672 : }
673 0 : if (poLayer->GotError())
674 : {
675 0 : CPLDebug("ADBC",
676 : "Connecting with 'LOAD spatial' did not work "
677 : "(%s). Retrying without it",
678 : osErrorMsg.c_str());
679 0 : ADBC_CALL(ConnectionRelease, m_connection.get(), error);
680 0 : m_connection.reset();
681 :
682 0 : ADBC_CALL(DatabaseRelease, &m_database, error);
683 0 : memset(&m_database, 0, sizeof(m_database));
684 :
685 0 : if (ADBC_CALL(DatabaseNew, &m_database, error) !=
686 : ADBC_STATUS_OK)
687 : {
688 0 : CPLError(CE_Failure, CPLE_AppDefined,
689 : "AdbcDatabaseNew() failed: %s",
690 : error.message());
691 0 : return false;
692 : }
693 0 : if (ADBC_CALL(DatabaseSetOption, &m_database, "path",
694 0 : ":memory:", error) != ADBC_STATUS_OK)
695 : {
696 0 : CPLError(CE_Failure, CPLE_AppDefined,
697 : "AdbcDatabaseSetOption() failed: %s",
698 : error.message());
699 0 : return false;
700 : }
701 :
702 0 : if (ADBC_CALL(DatabaseInit, &m_database, error) !=
703 : ADBC_STATUS_OK)
704 : {
705 0 : CPLError(CE_Failure, CPLE_AppDefined,
706 : "AdbcDatabaseInit() failed: %s",
707 : error.message());
708 0 : return false;
709 : }
710 :
711 0 : m_connection = std::make_unique<AdbcConnection>();
712 0 : if (ADBC_CALL(ConnectionNew, m_connection.get(), error) !=
713 : ADBC_STATUS_OK)
714 : {
715 0 : CPLError(CE_Failure, CPLE_AppDefined,
716 : "AdbcConnectionNew() failed: %s",
717 : error.message());
718 0 : return false;
719 : }
720 :
721 0 : if (ADBC_CALL(ConnectionInit, m_connection.get(),
722 0 : &m_database, error) != ADBC_STATUS_OK)
723 : {
724 0 : CPLError(CE_Failure, CPLE_AppDefined,
725 : "AdbcConnectionInit() failed: %s",
726 : error.message());
727 0 : return false;
728 : }
729 : }
730 : }
731 0 : if (!poLayer || poLayer->GotError())
732 : {
733 0 : if (m_bIsBigQuery)
734 0 : poLayer = std::make_unique<OGRADBCBigQueryLayer>(
735 0 : this, osLayerName.c_str(), pszSQL,
736 0 : /* bInternalUse = */ false);
737 : else
738 0 : poLayer = CreateLayer(pszSQL, osLayerName.c_str(), false);
739 0 : if (poLayer->GotError())
740 0 : return false;
741 : }
742 :
743 0 : poLayer->m_bIsParquetLayer = bIsParquetLayer;
744 0 : m_apoLayers.emplace_back(std::move(poLayer));
745 : }
746 : }
747 0 : else if (m_bIsDuckDBDataset || bIsSQLite3)
748 : {
749 : auto poLayerList = CreateInternalLayer(
750 0 : "SELECT name FROM sqlite_master WHERE type IN ('table', 'view')");
751 0 : if (poLayerList->GotError() ||
752 0 : poLayerList->GetLayerDefn()->GetFieldCount() != 1)
753 : {
754 0 : return false;
755 : }
756 :
757 0 : for (const auto &poFeature : poLayerList.get())
758 : {
759 0 : const char *pszLayerName = poFeature->GetFieldAsString(0);
760 0 : if (bIsSQLite3 && EQUAL(pszLayerName, "SpatialIndex"))
761 0 : continue;
762 : const std::string osStatement =
763 : CPLSPrintf("SELECT * FROM \"%s\"",
764 0 : OGRDuplicateCharacter(pszLayerName, '"').c_str());
765 : m_apoLayers.emplace_back(
766 0 : CreateLayer(osStatement.c_str(), pszLayerName, false));
767 0 : }
768 : }
769 0 : else if (bIsPostgreSQL)
770 : {
771 : auto poLayerList = CreateInternalLayer(
772 : "SELECT n.nspname, c.relname FROM pg_class c "
773 : "JOIN pg_namespace n ON c.relnamespace = n.oid "
774 : "AND c.relkind in ('r','v','m','f') "
775 : "AND n.nspname NOT IN ('pg_catalog', 'information_schema') "
776 0 : "ORDER BY c.oid");
777 0 : if (poLayerList->GotError() ||
778 0 : poLayerList->GetLayerDefn()->GetFieldCount() != 2)
779 : {
780 0 : return false;
781 : }
782 :
783 0 : for (const auto &poFeature : poLayerList.get())
784 : {
785 0 : const char *pszNamespace = poFeature->GetFieldAsString(0);
786 0 : const char *pszTableName = poFeature->GetFieldAsString(1);
787 : const std::string osStatement =
788 : CPLSPrintf("SELECT * FROM \"%s\".\"%s\"",
789 0 : OGRDuplicateCharacter(pszNamespace, '"').c_str(),
790 0 : OGRDuplicateCharacter(pszTableName, '"').c_str());
791 :
792 0 : m_apoLayers.emplace_back(CreateLayer(
793 : osStatement.c_str(),
794 0 : CPLSPrintf("%s.%s", pszNamespace, pszTableName), false));
795 : }
796 : }
797 0 : else if (m_bIsBigQuery)
798 : {
799 0 : if (!(pszBIGQUERY_DATASET_ID && pszBIGQUERY_DATASET_ID[0]))
800 : {
801 0 : CPLError(CE_Failure, CPLE_AppDefined,
802 : "Cannot list tables when BIGQUERY_DATASET_ID open option "
803 : "is not provided");
804 0 : return false;
805 : }
806 0 : const std::string s(pszBIGQUERY_DATASET_ID);
807 0 : if (!std::all_of(s.begin(), s.end(),
808 0 : [](char c) { return std::isalnum(c) || c == '_'; }))
809 : {
810 0 : CPLError(CE_Failure, CPLE_AppDefined,
811 : "Invalid characters found in BIGQUERY_DATASET_ID value");
812 0 : return false;
813 : }
814 : auto poLayerList = CreateInternalLayer(
815 : CPLSPrintf("SELECT table_name FROM %s.INFORMATION_SCHEMA.TABLES "
816 : "ORDER BY creation_time",
817 0 : pszBIGQUERY_DATASET_ID));
818 0 : if (poLayerList->GotError() ||
819 0 : poLayerList->GetLayerDefn()->GetFieldCount() != 1)
820 : {
821 0 : return false;
822 : }
823 :
824 0 : for (const auto &poFeature : poLayerList.get())
825 : {
826 0 : const char *pszTableName = poFeature->GetFieldAsString(0);
827 : const std::string osStatement = CPLSPrintf(
828 : "SELECT * FROM `%s`.`%s`",
829 0 : OGRDuplicateCharacter(pszBIGQUERY_DATASET_ID, '`').c_str(),
830 0 : OGRDuplicateCharacter(pszTableName, '`').c_str());
831 :
832 0 : m_apoLayers.emplace_back(std::make_unique<OGRADBCBigQueryLayer>(
833 : this, pszTableName, osStatement,
834 0 : /* bInternalUse = */ false));
835 : }
836 : }
837 :
838 0 : return true;
839 : }
840 :
841 : /************************************************************************/
842 : /* ICreateLayer() */
843 : /************************************************************************/
844 :
845 0 : OGRLayer *OGRADBCDataset::ICreateLayer(const char *pszName,
846 : const OGRGeomFieldDefn *poGeomFieldDefn,
847 : CSLConstList papszOptions)
848 : {
849 0 : if (!m_bIsBigQuery)
850 : {
851 0 : CPLError(CE_Failure, CPLE_NotSupported,
852 : "CreateLayer() only supported for BigQuery");
853 0 : return nullptr;
854 : }
855 0 : if (GetAccess() != GA_Update)
856 : {
857 0 : CPLError(
858 : CE_Failure, CPLE_NotSupported,
859 : "CreateLayer() only supported on datasets opened in update mode");
860 0 : return nullptr;
861 : }
862 0 : if (m_osBigQueryDatasetId.empty())
863 : {
864 0 : CPLError(CE_Failure, CPLE_AppDefined,
865 : "Open option BIGQUERY_DATASET_ID should be set");
866 0 : return nullptr;
867 : }
868 :
869 0 : if (GetLayerByName(pszName))
870 : {
871 0 : CPLError(CE_Failure, CPLE_AppDefined, "Table %s already exists",
872 : pszName);
873 0 : return nullptr;
874 : }
875 :
876 0 : if (poGeomFieldDefn)
877 : {
878 0 : const auto poSRS = poGeomFieldDefn->GetSpatialRef();
879 0 : if (poSRS && !poSRS->IsGeographic())
880 : {
881 0 : CPLError(CE_Failure, CPLE_NotSupported,
882 : "BigQuery only supports geographic CRS. Please reproject "
883 : "your layer to one (typically EPSG:4326)");
884 0 : return nullptr;
885 : }
886 : }
887 :
888 : const std::string osStatement = CPLSPrintf(
889 : "SELECT * FROM `%s`.`%s`",
890 0 : OGRDuplicateCharacter(m_osBigQueryDatasetId.c_str(), '`').c_str(),
891 0 : OGRDuplicateCharacter(pszName, '`').c_str());
892 :
893 : const char *pszFIDColName =
894 0 : CSLFetchNameValueDef(papszOptions, "FID", "ogc_fid");
895 : auto poLayer =
896 : std::make_unique<OGRADBCBigQueryLayer>(this, pszName, osStatement,
897 0 : /* bInternalUse = */ false);
898 0 : poLayer->SetDeferredCreation(pszFIDColName, poGeomFieldDefn);
899 0 : m_apoLayers.emplace_back(std::move(poLayer));
900 0 : return m_apoLayers.back().get();
901 : }
902 :
903 : /************************************************************************/
904 : /* DeleteLayer() */
905 : /************************************************************************/
906 :
907 0 : OGRErr OGRADBCDataset::DeleteLayer(int iLayer)
908 : {
909 0 : if (!m_bIsBigQuery)
910 : {
911 0 : CPLError(CE_Failure, CPLE_NotSupported,
912 : "DeleteLayer() only supported for BigQuery");
913 0 : return OGRERR_FAILURE;
914 : }
915 0 : if (GetAccess() != GA_Update)
916 : {
917 0 : CPLError(
918 : CE_Failure, CPLE_NotSupported,
919 : "DeleteLayer() only supported on datasets opened in update mode");
920 0 : return OGRERR_FAILURE;
921 : }
922 0 : if (iLayer < 0 || static_cast<size_t>(iLayer) >= m_apoLayers.size())
923 : {
924 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid layer index");
925 0 : return OGRERR_FAILURE;
926 : }
927 :
928 : auto poADBCLayer =
929 0 : dynamic_cast<OGRADBCBigQueryLayer *>(m_apoLayers[iLayer].get());
930 0 : if (poADBCLayer && !poADBCLayer->m_bDeferredCreation)
931 : {
932 0 : std::string osDatasetId;
933 0 : std::string osTableId;
934 0 : if (!poADBCLayer->GetBigQueryDatasetAndTableId(osDatasetId, osTableId))
935 : {
936 0 : CPLError(CE_Failure, CPLE_NotSupported,
937 : "DeleteLayer(): cannot get dataset and table ID");
938 0 : return OGRERR_FAILURE;
939 : }
940 :
941 0 : std::string osSQL = "DROP TABLE `";
942 0 : osSQL += OGRDuplicateCharacter(osDatasetId.c_str(), '`');
943 0 : osSQL += "`.`";
944 0 : osSQL += OGRDuplicateCharacter(osTableId.c_str(), '`');
945 0 : osSQL += "`";
946 : // CPLDebug("ADBC", "%s", osSQL.c_str());
947 0 : if (CreateInternalLayer(osSQL.c_str())->GotError())
948 : {
949 0 : return OGRERR_FAILURE;
950 : }
951 : }
952 :
953 0 : m_apoLayers.erase(m_apoLayers.begin() + iLayer);
954 0 : return OGRERR_NONE;
955 : }
956 :
957 : /************************************************************************/
958 : /* TestCapability() */
959 : /************************************************************************/
960 :
961 0 : int OGRADBCDataset::TestCapability(const char *pszCap) const
962 : {
963 0 : if (EQUAL(pszCap, ODsCCreateLayer) || EQUAL(pszCap, ODsCDeleteLayer))
964 0 : return m_bIsBigQuery && eAccess == GA_Update;
965 0 : return false;
966 : }
967 :
968 : /************************************************************************/
969 : /* GetLayerByName() */
970 : /************************************************************************/
971 :
972 0 : OGRLayer *OGRADBCDataset::GetLayerByName(const char *pszName)
973 : {
974 0 : OGRLayer *poLayer = GDALDataset::GetLayerByName(pszName);
975 0 : if (poLayer || !EQUAL(pszName, "table_list"))
976 0 : return poLayer;
977 :
978 0 : OGRADBCError error;
979 0 : auto objectsStream = std::make_unique<OGRArrowArrayStream>();
980 0 : ADBC_CALL(ConnectionGetObjects, m_connection.get(),
981 : ADBC_OBJECT_DEPTH_TABLES,
982 : /* catalog = */ nullptr,
983 : /* db_schema = */ nullptr,
984 : /* table_name = */ nullptr,
985 : /* table_type = */ nullptr,
986 : /* column_name = */ nullptr, objectsStream->get(), error);
987 :
988 0 : ArrowSchema schema = {};
989 0 : if (objectsStream->get_schema(&schema) != 0)
990 : {
991 0 : CPLError(CE_Failure, CPLE_AppDefined, "get_schema() failed");
992 0 : return nullptr;
993 : }
994 :
995 0 : OGRADBCLayer tmpLayer(this, "", std::move(objectsStream), &schema,
996 0 : /* bInternalUse = */ true);
997 0 : const auto tmpLayerDefn = tmpLayer.GetLayerDefn();
998 0 : if (tmpLayerDefn->GetFieldIndex("catalog_name") < 0 ||
999 0 : tmpLayerDefn->GetFieldIndex("catalog_db_schemas") < 0)
1000 : {
1001 0 : return nullptr;
1002 : }
1003 :
1004 : auto poTableListLayer =
1005 0 : std::make_unique<OGRMemLayer>("table_list", nullptr, wkbNone);
1006 : {
1007 0 : OGRFieldDefn oField("catalog_name", OFTString);
1008 0 : poTableListLayer->CreateField(&oField);
1009 : }
1010 : {
1011 0 : OGRFieldDefn oField("schema_name", OFTString);
1012 0 : poTableListLayer->CreateField(&oField);
1013 : }
1014 : {
1015 0 : OGRFieldDefn oField("table_name", OFTString);
1016 0 : poTableListLayer->CreateField(&oField);
1017 : }
1018 : {
1019 0 : OGRFieldDefn oField("table_type", OFTString);
1020 0 : poTableListLayer->CreateField(&oField);
1021 : }
1022 :
1023 0 : for (const auto &poFeature : tmpLayer)
1024 : {
1025 : const char *pszCatalogName =
1026 0 : poFeature->GetFieldAsString("catalog_name");
1027 : const char *pszCatalogDBSchemas =
1028 0 : poFeature->GetFieldAsString("catalog_db_schemas");
1029 0 : if (pszCatalogDBSchemas)
1030 : {
1031 0 : CPLJSONDocument oDoc;
1032 0 : if (oDoc.LoadMemory(pszCatalogDBSchemas))
1033 : {
1034 0 : auto oRoot = oDoc.GetRoot();
1035 0 : if (oRoot.GetType() == CPLJSONObject::Type::Array)
1036 : {
1037 0 : for (const auto &oSchema : oRoot.ToArray())
1038 : {
1039 0 : if (oSchema.GetType() == CPLJSONObject::Type::Object)
1040 : {
1041 : const std::string osSchemaName =
1042 0 : oSchema.GetString("schema_name");
1043 : const auto oTables =
1044 0 : oSchema.GetArray("db_schema_tables");
1045 0 : if (oTables.IsValid())
1046 : {
1047 0 : for (const auto &oTable : oTables)
1048 : {
1049 0 : if (oTable.GetType() ==
1050 : CPLJSONObject::Type::Object)
1051 : {
1052 : const std::string osTableName =
1053 0 : oTable.GetString("table_name");
1054 : const std::string osTableType =
1055 0 : oTable.GetString("table_type");
1056 0 : if (!osTableName.empty() &&
1057 0 : osTableType != "index" &&
1058 0 : osTableType != "trigger")
1059 : {
1060 : OGRFeature oFeat(
1061 : poTableListLayer
1062 0 : ->GetLayerDefn());
1063 0 : if (pszCatalogName)
1064 0 : oFeat.SetField("catalog_name",
1065 : pszCatalogName);
1066 0 : if (oSchema.GetObj("schema_name")
1067 0 : .IsValid())
1068 0 : oFeat.SetField(
1069 : "schema_name",
1070 : osSchemaName.c_str());
1071 0 : oFeat.SetField("table_name",
1072 : osTableName.c_str());
1073 0 : if (oTable.GetObj("table_type")
1074 0 : .IsValid())
1075 0 : oFeat.SetField(
1076 : "table_type",
1077 : osTableType.c_str());
1078 0 : CPL_IGNORE_RET_VAL(
1079 0 : poTableListLayer->CreateFeature(
1080 : &oFeat));
1081 : }
1082 : }
1083 : }
1084 : }
1085 : }
1086 : }
1087 : }
1088 : }
1089 : }
1090 : }
1091 :
1092 0 : m_apoLayers.emplace_back(std::move(poTableListLayer));
1093 0 : return m_apoLayers.back().get();
1094 : }
1095 :
1096 : #undef ADBC_CALL
|