From 59a18ab43f21317461b1a4c436cc6fac8200571b Mon Sep 17 00:00:00 2001 From: Erik Tierney Date: Wed, 5 Feb 2014 12:08:14 -0500 Subject: [PATCH] Add ability to optionally use a schema name in the Postgres block store. Add a test to make sure things work when we set a schema name. --- .../store/PostgresFullPrunedBlockStore.java | 63 ++++++++++++++++--- .../PostgresFullPrunedBlockChainTest.java | 23 ++++++- 2 files changed, 75 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/com/google/bitcoin/store/PostgresFullPrunedBlockStore.java b/core/src/main/java/com/google/bitcoin/store/PostgresFullPrunedBlockStore.java index 552d116a..9aa511fe 100644 --- a/core/src/main/java/com/google/bitcoin/store/PostgresFullPrunedBlockStore.java +++ b/core/src/main/java/com/google/bitcoin/store/PostgresFullPrunedBlockStore.java @@ -22,6 +22,7 @@ import com.google.common.collect.Lists; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.Nullable; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -49,6 +50,7 @@ public class PostgresFullPrunedBlockStore implements FullPrunedBlockStore { private int fullStoreDepth; private String username; private String password; + private String schemaName; private static final String driver = "org.postgresql.Driver"; private static final String CREATE_SETTINGS_TABLE = "CREATE TABLE settings (\n" + @@ -107,24 +109,59 @@ public class PostgresFullPrunedBlockStore implements FullPrunedBlockStore { */ public PostgresFullPrunedBlockStore(NetworkParameters params, int fullStoreDepth, String hostname, String dbName, String username, String password) throws BlockStoreException { - this(params, fullStoreDepth, "jdbc:postgresql://" + hostname + "/" + dbName, username, password); + this(params, "jdbc:postgresql://" + hostname + "/" + dbName, fullStoreDepth, username, password, null); } /** - * Creates a new PostgresFullPrunedBlockStore. + *

Create a new PostgresFullPrunedBlockStore, storing the tables in the schema specified. You may want to + * specify a schema to avoid name collisions, or just to keep the database better organized. The schema is not + * required, and if one is not provided than the default schema for the username will be used. See + * the postgres schema docs for more on + * schemas.

* - * @param params A copy of the NetworkParameters used - * @param fullStoreDepth The number of blocks of history stored in full (something like 1000 is pretty safe) - * @param connectionURL The jdbc url to connect to the database. - * @param username The database username - * @param password The password to the database - * @throws BlockStoreException if the database fails to open for any reason + * @param params A copy of the NetworkParameters used. + * @param fullStoreDepth The number of blocks of history stored in full (something like 1000 is pretty safe). + * @param hostname The hostname of the database to connect to. + * @param dbName The database to connect to. + * @param username The database username. + * @param password The password to the database. + * @param schemaName The name of the schema to put the tables in. May be null if no schema is being used. + * @throws BlockStoreException If the database fails to open for any reason. */ - public PostgresFullPrunedBlockStore(NetworkParameters params, int fullStoreDepth, String connectionURL, - String username, String password) throws BlockStoreException { + public PostgresFullPrunedBlockStore(NetworkParameters params, int fullStoreDepth, String hostname, String dbName, + String username, String password, @Nullable String schemaName) throws BlockStoreException { + this(params, "jdbc:postgresql://" + hostname + "/" + dbName, fullStoreDepth, username, password, schemaName); + } + + /** + *

Create a new PostgresFullPrunedBlockStore, using the full connection URL instead of a hostname and password, + * and optionally allowing a schema to be specified.

+ * + *

The connection URL will be passed to the database driver, and should look like + * "jdbc:postrgresql://host[:port]/databasename". You can use this to change the port, or specify additional + * parameters. See + * the PostgreSQL JDBC documentation for more on the connection URL.

+ * + *

This constructor also accepts a schema name to use, which can be used to avoid name collisions, or to keep the + * database organized. If no schema is provided the default schema for the username will be used. See + * the postgres schema docs for more on + * schemas.

+ * + * + * @param params A copy of the NetworkParameters used. + * @param connectionURL The jdbc url to connect to the database. + * @param fullStoreDepth The number of blocks of history stored in full (something like 1000 is pretty safe). + * @param username The database username. + * @param password The password to the database. + * @param schemaName The name of the schema to put the tables in. May be null if no schema is being used. + * @throws BlockStoreException If the database fails to open for any reason. + */ + public PostgresFullPrunedBlockStore(NetworkParameters params, String connectionURL, int fullStoreDepth, + String username, String password, @Nullable String schemaName) throws BlockStoreException { this.params = params; this.fullStoreDepth = fullStoreDepth; this.connectionURL = connectionURL; + this.schemaName = schemaName; this.username = username; this.password = password; @@ -165,6 +202,12 @@ public class PostgresFullPrunedBlockStore implements FullPrunedBlockStore { conn.set(DriverManager.getConnection(connectionURL, props)); Connection connection = conn.get(); + // set the schema if one is needed + if(schemaName != null) { + Statement s = connection.createStatement(); + s.execute("CREATE SCHEMA IF NOT EXISTS " + schemaName + ";"); + s.execute("set search_path to '" + schemaName +"';"); + } allConnections.add(conn.get()); log.info("Made a new connection to database " + connectionURL); } catch (SQLException ex) { diff --git a/core/src/test/java/com/google/bitcoin/core/PostgresFullPrunedBlockChainTest.java b/core/src/test/java/com/google/bitcoin/core/PostgresFullPrunedBlockChainTest.java index d5e89a51..f459660d 100644 --- a/core/src/test/java/com/google/bitcoin/core/PostgresFullPrunedBlockChainTest.java +++ b/core/src/test/java/com/google/bitcoin/core/PostgresFullPrunedBlockChainTest.java @@ -4,6 +4,7 @@ import com.google.bitcoin.store.BlockStoreException; import com.google.bitcoin.store.FullPrunedBlockStore; import com.google.bitcoin.store.PostgresFullPrunedBlockStore; import org.junit.Ignore; +import org.junit.Test; /** * A Postgres implementation of the {@link AbstractFullPrunedBlockChainTest} @@ -16,15 +17,35 @@ public class PostgresFullPrunedBlockChainTest extends AbstractFullPrunedBlockCha private static final String DB_NAME = "bitcoinj_test"; private static final String DB_USERNAME = "bitcoinj"; private static final String DB_PASSWORD = "password"; + private static final String DB_SCHEMA = "blockstore_schema"; + + // whether to run the test with a schema name + private boolean useSchema = false; @Override public FullPrunedBlockStore createStore(NetworkParameters params, int blockCount) throws BlockStoreException { - return new PostgresFullPrunedBlockStore(params, blockCount, DB_HOSTNAME, DB_NAME, DB_USERNAME, DB_PASSWORD); + if(useSchema) { + return new PostgresFullPrunedBlockStore(params, blockCount, DB_HOSTNAME, DB_NAME, DB_USERNAME, DB_PASSWORD, DB_SCHEMA); + } + else { + return new PostgresFullPrunedBlockStore(params, blockCount, DB_HOSTNAME, DB_NAME, DB_USERNAME, DB_PASSWORD); + } } @Override public void resetStore(FullPrunedBlockStore store) throws BlockStoreException { ((PostgresFullPrunedBlockStore)store).resetStore(); } + + @Test + public void testFirst100kBlocksWithCustomSchema() throws Exception { + boolean oldSchema = useSchema; + useSchema = true; + try { + super.testFirst100KBlocks(); + } finally { + useSchema = oldSchema; + } + } }