Skip to content

Commit b6c0560

Browse files
committed
Change order of statements in exported SQL file
When exporting a database to an SQL file we used to export it like this: - Table 1 schema - Table 1 data - Table 2 schema - Table 2 data With this commit that is changed like this: - Table 1 schema - Table 2 schema - Table 1 data - Table 2 data This makes the resulting SQL file more robust for import because it avoids any foreign key errors as long as foreign keys are deferred.
1 parent b384027 commit b6c0560

File tree

2 files changed

+98
-96
lines changed

2 files changed

+98
-96
lines changed

src/sqlitedb.cpp

Lines changed: 97 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,7 @@ bool DBBrowserDB::close()
488488
}
489489

490490
bool DBBrowserDB::dump(const QString& filename,
491-
const QStringList & tablesToDump,
491+
const QStringList& tablesToDump,
492492
bool insertColNames,
493493
bool insertNewSyntx,
494494
bool exportSchema,
@@ -501,6 +501,7 @@ bool DBBrowserDB::dump(const QString& filename,
501501
{
502502
QApplication::setOverrideCursor(Qt::WaitCursor);
503503

504+
// Count the total number of all records in all tables for the progress dialog
504505
size_t numRecordsTotal = 0, numRecordsCurrent = 0;
505506
objectMap objMap = schemata["main"]; // We only always export the main database, not the attached databases
506507
QList<sqlb::ObjectPtr> tables = objMap.values("table");
@@ -509,8 +510,8 @@ bool DBBrowserDB::dump(const QString& filename,
509510
{
510511
it.next();
511512

512-
// Remove the sqlite_stat1 table if there is one
513-
if(it.value()->name() == "sqlite_stat1" || it.value()->name() == "sqlite_sequence")
513+
// Remove the sqlite_stat1 and the sqlite_sequence tables if they exist. Also remove any tables which are not selected for export.
514+
if(it.value()->name() == "sqlite_stat1" || it.value()->name() == "sqlite_sequence" || !tablesToDump.contains(it.value()->name()))
514515
{
515516
it.remove();
516517
} else {
@@ -533,140 +534,141 @@ bool DBBrowserDB::dump(const QString& filename,
533534
// Put the SQL commands in a transaction block
534535
stream << "BEGIN TRANSACTION;\n";
535536

536-
// Loop through all tables first as they are required to generate views, indices etc. later
537-
for(auto it=tables.constBegin();it!=tables.constEnd();++it)
537+
// First export the schema of all selected tables. We need to export the schema of all tables before we export the first INSERT statement to
538+
// make sure foreign keys are working properly.
539+
if(exportSchema)
538540
{
539-
if (tablesToDump.indexOf((*it)->name()) == -1)
540-
continue;
541-
542-
// Write the SQL string used to create this table to the output file
543-
if(exportSchema)
541+
for(auto it : tables)
544542
{
543+
// Write the SQL string used to create this table to the output file
545544
if(!keepOldSchema)
546-
stream << QString("DROP TABLE IF EXISTS %1;\n").arg(sqlb::escapeIdentifier((*it)->name()));
545+
stream << QString("DROP TABLE IF EXISTS %1;\n").arg(sqlb::escapeIdentifier(it->name()));
547546

548-
if((*it)->fullyParsed())
549-
stream << (*it)->sql("main", true) << "\n";
547+
if(it->fullyParsed())
548+
stream << it->sql("main", true) << "\n";
550549
else
551-
stream << (*it)->originalSql() << ";\n";
552-
}
553-
554-
// If the user doesn't want the data to be exported skip the rest of the loop block here
555-
if(!exportData)
556-
continue;
557-
558-
// get columns
559-
QStringList cols((*it).dynamicCast<sqlb::Table>()->fieldNames());
560-
561-
QString sQuery = QString("SELECT * FROM %1;").arg(sqlb::escapeIdentifier((*it)->name()));
562-
QByteArray utf8Query = sQuery.toUtf8();
563-
sqlite3_stmt *stmt;
564-
QString lineSep(QString(")%1\n").arg(insertNewSyntx?',':';'));
550+
stream << it->originalSql() << ";\n";
551+
}
552+
}
565553

566-
int status = sqlite3_prepare_v2(this->_db, utf8Query.data(), utf8Query.size(), &stmt, NULL);
567-
if(SQLITE_OK == status)
554+
// Now export the data as well
555+
if(exportData)
556+
{
557+
for(auto it : tables)
568558
{
569-
int columns = sqlite3_column_count(stmt);
570-
size_t counter = 0;
571-
qApp->processEvents();
572-
while(sqlite3_step(stmt) == SQLITE_ROW)
573-
{
574-
if (counter) stream << lineSep;
559+
// get columns
560+
QStringList cols(it.dynamicCast<sqlb::Table>()->fieldNames());
575561

576-
if (!insertNewSyntx || !counter)
577-
{
578-
stream << "INSERT INTO " << sqlb::escapeIdentifier((*it)->name());
579-
if (insertColNames)
580-
stream << " (" << cols.join(",") << ")";
581-
stream << " VALUES (";
582-
}
583-
else
584-
{
585-
stream << " (";
586-
}
562+
QString sQuery = QString("SELECT * FROM %1;").arg(sqlb::escapeIdentifier(it->name()));
563+
QByteArray utf8Query = sQuery.toUtf8();
564+
sqlite3_stmt *stmt;
565+
QString lineSep(QString(")%1\n").arg(insertNewSyntx?',':';'));
587566

588-
for (int i = 0; i < columns; ++i)
567+
int status = sqlite3_prepare_v2(this->_db, utf8Query.data(), utf8Query.size(), &stmt, NULL);
568+
if(SQLITE_OK == status)
569+
{
570+
int columns = sqlite3_column_count(stmt);
571+
size_t counter = 0;
572+
qApp->processEvents();
573+
while(sqlite3_step(stmt) == SQLITE_ROW)
589574
{
590-
int fieldsize = sqlite3_column_bytes(stmt, i);
591-
int fieldtype = sqlite3_column_type(stmt, i);
592-
QByteArray bcontent(
593-
(const char*)sqlite3_column_blob(stmt, i),
594-
fieldsize);
575+
if (counter) stream << lineSep;
595576

596-
if(bcontent.left(2048).contains('\0')) // binary check
577+
if (!insertNewSyntx || !counter)
597578
{
598-
stream << QString("X'%1'").arg(QString(bcontent.toHex()));
579+
stream << "INSERT INTO " << sqlb::escapeIdentifier(it->name());
580+
if (insertColNames)
581+
stream << " (" << cols.join(",") << ")";
582+
stream << " VALUES (";
599583
}
600584
else
601585
{
602-
switch(fieldtype)
586+
stream << " (";
587+
}
588+
589+
for (int i = 0; i < columns; ++i)
590+
{
591+
int fieldsize = sqlite3_column_bytes(stmt, i);
592+
int fieldtype = sqlite3_column_type(stmt, i);
593+
QByteArray bcontent(
594+
(const char*)sqlite3_column_blob(stmt, i),
595+
fieldsize);
596+
597+
if(bcontent.left(2048).contains('\0')) // binary check
603598
{
604-
case SQLITE_TEXT:
605-
case SQLITE_BLOB:
606-
stream << "'" << bcontent.replace("'", "''") << "'";
607-
break;
608-
case SQLITE_NULL:
609-
stream << "NULL";
610-
break;
611-
case SQLITE_FLOAT:
612-
if(bcontent.indexOf("Inf") != -1)
613-
stream << "'" << bcontent << "'";
614-
else
599+
stream << QString("X'%1'").arg(QString(bcontent.toHex()));
600+
}
601+
else
602+
{
603+
switch(fieldtype)
604+
{
605+
case SQLITE_TEXT:
606+
case SQLITE_BLOB:
607+
stream << "'" << bcontent.replace("'", "''") << "'";
608+
break;
609+
case SQLITE_NULL:
610+
stream << "NULL";
611+
break;
612+
case SQLITE_FLOAT:
613+
if(bcontent.indexOf("Inf") != -1)
614+
stream << "'" << bcontent << "'";
615+
else
616+
stream << bcontent;
617+
break;
618+
default:
615619
stream << bcontent;
616-
break;
617-
default:
618-
stream << bcontent;
620+
}
619621
}
622+
if(i != columns - 1)
623+
stream << ',';
620624
}
621-
if(i != columns - 1)
622-
stream << ',';
623-
}
624625

625-
progress.setValue(++numRecordsCurrent);
626-
if(counter % 5000 == 0)
627-
qApp->processEvents();
628-
counter++;
626+
progress.setValue(++numRecordsCurrent);
627+
if(counter % 5000 == 0)
628+
qApp->processEvents();
629+
counter++;
629630

630-
if(progress.wasCanceled())
631-
{
632-
sqlite3_finalize(stmt);
633-
file.close();
634-
file.remove();
635-
QApplication::restoreOverrideCursor();
636-
return false;
631+
if(progress.wasCanceled())
632+
{
633+
sqlite3_finalize(stmt);
634+
file.close();
635+
file.remove();
636+
QApplication::restoreOverrideCursor();
637+
return false;
638+
}
637639
}
640+
if (counter > 0) stream << ");\n";
638641
}
639-
if (counter > 0) stream << ");\n";
642+
sqlite3_finalize(stmt);
640643
}
641-
sqlite3_finalize(stmt);
642644
}
643645

644-
// Now dump all the other objects (but only if we are exporting the schema)
646+
// Finally export all objects other than tables
645647
if(exportSchema)
646648
{
647-
for(auto it=objMap.constBegin();it!=objMap.constEnd();++it)
649+
for(auto it : objMap)
648650
{
649651
// Make sure it's not a table again
650-
if(it.value()->type() == sqlb::Object::Types::Table)
652+
if(it->type() == sqlb::Object::Types::Table)
651653
continue;
652654

653655
// If this object is based on a table (e.g. is an index for that table) it depends on the existence of this table.
654656
// So if we didn't export the base table this depends on, don't export this object either.
655-
if(!(*it)->baseTable().isEmpty() && !tablesToDump.contains((*it)->baseTable()))
657+
if(!it->baseTable().isEmpty() && !tablesToDump.contains(it->baseTable()))
656658
continue;
657659

658660
// Write the SQL string used to create this object to the output file
659-
if(!(*it)->originalSql().isEmpty())
661+
if(!it->originalSql().isEmpty())
660662
{
661663
if(!keepOldSchema)
662664
stream << QString("DROP %1 IF EXISTS %2;\n")
663-
.arg(sqlb::Object::typeToString((*it)->type()).toUpper())
664-
.arg(sqlb::escapeIdentifier((*it)->name()));
665+
.arg(sqlb::Object::typeToString(it->type()).toUpper())
666+
.arg(sqlb::escapeIdentifier(it->name()));
665667

666-
if((*it)->fullyParsed())
667-
stream << (*it)->sql("main", true) << "\n";
668+
if(it->fullyParsed())
669+
stream << it->sql("main", true) << "\n";
668670
else
669-
stream << (*it)->originalSql() << ";\n";
671+
stream << it->originalSql() << ";\n";
670672
}
671673
}
672674
}

src/sqlitedb.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class DBBrowserDB : public QObject
3737
bool revertToSavepoint(const QString& pointname = "RESTOREPOINT");
3838
bool releaseAllSavepoints();
3939
bool revertAll();
40-
bool dump(const QString & filename, const QStringList &tablesToDump, bool insertColNames, bool insertNew, bool exportSchema, bool exportData, bool keepOldSchema);
40+
bool dump(const QString& filename, const QStringList& tablesToDump, bool insertColNames, bool insertNew, bool exportSchema, bool exportData, bool keepOldSchema);
4141
bool executeSQL(QString statement, bool dirtyDB = true, bool logsql = true);
4242
bool executeMultiSQL(const QString& statement, bool dirty = true, bool log = false);
4343
const QString& lastError() const { return lastErrorMessage; }

0 commit comments

Comments
 (0)