/src/gdal/ogr/ogrsf_frmts/adbc/ogradbcbigquerylayer.cpp
Line | Count | Source |
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) 2025, Even Rouault <even dot rouault at spatialys.com> |
9 | | * |
10 | | * SPDX-License-Identifier: MIT |
11 | | ****************************************************************************/ |
12 | | |
13 | | #include "ogr_adbc.h" |
14 | | #include "ogr_p.h" |
15 | | |
16 | | #include <algorithm> |
17 | | |
18 | | /************************************************************************/ |
19 | | /* OGRADBCBigQueryLayer() */ |
20 | | /************************************************************************/ |
21 | | |
22 | | OGRADBCBigQueryLayer::OGRADBCBigQueryLayer(OGRADBCDataset *poDS, |
23 | | const char *pszName, |
24 | | const std::string &osStatement, |
25 | | bool bInternalUse) |
26 | 0 | : OGRADBCLayer(poDS, pszName, osStatement, bInternalUse) |
27 | 0 | { |
28 | 0 | } |
29 | | |
30 | | /************************************************************************/ |
31 | | /* GetBigQueryDatasetAndTableId() */ |
32 | | /************************************************************************/ |
33 | | |
34 | | bool OGRADBCBigQueryLayer::GetBigQueryDatasetAndTableId( |
35 | | std::string &osDatasetId, std::string &osTableId) const |
36 | 0 | { |
37 | 0 | auto nPos = CPLString(m_osBaseStatement).ifind(" FROM "); |
38 | 0 | if (nPos != std::string::npos) |
39 | 0 | { |
40 | 0 | nPos += strlen(" FROM "); |
41 | 0 | const auto nPos2 = m_osBaseStatement.find(' ', nPos); |
42 | 0 | const std::string osTableName = |
43 | 0 | (nPos2 != std::string::npos) |
44 | 0 | ? m_osBaseStatement.substr(nPos, nPos2 - nPos) |
45 | 0 | : m_osBaseStatement.substr(nPos); |
46 | |
|
47 | 0 | const auto nPosDot = osTableName.find('.'); |
48 | 0 | if (nPosDot != std::string::npos) |
49 | 0 | { |
50 | 0 | osDatasetId = osTableName.substr(0, nPosDot); |
51 | 0 | osTableId = osTableName.substr(nPosDot + 1); |
52 | 0 | if (osDatasetId.size() > 2 && osDatasetId[0] == '`' && |
53 | 0 | osDatasetId.back() == '`') |
54 | 0 | osDatasetId = osDatasetId.substr(1, osDatasetId.size() - 2); |
55 | 0 | if (osTableId.size() > 2 && osTableId[0] == '`' && |
56 | 0 | osTableId.back() == '`') |
57 | 0 | osTableId = osTableId.substr(1, osTableId.size() - 2); |
58 | 0 | return true; |
59 | 0 | } |
60 | 0 | } |
61 | 0 | return false; |
62 | 0 | } |
63 | | |
64 | | /************************************************************************/ |
65 | | /* BuildLayerDefn() */ |
66 | | /************************************************************************/ |
67 | | |
68 | | void OGRADBCBigQueryLayer::BuildLayerDefn() |
69 | 0 | { |
70 | 0 | if (!BuildLayerDefnInit(true)) |
71 | 0 | return; |
72 | | |
73 | 0 | std::map<std::string, std::unique_ptr<OGRSpatialReference>> oMapGeomColumns; |
74 | 0 | std::map<std::string, bool> oMapIsNullable; |
75 | |
|
76 | 0 | const bool bIsLikelyTableExtract = |
77 | 0 | !m_bInternalUse && |
78 | 0 | STARTS_WITH_CI(m_osBaseStatement.c_str(), "SELECT ") && |
79 | 0 | !STARTS_WITH_CI(m_osBaseStatement.c_str(), "SELECT COUNT("); |
80 | 0 | if (bIsLikelyTableExtract) |
81 | 0 | { |
82 | 0 | std::string osDatasetId; |
83 | 0 | std::string osTableId; |
84 | 0 | if (GetBigQueryDatasetAndTableId(osDatasetId, osTableId)) |
85 | 0 | { |
86 | 0 | auto poColumnList = m_poDS->CreateInternalLayer(CPLSPrintf( |
87 | 0 | "SELECT c.column_name, c.data_type, c.is_nullable, " |
88 | 0 | "keys.ordinal_position AS key_ordinal_position, " |
89 | 0 | "keys.position_in_unique_constraint FROM " |
90 | 0 | "`%s`.INFORMATION_SCHEMA.COLUMNS c " |
91 | 0 | "LEFT JOIN `%s`.INFORMATION_SCHEMA.KEY_COLUMN_USAGE keys ON " |
92 | 0 | "c.table_schema = keys.table_schema AND " |
93 | 0 | "c.table_name = keys.table_name AND " |
94 | 0 | "c.column_name = keys.column_name " |
95 | 0 | "WHERE c.table_name='%s' AND c.is_hidden = 'NO' " |
96 | 0 | "ORDER BY c.ordinal_position", |
97 | 0 | OGRDuplicateCharacter(osDatasetId.c_str(), '`').c_str(), |
98 | 0 | OGRDuplicateCharacter(osDatasetId.c_str(), '`').c_str(), |
99 | 0 | OGRDuplicateCharacter(osTableId.c_str(), '\'').c_str())); |
100 | 0 | if (poColumnList->GetLayerDefn()->GetFieldCount() == 5) |
101 | 0 | { |
102 | 0 | for (auto &&f : *poColumnList) |
103 | 0 | { |
104 | 0 | constexpr int IDX_COL_NAME = 0; |
105 | 0 | constexpr int IDX_DATA_TYPE = 1; |
106 | 0 | constexpr int IDX_IS_NULLABLE = 2; |
107 | 0 | constexpr int IDX_KEY_ORDINAL_POSITION = 3; |
108 | 0 | constexpr int IDX_POSITION_IN_UNIQUE_CONSTRAINT = 4; |
109 | 0 | const char *pszColName = f->GetFieldAsString(IDX_COL_NAME); |
110 | 0 | const char *pszColType = f->GetFieldAsString(IDX_DATA_TYPE); |
111 | 0 | if (EQUAL(pszColType, "GEOGRAPHY")) |
112 | 0 | { |
113 | 0 | auto poSRS = std::make_unique<OGRSpatialReference>(); |
114 | 0 | poSRS->SetAxisMappingStrategy( |
115 | 0 | OAMS_TRADITIONAL_GIS_ORDER); |
116 | 0 | poSRS->importFromEPSG(4326); |
117 | 0 | oMapGeomColumns[pszColName] = std::move(poSRS); |
118 | 0 | } |
119 | 0 | oMapIsNullable[pszColName] = |
120 | 0 | EQUAL(f->GetFieldAsString(IDX_IS_NULLABLE), "YES"); |
121 | 0 | if (f->IsFieldNull(IDX_POSITION_IN_UNIQUE_CONSTRAINT) && |
122 | 0 | !f->IsFieldNull(IDX_KEY_ORDINAL_POSITION)) |
123 | 0 | { |
124 | 0 | if (EQUAL(pszColType, "INT64") && |
125 | 0 | f->GetFieldAsInteger64(IDX_KEY_ORDINAL_POSITION) == |
126 | 0 | 1 && |
127 | 0 | m_osFIDColName.empty()) |
128 | 0 | { |
129 | 0 | m_osFIDColName = pszColName; |
130 | 0 | } |
131 | 0 | else |
132 | 0 | { |
133 | 0 | m_osFIDColName.clear(); |
134 | 0 | } |
135 | 0 | } |
136 | 0 | } |
137 | |
|
138 | 0 | if (!oMapGeomColumns.empty()) |
139 | 0 | { |
140 | 0 | std::string osNewStatement = "SELECT "; |
141 | 0 | for (int i = 0; i < m_schema.n_children; ++i) |
142 | 0 | { |
143 | 0 | if (i > 0) |
144 | 0 | osNewStatement += ", "; |
145 | 0 | const char *pszColName = m_schema.children[i]->name; |
146 | 0 | auto oIter = oMapGeomColumns.find(pszColName); |
147 | 0 | if (oIter != oMapGeomColumns.end()) |
148 | 0 | { |
149 | 0 | osNewStatement += "ST_AsBinary(`"; |
150 | 0 | osNewStatement += |
151 | 0 | OGRDuplicateCharacter(pszColName, '`'); |
152 | 0 | osNewStatement += "`) AS "; |
153 | 0 | } |
154 | 0 | osNewStatement += '`'; |
155 | 0 | osNewStatement += |
156 | 0 | OGRDuplicateCharacter(pszColName, '`'); |
157 | 0 | osNewStatement += '`'; |
158 | 0 | } |
159 | 0 | m_osModifiedSelect = osNewStatement; |
160 | 0 | osNewStatement += " FROM ("; |
161 | 0 | osNewStatement += m_osBaseStatement; |
162 | 0 | osNewStatement += " )"; |
163 | |
|
164 | | #ifdef DEBUG_VEBOSE |
165 | | CPLDebug("ADBC", "%s -> %s", m_osBaseStatement.c_str(), |
166 | | osNewStatement.c_str()); |
167 | | #endif |
168 | |
|
169 | 0 | CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler); |
170 | 0 | if (ReplaceStatement(osNewStatement.c_str())) |
171 | 0 | { |
172 | 0 | m_osModifiedBaseStatement = std::move(osNewStatement); |
173 | 0 | } |
174 | 0 | else |
175 | 0 | { |
176 | 0 | m_osModifiedSelect.clear(); |
177 | 0 | oMapGeomColumns.clear(); |
178 | 0 | } |
179 | 0 | } |
180 | 0 | } |
181 | 0 | } |
182 | 0 | } |
183 | |
|
184 | 0 | auto poLayerDefn = m_poAdapterLayer->GetLayerDefn(); |
185 | |
|
186 | 0 | for (int i = 0; i < m_schema.n_children; ++i) |
187 | 0 | { |
188 | 0 | const char *pszColName = m_schema.children[i]->name; |
189 | 0 | auto oIter = oMapGeomColumns.find(pszColName); |
190 | 0 | if (oIter != oMapGeomColumns.end()) |
191 | 0 | { |
192 | 0 | OGRGeomFieldDefn oGeomFieldDefn(pszColName, wkbUnknown); |
193 | 0 | auto poSRS = std::move(oIter->second).release(); |
194 | 0 | if (poSRS) |
195 | 0 | { |
196 | 0 | oGeomFieldDefn.SetSpatialRef(poSRS); |
197 | 0 | poSRS->Release(); |
198 | 0 | } |
199 | 0 | poLayerDefn->AddGeomFieldDefn(&oGeomFieldDefn); |
200 | 0 | } |
201 | 0 | else |
202 | 0 | { |
203 | 0 | m_poAdapterLayer->CreateFieldFromArrowSchema(m_schema.children[i]); |
204 | 0 | } |
205 | 0 | } |
206 | |
|
207 | 0 | if (bIsLikelyTableExtract) |
208 | 0 | { |
209 | 0 | for (int i = 0; i < poLayerDefn->GetFieldCount(); ++i) |
210 | 0 | { |
211 | 0 | auto poFldDefn = poLayerDefn->GetFieldDefn(i); |
212 | 0 | auto oIter = oMapIsNullable.find(poFldDefn->GetNameRef()); |
213 | 0 | if (oIter != oMapIsNullable.end()) |
214 | 0 | poFldDefn->SetNullable(oIter->second); |
215 | 0 | } |
216 | 0 | for (int i = 0; i < poLayerDefn->GetGeomFieldCount(); ++i) |
217 | 0 | { |
218 | 0 | auto poGFldDefn = poLayerDefn->GetGeomFieldDefn(i); |
219 | 0 | std::string osSQL = "SELECT DISTINCT ST_GeometryType(`"; |
220 | 0 | osSQL += OGRDuplicateCharacter(poGFldDefn->GetNameRef(), '`'); |
221 | 0 | osSQL += "`) FROM ("; |
222 | 0 | osSQL += m_osBaseStatement; |
223 | 0 | osSQL += ')'; |
224 | 0 | auto poGeomTypeList = m_poDS->CreateInternalLayer(osSQL.c_str()); |
225 | 0 | if (poGeomTypeList->GetLayerDefn()->GetFieldCount() == 1) |
226 | 0 | { |
227 | 0 | std::string osType; |
228 | 0 | for (auto &&f : *poGeomTypeList) |
229 | 0 | { |
230 | 0 | if (osType.empty()) |
231 | 0 | { |
232 | 0 | osType = f->GetFieldAsString(0); |
233 | 0 | } |
234 | 0 | else |
235 | 0 | { |
236 | 0 | osType.clear(); |
237 | 0 | break; |
238 | 0 | } |
239 | 0 | } |
240 | 0 | if (STARTS_WITH_CI(osType.c_str(), "ST_")) |
241 | 0 | { |
242 | 0 | poGFldDefn->SetType( |
243 | 0 | OGRFromOGCGeomType(osType.c_str() + strlen("ST_"))); |
244 | 0 | } |
245 | 0 | } |
246 | 0 | auto oIter = oMapIsNullable.find(poGFldDefn->GetNameRef()); |
247 | 0 | if (oIter != oMapIsNullable.end()) |
248 | 0 | poGFldDefn->SetNullable(oIter->second); |
249 | 0 | } |
250 | 0 | } |
251 | 0 | } |
252 | | |
253 | | /************************************************************************/ |
254 | | /* SetAttributeFilter() */ |
255 | | /************************************************************************/ |
256 | | |
257 | | OGRErr OGRADBCBigQueryLayer::SetAttributeFilter(const char *pszFilter) |
258 | 0 | { |
259 | 0 | if (!m_osModifiedSelect.empty()) |
260 | 0 | { |
261 | 0 | m_osAttributeFilter = pszFilter ? pszFilter : ""; |
262 | 0 | return UpdateStatement() ? OGRERR_NONE : OGRERR_FAILURE; |
263 | 0 | } |
264 | 0 | else |
265 | 0 | { |
266 | 0 | return OGRLayer::SetAttributeFilter(pszFilter); |
267 | 0 | } |
268 | 0 | } |
269 | | |
270 | | /************************************************************************/ |
271 | | /* GetFeatureCount() */ |
272 | | /************************************************************************/ |
273 | | |
274 | | GIntBig OGRADBCBigQueryLayer::GetFeatureCount(int /*bForce*/) |
275 | 0 | { |
276 | 0 | if (!m_poAdapterLayer) |
277 | 0 | BuildLayerDefn(); |
278 | 0 | if (m_bLayerDefinitionError) |
279 | 0 | return 0; |
280 | | |
281 | 0 | auto nCount = GetFeatureCountSelectCountStar(); |
282 | 0 | if (nCount >= 0) |
283 | 0 | return nCount; |
284 | | |
285 | 0 | return GetFeatureCountArrow(); |
286 | 0 | } |
287 | | |
288 | | /************************************************************************/ |
289 | | /* TestCapability() */ |
290 | | /************************************************************************/ |
291 | | |
292 | | int OGRADBCBigQueryLayer::TestCapability(const char *pszCap) const |
293 | 0 | { |
294 | 0 | if (!m_poAdapterLayer) |
295 | 0 | const_cast<OGRADBCBigQueryLayer *>(this)->BuildLayerDefn(); |
296 | |
|
297 | 0 | if (EQUAL(pszCap, OLCSequentialWrite) || EQUAL(pszCap, OLCCreateField)) |
298 | 0 | return m_poDS->GetAccess() == GA_Update; |
299 | | |
300 | 0 | if (EQUAL(pszCap, OLCRandomWrite) || EQUAL(pszCap, OLCDeleteFeature)) |
301 | 0 | return m_poDS->GetAccess() == GA_Update && !m_osFIDColName.empty(); |
302 | | |
303 | 0 | return OGRADBCLayer::TestCapability(pszCap); |
304 | 0 | } |
305 | | |
306 | | /************************************************************************/ |
307 | | /* IGetExtent() */ |
308 | | /************************************************************************/ |
309 | | |
310 | | OGRErr OGRADBCBigQueryLayer::IGetExtent(int iGeomField, OGREnvelope *psExtent, |
311 | | bool bForce) |
312 | 0 | { |
313 | 0 | if (!m_poAdapterLayer) |
314 | 0 | BuildLayerDefn(); |
315 | |
|
316 | 0 | const char *pszGeomColName = |
317 | 0 | GetLayerDefn()->GetGeomFieldDefn(iGeomField)->GetNameRef(); |
318 | 0 | std::string osSQL = "SELECT ST_Extent(`"; |
319 | 0 | osSQL += OGRDuplicateCharacter(pszGeomColName, '`'); |
320 | 0 | osSQL += "`) FROM ("; |
321 | 0 | osSQL += m_osBaseStatement; |
322 | 0 | osSQL += ")"; |
323 | 0 | auto poExtentLayer = m_poDS->CreateInternalLayer(osSQL.c_str()); |
324 | 0 | if (poExtentLayer->GetLayerDefn()->GetFieldCount() == 4) |
325 | 0 | { |
326 | 0 | auto f = std::unique_ptr<OGRFeature>(poExtentLayer->GetNextFeature()); |
327 | 0 | if (f && f->IsFieldSetAndNotNull(0)) |
328 | 0 | { |
329 | 0 | psExtent->MinX = f->GetFieldAsDouble(0); |
330 | 0 | psExtent->MinY = f->GetFieldAsDouble(1); |
331 | 0 | psExtent->MaxX = f->GetFieldAsDouble(2); |
332 | 0 | psExtent->MaxY = f->GetFieldAsDouble(3); |
333 | 0 | return OGRERR_NONE; |
334 | 0 | } |
335 | 0 | else |
336 | 0 | return OGRERR_FAILURE; |
337 | 0 | } |
338 | | |
339 | 0 | return OGRLayer::IGetExtent(iGeomField, psExtent, bForce); |
340 | 0 | } |
341 | | |
342 | | /************************************************************************/ |
343 | | /* GetCurrentStatement() */ |
344 | | /************************************************************************/ |
345 | | |
346 | | std::string OGRADBCBigQueryLayer::GetCurrentStatement() const |
347 | 0 | { |
348 | 0 | if (!m_osAttributeFilter.empty() || m_poFilterGeom) |
349 | 0 | { |
350 | 0 | std::string osStatement(m_osModifiedSelect); |
351 | 0 | osStatement.append(" FROM (") |
352 | 0 | .append(m_osBaseStatement) |
353 | 0 | .append(") WHERE "); |
354 | 0 | if (m_poFilterGeom) |
355 | 0 | { |
356 | 0 | if (m_sFilterEnvelope.MinX > 180 || m_sFilterEnvelope.MinY > 90 || |
357 | 0 | m_sFilterEnvelope.MaxX < -180 || m_sFilterEnvelope.MaxY < -90) |
358 | 0 | { |
359 | 0 | osStatement.append(" FALSE"); |
360 | 0 | return osStatement; |
361 | 0 | } |
362 | 0 | constexpr double EPSILON = 1e-8; |
363 | 0 | const double dfMinX = |
364 | 0 | std::max(-180.0, m_sFilterEnvelope.MinX - EPSILON); |
365 | 0 | const double dfMinY = |
366 | 0 | std::max(-90.0, m_sFilterEnvelope.MinY - EPSILON); |
367 | 0 | const double dfMaxX = |
368 | 0 | std::min(180.0, m_sFilterEnvelope.MaxX + EPSILON); |
369 | 0 | const double dfMaxY = |
370 | 0 | std::min(90.0, m_sFilterEnvelope.MaxY + EPSILON); |
371 | 0 | const char *pszGeomColName = |
372 | 0 | m_poAdapterLayer->GetLayerDefn() |
373 | 0 | ->GetGeomFieldDefn(m_iGeomFieldFilter) |
374 | 0 | ->GetNameRef(); |
375 | 0 | osStatement += |
376 | 0 | CPLSPrintf("ST_IntersectsBox(`%s`,%.17g,%.17g,%.17g,%.17g)", |
377 | 0 | OGRDuplicateCharacter(pszGeomColName, '`').c_str(), |
378 | 0 | dfMinX, dfMinY, dfMaxX, dfMaxY); |
379 | 0 | } |
380 | 0 | if (!m_osAttributeFilter.empty()) |
381 | 0 | { |
382 | 0 | if (m_poFilterGeom) |
383 | 0 | osStatement.append(" AND "); |
384 | 0 | osStatement.append("("); |
385 | 0 | osStatement.append(m_osAttributeFilter); |
386 | 0 | osStatement.append(")"); |
387 | 0 | } |
388 | |
|
389 | | #ifdef DEBUG_VEBOSE |
390 | | CPLDebug("ADBC", "%s", osStatement.c_str()); |
391 | | #endif |
392 | |
|
393 | 0 | return osStatement; |
394 | 0 | } |
395 | 0 | else |
396 | 0 | { |
397 | 0 | return m_osModifiedBaseStatement; |
398 | 0 | } |
399 | 0 | } |
400 | | |
401 | | /************************************************************************/ |
402 | | /* GetSQLType() */ |
403 | | /************************************************************************/ |
404 | | |
405 | | static std::string GetSQLType(const OGRFieldDefn *poField) |
406 | 0 | { |
407 | 0 | switch (poField->GetType()) |
408 | 0 | { |
409 | 0 | case OFTInteger: |
410 | 0 | return poField->GetSubType() == OFSTBoolean ? "BOOLEAN" : "INTEGER"; |
411 | 0 | case OFTInteger64: |
412 | 0 | return "INT64"; |
413 | 0 | case OFTReal: |
414 | 0 | return "FLOAT64"; |
415 | 0 | case OFTDate: |
416 | 0 | return "DATE"; |
417 | 0 | case OFTTime: |
418 | 0 | return "TIME"; |
419 | 0 | case OFTDateTime: |
420 | 0 | return "TIMESTAMP"; |
421 | 0 | case OFTString: |
422 | 0 | return poField->GetSubType() == OFSTJSON ? "JSON" : "STRING"; |
423 | 0 | case OFTBinary: |
424 | 0 | return "BYTES"; |
425 | 0 | case OFTStringList: |
426 | 0 | return "ARRAY<STRING>"; |
427 | 0 | case OFTRealList: |
428 | 0 | return "ARRAY<FLOAT64>"; |
429 | 0 | case OFTIntegerList: |
430 | 0 | return "ARRAY<INTEGER>"; |
431 | 0 | case OFTInteger64List: |
432 | 0 | return "ARRAY<INT64>"; |
433 | 0 | case OFTWideString: |
434 | 0 | case OFTWideStringList: |
435 | 0 | CPLError(CE_Failure, CPLE_NotSupported, "Unsupported type"); |
436 | 0 | break; |
437 | 0 | } |
438 | 0 | return std::string(); |
439 | 0 | } |
440 | | |
441 | | /************************************************************************/ |
442 | | /* CreateField() */ |
443 | | /************************************************************************/ |
444 | | |
445 | | OGRErr OGRADBCBigQueryLayer::CreateField(const OGRFieldDefn *poField, |
446 | | int /*bApproxOK*/) |
447 | 0 | { |
448 | 0 | if (m_poDS->GetAccess() != GA_Update) |
449 | 0 | { |
450 | 0 | CPLError( |
451 | 0 | CE_Failure, CPLE_NotSupported, |
452 | 0 | "CreateField() only supported on datasets opened in update mode"); |
453 | 0 | return OGRERR_FAILURE; |
454 | 0 | } |
455 | 0 | if (!m_poAdapterLayer) |
456 | 0 | BuildLayerDefn(); |
457 | 0 | if (m_bLayerDefinitionError) |
458 | 0 | return OGRERR_FAILURE; |
459 | | |
460 | 0 | if (GetLayerDefn()->GetFieldIndex(poField->GetNameRef()) >= 0) |
461 | 0 | { |
462 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Field '%s' already exists.", |
463 | 0 | poField->GetNameRef()); |
464 | 0 | return OGRERR_FAILURE; |
465 | 0 | } |
466 | | |
467 | 0 | const auto osSQLType = GetSQLType(poField); |
468 | 0 | if (osSQLType.empty()) |
469 | 0 | return OGRERR_FAILURE; |
470 | | |
471 | 0 | if (!m_bDeferredCreation) |
472 | 0 | { |
473 | 0 | std::string osDatasetId, osTableId; |
474 | 0 | if (!GetBigQueryDatasetAndTableId(osDatasetId, osTableId)) |
475 | 0 | { |
476 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
477 | 0 | "CreateField(): cannot get dataset and table ID"); |
478 | 0 | return OGRERR_FAILURE; |
479 | 0 | } |
480 | | |
481 | 0 | std::string osSQL = "ALTER TABLE `"; |
482 | 0 | osSQL += OGRDuplicateCharacter(osDatasetId.c_str(), '`'); |
483 | 0 | osSQL += "`.`"; |
484 | 0 | osSQL += OGRDuplicateCharacter(osTableId.c_str(), '`'); |
485 | 0 | osSQL += "` ADD COLUMN `"; |
486 | 0 | osSQL += OGRDuplicateCharacter(poField->GetNameRef(), '`'); |
487 | 0 | osSQL += "` "; |
488 | 0 | osSQL += osSQLType; |
489 | 0 | if (m_poDS->CreateInternalLayer(osSQL.c_str())->GotError()) |
490 | 0 | return OGRERR_FAILURE; |
491 | 0 | } |
492 | | |
493 | 0 | return m_poAdapterLayer->CreateField(poField, false); |
494 | 0 | } |
495 | | |
496 | | /************************************************************************/ |
497 | | /* GetFieldValue() */ |
498 | | /************************************************************************/ |
499 | | |
500 | | static std::string GetFieldValue(const OGRFieldDefn *poFldDefn, |
501 | | OGRFeature *poFeature, int iField) |
502 | 0 | { |
503 | 0 | std::string osVal; |
504 | |
|
505 | 0 | if (poFeature->IsFieldNull(iField)) |
506 | 0 | osVal = "NULL"; |
507 | | |
508 | 0 | else if (poFldDefn->GetType() == OFTInteger || |
509 | 0 | poFldDefn->GetType() == OFTInteger64) |
510 | 0 | { |
511 | 0 | const auto nVal = poFeature->GetFieldAsInteger64(iField); |
512 | 0 | if (poFldDefn->GetSubType() == OFSTBoolean) |
513 | 0 | osVal = nVal ? "TRUE" : "FALSE"; |
514 | 0 | else |
515 | 0 | osVal = std::to_string(nVal); |
516 | 0 | } |
517 | 0 | else if (poFldDefn->GetType() == OFTReal) |
518 | 0 | { |
519 | 0 | osVal = CPLSPrintf("%.17g", poFeature->GetFieldAsDouble(iField)); |
520 | 0 | } |
521 | 0 | else if (poFldDefn->GetType() == OFTDate) |
522 | 0 | { |
523 | 0 | char szTmpFieldValue[OGR_SIZEOF_ISO8601_DATETIME_BUFFER]; |
524 | 0 | constexpr bool bAlwaysMillisecond = false; |
525 | 0 | OGRGetISO8601DateTime(poFeature->GetRawFieldRef(iField), |
526 | 0 | bAlwaysMillisecond, szTmpFieldValue); |
527 | 0 | szTmpFieldValue[strlen("YYYY-MM-DD")] = 0; |
528 | 0 | osVal += "DATE \'"; |
529 | 0 | osVal += szTmpFieldValue; |
530 | 0 | osVal += '\''; |
531 | 0 | } |
532 | 0 | else if (poFldDefn->GetType() == OFTDateTime) |
533 | 0 | { |
534 | 0 | osVal += '\''; |
535 | 0 | osVal += poFeature->GetFieldAsISO8601DateTime(iField, nullptr); |
536 | 0 | osVal += '\''; |
537 | 0 | } |
538 | 0 | else if (poFldDefn->GetType() == OFTBinary) |
539 | 0 | { |
540 | 0 | osVal += "b'"; |
541 | 0 | int nCount = 0; |
542 | 0 | GByte *pabyVal = poFeature->GetFieldAsBinary(iField, &nCount); |
543 | 0 | osVal.reserve(nCount * 4 + 4); |
544 | 0 | for (int i = 0; i < nCount; ++i) |
545 | 0 | { |
546 | 0 | osVal += CPLSPrintf("\\x%02X", pabyVal[i]); |
547 | 0 | } |
548 | 0 | osVal += '\''; |
549 | 0 | } |
550 | 0 | else if (poFldDefn->GetType() == OFTStringList) |
551 | 0 | { |
552 | 0 | CSLConstList papszStr = poFeature->GetFieldAsStringList(iField); |
553 | 0 | osVal += '['; |
554 | 0 | for (int i = 0; papszStr && papszStr[i]; ++i) |
555 | 0 | { |
556 | 0 | if (i > 0) |
557 | 0 | osVal += ','; |
558 | 0 | osVal += '\''; |
559 | 0 | osVal += OGRDuplicateCharacter(papszStr[i], '\''); |
560 | 0 | osVal += '\''; |
561 | 0 | } |
562 | 0 | osVal += ']'; |
563 | 0 | } |
564 | 0 | else if (poFldDefn->GetType() == OFTIntegerList) |
565 | 0 | { |
566 | 0 | int nCount = 0; |
567 | 0 | const int *panVals = poFeature->GetFieldAsIntegerList(iField, &nCount); |
568 | 0 | osVal += '['; |
569 | 0 | for (int i = 0; i < nCount; ++i) |
570 | 0 | { |
571 | 0 | if (i > 0) |
572 | 0 | osVal += ','; |
573 | 0 | osVal += std::to_string(panVals[i]); |
574 | 0 | } |
575 | 0 | osVal += ']'; |
576 | 0 | } |
577 | 0 | else if (poFldDefn->GetType() == OFTInteger64List) |
578 | 0 | { |
579 | 0 | int nCount = 0; |
580 | 0 | const auto *panVals = |
581 | 0 | poFeature->GetFieldAsInteger64List(iField, &nCount); |
582 | 0 | osVal += '['; |
583 | 0 | for (int i = 0; i < nCount; ++i) |
584 | 0 | { |
585 | 0 | if (i > 0) |
586 | 0 | osVal += ','; |
587 | 0 | osVal += std::to_string(panVals[i]); |
588 | 0 | } |
589 | 0 | osVal += ']'; |
590 | 0 | } |
591 | 0 | else if (poFldDefn->GetType() == OFTRealList) |
592 | 0 | { |
593 | 0 | int nCount = 0; |
594 | 0 | const double *padfVals = |
595 | 0 | poFeature->GetFieldAsDoubleList(iField, &nCount); |
596 | 0 | osVal += '['; |
597 | 0 | for (int i = 0; i < nCount; ++i) |
598 | 0 | { |
599 | 0 | if (i > 0) |
600 | 0 | osVal += ','; |
601 | 0 | osVal += CPLSPrintf("%.17g", padfVals[i]); |
602 | 0 | } |
603 | 0 | osVal += ']'; |
604 | 0 | } |
605 | 0 | else |
606 | 0 | { |
607 | | // Cf https://cloud.google.com/bigquery/docs/json-data?hl=en#create_a_json_value |
608 | 0 | if (poFldDefn->GetSubType() == OFSTJSON) |
609 | 0 | osVal += "JSON "; |
610 | 0 | osVal += '\''; |
611 | 0 | osVal += |
612 | 0 | OGRDuplicateCharacter(poFeature->GetFieldAsString(iField), '\''); |
613 | 0 | osVal += '\''; |
614 | 0 | } |
615 | 0 | return osVal; |
616 | 0 | } |
617 | | |
618 | | /************************************************************************/ |
619 | | /* ICreateFeature() */ |
620 | | /************************************************************************/ |
621 | | |
622 | | OGRErr OGRADBCBigQueryLayer::ICreateFeature(OGRFeature *poFeature) |
623 | 0 | { |
624 | 0 | if (m_poDS->GetAccess() != GA_Update) |
625 | 0 | { |
626 | 0 | CPLError( |
627 | 0 | CE_Failure, CPLE_NotSupported, |
628 | 0 | "CreateFeature() only supported on datasets opened in update mode"); |
629 | 0 | return OGRERR_FAILURE; |
630 | 0 | } |
631 | 0 | if (!m_poAdapterLayer) |
632 | 0 | BuildLayerDefn(); |
633 | 0 | if (m_bDeferredCreation) |
634 | 0 | RunDeferredCreation(); |
635 | 0 | if (m_bLayerDefinitionError) |
636 | 0 | return OGRERR_FAILURE; |
637 | | |
638 | 0 | std::string osDatasetId; |
639 | 0 | std::string osTableId; |
640 | 0 | if (!STARTS_WITH_CI(m_osBaseStatement.c_str(), "SELECT * FROM ") || |
641 | 0 | CPLString(m_osBaseStatement).ifind(" WHERE ") != std::string::npos || |
642 | 0 | !GetBigQueryDatasetAndTableId(osDatasetId, osTableId)) |
643 | 0 | { |
644 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
645 | 0 | "CreateFeature(): cannot get dataset and table ID"); |
646 | 0 | return OGRERR_FAILURE; |
647 | 0 | } |
648 | | |
649 | 0 | std::string osFieldNames; |
650 | 0 | std::string osFieldValues; |
651 | |
|
652 | 0 | if (!m_osFIDColName.empty()) |
653 | 0 | { |
654 | 0 | if (poFeature->GetFID() < 0) |
655 | 0 | { |
656 | 0 | if (m_nMaxFeatureID < 0) |
657 | 0 | { |
658 | 0 | std::string osSQL = "SELECT MAX(`"; |
659 | 0 | osSQL += OGRDuplicateCharacter(m_osFIDColName.c_str(), '`'); |
660 | 0 | osSQL += "`) FROM ("; |
661 | 0 | osSQL += m_osBaseStatement; |
662 | 0 | osSQL += ')'; |
663 | |
|
664 | 0 | auto poMaxFIDLayer = m_poDS->CreateInternalLayer(osSQL.c_str()); |
665 | 0 | if (poMaxFIDLayer->GetLayerDefn()->GetFieldCount() != 1) |
666 | 0 | return OGRERR_FAILURE; |
667 | 0 | auto f = std::unique_ptr<OGRFeature>( |
668 | 0 | poMaxFIDLayer->GetNextFeature()); |
669 | 0 | if (f) |
670 | 0 | m_nMaxFeatureID = f->GetFieldAsInteger64(0); |
671 | 0 | else |
672 | 0 | m_nMaxFeatureID = 0; |
673 | 0 | } |
674 | 0 | poFeature->SetFID(++m_nMaxFeatureID); |
675 | 0 | } |
676 | 0 | osFieldNames = m_osFIDColName; |
677 | 0 | osFieldValues = std::to_string(poFeature->GetFID()); |
678 | 0 | } |
679 | | |
680 | 0 | auto poFeatureDefn = GetLayerDefn(); |
681 | 0 | for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); ++i) |
682 | 0 | { |
683 | 0 | const char *pszName = poFeatureDefn->GetGeomFieldDefn(i)->GetNameRef(); |
684 | 0 | if (!osFieldNames.empty()) |
685 | 0 | osFieldNames += ", "; |
686 | 0 | osFieldNames += '`'; |
687 | 0 | osFieldNames += OGRDuplicateCharacter(pszName, '`'); |
688 | 0 | osFieldNames += '`'; |
689 | |
|
690 | 0 | if (!osFieldValues.empty()) |
691 | 0 | osFieldValues += ", "; |
692 | |
|
693 | 0 | const auto poGeom = poFeature->GetGeomFieldRef(i); |
694 | 0 | if (poGeom) |
695 | 0 | { |
696 | 0 | osFieldValues += "ST_GeogFromText('"; |
697 | 0 | char *pszWKT = nullptr; |
698 | 0 | poGeom->exportToWkt(&pszWKT, wkbVariantIso); |
699 | 0 | osFieldValues += pszWKT; |
700 | 0 | CPLFree(pszWKT); |
701 | 0 | osFieldValues += "')"; |
702 | 0 | } |
703 | 0 | else |
704 | 0 | { |
705 | 0 | osFieldValues += "NULL"; |
706 | 0 | } |
707 | 0 | } |
708 | 0 | for (int i = 0; i < poFeatureDefn->GetFieldCount(); ++i) |
709 | 0 | { |
710 | 0 | const auto poFieldDefn = poFeatureDefn->GetFieldDefn(i); |
711 | 0 | const char *pszName = poFieldDefn->GetNameRef(); |
712 | 0 | if (!EQUAL(pszName, m_osFIDColName.c_str()) && poFeature->IsFieldSet(i)) |
713 | 0 | { |
714 | 0 | if (!osFieldNames.empty()) |
715 | 0 | osFieldNames += ", "; |
716 | 0 | osFieldNames += '`'; |
717 | 0 | osFieldNames += OGRDuplicateCharacter(pszName, '`'); |
718 | 0 | osFieldNames += '`'; |
719 | |
|
720 | 0 | if (!osFieldValues.empty()) |
721 | 0 | osFieldValues += ", "; |
722 | |
|
723 | 0 | osFieldValues += GetFieldValue(poFieldDefn, poFeature, i); |
724 | 0 | } |
725 | 0 | } |
726 | |
|
727 | 0 | std::string osSQL = "INSERT INTO `"; |
728 | 0 | osSQL += OGRDuplicateCharacter(osDatasetId, '`'); |
729 | 0 | osSQL += "`.`"; |
730 | 0 | osSQL += OGRDuplicateCharacter(osTableId, '`'); |
731 | 0 | osSQL += "` "; |
732 | 0 | if (osFieldNames.empty()) |
733 | 0 | { |
734 | 0 | osSQL += "DEFAULT VALUES"; |
735 | 0 | } |
736 | 0 | else |
737 | 0 | { |
738 | 0 | osSQL += '('; |
739 | 0 | osSQL += osFieldNames; |
740 | 0 | osSQL += ") VALUES ("; |
741 | 0 | osSQL += osFieldValues; |
742 | 0 | osSQL += ')'; |
743 | 0 | } |
744 | 0 | return m_poDS->CreateInternalLayer(osSQL.c_str())->GotError() |
745 | 0 | ? OGRERR_FAILURE |
746 | 0 | : OGRERR_NONE; |
747 | 0 | } |
748 | | |
749 | | /************************************************************************/ |
750 | | /* ISetFeature() */ |
751 | | /************************************************************************/ |
752 | | |
753 | | OGRErr OGRADBCBigQueryLayer::ISetFeature(OGRFeature *poFeature) |
754 | 0 | { |
755 | 0 | if (m_poDS->GetAccess() != GA_Update) |
756 | 0 | { |
757 | 0 | CPLError( |
758 | 0 | CE_Failure, CPLE_NotSupported, |
759 | 0 | "SetFeature() only supported on datasets opened in update mode"); |
760 | 0 | return OGRERR_FAILURE; |
761 | 0 | } |
762 | 0 | if (m_osFIDColName.empty()) |
763 | 0 | { |
764 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
765 | 0 | "SetFeature() only supported on tables with a INT64 single " |
766 | 0 | "column primary key"); |
767 | 0 | return OGRERR_FAILURE; |
768 | 0 | } |
769 | 0 | if (poFeature->GetFID() < 0) |
770 | 0 | { |
771 | 0 | return OGRERR_NON_EXISTING_FEATURE; |
772 | 0 | } |
773 | | |
774 | 0 | if (!m_poAdapterLayer) |
775 | 0 | BuildLayerDefn(); |
776 | 0 | if (m_bDeferredCreation) |
777 | 0 | RunDeferredCreation(); |
778 | 0 | if (m_bLayerDefinitionError) |
779 | 0 | return OGRERR_FAILURE; |
780 | | |
781 | 0 | std::string osDatasetId; |
782 | 0 | std::string osTableId; |
783 | 0 | if (!STARTS_WITH_CI(m_osBaseStatement.c_str(), "SELECT * FROM ") || |
784 | 0 | CPLString(m_osBaseStatement).ifind(" WHERE ") != std::string::npos || |
785 | 0 | !GetBigQueryDatasetAndTableId(osDatasetId, osTableId)) |
786 | 0 | { |
787 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
788 | 0 | "SetFeature(): cannot get dataset and table ID"); |
789 | 0 | return OGRERR_FAILURE; |
790 | 0 | } |
791 | | |
792 | 0 | std::string osSQL = "UPDATE `"; |
793 | 0 | osSQL += OGRDuplicateCharacter(osDatasetId, '`'); |
794 | 0 | osSQL += "`.`"; |
795 | 0 | osSQL += OGRDuplicateCharacter(osTableId, '`'); |
796 | 0 | osSQL += "` SET "; |
797 | |
|
798 | 0 | bool bAddComma = false; |
799 | 0 | const auto poFeatureDefn = GetLayerDefn(); |
800 | |
|
801 | 0 | for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); ++i) |
802 | 0 | { |
803 | 0 | const char *pszName = poFeatureDefn->GetGeomFieldDefn(i)->GetNameRef(); |
804 | 0 | if (bAddComma) |
805 | 0 | osSQL += ", "; |
806 | 0 | bAddComma = true; |
807 | 0 | osSQL += '`'; |
808 | 0 | osSQL += OGRDuplicateCharacter(pszName, '`'); |
809 | 0 | osSQL += "` = "; |
810 | 0 | const auto poGeom = poFeature->GetGeomFieldRef(i); |
811 | 0 | if (poGeom) |
812 | 0 | { |
813 | 0 | osSQL += "ST_GeogFromText('"; |
814 | 0 | char *pszWKT = nullptr; |
815 | 0 | poGeom->exportToWkt(&pszWKT, wkbVariantIso); |
816 | 0 | osSQL += pszWKT; |
817 | 0 | CPLFree(pszWKT); |
818 | 0 | osSQL += "')"; |
819 | 0 | } |
820 | 0 | else |
821 | 0 | { |
822 | 0 | osSQL += "NULL"; |
823 | 0 | } |
824 | 0 | } |
825 | |
|
826 | 0 | for (int i = 0; i < poFeatureDefn->GetFieldCount(); ++i) |
827 | 0 | { |
828 | 0 | const auto poFieldDefn = poFeatureDefn->GetFieldDefn(i); |
829 | 0 | const char *pszName = poFieldDefn->GetNameRef(); |
830 | 0 | if (!EQUAL(pszName, m_osFIDColName.c_str()) && poFeature->IsFieldSet(i)) |
831 | 0 | { |
832 | 0 | if (bAddComma) |
833 | 0 | osSQL += ", "; |
834 | 0 | bAddComma = true; |
835 | 0 | osSQL += '`'; |
836 | 0 | osSQL += OGRDuplicateCharacter(pszName, '`'); |
837 | 0 | osSQL += "` = "; |
838 | 0 | osSQL += GetFieldValue(poFieldDefn, poFeature, i); |
839 | 0 | } |
840 | 0 | } |
841 | |
|
842 | 0 | osSQL += " WHERE `"; |
843 | 0 | osSQL += OGRDuplicateCharacter(m_osFIDColName, '`'); |
844 | 0 | osSQL += "` = "; |
845 | 0 | osSQL += std::to_string(poFeature->GetFID()); |
846 | |
|
847 | 0 | return bAddComma && m_poDS->CreateInternalLayer(osSQL.c_str())->GotError() |
848 | 0 | ? OGRERR_FAILURE |
849 | 0 | : OGRERR_NONE; |
850 | 0 | } |
851 | | |
852 | | /************************************************************************/ |
853 | | /* DeleteFeature() */ |
854 | | /************************************************************************/ |
855 | | |
856 | | OGRErr OGRADBCBigQueryLayer::DeleteFeature(GIntBig nFID) |
857 | 0 | { |
858 | 0 | if (m_poDS->GetAccess() != GA_Update) |
859 | 0 | { |
860 | 0 | CPLError( |
861 | 0 | CE_Failure, CPLE_NotSupported, |
862 | 0 | "DeleteFeature() only supported on datasets opened in update mode"); |
863 | 0 | return OGRERR_FAILURE; |
864 | 0 | } |
865 | 0 | if (m_osFIDColName.empty()) |
866 | 0 | { |
867 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
868 | 0 | "DeleteFeature() only supported on tables with a INT64 single " |
869 | 0 | "column primary key"); |
870 | 0 | return OGRERR_FAILURE; |
871 | 0 | } |
872 | 0 | if (!m_poAdapterLayer) |
873 | 0 | BuildLayerDefn(); |
874 | 0 | if (m_bDeferredCreation) |
875 | 0 | RunDeferredCreation(); |
876 | 0 | if (m_bLayerDefinitionError) |
877 | 0 | return OGRERR_FAILURE; |
878 | 0 | if (nFID < 0) |
879 | 0 | return OGRERR_NON_EXISTING_FEATURE; |
880 | | |
881 | 0 | std::string osDatasetId; |
882 | 0 | std::string osTableId; |
883 | 0 | if (!GetBigQueryDatasetAndTableId(osDatasetId, osTableId)) |
884 | 0 | { |
885 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
886 | 0 | "DeleteFeature(): cannot get dataset and table ID"); |
887 | 0 | return OGRERR_FAILURE; |
888 | 0 | } |
889 | | |
890 | 0 | std::string osSQL = "DELETE FROM `"; |
891 | 0 | osSQL += OGRDuplicateCharacter(osDatasetId, '`'); |
892 | 0 | osSQL += "`.`"; |
893 | 0 | osSQL += OGRDuplicateCharacter(osTableId, '`'); |
894 | 0 | osSQL += "` WHERE `"; |
895 | 0 | osSQL += OGRDuplicateCharacter(m_osFIDColName, '`'); |
896 | 0 | osSQL += "` = "; |
897 | 0 | osSQL += std::to_string(nFID); |
898 | |
|
899 | 0 | return m_poDS->CreateInternalLayer(osSQL.c_str())->GotError() |
900 | 0 | ? OGRERR_FAILURE |
901 | 0 | : OGRERR_NONE; |
902 | 0 | } |
903 | | |
904 | | /************************************************************************/ |
905 | | /* SetDeferredCreation() */ |
906 | | /************************************************************************/ |
907 | | |
908 | | void OGRADBCBigQueryLayer::SetDeferredCreation( |
909 | | const char *pszFIDColName, const OGRGeomFieldDefn *poGeomFieldDefn) |
910 | 0 | { |
911 | 0 | m_bDeferredCreation = true; |
912 | 0 | m_osFIDColName = pszFIDColName; |
913 | 0 | m_poAdapterLayer = std::make_unique<OGRArrowArrayToOGRFeatureAdapterLayer>( |
914 | 0 | GetDescription()); |
915 | 0 | if (poGeomFieldDefn && poGeomFieldDefn->GetType() != wkbNone) |
916 | 0 | { |
917 | 0 | OGRGeomFieldDefn oFieldDefn(poGeomFieldDefn); |
918 | 0 | if (oFieldDefn.GetNameRef()[0] == '\0') |
919 | 0 | oFieldDefn.SetName("geog"); |
920 | 0 | m_poAdapterLayer->CreateGeomField(&oFieldDefn, false); |
921 | 0 | } |
922 | 0 | } |
923 | | |
924 | | /************************************************************************/ |
925 | | /* RunDeferredCreation() */ |
926 | | /************************************************************************/ |
927 | | |
928 | | bool OGRADBCBigQueryLayer::RunDeferredCreation() |
929 | 0 | { |
930 | 0 | if (m_bDeferredCreation) |
931 | 0 | { |
932 | 0 | m_bDeferredCreation = false; |
933 | |
|
934 | 0 | auto poFeatureDefn = m_poAdapterLayer->GetLayerDefn(); |
935 | 0 | std::string osDatasetId; |
936 | 0 | std::string osTableId; |
937 | 0 | CPL_IGNORE_RET_VAL( |
938 | 0 | GetBigQueryDatasetAndTableId(osDatasetId, osTableId)); |
939 | |
|
940 | 0 | std::string osSQL = "CREATE TABLE `"; |
941 | 0 | osSQL += OGRDuplicateCharacter(osDatasetId.c_str(), '`'); |
942 | 0 | osSQL += "`.`"; |
943 | 0 | osSQL += OGRDuplicateCharacter(osTableId.c_str(), '`'); |
944 | 0 | osSQL += "` ("; |
945 | 0 | bool bAddComma = false; |
946 | 0 | if (!m_osFIDColName.empty()) |
947 | 0 | { |
948 | 0 | osSQL += '`'; |
949 | 0 | osSQL += OGRDuplicateCharacter(m_osFIDColName.c_str(), '`'); |
950 | 0 | osSQL += "` INT64 PRIMARY KEY NOT ENFORCED"; |
951 | 0 | bAddComma = true; |
952 | 0 | } |
953 | 0 | for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); ++i) |
954 | 0 | { |
955 | 0 | const auto poFieldDefn = poFeatureDefn->GetGeomFieldDefn(i); |
956 | 0 | if (bAddComma) |
957 | 0 | osSQL += ", "; |
958 | 0 | bAddComma = true; |
959 | 0 | osSQL += '`'; |
960 | 0 | osSQL += OGRDuplicateCharacter(poFieldDefn->GetNameRef(), '`'); |
961 | 0 | osSQL += "` GEOGRAPHY"; |
962 | 0 | if (!poFieldDefn->IsNullable()) |
963 | 0 | osSQL += " NOT NULL"; |
964 | 0 | } |
965 | 0 | for (int i = 0; i < poFeatureDefn->GetFieldCount(); ++i) |
966 | 0 | { |
967 | 0 | const auto poFieldDefn = poFeatureDefn->GetFieldDefn(i); |
968 | 0 | if (bAddComma) |
969 | 0 | osSQL += ", "; |
970 | 0 | bAddComma = true; |
971 | 0 | osSQL += '`'; |
972 | 0 | osSQL += OGRDuplicateCharacter(poFieldDefn->GetNameRef(), '`'); |
973 | 0 | osSQL += "` "; |
974 | 0 | osSQL += GetSQLType(poFieldDefn); |
975 | 0 | if (!poFieldDefn->IsNullable()) |
976 | 0 | osSQL += " NOT NULL"; |
977 | 0 | } |
978 | 0 | osSQL += ')'; |
979 | |
|
980 | 0 | m_bLayerDefinitionError = |
981 | 0 | m_poDS->CreateInternalLayer(osSQL.c_str())->GotError(); |
982 | 0 | } |
983 | 0 | return !m_bLayerDefinitionError; |
984 | 0 | } |