1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2025-12-16 09:11:08 +01:00

SubkeyLookup: Switch to batch inserting

This commit is contained in:
Paul Schaub 2022-02-25 17:47:42 +01:00
parent d53d62a6e9
commit 1495baaf6e
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
9 changed files with 110 additions and 86 deletions

View file

@ -4,27 +4,44 @@
package pgp.cert_d.jdbc.sqlite;
/**
* Subkey-ID database entry.
*/
public class Entry {
private final int id;
private final String identifier;
private final String certificate;
private final long subkeyId;
public Entry(int id, long subkeyId, String identifier) {
public Entry(int id, long subkeyId, String certificate) {
this.id = id;
this.subkeyId = subkeyId;
this.identifier = identifier;
this.certificate = certificate;
}
/**
* Get the internal ID of this entry in the database.
*
* @return internal id
*/
public int getId() {
return id;
}
/**
* Return the key-ID of the subkey.
*
* @return subkey id
*/
public long getSubkeyId() {
return subkeyId;
}
public String getIdentifier() {
return identifier;
/**
* Return the fingerprint of the certificate the subkey belongs to.
* @return fingerprint
*/
public String getCertificate() {
return certificate;
}
}

View file

@ -28,16 +28,18 @@ public class SqliteSubkeyLookup implements SubkeyLookup {
private static final String CREATE_TABLE_STMT = "" +
"CREATE TABLE IF NOT EXISTS subkey_lookup (\n" +
" id integer PRIMARY KEY,\n" +
" identifier text NOT NULL,\n" +
" subkey_id integer NOT NULL,\n" +
" UNIQUE(identifier, subkey_id)\n" +
" id integer PRIMARY KEY,\n" + // id (internal to the database)
" certificate text NOT NULL,\n" + // certificate fingerprint
" subkey_id integer NOT NULL,\n" + // subkey id
" UNIQUE(certificate, subkey_id)\n" +
")";
private static final String INSERT_STMT = "" +
"INSERT INTO subkey_lookup(identifier, subkey_id) VALUES (?,?)";
"INSERT INTO subkey_lookup(certificate, subkey_id) " +
"VALUES (?,?)";
private static final String QUERY_STMT = "" +
"SELECT * FROM subkey_lookup WHERE subkey_id=?";
"SELECT * FROM subkey_lookup " +
"WHERE subkey_id=?";
public SqliteSubkeyLookup(String databaseURL) throws SQLException {
this.databaseUrl = databaseURL;
@ -54,21 +56,27 @@ public class SqliteSubkeyLookup implements SubkeyLookup {
return new SqliteSubkeyLookup("jdbc:sqlite:" + databaseFile.getAbsolutePath());
}
public void insert(long subkeyId, String identifier, boolean ignoreDuplicates) throws SQLException {
public void insertValues(String certificate, List<Long> subkeyIds) throws SQLException {
try (Connection connection = getConnection(); PreparedStatement statement = connection.prepareStatement(INSERT_STMT)) {
statement.setString(1, identifier);
statement.setLong(2, subkeyId);
statement.executeUpdate();
} catch (SQLiteException e) {
// throw any exception, except:
// ignore unique constraint-related exceptions if we ignoreDuplicates
if (!ignoreDuplicates || e.getResultCode().code != SQLiteErrorCode.SQLITE_CONSTRAINT_UNIQUE.code) {
throw e;
for (long subkeyId : subkeyIds) {
try {
statement.setString(1, certificate);
statement.setLong(2, subkeyId);
statement.executeUpdate();
} catch (SQLiteException e) {
// throw any exception, except:
// ignore unique constraint-related exceptions if we ignoreDuplicates
if (e.getResultCode().code == SQLiteErrorCode.SQLITE_CONSTRAINT_UNIQUE.code) {
// ignore duplicates
} else {
throw e;
}
}
}
}
}
public List<Entry> get(long subkeyId) throws SQLException {
public List<Entry> selectValues(long subkeyId) throws SQLException {
List<Entry> results = new ArrayList<>();
try (Connection connection = getConnection(); PreparedStatement statement = connection.prepareStatement(QUERY_STMT)) {
statement.setLong(1, subkeyId);
@ -77,7 +85,7 @@ public class SqliteSubkeyLookup implements SubkeyLookup {
Entry entry = new Entry(
resultSet.getInt("id"),
resultSet.getLong("subkey_id"),
resultSet.getString("identifier"));
resultSet.getString("certificate"));
results.add(entry);
}
}
@ -86,27 +94,26 @@ public class SqliteSubkeyLookup implements SubkeyLookup {
}
@Override
public Set<String> getIdentifiersForSubkeyId(long subkeyId) throws IOException {
public Set<String> getCertificatesForSubkeyId(long subkeyId) throws IOException {
try {
List<Entry> entries = get(subkeyId);
Set<String> identifiers = new HashSet<>();
List<Entry> entries = selectValues(subkeyId);
Set<String> certificates = new HashSet<>();
for (Entry entry : entries) {
identifiers.add(entry.getIdentifier());
certificates.add(entry.getCertificate());
}
return Collections.unmodifiableSet(identifiers);
return Collections.unmodifiableSet(certificates);
} catch (SQLException e) {
throw new IOException("Cannot query for subkey lookup entries.", e);
}
}
@Override
public void storeIdentifierForSubkeyId(long subkeyId, String identifier)
throws IOException {
public void storeCertificateSubkeyIds(String certificate, List<Long> subkeyIds) throws IOException {
try {
insert(subkeyId, identifier, true);
insertValues(certificate, subkeyIds);
} catch (SQLException e) {
throw new IOException("Cannot store subkey lookup entry in database.", e);
throw new IOException("Cannot store subkey lookup entries in database.", e);
}
}
}

View file

@ -16,6 +16,6 @@ public class EntryTest {
assertEquals(1, entry.getId());
assertEquals(123L, entry.getSubkeyId());
assertEquals("eb85bb5fa33a75e15e944e63f231550c4f47e38e", entry.getIdentifier());
assertEquals("eb85bb5fa33a75e15e944e63f231550c4f47e38e", entry.getCertificate());
}
}

View file

@ -5,7 +5,6 @@
package pgp.cert_d.jdbc.sqlite;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.File;
@ -34,40 +33,35 @@ public class SqliteSubkeyLookupTest {
@Test
public void simpleInsertAndGet() throws IOException {
lookup.storeIdentifierForSubkeyId(123L, "eb85bb5fa33a75e15e944e63f231550c4f47e38e");
lookup.storeIdentifierForSubkeyId(234L, "eb85bb5fa33a75e15e944e63f231550c4f47e38e");
lookup.storeIdentifierForSubkeyId(234L, "d1a66e1a23b182c9980f788cfbfcc82a015e7330");
lookup.storeCertificateSubkeyIds("eb85bb5fa33a75e15e944e63f231550c4f47e38e", Arrays.asList(123L, 234L));
lookup.storeCertificateSubkeyIds("d1a66e1a23b182c9980f788cfbfcc82a015e7330", Collections.singletonList(234L));
assertEquals(Collections.singleton("eb85bb5fa33a75e15e944e63f231550c4f47e38e"), lookup.getIdentifiersForSubkeyId(123L));
assertEquals(Collections.singleton("eb85bb5fa33a75e15e944e63f231550c4f47e38e"), lookup.getCertificatesForSubkeyId(123L));
assertEquals(
new HashSet<>(Arrays.asList("eb85bb5fa33a75e15e944e63f231550c4f47e38e", "d1a66e1a23b182c9980f788cfbfcc82a015e7330")),
lookup.getIdentifiersForSubkeyId(234L));
lookup.getCertificatesForSubkeyId(234L));
}
@Test
public void getNonExistingSubkeyYieldsNull() throws IOException, SQLException {
assertTrue(lookup.get(6666666).isEmpty());
assertTrue(lookup.getIdentifiersForSubkeyId(6666666).isEmpty());
assertTrue(lookup.selectValues(6666666).isEmpty());
assertTrue(lookup.getCertificatesForSubkeyId(6666666).isEmpty());
}
@Test
public void secondInstanceLookupTest() throws IOException, SQLException {
lookup.storeIdentifierForSubkeyId(1337, "eb85bb5fa33a75e15e944e63f231550c4f47e38e");
assertEquals(Collections.singleton("eb85bb5fa33a75e15e944e63f231550c4f47e38e"), lookup.getIdentifiersForSubkeyId(1337));
lookup.storeCertificateSubkeyIds("eb85bb5fa33a75e15e944e63f231550c4f47e38e", Collections.singletonList(1337L));
assertEquals(Collections.singleton("eb85bb5fa33a75e15e944e63f231550c4f47e38e"), lookup.getCertificatesForSubkeyId(1337));
// do the lookup using a second db instance on the same file
SqliteSubkeyLookup secondInstance = SqliteSubkeyLookup.forDatabaseFile(databaseFile);
assertEquals(Collections.singleton("eb85bb5fa33a75e15e944e63f231550c4f47e38e"), secondInstance.getIdentifiersForSubkeyId(1337));
assertEquals(Collections.singleton("eb85bb5fa33a75e15e944e63f231550c4f47e38e"), secondInstance.getCertificatesForSubkeyId(1337));
}
@Test
public void ignoreInsertDuplicates() throws IOException {
lookup.storeIdentifierForSubkeyId(123L, "d1a66e1a23b182c9980f788cfbfcc82a015e7330");
lookup.storeCertificateSubkeyIds("d1a66e1a23b182c9980f788cfbfcc82a015e7330", Arrays.asList(123L, 234L));
// per default we ignore duplicates
lookup.storeIdentifierForSubkeyId(123L, "d1a66e1a23b182c9980f788cfbfcc82a015e7330");
// if we choose not to ignore duplicates, we raise an exception
assertThrows(SQLException.class, () ->
lookup.insert(123L, "d1a66e1a23b182c9980f788cfbfcc82a015e7330", false));
lookup.storeCertificateSubkeyIds("d1a66e1a23b182c9980f788cfbfcc82a015e7330", Arrays.asList(123L, 512L));
}
}