|
49 | 49 | #include "qgsproviderregistry.h"
|
50 | 50 | #include "sqlite3.h"
|
51 | 51 | #include "qgstransaction.h"
|
| 52 | +#include "qgsthreadingutils.h" |
52 | 53 |
|
53 | 54 | const QString QgsExpressionFunction::helpText() const
|
54 | 55 | {
|
@@ -1377,151 +1378,162 @@ static QVariant fcnNumSelected( const QVariantList &values, const QgsExpressionC
|
1377 | 1378 | static QVariant fcnSqliteFetchAndIncrement( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
|
1378 | 1379 | {
|
1379 | 1380 | static QMap<QString, qlonglong> counterCache;
|
| 1381 | + QVariant functionResult; |
1380 | 1382 |
|
1381 |
| - QString database; |
1382 |
| - const QgsVectorLayer *layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), parent ); |
1383 |
| - |
1384 |
| - if ( layer ) |
| 1383 | + std::function<void()> fetchAndIncrementFunc = [ =, &functionResult ]() |
1385 | 1384 | {
|
1386 |
| - const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() ); |
1387 |
| - database = decodedUri.value( QStringLiteral( "path" ) ).toString(); |
1388 |
| - if ( database.isEmpty() ) |
| 1385 | + QString database; |
| 1386 | + const QgsVectorLayer *layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), parent ); |
| 1387 | + |
| 1388 | + if ( layer ) |
1389 | 1389 | {
|
1390 |
| - parent->setEvalErrorString( QObject::tr( "Could not extract file path from layer `%1`." ).arg( layer->name() ) ); |
| 1390 | + const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() ); |
| 1391 | + database = decodedUri.value( QStringLiteral( "path" ) ).toString(); |
| 1392 | + if ( database.isEmpty() ) |
| 1393 | + { |
| 1394 | + parent->setEvalErrorString( QObject::tr( "Could not extract file path from layer `%1`." ).arg( layer->name() ) ); |
| 1395 | + } |
| 1396 | + } |
| 1397 | + else |
| 1398 | + { |
| 1399 | + database = values.at( 0 ).toString(); |
1391 | 1400 | }
|
1392 |
| - } |
1393 |
| - else |
1394 |
| - { |
1395 |
| - database = values.at( 0 ).toString(); |
1396 |
| - } |
1397 |
| - |
1398 |
| - const QString table = values.at( 1 ).toString(); |
1399 |
| - const QString idColumn = values.at( 2 ).toString(); |
1400 |
| - const QString filterAttribute = values.at( 3 ).toString(); |
1401 |
| - const QVariant filterValue = values.at( 4 ).toString(); |
1402 |
| - const QVariantMap defaultValues = values.at( 5 ).toMap(); |
1403 |
| - |
1404 |
| - // read from database |
1405 |
| - sqlite3_database_unique_ptr sqliteDb; |
1406 |
| - sqlite3_statement_unique_ptr sqliteStatement; |
1407 | 1401 |
|
1408 |
| - if ( sqliteDb.open_v2( database, SQLITE_OPEN_READWRITE, nullptr ) != SQLITE_OK ) |
1409 |
| - { |
1410 |
| - parent->setEvalErrorString( QObject::tr( "Could not open sqlite database %1. Error %2. " ).arg( database, sqliteDb.errorMessage() ) ); |
1411 |
| - return QVariant(); |
1412 |
| - } |
| 1402 | + const QString table = values.at( 1 ).toString(); |
| 1403 | + const QString idColumn = values.at( 2 ).toString(); |
| 1404 | + const QString filterAttribute = values.at( 3 ).toString(); |
| 1405 | + const QVariant filterValue = values.at( 4 ).toString(); |
| 1406 | + const QVariantMap defaultValues = values.at( 5 ).toMap(); |
1413 | 1407 |
|
1414 |
| - QString errorMessage; |
1415 |
| - QString currentValSql; |
| 1408 | + // read from database |
| 1409 | + sqlite3_database_unique_ptr sqliteDb; |
| 1410 | + sqlite3_statement_unique_ptr sqliteStatement; |
1416 | 1411 |
|
1417 |
| - qlonglong nextId; |
1418 |
| - bool cachedMode = false; |
1419 |
| - bool valueRetrieved = false; |
| 1412 | + if ( sqliteDb.open_v2( database, SQLITE_OPEN_READWRITE, nullptr ) != SQLITE_OK ) |
| 1413 | + { |
| 1414 | + parent->setEvalErrorString( QObject::tr( "Could not open sqlite database %1. Error %2. " ).arg( database, sqliteDb.errorMessage() ) ); |
| 1415 | + functionResult = QVariant(); |
| 1416 | + return; |
| 1417 | + } |
1420 | 1418 |
|
1421 |
| - QString cacheString = QStringLiteral( "%1:%2:%3:%4:%5" ).arg( database, table, idColumn, filterAttribute, filterValue.toString() ); |
| 1419 | + QString errorMessage; |
| 1420 | + QString currentValSql; |
1422 | 1421 |
|
1423 |
| - // Running in transaction mode, check for cached value first |
1424 |
| - if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() ) |
1425 |
| - { |
1426 |
| - cachedMode = true; |
| 1422 | + qlonglong nextId; |
| 1423 | + bool cachedMode = false; |
| 1424 | + bool valueRetrieved = false; |
1427 | 1425 |
|
1428 |
| - auto cachedCounter = counterCache.find( cacheString ); |
| 1426 | + QString cacheString = QStringLiteral( "%1:%2:%3:%4:%5" ).arg( database, table, idColumn, filterAttribute, filterValue.toString() ); |
1429 | 1427 |
|
1430 |
| - if ( cachedCounter != counterCache.end() ) |
| 1428 | + // Running in transaction mode, check for cached value first |
| 1429 | + if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() ) |
1431 | 1430 | {
|
1432 |
| - qlonglong &cachedValue = cachedCounter.value(); |
1433 |
| - nextId = cachedValue; |
1434 |
| - nextId += 1; |
1435 |
| - cachedValue = nextId; |
1436 |
| - valueRetrieved = true; |
1437 |
| - } |
1438 |
| - } |
| 1431 | + cachedMode = true; |
1439 | 1432 |
|
1440 |
| - // Either not in cached mode or no cached value found, obtain from DB |
1441 |
| - if ( !cachedMode || !valueRetrieved ) |
1442 |
| - { |
1443 |
| - int result = SQLITE_ERROR; |
| 1433 | + auto cachedCounter = counterCache.find( cacheString ); |
1444 | 1434 |
|
1445 |
| - currentValSql = QStringLiteral( "SELECT %1 FROM %2" ).arg( QgsSqliteUtils::quotedIdentifier( idColumn ), QgsSqliteUtils::quotedIdentifier( table ) ); |
1446 |
| - if ( !filterAttribute.isNull() ) |
1447 |
| - { |
1448 |
| - currentValSql += QStringLiteral( " WHERE %1 = %2" ).arg( QgsSqliteUtils::quotedIdentifier( filterAttribute ), QgsSqliteUtils::quotedValue( filterValue ) ); |
| 1435 | + if ( cachedCounter != counterCache.end() ) |
| 1436 | + { |
| 1437 | + qlonglong &cachedValue = cachedCounter.value(); |
| 1438 | + nextId = cachedValue; |
| 1439 | + nextId += 1; |
| 1440 | + cachedValue = nextId; |
| 1441 | + valueRetrieved = true; |
| 1442 | + } |
1449 | 1443 | }
|
1450 | 1444 |
|
1451 |
| - sqliteStatement = sqliteDb.prepare( currentValSql, result ); |
1452 |
| - |
1453 |
| - if ( result == SQLITE_OK ) |
| 1445 | + // Either not in cached mode or no cached value found, obtain from DB |
| 1446 | + if ( !cachedMode || !valueRetrieved ) |
1454 | 1447 | {
|
1455 |
| - nextId = 0; |
1456 |
| - if ( sqliteStatement.step() == SQLITE_ROW ) |
| 1448 | + int result = SQLITE_ERROR; |
| 1449 | + |
| 1450 | + currentValSql = QStringLiteral( "SELECT %1 FROM %2" ).arg( QgsSqliteUtils::quotedIdentifier( idColumn ), QgsSqliteUtils::quotedIdentifier( table ) ); |
| 1451 | + if ( !filterAttribute.isNull() ) |
1457 | 1452 | {
|
1458 |
| - nextId = sqliteStatement.columnAsInt64( 0 ) + 1; |
| 1453 | + currentValSql += QStringLiteral( " WHERE %1 = %2" ).arg( QgsSqliteUtils::quotedIdentifier( filterAttribute ), QgsSqliteUtils::quotedValue( filterValue ) ); |
1459 | 1454 | }
|
1460 | 1455 |
|
1461 |
| - // If in cached mode: add value to cache and connect to transaction |
1462 |
| - if ( cachedMode && result == SQLITE_OK ) |
| 1456 | + sqliteStatement = sqliteDb.prepare( currentValSql, result ); |
| 1457 | + |
| 1458 | + if ( result == SQLITE_OK ) |
1463 | 1459 | {
|
1464 |
| - counterCache.insert( cacheString, nextId ); |
| 1460 | + nextId = 0; |
| 1461 | + if ( sqliteStatement.step() == SQLITE_ROW ) |
| 1462 | + { |
| 1463 | + nextId = sqliteStatement.columnAsInt64( 0 ) + 1; |
| 1464 | + } |
1465 | 1465 |
|
1466 |
| - QObject::connect( layer->dataProvider()->transaction(), &QgsTransaction::destroyed, [cacheString]() |
| 1466 | + // If in cached mode: add value to cache and connect to transaction |
| 1467 | + if ( cachedMode && result == SQLITE_OK ) |
1467 | 1468 | {
|
1468 |
| - counterCache.remove( cacheString ); |
1469 |
| - } ); |
| 1469 | + counterCache.insert( cacheString, nextId ); |
| 1470 | + |
| 1471 | + QObject::connect( layer->dataProvider()->transaction(), &QgsTransaction::destroyed, [cacheString]() |
| 1472 | + { |
| 1473 | + counterCache.remove( cacheString ); |
| 1474 | + } ); |
| 1475 | + } |
| 1476 | + valueRetrieved = true; |
1470 | 1477 | }
|
1471 |
| - valueRetrieved = true; |
1472 | 1478 | }
|
1473 |
| - } |
1474 | 1479 |
|
1475 |
| - if ( valueRetrieved ) |
1476 |
| - { |
1477 |
| - QString upsertSql; |
1478 |
| - upsertSql = QStringLiteral( "INSERT OR REPLACE INTO %1" ).arg( QgsSqliteUtils::quotedIdentifier( table ) ); |
1479 |
| - QStringList cols; |
1480 |
| - QStringList vals; |
1481 |
| - cols << QgsSqliteUtils::quotedIdentifier( idColumn ); |
1482 |
| - vals << QgsSqliteUtils::quotedValue( nextId ); |
1483 |
| - |
1484 |
| - if ( !filterAttribute.isNull() ) |
| 1480 | + if ( valueRetrieved ) |
1485 | 1481 | {
|
1486 |
| - cols << QgsSqliteUtils::quotedIdentifier( filterAttribute ); |
1487 |
| - vals << QgsSqliteUtils::quotedValue( filterValue ); |
1488 |
| - } |
| 1482 | + QString upsertSql; |
| 1483 | + upsertSql = QStringLiteral( "INSERT OR REPLACE INTO %1" ).arg( QgsSqliteUtils::quotedIdentifier( table ) ); |
| 1484 | + QStringList cols; |
| 1485 | + QStringList vals; |
| 1486 | + cols << QgsSqliteUtils::quotedIdentifier( idColumn ); |
| 1487 | + vals << QgsSqliteUtils::quotedValue( nextId ); |
| 1488 | + |
| 1489 | + if ( !filterAttribute.isNull() ) |
| 1490 | + { |
| 1491 | + cols << QgsSqliteUtils::quotedIdentifier( filterAttribute ); |
| 1492 | + vals << QgsSqliteUtils::quotedValue( filterValue ); |
| 1493 | + } |
1489 | 1494 |
|
1490 |
| - for ( QVariantMap::const_iterator iter = defaultValues.constBegin(); iter != defaultValues.constEnd(); ++iter ) |
1491 |
| - { |
1492 |
| - cols << QgsSqliteUtils::quotedIdentifier( iter.key() ); |
1493 |
| - vals << iter.value().toString(); |
1494 |
| - } |
| 1495 | + for ( QVariantMap::const_iterator iter = defaultValues.constBegin(); iter != defaultValues.constEnd(); ++iter ) |
| 1496 | + { |
| 1497 | + cols << QgsSqliteUtils::quotedIdentifier( iter.key() ); |
| 1498 | + vals << iter.value().toString(); |
| 1499 | + } |
1495 | 1500 |
|
1496 |
| - upsertSql += QLatin1String( " (" ) + cols.join( ',' ) + ')'; |
1497 |
| - upsertSql += QLatin1String( " VALUES " ); |
1498 |
| - upsertSql += '(' + vals.join( ',' ) + ')'; |
| 1501 | + upsertSql += QLatin1String( " (" ) + cols.join( ',' ) + ')'; |
| 1502 | + upsertSql += QLatin1String( " VALUES " ); |
| 1503 | + upsertSql += '(' + vals.join( ',' ) + ')'; |
1499 | 1504 |
|
1500 |
| - int result = SQLITE_ERROR; |
1501 |
| - if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() ) |
1502 |
| - { |
1503 |
| - QgsTransaction *transaction = layer->dataProvider()->transaction(); |
1504 |
| - if ( transaction->executeSql( upsertSql, errorMessage ) ) |
| 1505 | + int result = SQLITE_ERROR; |
| 1506 | + if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() ) |
1505 | 1507 | {
|
1506 |
| - result = SQLITE_OK; |
| 1508 | + QgsTransaction *transaction = layer->dataProvider()->transaction(); |
| 1509 | + if ( transaction->executeSql( upsertSql, errorMessage ) ) |
| 1510 | + { |
| 1511 | + result = SQLITE_OK; |
| 1512 | + } |
| 1513 | + } |
| 1514 | + else |
| 1515 | + { |
| 1516 | + result = sqliteDb.exec( upsertSql, errorMessage ); |
| 1517 | + } |
| 1518 | + if ( result == SQLITE_OK ) |
| 1519 | + { |
| 1520 | + functionResult = QVariant( nextId ); |
| 1521 | + return; |
| 1522 | + } |
| 1523 | + else |
| 1524 | + { |
| 1525 | + parent->setEvalErrorString( QStringLiteral( "Could not increment value: SQLite error: \"%1\" (%2)." ).arg( errorMessage, QString::number( result ) ) ); |
| 1526 | + functionResult = QVariant(); |
| 1527 | + return; |
1507 | 1528 | }
|
1508 | 1529 | }
|
1509 |
| - else |
1510 |
| - { |
1511 |
| - result = sqliteDb.exec( upsertSql, errorMessage ); |
1512 |
| - } |
1513 |
| - if ( result == SQLITE_OK ) |
1514 |
| - { |
1515 |
| - return nextId; |
1516 |
| - } |
1517 |
| - else |
1518 |
| - { |
1519 |
| - parent->setEvalErrorString( QStringLiteral( "Could not increment value: SQLite error: \"%1\" (%2)." ).arg( errorMessage, QString::number( result ) ) ); |
1520 |
| - return QVariant(); |
1521 |
| - } |
1522 |
| - } |
1523 | 1530 |
|
1524 |
| - return QVariant(); // really? |
| 1531 | + functionResult = QVariant(); |
| 1532 | + }; |
| 1533 | + |
| 1534 | + QgsThreadingUtils::runOnMainThread( fetchAndIncrementFunc ); |
| 1535 | + |
| 1536 | + return functionResult; |
1525 | 1537 | }
|
1526 | 1538 |
|
1527 | 1539 | static QVariant fcnConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
|
|
0 commit comments