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 : pszADBCDriverName =
390 0 : CPLGetConfigOption("OGR_ADBC_LIBDUCKDB", pszADBCDriverName);
391 0 : if (OGRADBCLoadDriver(pszADBCDriverName, "duckdb_adbc_init", &m_driver,
392 0 : error) != ADBC_STATUS_OK)
393 : {
394 0 : CPLError(CE_Failure, CPLE_AppDefined, "AdbcLoadDriver() failed: %s",
395 : error.message());
396 0 : return false;
397 : }
398 : }
399 : else
400 : {
401 1 : if (OGRADBCLoadDriver(pszADBCDriverName, nullptr, &m_driver, error) !=
402 : ADBC_STATUS_OK)
403 : {
404 1 : CPLError(CE_Failure, CPLE_AppDefined, "AdbcLoadDriver() failed: %s",
405 : error.message());
406 1 : return false;
407 : }
408 : }
409 :
410 : // Allocate the database
411 0 : if (ADBC_CALL(DatabaseNew, &m_database, error) != ADBC_STATUS_OK)
412 : {
413 0 : CPLError(CE_Failure, CPLE_AppDefined, "AdbcDatabaseNew() failed: %s",
414 : error.message());
415 0 : return false;
416 : }
417 :
418 : // Set options
419 0 : if (m_bIsDuckDBDriver && pszFilename[0])
420 : {
421 : VSIStatBuf sStatBuf;
422 0 : if (!bIsParquet && VSIStat(pszFilename, &sStatBuf) != 0 &&
423 0 : strcmp(pszFilename, ":memory:") != 0)
424 : {
425 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s does not exist",
426 : pszFilename);
427 0 : return false;
428 : }
429 0 : if (ADBC_CALL(DatabaseSetOption, &m_database, "path",
430 : bIsParquet ? ":memory:" : pszFilename,
431 0 : error) != ADBC_STATUS_OK)
432 : {
433 0 : CPLError(CE_Failure, CPLE_AppDefined,
434 : "AdbcDatabaseSetOption() failed: %s", error.message());
435 0 : return false;
436 0 : }
437 : }
438 0 : else if (pszFilename[0])
439 : {
440 0 : if (ADBC_CALL(DatabaseSetOption, &m_database, "uri", pszFilename,
441 0 : error) != ADBC_STATUS_OK)
442 : {
443 0 : CPLError(CE_Failure, CPLE_AppDefined,
444 : "AdbcDatabaseSetOption() failed: %s", error.message());
445 0 : return false;
446 : }
447 : }
448 :
449 0 : const auto GetAsOpenOptionOrConfigOption = [poOpenInfo](const char *pszKey)
450 : {
451 : const char *pszVal =
452 0 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, pszKey);
453 0 : if (pszVal)
454 0 : return pszVal;
455 : // Below comments are for scripts/collect_config_options.py
456 : // CPLGetConfigOption("BIGQUERY_PROJECT_ID", nullptr);
457 : // CPLGetConfigOption("BIGQUERY_DATASET_ID", nullptr);
458 : // CPLGetConfigOption("BIGQUERY_JSON_CREDENTIAL_STRING", nullptr);
459 : // CPLGetConfigOption("BIGQUERY_JSON_CREDENTIAL_FILE", nullptr);
460 0 : return CPLGetConfigOption(pszKey, nullptr);
461 0 : };
462 :
463 : const char *pszBIGQUERY_PROJECT_ID =
464 0 : GetAsOpenOptionOrConfigOption("BIGQUERY_PROJECT_ID");
465 0 : if (pszBIGQUERY_PROJECT_ID && pszBIGQUERY_PROJECT_ID[0])
466 : {
467 0 : if (ADBC_CALL(DatabaseSetOption, &m_database,
468 : "adbc.bigquery.sql.project_id", pszBIGQUERY_PROJECT_ID,
469 0 : error) != ADBC_STATUS_OK)
470 : {
471 0 : CPLError(CE_Failure, CPLE_AppDefined,
472 : "AdbcDatabaseSetOption() failed: %s", error.message());
473 0 : return false;
474 : }
475 : }
476 :
477 : const char *pszBIGQUERY_DATASET_ID =
478 0 : GetAsOpenOptionOrConfigOption("BIGQUERY_DATASET_ID");
479 0 : if (pszBIGQUERY_DATASET_ID && pszBIGQUERY_DATASET_ID[0])
480 : {
481 0 : m_osBigQueryDatasetId = pszBIGQUERY_DATASET_ID;
482 0 : if (ADBC_CALL(DatabaseSetOption, &m_database,
483 : "adbc.bigquery.sql.dataset_id", pszBIGQUERY_DATASET_ID,
484 0 : error) != ADBC_STATUS_OK)
485 : {
486 0 : CPLError(CE_Failure, CPLE_AppDefined,
487 : "AdbcDatabaseSetOption() failed: %s", error.message());
488 0 : return false;
489 : }
490 : }
491 :
492 : const char *pszBIGQUERY_JSON_CREDENTIAL_STRING =
493 0 : GetAsOpenOptionOrConfigOption("BIGQUERY_JSON_CREDENTIAL_STRING");
494 : const char *pszBIGQUERY_JSON_CREDENTIAL_FILE =
495 0 : GetAsOpenOptionOrConfigOption("BIGQUERY_JSON_CREDENTIAL_FILE");
496 0 : if (pszBIGQUERY_JSON_CREDENTIAL_STRING &&
497 0 : pszBIGQUERY_JSON_CREDENTIAL_STRING[0])
498 : {
499 0 : if (pszBIGQUERY_JSON_CREDENTIAL_FILE &&
500 0 : pszBIGQUERY_JSON_CREDENTIAL_FILE[0])
501 : {
502 0 : CPLError(CE_Warning, CPLE_AppDefined,
503 : "BIGQUERY_JSON_CREDENTIAL_FILE ignored when "
504 : "BIGQUERY_JSON_CREDENTIAL_STRING is set");
505 : }
506 :
507 0 : if (ADBC_CALL(DatabaseSetOption, &m_database,
508 : "adbc.bigquery.sql.auth_credentials",
509 : "adbc.bigquery.sql.auth_type.json_credential_string",
510 0 : error) != ADBC_STATUS_OK)
511 : {
512 0 : CPLError(CE_Failure, CPLE_AppDefined,
513 : "AdbcDatabaseSetOption() failed: %s", error.message());
514 0 : return false;
515 : }
516 :
517 0 : if (ADBC_CALL(DatabaseSetOption, &m_database,
518 : "adbc.bigquery.sql.auth_credentials",
519 : pszBIGQUERY_JSON_CREDENTIAL_STRING,
520 0 : error) != ADBC_STATUS_OK)
521 : {
522 0 : CPLError(CE_Failure, CPLE_AppDefined,
523 : "AdbcDatabaseSetOption() failed: %s", error.message());
524 0 : return false;
525 : }
526 : }
527 0 : else if (pszBIGQUERY_JSON_CREDENTIAL_FILE &&
528 0 : pszBIGQUERY_JSON_CREDENTIAL_FILE[0])
529 : {
530 0 : if (ADBC_CALL(DatabaseSetOption, &m_database,
531 : "adbc.bigquery.sql.auth_credentials",
532 : "adbc.bigquery.sql.auth_type.json_credential_file",
533 0 : error) != ADBC_STATUS_OK)
534 : {
535 0 : CPLError(CE_Failure, CPLE_AppDefined,
536 : "AdbcDatabaseSetOption() failed: %s", error.message());
537 0 : return false;
538 : }
539 :
540 0 : if (ADBC_CALL(DatabaseSetOption, &m_database,
541 : "adbc.bigquery.sql.auth_credentials",
542 : pszBIGQUERY_JSON_CREDENTIAL_FILE,
543 0 : error) != ADBC_STATUS_OK)
544 : {
545 0 : CPLError(CE_Failure, CPLE_AppDefined,
546 : "AdbcDatabaseSetOption() failed: %s", error.message());
547 0 : return false;
548 : }
549 : }
550 :
551 0 : if (m_bIsBigQuery)
552 : {
553 0 : if (!(pszBIGQUERY_PROJECT_ID && pszBIGQUERY_PROJECT_ID[0]))
554 : {
555 0 : CPLError(CE_Failure, CPLE_AppDefined,
556 : "Required BIGQUERY_PROJECT_ID open option not provided");
557 0 : return false;
558 : }
559 0 : if (!(pszBIGQUERY_JSON_CREDENTIAL_STRING &&
560 0 : pszBIGQUERY_JSON_CREDENTIAL_STRING[0]) &&
561 0 : !(pszBIGQUERY_JSON_CREDENTIAL_FILE &&
562 0 : pszBIGQUERY_JSON_CREDENTIAL_FILE[0]))
563 : {
564 0 : CPLError(CE_Failure, CPLE_AppDefined,
565 : "Required BIGQUERY_JSON_CREDENTIAL_STRING or "
566 : "BIGQUERY_JSON_CREDENTIAL_FILE open option not provided");
567 0 : return false;
568 : }
569 : }
570 :
571 0 : for (const auto &[pszKey, pszValue] : cpl::IterateNameValue(
572 0 : static_cast<CSLConstList>(poOpenInfo->papszOpenOptions)))
573 : {
574 0 : if (STARTS_WITH_CI(pszKey, "ADBC_OPTION_"))
575 : {
576 0 : if (ADBC_CALL(DatabaseSetOption, &m_database,
577 : pszKey + strlen("ADBC_OPTION_"), pszValue,
578 0 : error) != ADBC_STATUS_OK)
579 : {
580 0 : CPLError(CE_Failure, CPLE_AppDefined,
581 : "AdbcDatabaseSetOption() failed: %s", error.message());
582 0 : return false;
583 : }
584 : }
585 : }
586 :
587 0 : if (ADBC_CALL(DatabaseInit, &m_database, error) != ADBC_STATUS_OK)
588 : {
589 0 : CPLError(CE_Failure, CPLE_AppDefined, "AdbcDatabaseInit() failed: %s",
590 : error.message());
591 0 : return false;
592 : }
593 :
594 0 : m_connection = std::make_unique<AdbcConnection>();
595 0 : if (ADBC_CALL(ConnectionNew, m_connection.get(), error) != ADBC_STATUS_OK)
596 : {
597 0 : CPLError(CE_Failure, CPLE_AppDefined, "AdbcConnectionNew() failed: %s",
598 : error.message());
599 0 : return false;
600 : }
601 :
602 0 : if (ADBC_CALL(ConnectionInit, m_connection.get(), &m_database, error) !=
603 : ADBC_STATUS_OK)
604 : {
605 0 : CPLError(CE_Failure, CPLE_AppDefined, "AdbcConnectionInit() failed: %s",
606 : error.message());
607 0 : return false;
608 : }
609 :
610 0 : char **papszPreludeStatements = CSLFetchNameValueMultiple(
611 0 : poOpenInfo->papszOpenOptions, "PRELUDE_STATEMENTS");
612 0 : for (const char *pszStatement :
613 0 : cpl::Iterate(CSLConstList(papszPreludeStatements)))
614 : {
615 0 : CPL_IGNORE_RET_VAL(CreateInternalLayer(pszStatement)->GotError());
616 : }
617 0 : CSLDestroy(papszPreludeStatements);
618 0 : if (m_bIsDuckDBDriver && CPLTestBool(CPLGetConfigOption(
619 : "OGR_ADBC_AUTO_LOAD_DUCKDB_SPATIAL", "ON")))
620 : {
621 : auto poTmpLayer =
622 : CreateInternalLayer("SELECT 1 FROM duckdb_extensions() WHERE "
623 0 : "extension_name='spatial' AND loaded = false");
624 0 : if (!poTmpLayer->GotError() &&
625 0 : std::unique_ptr<OGRFeature>(poTmpLayer->GetNextFeature()) !=
626 : nullptr)
627 : {
628 0 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
629 0 : CPL_IGNORE_RET_VAL(CreateInternalLayer("LOAD spatial")->GotError());
630 : }
631 :
632 : poTmpLayer =
633 0 : CreateInternalLayer("SELECT 1 FROM duckdb_extensions() WHERE "
634 0 : "extension_name='spatial' AND loaded = true");
635 0 : m_bSpatialLoaded = !poTmpLayer->GotError() &&
636 0 : std::unique_ptr<OGRFeature>(
637 0 : poTmpLayer->GetNextFeature()) != nullptr;
638 : }
639 :
640 0 : std::string osLayerName = "RESULTSET";
641 0 : std::string osSQL;
642 0 : bool bIsParquetLayer = false;
643 0 : if (bIsParquet)
644 : {
645 0 : if (m_osParquetFilename.empty())
646 0 : m_osParquetFilename = pszFilename;
647 0 : osLayerName = CPLGetBasenameSafe(m_osParquetFilename.c_str());
648 0 : if (osLayerName == "*")
649 0 : osLayerName = CPLGetBasenameSafe(
650 0 : CPLGetDirnameSafe(m_osParquetFilename.c_str()).c_str());
651 0 : if (!pszSQL)
652 : {
653 : osSQL =
654 : CPLSPrintf("SELECT * FROM read_parquet('%s')",
655 0 : OGRDuplicateCharacter(pszFilename, '\'').c_str());
656 0 : pszSQL = osSQL.c_str();
657 0 : bIsParquetLayer = true;
658 : }
659 : }
660 :
661 0 : if (pszSQL)
662 : {
663 0 : if (pszSQL[0])
664 : {
665 0 : std::unique_ptr<OGRADBCLayer> poLayer;
666 0 : if ((bIsParquet || m_bIsDuckDBDataset) && m_bSpatialLoaded)
667 : {
668 0 : std::string osErrorMsg;
669 : {
670 0 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
671 0 : poLayer = CreateLayer(pszSQL, osLayerName.c_str(), false);
672 0 : if (poLayer->GotError())
673 0 : osErrorMsg = CPLGetLastErrorMsg();
674 : }
675 0 : if (poLayer->GotError())
676 : {
677 0 : CPLDebug("ADBC",
678 : "Connecting with 'LOAD spatial' did not work "
679 : "(%s). Retrying without it",
680 : osErrorMsg.c_str());
681 0 : ADBC_CALL(ConnectionRelease, m_connection.get(), error);
682 0 : m_connection.reset();
683 :
684 0 : ADBC_CALL(DatabaseRelease, &m_database, error);
685 0 : memset(&m_database, 0, sizeof(m_database));
686 :
687 0 : if (ADBC_CALL(DatabaseNew, &m_database, error) !=
688 : ADBC_STATUS_OK)
689 : {
690 0 : CPLError(CE_Failure, CPLE_AppDefined,
691 : "AdbcDatabaseNew() failed: %s",
692 : error.message());
693 0 : return false;
694 : }
695 0 : if (ADBC_CALL(DatabaseSetOption, &m_database, "path",
696 0 : ":memory:", error) != ADBC_STATUS_OK)
697 : {
698 0 : CPLError(CE_Failure, CPLE_AppDefined,
699 : "AdbcDatabaseSetOption() failed: %s",
700 : error.message());
701 0 : return false;
702 : }
703 :
704 0 : if (ADBC_CALL(DatabaseInit, &m_database, error) !=
705 : ADBC_STATUS_OK)
706 : {
707 0 : CPLError(CE_Failure, CPLE_AppDefined,
708 : "AdbcDatabaseInit() failed: %s",
709 : error.message());
710 0 : return false;
711 : }
712 :
713 0 : m_connection = std::make_unique<AdbcConnection>();
714 0 : if (ADBC_CALL(ConnectionNew, m_connection.get(), error) !=
715 : ADBC_STATUS_OK)
716 : {
717 0 : CPLError(CE_Failure, CPLE_AppDefined,
718 : "AdbcConnectionNew() failed: %s",
719 : error.message());
720 0 : return false;
721 : }
722 :
723 0 : if (ADBC_CALL(ConnectionInit, m_connection.get(),
724 0 : &m_database, error) != ADBC_STATUS_OK)
725 : {
726 0 : CPLError(CE_Failure, CPLE_AppDefined,
727 : "AdbcConnectionInit() failed: %s",
728 : error.message());
729 0 : return false;
730 : }
731 : }
732 : }
733 0 : if (!poLayer || poLayer->GotError())
734 : {
735 0 : if (m_bIsBigQuery)
736 0 : poLayer = std::make_unique<OGRADBCBigQueryLayer>(
737 0 : this, osLayerName.c_str(), pszSQL,
738 0 : /* bInternalUse = */ false);
739 : else
740 0 : poLayer = CreateLayer(pszSQL, osLayerName.c_str(), false);
741 0 : if (poLayer->GotError())
742 0 : return false;
743 : }
744 :
745 0 : poLayer->m_bIsParquetLayer = bIsParquetLayer;
746 0 : m_apoLayers.emplace_back(std::move(poLayer));
747 : }
748 : }
749 0 : else if (m_bIsDuckDBDataset || bIsSQLite3)
750 : {
751 : auto poLayerList = CreateInternalLayer(
752 0 : "SELECT name FROM sqlite_master WHERE type IN ('table', 'view')");
753 0 : if (poLayerList->GotError() ||
754 0 : poLayerList->GetLayerDefn()->GetFieldCount() != 1)
755 : {
756 0 : return false;
757 : }
758 :
759 0 : for (const auto &poFeature : poLayerList.get())
760 : {
761 0 : const char *pszLayerName = poFeature->GetFieldAsString(0);
762 0 : if (bIsSQLite3 && EQUAL(pszLayerName, "SpatialIndex"))
763 0 : continue;
764 : const std::string osStatement =
765 : CPLSPrintf("SELECT * FROM \"%s\"",
766 0 : OGRDuplicateCharacter(pszLayerName, '"').c_str());
767 : m_apoLayers.emplace_back(
768 0 : CreateLayer(osStatement.c_str(), pszLayerName, false));
769 0 : }
770 : }
771 0 : else if (bIsPostgreSQL)
772 : {
773 : auto poLayerList = CreateInternalLayer(
774 : "SELECT n.nspname, c.relname FROM pg_class c "
775 : "JOIN pg_namespace n ON c.relnamespace = n.oid "
776 : "AND c.relkind in ('r','v','m','f') "
777 : "AND n.nspname NOT IN ('pg_catalog', 'information_schema') "
778 0 : "ORDER BY c.oid");
779 0 : if (poLayerList->GotError() ||
780 0 : poLayerList->GetLayerDefn()->GetFieldCount() != 2)
781 : {
782 0 : return false;
783 : }
784 :
785 0 : for (const auto &poFeature : poLayerList.get())
786 : {
787 0 : const char *pszNamespace = poFeature->GetFieldAsString(0);
788 0 : const char *pszTableName = poFeature->GetFieldAsString(1);
789 : const std::string osStatement =
790 : CPLSPrintf("SELECT * FROM \"%s\".\"%s\"",
791 0 : OGRDuplicateCharacter(pszNamespace, '"').c_str(),
792 0 : OGRDuplicateCharacter(pszTableName, '"').c_str());
793 :
794 0 : m_apoLayers.emplace_back(CreateLayer(
795 : osStatement.c_str(),
796 0 : CPLSPrintf("%s.%s", pszNamespace, pszTableName), false));
797 : }
798 : }
799 0 : else if (m_bIsBigQuery)
800 : {
801 0 : if (!(pszBIGQUERY_DATASET_ID && pszBIGQUERY_DATASET_ID[0]))
802 : {
803 0 : CPLError(CE_Failure, CPLE_AppDefined,
804 : "Cannot list tables when BIGQUERY_DATASET_ID open option "
805 : "is not provided");
806 0 : return false;
807 : }
808 0 : const std::string s(pszBIGQUERY_DATASET_ID);
809 0 : if (!std::all_of(s.begin(), s.end(),
810 0 : [](char c) { return std::isalnum(c) || c == '_'; }))
811 : {
812 0 : CPLError(CE_Failure, CPLE_AppDefined,
813 : "Invalid characters found in BIGQUERY_DATASET_ID value");
814 0 : return false;
815 : }
816 : auto poLayerList = CreateInternalLayer(
817 : CPLSPrintf("SELECT table_name FROM %s.INFORMATION_SCHEMA.TABLES "
818 : "ORDER BY creation_time",
819 0 : pszBIGQUERY_DATASET_ID));
820 0 : if (poLayerList->GotError() ||
821 0 : poLayerList->GetLayerDefn()->GetFieldCount() != 1)
822 : {
823 0 : return false;
824 : }
825 :
826 0 : for (const auto &poFeature : poLayerList.get())
827 : {
828 0 : const char *pszTableName = poFeature->GetFieldAsString(0);
829 : const std::string osStatement = CPLSPrintf(
830 : "SELECT * FROM `%s`.`%s`",
831 0 : OGRDuplicateCharacter(pszBIGQUERY_DATASET_ID, '`').c_str(),
832 0 : OGRDuplicateCharacter(pszTableName, '`').c_str());
833 :
834 0 : m_apoLayers.emplace_back(std::make_unique<OGRADBCBigQueryLayer>(
835 : this, pszTableName, osStatement,
836 0 : /* bInternalUse = */ false));
837 : }
838 : }
839 :
840 0 : return true;
841 : }
842 :
843 : /************************************************************************/
844 : /* ICreateLayer() */
845 : /************************************************************************/
846 :
847 0 : OGRLayer *OGRADBCDataset::ICreateLayer(const char *pszName,
848 : const OGRGeomFieldDefn *poGeomFieldDefn,
849 : CSLConstList papszOptions)
850 : {
851 0 : if (!m_bIsBigQuery)
852 : {
853 0 : CPLError(CE_Failure, CPLE_NotSupported,
854 : "CreateLayer() only supported for BigQuery");
855 0 : return nullptr;
856 : }
857 0 : if (GetAccess() != GA_Update)
858 : {
859 0 : CPLError(
860 : CE_Failure, CPLE_NotSupported,
861 : "CreateLayer() only supported on datasets opened in update mode");
862 0 : return nullptr;
863 : }
864 0 : if (m_osBigQueryDatasetId.empty())
865 : {
866 0 : CPLError(CE_Failure, CPLE_AppDefined,
867 : "Open option BIGQUERY_DATASET_ID should be set");
868 0 : return nullptr;
869 : }
870 :
871 0 : if (GetLayerByName(pszName))
872 : {
873 0 : CPLError(CE_Failure, CPLE_AppDefined, "Table %s already exists",
874 : pszName);
875 0 : return nullptr;
876 : }
877 :
878 0 : if (poGeomFieldDefn)
879 : {
880 0 : const auto poSRS = poGeomFieldDefn->GetSpatialRef();
881 0 : if (poSRS && !poSRS->IsGeographic())
882 : {
883 0 : CPLError(CE_Failure, CPLE_NotSupported,
884 : "BigQuery only supports geographic CRS. Please reproject "
885 : "your layer to one (typically EPSG:4326)");
886 0 : return nullptr;
887 : }
888 : }
889 :
890 : const std::string osStatement = CPLSPrintf(
891 : "SELECT * FROM `%s`.`%s`",
892 0 : OGRDuplicateCharacter(m_osBigQueryDatasetId.c_str(), '`').c_str(),
893 0 : OGRDuplicateCharacter(pszName, '`').c_str());
894 :
895 : const char *pszFIDColName =
896 0 : CSLFetchNameValueDef(papszOptions, "FID", "ogc_fid");
897 : auto poLayer =
898 : std::make_unique<OGRADBCBigQueryLayer>(this, pszName, osStatement,
899 0 : /* bInternalUse = */ false);
900 0 : poLayer->SetDeferredCreation(pszFIDColName, poGeomFieldDefn);
901 0 : m_apoLayers.emplace_back(std::move(poLayer));
902 0 : return m_apoLayers.back().get();
903 : }
904 :
905 : /************************************************************************/
906 : /* DeleteLayer() */
907 : /************************************************************************/
908 :
909 0 : OGRErr OGRADBCDataset::DeleteLayer(int iLayer)
910 : {
911 0 : if (!m_bIsBigQuery)
912 : {
913 0 : CPLError(CE_Failure, CPLE_NotSupported,
914 : "DeleteLayer() only supported for BigQuery");
915 0 : return OGRERR_FAILURE;
916 : }
917 0 : if (GetAccess() != GA_Update)
918 : {
919 0 : CPLError(
920 : CE_Failure, CPLE_NotSupported,
921 : "DeleteLayer() only supported on datasets opened in update mode");
922 0 : return OGRERR_FAILURE;
923 : }
924 0 : if (iLayer < 0 || static_cast<size_t>(iLayer) >= m_apoLayers.size())
925 : {
926 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid layer index");
927 0 : return OGRERR_FAILURE;
928 : }
929 :
930 : auto poADBCLayer =
931 0 : dynamic_cast<OGRADBCBigQueryLayer *>(m_apoLayers[iLayer].get());
932 0 : if (poADBCLayer && !poADBCLayer->m_bDeferredCreation)
933 : {
934 0 : std::string osDatasetId;
935 0 : std::string osTableId;
936 0 : if (!poADBCLayer->GetBigQueryDatasetAndTableId(osDatasetId, osTableId))
937 : {
938 0 : CPLError(CE_Failure, CPLE_NotSupported,
939 : "DeleteLayer(): cannot get dataset and table ID");
940 0 : return OGRERR_FAILURE;
941 : }
942 :
943 0 : std::string osSQL = "DROP TABLE `";
944 0 : osSQL += OGRDuplicateCharacter(osDatasetId.c_str(), '`');
945 0 : osSQL += "`.`";
946 0 : osSQL += OGRDuplicateCharacter(osTableId.c_str(), '`');
947 0 : osSQL += "`";
948 : // CPLDebug("ADBC", "%s", osSQL.c_str());
949 0 : if (CreateInternalLayer(osSQL.c_str())->GotError())
950 : {
951 0 : return OGRERR_FAILURE;
952 : }
953 : }
954 :
955 0 : m_apoLayers.erase(m_apoLayers.begin() + iLayer);
956 0 : return OGRERR_NONE;
957 : }
958 :
959 : /************************************************************************/
960 : /* TestCapability() */
961 : /************************************************************************/
962 :
963 0 : int OGRADBCDataset::TestCapability(const char *pszCap) const
964 : {
965 0 : if (EQUAL(pszCap, ODsCCreateLayer) || EQUAL(pszCap, ODsCDeleteLayer))
966 0 : return m_bIsBigQuery && eAccess == GA_Update;
967 0 : return false;
968 : }
969 :
970 : /************************************************************************/
971 : /* GetLayerByName() */
972 : /************************************************************************/
973 :
974 0 : OGRLayer *OGRADBCDataset::GetLayerByName(const char *pszName)
975 : {
976 0 : OGRLayer *poLayer = GDALDataset::GetLayerByName(pszName);
977 0 : if (poLayer || !EQUAL(pszName, "table_list"))
978 0 : return poLayer;
979 :
980 0 : OGRADBCError error;
981 0 : auto objectsStream = std::make_unique<OGRArrowArrayStream>();
982 0 : ADBC_CALL(ConnectionGetObjects, m_connection.get(),
983 : ADBC_OBJECT_DEPTH_TABLES,
984 : /* catalog = */ nullptr,
985 : /* db_schema = */ nullptr,
986 : /* table_name = */ nullptr,
987 : /* table_type = */ nullptr,
988 : /* column_name = */ nullptr, objectsStream->get(), error);
989 :
990 0 : ArrowSchema schema = {};
991 0 : if (objectsStream->get_schema(&schema) != 0)
992 : {
993 0 : CPLError(CE_Failure, CPLE_AppDefined, "get_schema() failed");
994 0 : return nullptr;
995 : }
996 :
997 0 : OGRADBCLayer tmpLayer(this, "", std::move(objectsStream), &schema,
998 0 : /* bInternalUse = */ true);
999 0 : const auto tmpLayerDefn = tmpLayer.GetLayerDefn();
1000 0 : if (tmpLayerDefn->GetFieldIndex("catalog_name") < 0 ||
1001 0 : tmpLayerDefn->GetFieldIndex("catalog_db_schemas") < 0)
1002 : {
1003 0 : return nullptr;
1004 : }
1005 :
1006 : auto poTableListLayer =
1007 0 : std::make_unique<OGRMemLayer>("table_list", nullptr, wkbNone);
1008 : {
1009 0 : OGRFieldDefn oField("catalog_name", OFTString);
1010 0 : poTableListLayer->CreateField(&oField);
1011 : }
1012 : {
1013 0 : OGRFieldDefn oField("schema_name", OFTString);
1014 0 : poTableListLayer->CreateField(&oField);
1015 : }
1016 : {
1017 0 : OGRFieldDefn oField("table_name", OFTString);
1018 0 : poTableListLayer->CreateField(&oField);
1019 : }
1020 : {
1021 0 : OGRFieldDefn oField("table_type", OFTString);
1022 0 : poTableListLayer->CreateField(&oField);
1023 : }
1024 :
1025 0 : for (const auto &poFeature : tmpLayer)
1026 : {
1027 : const char *pszCatalogName =
1028 0 : poFeature->GetFieldAsString("catalog_name");
1029 : const char *pszCatalogDBSchemas =
1030 0 : poFeature->GetFieldAsString("catalog_db_schemas");
1031 0 : if (pszCatalogDBSchemas)
1032 : {
1033 0 : CPLJSONDocument oDoc;
1034 0 : if (oDoc.LoadMemory(pszCatalogDBSchemas))
1035 : {
1036 0 : auto oRoot = oDoc.GetRoot();
1037 0 : if (oRoot.GetType() == CPLJSONObject::Type::Array)
1038 : {
1039 0 : for (const auto &oSchema : oRoot.ToArray())
1040 : {
1041 0 : if (oSchema.GetType() == CPLJSONObject::Type::Object)
1042 : {
1043 : const std::string osSchemaName =
1044 0 : oSchema.GetString("schema_name");
1045 : const auto oTables =
1046 0 : oSchema.GetArray("db_schema_tables");
1047 0 : if (oTables.IsValid())
1048 : {
1049 0 : for (const auto &oTable : oTables)
1050 : {
1051 0 : if (oTable.GetType() ==
1052 : CPLJSONObject::Type::Object)
1053 : {
1054 : const std::string osTableName =
1055 0 : oTable.GetString("table_name");
1056 : const std::string osTableType =
1057 0 : oTable.GetString("table_type");
1058 0 : if (!osTableName.empty() &&
1059 0 : osTableType != "index" &&
1060 0 : osTableType != "trigger")
1061 : {
1062 : auto poFeat =
1063 : std::make_unique<OGRFeature>(
1064 : poTableListLayer
1065 0 : ->GetLayerDefn());
1066 0 : if (pszCatalogName)
1067 0 : poFeat->SetField(
1068 : "catalog_name",
1069 : pszCatalogName);
1070 0 : if (oSchema.GetObj("schema_name")
1071 0 : .IsValid())
1072 0 : poFeat->SetField(
1073 : "schema_name",
1074 : osSchemaName.c_str());
1075 0 : poFeat->SetField(
1076 : "table_name",
1077 : osTableName.c_str());
1078 0 : if (oTable.GetObj("table_type")
1079 0 : .IsValid())
1080 0 : poFeat->SetField(
1081 : "table_type",
1082 : osTableType.c_str());
1083 0 : CPL_IGNORE_RET_VAL(
1084 0 : poTableListLayer->CreateFeature(
1085 0 : std::move(poFeat)));
1086 : }
1087 : }
1088 : }
1089 : }
1090 : }
1091 : }
1092 : }
1093 : }
1094 : }
1095 : }
1096 :
1097 0 : m_apoLayers.emplace_back(std::move(poTableListLayer));
1098 0 : return m_apoLayers.back().get();
1099 : }
1100 :
1101 : #undef ADBC_CALL
|