mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Valery Kharseko
14 hours ago 40c239b483e82f359f1527f92aac04c9bb461301
[#885] Ask the catalog for the table of a tree by name (#886)
2 files modified
87 ■■■■■ changed files
opendj-server-legacy/src/main/java/org/opends/server/backends/jdbc/JDBCStorage.java 36 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/backends/jdbc/TestCase.java 51 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/backends/jdbc/JDBCStorage.java
@@ -134,6 +134,23 @@
        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();
@@ -266,10 +283,21 @@
        }
        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) {
opendj-server-legacy/src/test/java/org/opends/server/backends/jdbc/TestCase.java
@@ -139,6 +139,57 @@
    }
    /**
     * 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).
     */