Files
sqlitebrowser/src/ImportCsvDialog.cpp
Martin Kleusberg b9afbe2aea EditTableDialog: Allow some more changes to existing tables
Allow setting the default value, the check values and under some
circumstances the not null flag when editing existing tables.

This required some changes to DBBrowserDB::renameColumn() which is now
using some more features of sqlitetypes.cpp but could still be improved.
2013-05-31 16:13:58 +02:00

218 lines
6.8 KiB
C++

#include "ImportCsvDialog.h"
#include "ui_ImportCsvDialog.h"
#include <QMessageBox>
#include <QProgressDialog>
#include <QPushButton>
#include <QDateTime>
#include "sqlitedb.h"
ImportCsvDialog::ImportCsvDialog(const QString& filename, DBBrowserDB* db, QWidget* parent)
: QDialog(parent),
ui(new Ui::ImportCsvDialog),
csvFilename(filename),
pdb(db)
{
ui->setupUi(this);
checkInput();
updatePreview();
}
ImportCsvDialog::~ImportCsvDialog()
{
delete ui;
}
namespace {
void rollback(ImportCsvDialog* dialog, DBBrowserDB* pdb, QProgressDialog& progress, const QString& savepointName)
{
progress.hide();
QApplication::restoreOverrideCursor(); // restore original cursor
QString error = QObject::tr("Error importing data. Message from database engine: %1").arg(pdb->lastErrorMessage);
QMessageBox::warning(dialog, QApplication::applicationName(), error);
pdb->revert(savepointName);
}
}
void ImportCsvDialog::accept()
{
QString sql;
// Parse all csv data
int numfields;
QStringList curList = pdb->decodeCSV(csvFilename, currentSeparatorChar(), currentQuoteChar(), -1, &numfields);
// Can not operate on an empty result
if(numfields == 0)
return;
// Generate field names. These are either taken from the first CSV row or are generated in the format of "fieldXY" depending on the user input
QStringList fieldList;
if(ui->checkboxHeader->isChecked())
{
int cfieldnum = 0;
while(!curList.empty() && cfieldnum != numfields)
{
// Remove invalid characters
QString thisfield = curList.front();
thisfield.replace(" ", "");
thisfield.replace('"', "");
thisfield.replace("'","");
thisfield.replace(",","");
thisfield.replace(";","");
// Avoid empty field names
if(thisfield.isEmpty())
thisfield = QString("field%1").arg(cfieldnum+1);
fieldList.push_back(thisfield);
cfieldnum++;
curList.pop_front();
}
} else {
for(int i=0;i<numfields;i++)
fieldList.push_back(QString("field%1").arg(i+1));
}
// Show progress dialog
QProgressDialog progress(tr("Inserting data..."), tr("Cancel"), 0, curList.size());
progress.setWindowModality(Qt::ApplicationModal);
// declare local variables we will need before the rollback jump
int colNum = 0;
// Are we importing into an existing table?
bool importToExistingTable = false;
objectMap objects = pdb->getBrowsableObjects();
for(objectMap::ConstIterator i=objects.begin();i!=objects.end();++i)
{
if(i.value().gettype() == "table" && i.value().getname() == ui->editName->text())
{
if(i.value().fldmap.size() != numfields)
{
QMessageBox::warning(this, QApplication::applicationName(),
tr("There is already a table of that name and an import into an existing table is only possible if the number of columns match."));
return;
} else {
if(QMessageBox::question(this, QApplication::applicationName(), tr("There is already a table of that name. Do you want to import the data into it?"), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes)
{
importToExistingTable = true;
break;
} else {
return;
}
}
}
}
// Create a savepoint, so we can rollback in case of any errors during importing
// db needs to be saved or an error will occur
QString restorepointName = QString("CSVIMPORT_%1").arg(QDateTime::currentMSecsSinceEpoch());
if(!pdb->setRestorePoint(restorepointName))
return rollback(this, pdb, progress, restorepointName);
// Create table
if(!importToExistingTable)
{
if(!pdb->createTable(ui->editName->text(), fieldList))
return rollback(this, pdb, progress, restorepointName);
}
// now lets import all data, one row at a time
for(int i=0;i<curList.size();++i)
{
if(colNum == 0)
sql = QString("INSERT INTO `%1` VALUES(").arg(ui->editName->text());
// need to mprintf here
char* formSQL = sqlite3_mprintf("%Q", (const char*)curList[i].toUtf8());
sql.append(formSQL);
if(formSQL)
sqlite3_free(formSQL);
colNum++;
if(colNum < numfields)
{
sql.append(",");
} else {
colNum = 0;
sql.append(");");
if(!pdb->executeSQL(sql, false, false))
return rollback(this, pdb, progress, restorepointName);
}
progress.setValue(i);
if(progress.wasCanceled())
return rollback(this, pdb, progress, restorepointName);
}
QApplication::restoreOverrideCursor(); // restore original cursor
QDialog::accept();
}
void ImportCsvDialog::updatePreview()
{
// Get preview data
int numfields;
int maxrecs = 20;
QStringList curList = pdb->decodeCSV(csvFilename, currentSeparatorChar(), currentQuoteChar(), maxrecs, &numfields);
// Reset preview widget
ui->tablePreview->clear();
ui->tablePreview->setColumnCount(numfields);
// Exit if there are no lines to preview at all
if(numfields == 0)
return;
// Use first row as header if necessary
if(ui->checkboxHeader->isChecked())
{
ui->tablePreview->setHorizontalHeaderLabels(curList);
// Remove this row to not show it in the data section
for(int e=0;e<numfields;e++)
curList.pop_front();
}
// Fill data section
ui->tablePreview->setRowCount(curList.count() / numfields);
int rowNum = 0;
int colNum = 0;
for(QStringList::Iterator ct=curList.begin();ct!=curList.end();++ct)
{
if(colNum == 0)
ui->tablePreview->setVerticalHeaderItem(rowNum, new QTableWidgetItem(QString::number(rowNum + 1)));
ui->tablePreview->setItem(rowNum, colNum, new QTableWidgetItem(*ct));
colNum++;
if(colNum == numfields)
{
colNum = 0;
rowNum++;
}
}
}
void ImportCsvDialog::checkInput()
{
ui->editName->setText(ui->editName->text().trimmed());
bool valid = true;
if(ui->editName->text().isEmpty() || ui->editName->text().contains(" "))
valid = false;
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(valid);
}
char ImportCsvDialog::currentQuoteChar()
{
if(ui->comboQuote->currentText().length())
return ui->comboQuote->currentText().at(0).toAscii();
else
return 0;
}
char ImportCsvDialog::currentSeparatorChar()
{
return ui->comboSeparator->currentText() == tr("Tab") ? '\t' : ui->comboSeparator->currentText().at(0).toAscii();
}