Skip to content

Commit

Permalink
Fix updateRow when there are primary keys and unique keys, PR pgjdbc#…
Browse files Browse the repository at this point in the history
…2199 made some incorrect assumptions about unique keys that were incorrect.

When we have both primary keys and unique keys only use the Primary keys for updating the row.
  • Loading branch information
davecramer committed Jul 12, 2021
1 parent d985dc1 commit ec79b0a
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2182,7 +2182,7 @@ protected ResultSet getPrimaryUniqueKeys(@Nullable String catalog, @Nullable Str
sql = "SELECT NULL AS TABLE_CAT, n.nspname AS TABLE_SCHEM, "
+ " ct.relname AS TABLE_NAME, a.attname AS COLUMN_NAME, "
+ " (information_schema._pg_expandarray(i.indkey)).n AS KEY_SEQ, ci.relname AS PK_NAME, "
+ " information_schema._pg_expandarray(i.indkey) AS KEYS, a.attnum AS A_ATTNUM "
+ " information_schema._pg_expandarray(i.indkey) AS KEYS, a.attnum AS A_ATTNUM, i.indisprimary AS IS_PRIMARY "
+ "FROM pg_catalog.pg_class ct "
+ " JOIN pg_catalog.pg_attribute a ON (ct.oid = a.attrelid) "
+ " JOIN pg_catalog.pg_namespace n ON (ct.relnamespace = n.oid) "
Expand All @@ -2205,7 +2205,8 @@ protected ResultSet getPrimaryUniqueKeys(@Nullable String catalog, @Nullable Str
+ " result.TABLE_NAME, "
+ " result.COLUMN_NAME, "
+ " result.KEY_SEQ, "
+ " result.PK_NAME "
+ " result.PK_NAME, "
+ " result.IS_PRIMARY "
+ "FROM "
+ " (" + sql + " ) result"
+ " where "
Expand Down
36 changes: 33 additions & 3 deletions pgjdbc/src/main/java/org/postgresql/jdbc/PgResultSet.java
Original file line number Diff line number Diff line change
Expand Up @@ -1418,6 +1418,10 @@ public synchronized void updateRow() throws SQLException {
List<PrimaryKey> primaryKeys = castNonNull(this.primaryKeys, "primaryKeys");
int numKeys = primaryKeys.size();

// since we now return both primary and unique keys in order to optimize finding unique keys to
// use in updateRow we want to know if we are using primary keys or unique keys to do the update
// primary keys cannot be null, unique keys can. If we have a primary key then we don't need
// the unique keys. If we have no primary keys then we have to handle null unique keys.
for (int i = 0; i < numKeys; i++) {
PrimaryKey primaryKey = primaryKeys.get(i);
Utils.escapeIdentifier(updateSQL, primaryKey.name);
Expand Down Expand Up @@ -1634,10 +1638,34 @@ boolean isUpdateable() throws SQLException {
/* make sure that the user has included the primary key in the resultset */
if (index > 0) {
i++;
primaryKeys.add(new PrimaryKey(index, columnName)); // get the primary key information
primaryKeys.add(new PrimaryKey(index, columnName, rs.getBoolean("IS_PRIMARY"))); // get the primary key information
}
}

// look to see if we have primary keys, if we do remove the unique keys, we added them so they could be used without any primary keys
// this is convoluted but easier to do in java than in SQL.

boolean usingPrimaryKeys = false;
for ( int j = 0; j < i; j++ ) {
if (primaryKeys.get(j).isPrimary) {
usingPrimaryKeys = true;
}
}

if ( usingPrimaryKeys ) {
boolean uniqueKeys = false;
do {
for (int j = 0; j < i; j++) {
if (!primaryKeys.get(j).isPrimary) {
primaryKeys.remove(j);
uniqueKeys = true;
i--;
numPKcolumns--;
break;
}
}
} while ( uniqueKeys );
}
rs.close();
connection.getLogger().log(Level.FINE, "no of keys={0}", i);

Expand All @@ -1658,7 +1686,7 @@ boolean isUpdateable() throws SQLException {

// oidIndex will be >0 if the oid was in the select list
if (oidIndex > 0) {
primaryKeys.add(new PrimaryKey(oidIndex, "oid"));
primaryKeys.add(new PrimaryKey(oidIndex, "oid", true));
usingOID = true;
updateable = true;
}
Expand Down Expand Up @@ -3293,10 +3321,12 @@ protected Object getUUID(byte[] data) throws SQLException {
private class PrimaryKey {
int index; // where in the result set is this primaryKey
String name; // what is the columnName of this primary Key
boolean isPrimary; // in order to allow unique keys to be used for updating rows we include them as primary keys

PrimaryKey(int index, String name) {
PrimaryKey(int index, String name, boolean isPrimary) {
this.index = index;
this.name = name;
this.isPrimary = isPrimary;
}

@Nullable Object getValue() throws SQLException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,7 @@
import java.io.ByteArrayInputStream;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.sql.Array;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.sql.Types;
import java.sql.*;
import java.util.TimeZone;

public class UpdateableResultTest extends BaseTest4 {
Expand All @@ -39,6 +33,7 @@ public void setUp() throws Exception {
super.setUp();
TestUtil.createTable(con, "updateable",
"id int primary key, name text, notselected text, ts timestamp with time zone, intarr int[]");
TestUtil.createTable(con, "hasdate", "id int primary key, dt date unique, name text");
TestUtil.createTable(con, "second", "id1 int primary key, name1 text");
TestUtil.createTable(con, "serialtable", "gen_id serial primary key, name text");
TestUtil.createTable(con, "compositepktable", "gen_id serial, name text, dec_id serial");
Expand Down Expand Up @@ -71,6 +66,7 @@ public void tearDown() throws SQLException {
TestUtil.dropTable(con, "nopkmulticol");
TestUtil.dropTable(con, "booltable");
TestUtil.dropTable(con, "uniqueconstraint");
TestUtil.dropTable(con, "hasdate");
super.tearDown();
}

Expand Down Expand Up @@ -447,6 +443,26 @@ public void testUpdateable() throws SQLException {
st.close();
}

@Test
public void testUpdateDate() throws Exception{
Date testDate = Date.valueOf("2021-01-01");
TestUtil.execute( "insert into hasdate values (1,'2021-01-01'::date)", con);
con.setAutoCommit(false);
String sql = "SELECT * FROM hasdate where id=1";
ResultSet rs = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE ).executeQuery(sql);
assertTrue(rs.next());
assertEquals(testDate, rs.getDate("dt"));
rs.updateDate("dt", Date.valueOf("2020-01-01"));
rs.updateRow();
assertEquals(Date.valueOf("2020-01-01"), rs.getDate("dt"));
System.out.println("After Update: " + rs.getDate("dt"));
con.commit();
rs = con.createStatement().executeQuery("select dt from hasdate where id=1");
assertTrue(rs.next());
assertEquals(Date.valueOf("2020-01-01"), rs.getDate("dt"));
rs.close();
}

@Test
public void test2193() throws Exception {
Statement st =
Expand Down

0 comments on commit ec79b0a

Please sign in to comment.