13
13
* (at your option) any later version. *
14
14
* *
15
15
***************************************************************************/
16
+
17
+ #include < sqlite3.h>
18
+
16
19
#include " qgsgeopackageproviderconnection.h"
17
20
#include " qgsogrdbconnection.h"
18
21
#include " qgssettings.h"
25
28
26
29
#include < QTextCodec>
27
30
31
+
28
32
QgsGeoPackageProviderConnection::QgsGeoPackageProviderConnection ( const QString &name )
29
33
: QgsAbstractDatabaseProviderConnection( name )
30
34
{
@@ -278,8 +282,9 @@ QList<QgsGeoPackageProviderConnection::TableProperty> QgsGeoPackageProviderConne
278
282
}
279
283
280
284
QgsGeoPackageProviderConnection::TableProperty property;
281
- property.setTableName ( row.at ( 0 ).toString () );
282
- property.setPrimaryKeyColumns ( { QStringLiteral ( " fid" ) } );
285
+ const QString tableName { row.at ( 0 ).toString () };
286
+ property.setTableName ( tableName );
287
+ property.setPrimaryKeyColumns ( { primaryKeyColumnName ( tableName ) } );
283
288
property.setGeometryColumnCount ( 0 );
284
289
static const QStringList aspatialTypes = { QStringLiteral ( " attributes" ), QStringLiteral ( " aspatial" ) };
285
290
const QString dataType = row.at ( 1 ).toString ();
@@ -405,10 +410,25 @@ QgsAbstractDatabaseProviderConnection::QueryResult QgsGeoPackageProviderConnecti
405
410
if ( fet.reset ( OGR_L_GetNextFeature ( ogrLayer ) ), fet )
406
411
{
407
412
413
+ // pk column name
414
+ QString pkColumnName;
415
+
408
416
QgsFields fields { QgsOgrUtils::readOgrFields ( fet.get (), QTextCodec::codecForName ( " UTF-8" ) ) };
409
417
410
- // pk column name, hardcoded to "fid" (FIXME)
411
- QString pkColumnName { QStringLiteral ( " fid" ) };
418
+ // We try to guess the table name from the FROM clause
419
+ thread_local const QRegularExpression tableNameRegexp { QStringLiteral ( R"re( (?<=from|join)\s+(\w+)|"([^"]+)")re" ), QRegularExpression::PatternOption::CaseInsensitiveOption };
420
+ const auto match { tableNameRegexp.match ( sql ) };
421
+ if ( match.hasMatch () )
422
+ {
423
+ pkColumnName = primaryKeyColumnName ( match.captured ( match.lastCapturedIndex () ) );
424
+ }
425
+
426
+ // default to "fid"
427
+ if ( pkColumnName.isEmpty () )
428
+ {
429
+ pkColumnName = QStringLiteral ( " fid" );
430
+ }
431
+
412
432
// geom column name
413
433
QString geomColumnName;
414
434
@@ -424,15 +444,17 @@ QgsAbstractDatabaseProviderConnection::QueryResult QgsGeoPackageProviderConnecti
424
444
geomColumnName = OGR_GFld_GetNameRef ( geomFldDef );
425
445
}
426
446
}
427
-
428
447
}
429
448
430
- // May need to prepend PK and append geometries to the columns
431
- thread_local const QRegularExpression pkRegExp { QStringLiteral ( R"( ^select\s+(\*|%1)[,\s+](.*)from)" ).arg ( pkColumnName ), QRegularExpression::PatternOption::CaseInsensitiveOption };
432
- if ( pkRegExp.match ( sql.trimmed () ).hasMatch () )
449
+ // May need to prepend PK and append geometry to the columns
450
+ if ( ! pkColumnName.isEmpty () )
433
451
{
434
- iterator->setPrimaryKeyColumnName ( pkColumnName );
435
- results.appendColumn ( pkColumnName );
452
+ const QRegularExpression pkRegExp { QStringLiteral ( R"( ^select\s+(\*|%1)[,\s+](.*)from)" ).arg ( pkColumnName ), QRegularExpression::PatternOption::CaseInsensitiveOption };
453
+ if ( pkRegExp.match ( sql.trimmed () ).hasMatch () )
454
+ {
455
+ iterator->setPrimaryKeyColumnName ( pkColumnName );
456
+ results.appendColumn ( pkColumnName );
457
+ }
436
458
}
437
459
438
460
// Add other fields
@@ -487,6 +509,47 @@ QgsAbstractDatabaseProviderConnection::QueryResult QgsGeoPackageProviderConnecti
487
509
return QgsAbstractDatabaseProviderConnection::QueryResult ();
488
510
}
489
511
512
+ QString QgsGeoPackageProviderConnection::primaryKeyColumnName ( const QString &table ) const
513
+ {
514
+ QString pkName;
515
+
516
+ sqlite3_database_unique_ptr sqliteHandle;
517
+ if ( SQLITE_OK == sqliteHandle.open_v2 ( uri (), SQLITE_OPEN_READONLY, nullptr ) )
518
+ {
519
+ char *errMsg;
520
+
521
+ const QString sql { QStringLiteral ( " PRAGMA table_info(%1)" )
522
+ .arg ( QgsSqliteUtils::quotedString ( table ) )};
523
+
524
+ std::vector<std::string> rows;
525
+ auto cb = [ ](
526
+ void *data /* Data provided in the 4th argument of sqlite3_exec() */ ,
527
+ int /* The number of columns in row */ ,
528
+ char **argv /* An array of strings representing fields in the row */ ,
529
+ char ** /* An array of strings representing column names */ ) -> int
530
+ {
531
+ if ( std::string ( argv[5 ] ).compare ( " 1" ) == 0 )
532
+ static_cast <std::vector<std::string>*>( data )->push_back ( argv[1 ] );
533
+ return 0 ;
534
+ };
535
+
536
+ // Columns 'cid', 'name', 'type', 'notnull', 'dflt_value', 'pk']
537
+ const int ret = sqlite3_exec ( sqliteHandle.get (), sql.toUtf8 (), cb, ( void * )&rows, &errMsg );
538
+
539
+ if ( errMsg )
540
+ {
541
+ sqlite3_free ( errMsg );
542
+ }
543
+
544
+ if ( SQLITE_OK == ret && rows.size () > 0 )
545
+ {
546
+ pkName = QString::fromStdString ( rows[0 ] );
547
+ }
548
+ }
549
+
550
+ return pkName;
551
+ }
552
+
490
553
QVariantList QgsGeoPackageProviderResultIterator::nextRowPrivate ()
491
554
{
492
555
const QVariantList currentRow = mNextRow ;
@@ -585,9 +648,12 @@ QgsFields QgsGeoPackageProviderConnection::fields( const QString &schema, const
585
648
// Get fields from layer
586
649
QgsFields fieldList;
587
650
588
- // Prepend PK "fid" hardcoded (FIXME): there might be a way to get the PK name here
589
- // but there is probably no way for the general execSql case.
590
- fieldList.append ( QgsField{ QStringLiteral ( " fid" ), QVariant::LongLong } );
651
+ const QString pkname { primaryKeyColumnName ( table ) };
652
+
653
+ if ( ! pkname.isEmpty () )
654
+ {
655
+ fieldList.append ( QgsField{ pkname, QVariant::LongLong } );
656
+ }
591
657
592
658
QgsVectorLayer::LayerOptions options { false , true };
593
659
options.skipCrsValidation = true ;
0 commit comments