diff --git a/src/ImportCsvDialog.cpp b/src/ImportCsvDialog.cpp index 19ba88a9..79d90249 100644 --- a/src/ImportCsvDialog.cpp +++ b/src/ImportCsvDialog.cpp @@ -33,6 +33,9 @@ ImportCsvDialog::ImportCsvDialog(const QStringList &filenames, DBBrowserDB* db, { ui->setupUi(this); + // Hide "Advanced" section of the settings + toggleAdvancedSection(false); + // Get the actual file name out of the provided path and use it as the default table name for import // For importing several files at once, the fields have to be the same so we can safely use the first QFileInfo file(filenames.first()); @@ -53,7 +56,6 @@ ImportCsvDialog::ImportCsvDialog(const QStringList &filenames, DBBrowserDB* db, ui->comboSeparator->blockSignals(true); ui->comboQuote->blockSignals(true); ui->comboEncoding->blockSignals(true); - ui->comboMissingValues->blockSignals(true); ui->checkboxHeader->setChecked(Settings::getValue("importcsv", "firstrowheader").toBool()); ui->checkBoxTrimFields->setChecked(Settings::getValue("importcsv", "trimfields").toBool()); @@ -61,7 +63,6 @@ ImportCsvDialog::ImportCsvDialog(const QStringList &filenames, DBBrowserDB* db, setSeparatorChar(Settings::getValue("importcsv", "separator").toInt()); setQuoteChar(Settings::getValue("importcsv", "quotecharacter").toInt()); setEncoding(Settings::getValue("importcsv", "encoding").toString()); - setMissingValues(Settings::getValue("importcsv", "missingvalues").toString()); ui->checkboxHeader->blockSignals(false); ui->checkBoxTrimFields->blockSignals(false); @@ -69,7 +70,6 @@ ImportCsvDialog::ImportCsvDialog(const QStringList &filenames, DBBrowserDB* db, ui->comboSeparator->blockSignals(false); ui->comboQuote->blockSignals(false); ui->comboEncoding->blockSignals(false); - ui->comboMissingValues->blockSignals(false); // Prepare and show interface depending on how many files are selected if (csvFilenames.length() > 1) @@ -169,7 +169,6 @@ void ImportCsvDialog::accept() Settings::setValue("importcsv", "trimfields", ui->checkBoxTrimFields->isChecked()); Settings::setValue("importcsv", "separatetables", ui->checkBoxSeparateTables->isChecked()); Settings::setValue("importcsv", "encoding", currentEncoding()); - Settings::setValue("importcsv", "missingvalues", missingValues()); // Get all the selected files and start the import if (ui->filePickerBlock->isVisible()) @@ -540,7 +539,9 @@ bool ImportCsvDialog::importCsv(const QString& fileName, const QString& name) // Create table QVector nullValues; - bool missingValuesAsNull = missingValues() == "null"; + std::vector failOnMissingFieldList; + bool ignoreDefaults = ui->checkIgnoreDefaults->isChecked(); + bool failOnMissing = ui->checkFailOnMissing->isChecked(); if(!importToExistingTable) { if(!pdb->createTable(sqlb::ObjectIdentifier("main", tableName), fieldList)) @@ -562,14 +563,39 @@ bool ImportCsvDialog::importCsv(const QString& fileName, const QString& name) { for(const sqlb::FieldPtr& f : tbl->fields()) { - if(f->isInteger() && f->notnull()) // If this is an integer column but NULL isn't allowed, insert 0 - nullValues << "0"; - else if(f->isInteger() && !f->notnull()) // If this is an integer column and NULL is allowed, insert NULL - nullValues << QByteArray(); - else if(missingValuesAsNull) // If we requested NULL values, do that - nullValues << QByteArray(); - else // Otherwise, insert an empty string, like .import does - nullValues << ""; + // For determining the value for empty fields we follow a set of rules + + // Normally we don't have to fail the import when importing an empty field. This last value of the vector + // is changed to true later if we actually do want to fail the import for this field. + failOnMissingFieldList.push_back(false); + + // If a field has a default value, that gets priority over everything else. + // Exception: if the user wants to ignore default values we never use them. + if(!ignoreDefaults && !f->defaultValue().isNull()) + { + nullValues << f->defaultValue().toUtf8(); + } else { + // If it has no default value, check if the field is NOT NULL + if(f->notnull()) + { + // The field is NOT NULL + + // If this is an integer column insert 0. Otherwise insert an empty string. + if(f->isInteger()) + nullValues << "0"; + else + nullValues << ""; + + // If the user wants to fail the import, remember this field + if(failOnMissing) + failOnMissingFieldList.back() = true; + } else { + // The field is not NOT NULL (stupid double negation here! NULL values are allowed in this case) + + // Just insert a NULL value + nullValues << QByteArray(); + } + } } } } @@ -606,12 +632,16 @@ bool ImportCsvDialog::importCsv(const QString& fileName, const QString& name) // When importing into an existing table where we could find out something about its table definition if(importToExistingTable && data.fields[i].data_length == 0 && static_cast(nullValues.size()) > i) { + // Do we want to fail when trying to import an empty value into this field? Then exit with an error. + if(failOnMissingFieldList.at(i)) + return false; + // This is an empty value. We'll need to look up how to handle it depending on the field to be inserted into. const QByteArray& val = nullValues.at(i); if(!val.isNull()) // No need to bind NULL values here as that is the default bound value in SQLite sqlite3_bind_text(stmt, i+1, val, val.size(), SQLITE_STATIC); // When importing into a new table, use the missing values setting directly - } else if(!importToExistingTable && data.fields[i].data_length == 0 && missingValuesAsNull) { + } else if(!importToExistingTable && data.fields[i].data_length == 0) { // No need to bind NULL values here as that is the default bound value in SQLite } else { // This is a non-empty value, or we want to insert the empty string. Just add it to the statement @@ -742,18 +772,10 @@ QString ImportCsvDialog::currentEncoding() const return ui->comboEncoding->currentText(); } -void ImportCsvDialog::setMissingValues(const QString& sValue) +void ImportCsvDialog::toggleAdvancedSection(bool show) { - if(sValue == "null") - ui->comboMissingValues->setCurrentIndex(1); - else - ui->comboMissingValues->setCurrentIndex(0); -} - -QString ImportCsvDialog::missingValues() const -{ - if(ui->comboMissingValues->currentIndex() == 0) - return "emptystring"; - else - return "null"; + ui->labelFailOnMissing->setVisible(show); + ui->checkFailOnMissing->setVisible(show); + ui->labelIgnoreDefaults->setVisible(show); + ui->checkIgnoreDefaults->setVisible(show); } diff --git a/src/ImportCsvDialog.h b/src/ImportCsvDialog.h index af6c5fac..e74c5fe4 100644 --- a/src/ImportCsvDialog.h +++ b/src/ImportCsvDialog.h @@ -31,6 +31,7 @@ private slots: void updateSelectedFilePreview(); void updateSelection(bool); void matchSimilar(); + void toggleAdvancedSection(bool show); private: Ui::ImportCsvDialog* ui; @@ -53,9 +54,6 @@ private: void setEncoding(const QString& sEnc); QString currentEncoding() const; - - void setMissingValues(const QString& sValue); - QString missingValues() const; }; #endif diff --git a/src/ImportCsvDialog.ui b/src/ImportCsvDialog.ui index 01ce8453..fe24d725 100644 --- a/src/ImportCsvDialog.ui +++ b/src/ImportCsvDialog.ui @@ -7,13 +7,13 @@ 0 0 788 - 637 + 717 Import CSV file - + @@ -22,7 +22,7 @@ - &Table name + Table na&me editName @@ -49,7 +49,7 @@ - + Field &separator @@ -59,7 +59,7 @@ - + @@ -112,7 +112,7 @@ - + &Quote character @@ -122,7 +122,7 @@ - + @@ -170,7 +170,7 @@ - + &Encoding @@ -180,7 +180,7 @@ - + @@ -224,52 +224,17 @@ - - - - Missing values - - - - - - - - - - Empty string - - - - - NULL - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - + Trim fields? + + checkBoxTrimFields + - + @@ -279,20 +244,71 @@ - + Separate tables + + checkBoxSeparateTables + - + + + + + Advanced + + + + :/icons/down:/icons/down + + + true + + + + + + + When importing an empty value from the CSV file into an existing table with a default value for this column, that default value is inserted. Activate this option to insert an empty value instead. + + + + + + + Ignore default &values + + + checkIgnoreDefaults + + + + + + + Activate this option to stop the import when trying to import an empty value into a NOT NULL column without a default value. + + + + + + + Fail on missing values + + + checkFailOnMissing + + + @@ -400,12 +416,17 @@ editCustomEncoding checkBoxTrimFields checkBoxSeparateTables + buttonAdvanced + checkIgnoreDefaults + checkFailOnMissing filePicker toggleSelected matchSimilar tablePreview - + + + comboSeparator @@ -510,8 +531,8 @@ updatePreview() - 266 - 165 + 263 + 183 572 @@ -542,8 +563,8 @@ updateSelection(bool) - 674 - 258 + 780 + 337 368 @@ -575,7 +596,7 @@ 272 - 478 + 677 157 @@ -591,7 +612,7 @@ 340 - 478 + 677 286 @@ -606,8 +627,8 @@ checkInput() - 176 - 216 + 194 + 236 368 @@ -622,8 +643,8 @@ matchSimilar() - 682 - 279 + 780 + 378 368 @@ -631,11 +652,28 @@ + + buttonAdvanced + toggled(bool) + ImportCsvDialog + toggleAdvancedSection(bool) + + + 214 + 259 + + + 393 + 358 + + + updatePreview() checkInput() updateSelection(bool) matchSimilar() + toggleAdvancedSection(bool)