Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,23 @@ String getTableName(TreeName treeName) {
return tree2table.get(treeName);
}

/**
* The form a catalog pattern has to take to match an identifier this backend created unquoted.
* An unquoted identifier is folded when it is stored - to upper case on oracle, to lower case
* on postgresql - and a metadata pattern is matched against the stored form, not against the
* name as it was written. The driver is asked which way it folds, rather than its class name
* being matched, since this is what the JDBC contract exposes these two methods for.
*/
static String storedIdentifier(DatabaseMetaData metaData, String name) throws SQLException {
if (metaData.storesUpperCaseIdentifiers()) {
return name.toUpperCase();
}
if (metaData.storesLowerCaseIdentifiers()) {
return name.toLowerCase();
}
return name;
}

@Override
public void removeStorageFiles() throws StorageRuntimeException {
final boolean isOpen=getStorageStatus().isWorking();
Expand Down Expand Up @@ -266,10 +283,21 @@ public WriteableTransactionTransactionImpl(Connection con) {
}

boolean isExistsTable(TreeName treeName) {
try (final ResultSet rs = con.getMetaData().getTables(null, null, null, new String[]{"TABLE"})) {
while (rs.next()) {
if (tree2table.get(treeName).equalsIgnoreCase(rs.getString("TABLE_NAME"))) {
return true;
final String tableName = getTableName(treeName);
try {
final DatabaseMetaData metaData = con.getMetaData();
// asked of the catalog by name: openTree(createOnDemand) calls this for every tree
// of the backend - about 25 of them for a stock suffix, on every open - and listing
// every table of the database each time costs the whole catalog once per tree, on a
// database this backend may well be sharing with something else
try (final ResultSet rs = metaData.getTables(null, null,
storedIdentifier(metaData, tableName), new String[]{"TABLE"})) {
while (rs.next()) {
// the name still has to be compared: "_" is a single-character wildcard in a
// metadata pattern, so "opendj_<hash>" also matches a table named "opendjX<hash>"
if (tableName.equalsIgnoreCase(rs.getString("TABLE_NAME"))) {
return true;
}
}
}
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,57 @@ private static ByteString value(int i) {
return ByteString.valueOfUtf8("value" + i);
}

/**
* openTree() and deleteTree() ask the catalog whether the table of a tree is there. The name is
* looked up in the form the catalog stores it - an unquoted identifier is folded to upper case
* on oracle and to lower case on postgresql - so getting that wrong makes a second open try to
* create a table that is already there, and a second delete drop one that is already gone (#885).
*/
@Test
public void testTableOfATreeIsFoundByName() throws Exception {
final JDBCStorage storage = new JDBCStorage(createBackendCfg(), null);
final TreeName tree = new TreeName("testCatalogLookup", "tree");
try {
storage.open(AccessMode.READ_WRITE);
storage.write(new WriteOperation() {
@Override
public void run(WriteableTransaction txn) throws Exception {
txn.openTree(tree, true);
txn.put(tree, key(1), value(1));
}
});
// the table is there now: opening the tree again must find it, not create it a second time
storage.write(new WriteOperation() {
@Override
public void run(WriteableTransaction txn) throws Exception {
txn.openTree(tree, true);
}
});
assertEquals(storage.read(new ReadOperation<ByteString>() {
@Override
public ByteString run(ReadableTransaction txn) throws Exception {
return txn.read(tree, key(1));
}
}), value(1));

storage.write(new WriteOperation() {
@Override
public void run(WriteableTransaction txn) throws Exception {
txn.deleteTree(tree);
}
});
// and gone now: deleting it again must find nothing rather than drop what is not there
storage.write(new WriteOperation() {
@Override
public void run(WriteableTransaction txn) throws Exception {
txn.deleteTree(tree);
}
});
} finally {
storage.close();
}
}

/**
* Forward repositioning inside the already-fetched batch must be served from the buffer without SQL,
* and batch sizes must grow from "fetchsize.initial" to "fetchsize" on sequential reads (#860).
Expand Down
Loading