mirror of
https://github.com/Wisser/Jailer.git
synced 2026-02-11 19:08:29 -06:00
Support for nullable primary key columns
This commit is contained in:
@@ -212,7 +212,7 @@
|
||||
|
||||
<td>Minimum length of all paths from the row back to
|
||||
any subject row.<br />
|
||||
The distance of subject rows is 0.</td>
|
||||
The distance of a subject row is 0.</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
@@ -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).<br />
|
||||
model editor. Note that a key must be unique.<br />
|
||||
<br />
|
||||
(On Oracle, however, <i>rowid</i>-pseudo columns can be used
|
||||
instead of primary keys)<br />
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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 <code>true</code>, 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 <code>true</code>, switch of autocommit and commit explicitly
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -72,12 +72,10 @@ public class PrimaryKey {
|
||||
* @return a match of all columns of <code>primaryKey</code>
|
||||
*/
|
||||
public Map<Column, Column> match(PrimaryKey primaryKey) {
|
||||
|
||||
// TODO check completeness of matching
|
||||
|
||||
if (Configuration.getInstance().getDoMinimizeUPK() || !primaryKey.needsOrderedMatch) {
|
||||
Map<Column, Column> match = new HashMap<Column, Column>();
|
||||
boolean minimize = Configuration.getInstance().getDoMinimizeUPK() || !primaryKey.needsOrderedMatch;
|
||||
if (minimize) {
|
||||
Set<Integer> assignedUPKColumns = new HashSet<Integer>();
|
||||
Map<Column, Column> match = new HashMap<Column, Column>();
|
||||
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<Column, Column> match = new HashMap<Column, Column>();
|
||||
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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -392,21 +392,46 @@
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JLabel" name="jLabel12">
|
||||
<Component class="javax.swing.JLabel" name="warnPKChangedLabel">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||
<FontInfo relative="true">
|
||||
<Font component="jLabel12" property="font" relativeSize="true" size="1"/>
|
||||
<Font component="warnPKChangedLabel" property="font" relativeSize="true" size="1"/>
|
||||
</FontInfo>
|
||||
</Property>
|
||||
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
|
||||
<Color blue="0" green="0" red="cd" type="rgb"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" value="<html>Primary key has been changed. <br> Please keep in mind that the PK must be unique and never <i>null</i>. <br> It is recommended to check the integrity of the PK.<br> To do that, please select the option "check primary keys" in the export dialog or use the button below. </html>"/>
|
||||
<Property name="text" type="java.lang.String" value="<html>Primary key has been changed.<br>Keep in mind that the primary key must be unique.<br>It is recommended to check the integrity of the primary key.<br>To do that, please select the option "check primary keys" in the export dialog or use the button below. </html>"/>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="1.0" weightY="1.0"/>
|
||||
<GridBagConstraints gridX="1" gridY="1" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="1.0" weightY="1.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Component class="javax.swing.JSeparator" name="warnSeparator">
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="1" gridY="2" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="0" insetsBottom="2" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="warnNullablePKLabel">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||
<FontInfo relative="true">
|
||||
<Font component="warnNullablePKLabel" property="font" relativeSize="true" size="1"/>
|
||||
</FontInfo>
|
||||
</Property>
|
||||
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
|
||||
<Color blue="0" green="0" red="cd" type="rgb"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" value="<html>Nullable primary key columns can have a negative impact on performance.</html>"/>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="1" gridY="3" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="1.0" weightY="1.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
|
||||
@@ -72,13 +72,18 @@ public abstract class TableEditor extends javax.swing.JDialog {
|
||||
|
||||
private void updateTable(List<ColumnModel> model) {
|
||||
List<Column> pkColumns = new ArrayList<Column>();
|
||||
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("<html>Primary key has been changed. <br> Please keep in mind that the PK must be unique and never <i>null</i>. <br> It is recommended to check the integrity of the PK.<br> To do that, please select the option \"check primary keys\" in the export dialog or use the button below. </html>");
|
||||
warnPKChangedLabel.setFont(warnPKChangedLabel.getFont().deriveFont(warnPKChangedLabel.getFont().getSize()+1f));
|
||||
warnPKChangedLabel.setForeground(new java.awt.Color(205, 0, 0));
|
||||
warnPKChangedLabel.setText("<html>Primary key has been changed.<br>Keep in mind that the primary key must be unique.<br>It is recommended to check the integrity of the primary key.<br>To do that, please select the option \"check primary keys\" in the export dialog or use the button below. </html>");
|
||||
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("<html>Nullable primary key columns can have a negative impact on performance.</html>");
|
||||
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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user