diff --git a/docs/faq.html b/docs/faq.html
index fdcf57c5d..d1a8c61ef 100644
--- a/docs/faq.html
+++ b/docs/faq.html
@@ -212,7 +212,7 @@
Minimum length of all paths from the row back to
any subject row.
- The distance of subject rows is 0. |
+ The distance of a subject row is 0.
@@ -351,8 +351,7 @@
key is associated with the subject table. In this case, you
should define a key of the table in the tool's data
model (not in the database!) manually, using the data
- model editor. Note that a key must be unique and known (i.e.
- not NULL).
+ model editor. Note that a key must be unique.
(On Oracle, however, rowid-pseudo columns can be used
instead of primary keys)
diff --git a/src/main/engine/net/sf/jailer/database/PrimaryKeyValidator.java b/src/main/engine/net/sf/jailer/database/PrimaryKeyValidator.java
index 2db12ef2b..23d3cf15d 100644
--- a/src/main/engine/net/sf/jailer/database/PrimaryKeyValidator.java
+++ b/src/main/engine/net/sf/jailer/database/PrimaryKeyValidator.java
@@ -137,7 +137,7 @@ public abstract class PrimaryKeyValidator {
private void throwIfErrorFound() throws SqlException {
if (errorMessage.length() > 0) {
- SqlException e = new SqlException(errorMessage.toString(), errorStatements.toString(), null);
+ SqlException e = new SqlException("Invalid Primary Key", errorMessage.toString(), errorStatements.toString(), null);
e.setFormatted(true);
throw e;
}
@@ -151,7 +151,7 @@ public abstract class PrimaryKeyValidator {
}
pks.append(quoting.requote(pkCol.name));
}
- final String sql = "Select * from " + quoting.requote(table.getName()) + " " +
+ final String sql = "Select " + pks + " from " + quoting.requote(table.getName()) + " " +
"Group by " + pks + " having count(*) > 1";
try {
session.executeQuery(sql, new Session.AbstractResultSetReader() {
@@ -159,7 +159,7 @@ public abstract class PrimaryKeyValidator {
public void readCurrentRow(ResultSet resultSet) throws SQLException {
addError("Primary key of table \"" + table.getName() + "\" is not unique.", sql.toString());
}
- }, null, cancellationContext, 1);
+ }, null, cancellationContext, 1, true);
} catch (SqlException e) {
addError("Table \"" + table.getName() + "\": " + e.message, sql.toString());
}
@@ -184,7 +184,7 @@ public abstract class PrimaryKeyValidator {
public void readCurrentRow(ResultSet resultSet) throws SQLException {
addError("Primary key of table \"" + table.getName() + "\" contains null.", sql.toString());
}
- }, null, cancellationContext, 1);
+ }, null, cancellationContext, 1, true);
} catch (SqlException e) {
addError("Table \"" + table.getName() + "\": " + e.message, sql.toString());
}
diff --git a/src/main/engine/net/sf/jailer/database/Session.java b/src/main/engine/net/sf/jailer/database/Session.java
index 98c6ab385..6a72378c1 100644
--- a/src/main/engine/net/sf/jailer/database/Session.java
+++ b/src/main/engine/net/sf/jailer/database/Session.java
@@ -463,8 +463,8 @@ public class Session {
* @param sqlQuery the query in SQL
* @param reader the reader for the result
* @param alternativeSQL query to be executed if sqlQuery fails
- * @param limit row limit, 0 for unlimited
* @param context cancellation context
+ * @param limit row limit, 0 for unlimited
* @param withExplicitCommit if true, switch of autocommit and commit explicitly
*/
public long executeQuery(String sqlQuery, ResultSetReader reader, String alternativeSQL, Object context, int limit, boolean withExplicitCommit) throws SQLException {
@@ -477,8 +477,8 @@ public class Session {
* @param sqlQuery the query in SQL
* @param reader the reader for the result
* @param alternativeSQL query to be executed if sqlQuery fails
- * @param limit row limit, 0 for unlimited
* @param context cancellation context
+ * @param limit row limit, 0 for unlimited
*/
public long executeQuery(String sqlQuery, ResultSetReader reader, String alternativeSQL, Object context, int limit) throws SQLException {
return executeQuery(sqlQuery, reader, alternativeSQL, context, limit, 0, false);
@@ -491,8 +491,8 @@ public class Session {
* @param sqlQuery the query in SQL
* @param reader the reader for the result
* @param alternativeSQL query to be executed if sqlQuery fails
- * @param limit row limit, 0 for unlimited
* @param context cancellation context
+ * @param limit row limit, 0 for unlimited
* @param timeout the timeout in sec
* @param withExplicitCommit if true, switch of autocommit and commit explicitly
*/
diff --git a/src/main/engine/net/sf/jailer/database/SqlException.java b/src/main/engine/net/sf/jailer/database/SqlException.java
index 8bef8ee84..5e4543039 100644
--- a/src/main/engine/net/sf/jailer/database/SqlException.java
+++ b/src/main/engine/net/sf/jailer/database/SqlException.java
@@ -25,12 +25,18 @@ import java.sql.SQLException;
public class SqlException extends SQLException {
public final String message;
+ public final String errorDialogTitle;
public final String sqlStatement;
private boolean insufficientPrivileges = false;
private boolean isFormatted = false;
-
+
public SqlException(String message, String sqlStatement, Throwable t) {
+ this(null, message, sqlStatement, t);
+ }
+
+ public SqlException(String errorDialogTitle, String message, String sqlStatement, Throwable t) {
super(message, t);
+ this.errorDialogTitle = errorDialogTitle;
this.message = t == null? message : t.getMessage();
this.sqlStatement = sqlStatement;
}
diff --git a/src/main/engine/net/sf/jailer/datamodel/PrimaryKey.java b/src/main/engine/net/sf/jailer/datamodel/PrimaryKey.java
index 1a7284c4f..0fdee41a7 100644
--- a/src/main/engine/net/sf/jailer/datamodel/PrimaryKey.java
+++ b/src/main/engine/net/sf/jailer/datamodel/PrimaryKey.java
@@ -72,12 +72,10 @@ public class PrimaryKey {
* @return a match of all columns of primaryKey
*/
public Map match(PrimaryKey primaryKey) {
-
- // TODO check completeness of matching
-
- if (Configuration.getInstance().getDoMinimizeUPK() || !primaryKey.needsOrderedMatch) {
+ Map match = new HashMap();
+ boolean minimize = Configuration.getInstance().getDoMinimizeUPK() || !primaryKey.needsOrderedMatch;
+ if (minimize) {
Set assignedUPKColumns = new HashSet();
- Map match = new HashMap();
for (Column column: getColumns()) {
for (int i = 0; i < primaryKey.getColumns().size(); ++i) {
if (assignedUPKColumns.contains(i)) {
@@ -94,9 +92,7 @@ public class PrimaryKey {
}
}
}
- return match;
} else {
- Map match = new HashMap();
int i = 0;
for (Column column: getColumns()) {
if (i >= primaryKey.getColumns().size()) {
@@ -111,8 +107,16 @@ public class PrimaryKey {
}
}
}
- return match;
}
+
+ if (match.size() != primaryKey.columns.size()) {
+ throw new IllegalStateException("Incomplete pk-upk-match. (" + minimize + ")\n"
+ + "PK: " + primaryKey.toSQL(null) + "\n"
+ + "UPK: " + toSQL(null) + "\n"
+ + "Match: " + match + "\n");
+ }
+
+ return match;
}
public static boolean isAssignable(Column uPKColumn, Column entityColumn) {
diff --git a/src/main/engine/net/sf/jailer/subsetting/SubsettingEngine.java b/src/main/engine/net/sf/jailer/subsetting/SubsettingEngine.java
index a1a291f68..4a1119485 100644
--- a/src/main/engine/net/sf/jailer/subsetting/SubsettingEngine.java
+++ b/src/main/engine/net/sf/jailer/subsetting/SubsettingEngine.java
@@ -1475,9 +1475,9 @@ public class SubsettingEngine {
if (scriptFile != null && scriptFormat != ScriptFormat.XML && exportStatistic.getTotal() != exportedCount) {
String message =
"The number of rows collected (" + exportStatistic.getTotal() + ") differs from that of the exported ones (" + exportedCount + ").\n" +
- "This may have been caused by an invalid primary key definition.\nPlease note that each primary key must be unique and never null.\n" +
+ "This may have been caused by an invalid primary key definition.\nPlease note that each primary key must be unique.\n" +
"It is recommended to check the integrity of the primary keys.\n" +
- "To do this, use the cli/api-argument \"-check-primary-keys\".";
+ "To do this, use the menu item \"Check primary keys\" in the menu called \"DataModel\".";
if (executionContext.isAbortInCaseOfInconsistency()) {
throw new InconsistentSubsettingResultException(message);
} else {
diff --git a/src/main/gui/net/sf/jailer/ui/ExportDialog.java b/src/main/gui/net/sf/jailer/ui/ExportDialog.java
index 9355cf8eb..45326473d 100644
--- a/src/main/gui/net/sf/jailer/ui/ExportDialog.java
+++ b/src/main/gui/net/sf/jailer/ui/ExportDialog.java
@@ -2307,7 +2307,6 @@ public abstract class ExportDialog extends javax.swing.JDialog {
}//GEN-LAST:event_independentWorkingTablesActionPerformed
private void insertActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_insertActionPerformed
- // TODO add your handling code here:
}//GEN-LAST:event_insertActionPerformed
public boolean isOk() {
diff --git a/src/main/gui/net/sf/jailer/ui/SqlErrorDialog.java b/src/main/gui/net/sf/jailer/ui/SqlErrorDialog.java
index 0cbd5ecc7..8a7479623 100644
--- a/src/main/gui/net/sf/jailer/ui/SqlErrorDialog.java
+++ b/src/main/gui/net/sf/jailer/ui/SqlErrorDialog.java
@@ -116,6 +116,10 @@ public class SqlErrorDialog extends javax.swing.JDialog {
sendButton.addKeyListener(keyListener);
jButton1.setVisible(false);
}
+ } else {
+ if (title != null) {
+ setTitle(title);
+ }
}
if (sendButton.isVisible() && UpdateInfoManager.currentDownloadableRelease != null) {
diff --git a/src/main/gui/net/sf/jailer/ui/TableEditor.form b/src/main/gui/net/sf/jailer/ui/TableEditor.form
index c0669d321..e6bb16d6a 100644
--- a/src/main/gui/net/sf/jailer/ui/TableEditor.form
+++ b/src/main/gui/net/sf/jailer/ui/TableEditor.form
@@ -392,21 +392,46 @@
-
+
-
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/gui/net/sf/jailer/ui/TableEditor.java b/src/main/gui/net/sf/jailer/ui/TableEditor.java
index 99e3c6cde..2729b35a1 100644
--- a/src/main/gui/net/sf/jailer/ui/TableEditor.java
+++ b/src/main/gui/net/sf/jailer/ui/TableEditor.java
@@ -72,13 +72,18 @@ public abstract class TableEditor extends javax.swing.JDialog {
private void updateTable(List model) {
List pkColumns = new ArrayList();
+ hasNullablePKColumn = false;
for (ColumnModel cm: model) {
if (cm.isPk) {
pkColumns.add(cm.column);
+ if (cm.column.isNullable) {
+ hasNullablePKColumn = true;
+ }
}
}
PrimaryKey primaryKey = new PrimaryKeyFactory(null).createPrimaryKey(pkColumns, null);
table = new Table(nameField.getText().trim(), primaryKey, false, false);
+ updateWarningPanel();
}
@SuppressWarnings("serial")
@@ -176,7 +181,8 @@ public abstract class TableEditor extends javax.swing.JDialog {
c.isNullable = columnIsNullable.isSelected();
element.column = c;
if (element.isPk != primaryKey1.isSelected()) {
- pkChangedWarning();
+ pkChanged = true;
+ updateWarningPanel();
}
element.isPk = primaryKey1.isSelected();
}
@@ -294,7 +300,9 @@ public abstract class TableEditor extends javax.swing.JDialog {
jLabel7 = new javax.swing.JLabel();
displayName = new javax.swing.JTextField();
warnPanel = new javax.swing.JPanel();
- jLabel12 = new javax.swing.JLabel();
+ warnPKChangedLabel = new javax.swing.JLabel();
+ warnSeparator = new javax.swing.JSeparator();
+ warnNullablePKLabel = new javax.swing.JLabel();
columnDetailsPanel.setLayout(new java.awt.GridBagLayout());
@@ -649,14 +657,33 @@ public abstract class TableEditor extends javax.swing.JDialog {
warnPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Warning"));
warnPanel.setLayout(new java.awt.GridBagLayout());
- jLabel12.setFont(jLabel12.getFont().deriveFont(jLabel12.getFont().getSize()+1f));
- jLabel12.setForeground(new java.awt.Color(205, 0, 0));
- jLabel12.setText("Primary key has been changed.
Please keep in mind that the PK must be unique and never null.
It is recommended to check the integrity of the PK.
To do that, please select the option \"check primary keys\" in the export dialog or use the button below. ");
+ warnPKChangedLabel.setFont(warnPKChangedLabel.getFont().deriveFont(warnPKChangedLabel.getFont().getSize()+1f));
+ warnPKChangedLabel.setForeground(new java.awt.Color(205, 0, 0));
+ warnPKChangedLabel.setText("Primary key has been changed.
Keep in mind that the primary key must be unique.
It is recommended to check the integrity of the primary key.
To do that, please select the option \"check primary keys\" in the export dialog or use the button below. ");
gridBagConstraints = new java.awt.GridBagConstraints();
+ gridBagConstraints.gridx = 1;
+ gridBagConstraints.gridy = 1;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
- warnPanel.add(jLabel12, gridBagConstraints);
+ warnPanel.add(warnPKChangedLabel, gridBagConstraints);
+ gridBagConstraints = new java.awt.GridBagConstraints();
+ gridBagConstraints.gridx = 1;
+ gridBagConstraints.gridy = 2;
+ gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
+ gridBagConstraints.insets = new java.awt.Insets(2, 0, 2, 0);
+ warnPanel.add(warnSeparator, gridBagConstraints);
+
+ warnNullablePKLabel.setFont(warnNullablePKLabel.getFont().deriveFont(warnNullablePKLabel.getFont().getSize()+1f));
+ warnNullablePKLabel.setForeground(new java.awt.Color(205, 0, 0));
+ warnNullablePKLabel.setText("Nullable primary key columns can have a negative impact on performance.");
+ gridBagConstraints = new java.awt.GridBagConstraints();
+ gridBagConstraints.gridx = 1;
+ gridBagConstraints.gridy = 3;
+ gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
+ gridBagConstraints.weightx = 1.0;
+ gridBagConstraints.weighty = 1.0;
+ warnPanel.add(warnNullablePKLabel, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
@@ -957,10 +984,14 @@ public abstract class TableEditor extends javax.swing.JDialog {
return false;
}
- private void pkChangedWarning() {
- if (!warnPanel.isVisible()) {
- warnPanel.setVisible(true);
- }
+ private boolean pkChanged = false;
+ private boolean hasNullablePKColumn = false;
+
+ private void updateWarningPanel() {
+ warnPanel.setVisible(pkChanged || hasNullablePKColumn);
+ warnPKChangedLabel.setVisible(pkChanged);
+ warnSeparator.setVisible(pkChanged && hasNullablePKColumn);
+ warnNullablePKLabel.setVisible(hasNullablePKColumn);
}
// Variables declaration - do not modify//GEN-BEGIN:variables
@@ -980,7 +1011,6 @@ public abstract class TableEditor extends javax.swing.JDialog {
private javax.swing.JLabel jLabel1;
private javax.swing.JLabel jLabel10;
private javax.swing.JLabel jLabel11;
- private javax.swing.JLabel jLabel12;
private javax.swing.JLabel jLabel2;
private javax.swing.JLabel jLabel3;
private javax.swing.JLabel jLabel4;
@@ -994,7 +1024,10 @@ public abstract class TableEditor extends javax.swing.JDialog {
private javax.swing.JCheckBox primaryKey1;
private javax.swing.JPanel slotPanel;
private javax.swing.JCheckBox upsertCheckbox;
+ private javax.swing.JLabel warnNullablePKLabel;
+ private javax.swing.JLabel warnPKChangedLabel;
private javax.swing.JPanel warnPanel;
+ private javax.swing.JSeparator warnSeparator;
// End of variables declaration//GEN-END:variables
private static final long serialVersionUID = -3331167410435129849L;
diff --git a/src/main/gui/net/sf/jailer/ui/UIUtil.java b/src/main/gui/net/sf/jailer/ui/UIUtil.java
index c5592f5fc..4e06dd787 100644
--- a/src/main/gui/net/sf/jailer/ui/UIUtil.java
+++ b/src/main/gui/net/sf/jailer/ui/UIUtil.java
@@ -751,6 +751,7 @@ public class UIUtil {
if (t instanceof SqlException) {
String message = ((SqlException) t).getMessage();
String sql = ((SqlException) t).sqlStatement;
+ String errorDialogTitle = ((SqlException) t).errorDialogTitle;
if (message != null) {
if (sql != null) {
String iMsg = message.toString() + "\n" + JailerVersion.VERSION + "\n" + sql;
@@ -758,7 +759,7 @@ public class UIUtil {
}
}
new SqlErrorDialog(parent == null ? null : parent instanceof Window? (Window) parent : SwingUtilities.getWindowAncestor(parent),
- ((SqlException) t).isFormatted()? message : lineWrap(message, 120).toString(), sql, ((SqlException) t).isFormatted(), true, null, false, additionalControl);
+ ((SqlException) t).isFormatted()? message : lineWrap(message, 120).toString(), sql, ((SqlException) t).isFormatted(), true, errorDialogTitle, false, additionalControl);
return;
}
if (t instanceof CancellationException) {
diff --git a/src/main/gui/net/sf/jailer/ui/progress/SingleStageProgressListener.java b/src/main/gui/net/sf/jailer/ui/progress/SingleStageProgressListener.java
index b2ffd8087..c1d826c18 100644
--- a/src/main/gui/net/sf/jailer/ui/progress/SingleStageProgressListener.java
+++ b/src/main/gui/net/sf/jailer/ui/progress/SingleStageProgressListener.java
@@ -429,7 +429,7 @@ public abstract class SingleStageProgressListener implements ProgressListener {
warned = true;
String message =
"Warning: The number of rows collected (" + finalCollectedRows + ") differs from that of the exported ones (" + exportedRows.get() + ").\n \n" +
- "This may have been caused by an invalid primary key definition.\nPlease note that each primary key must be unique and never null.\n \n" +
+ "This may have been caused by an invalid primary key definition.\nPlease note that each primary key must be unique.\n \n" +
"It is recommended to check the integrity of the primary keys.\n" +
"To do this, use the button below \nor the menu item \"Check primary keys\" in the menu called \"DataModel\".";
final JButton button = new JButton("Check Primary Keys");