From c188c7d61b2766c2e50b79df7701cfa1791af70e Mon Sep 17 00:00:00 2001 From: Jinhang Choi Date: Thu, 5 Jun 2014 15:16:26 +0900 Subject: [PATCH] TAJO-847: Supporting MariaDB-based Store, which is compatible with MySQL. This patch also includes resolution of NullPointerException in Connection (AbstractDBStore) when Tajo Master tries to check isValid exactly after Session is removed from SessionManager. --- .../tajo/catalog/store/AbstractDBStore.java | 11 +- .../tajo/catalog/store/MariaDBStore.java | 289 ++++++++++++++++++ .../resources/schemas/mariadb/columns.sql | 9 + .../resources/schemas/mariadb/databases.sql | 7 + .../resources/schemas/mariadb/indexes.sql | 16 + .../schemas/mariadb/partition_methods.sql | 7 + .../resources/schemas/mariadb/partitions.sql | 12 + .../main/resources/schemas/mariadb/stats.sql | 6 + .../schemas/mariadb/table_properties.sql | 7 + .../main/resources/schemas/mariadb/tables.sql | 11 + .../resources/schemas/mariadb/tablespaces.sql | 7 + .../org/apache/tajo/catalog/TestCatalog.java | 5 +- .../configuration/catalog_configuration.rst | 29 +- 13 files changed, 411 insertions(+), 5 deletions(-) create mode 100644 tajo-catalog/tajo-catalog-server/src/main/java/org/apache/tajo/catalog/store/MariaDBStore.java create mode 100644 tajo-catalog/tajo-catalog-server/src/main/resources/schemas/mariadb/columns.sql create mode 100644 tajo-catalog/tajo-catalog-server/src/main/resources/schemas/mariadb/databases.sql create mode 100644 tajo-catalog/tajo-catalog-server/src/main/resources/schemas/mariadb/indexes.sql create mode 100644 tajo-catalog/tajo-catalog-server/src/main/resources/schemas/mariadb/partition_methods.sql create mode 100644 tajo-catalog/tajo-catalog-server/src/main/resources/schemas/mariadb/partitions.sql create mode 100644 tajo-catalog/tajo-catalog-server/src/main/resources/schemas/mariadb/stats.sql create mode 100644 tajo-catalog/tajo-catalog-server/src/main/resources/schemas/mariadb/table_properties.sql create mode 100644 tajo-catalog/tajo-catalog-server/src/main/resources/schemas/mariadb/tables.sql create mode 100644 tajo-catalog/tajo-catalog-server/src/main/resources/schemas/mariadb/tablespaces.sql diff --git a/tajo-catalog/tajo-catalog-server/src/main/java/org/apache/tajo/catalog/store/AbstractDBStore.java b/tajo-catalog/tajo-catalog-server/src/main/java/org/apache/tajo/catalog/store/AbstractDBStore.java index 234af19b3b..6d7a947ad7 100644 --- a/tajo-catalog/tajo-catalog-server/src/main/java/org/apache/tajo/catalog/store/AbstractDBStore.java +++ b/tajo-catalog/tajo-catalog-server/src/main/java/org/apache/tajo/catalog/store/AbstractDBStore.java @@ -150,8 +150,17 @@ protected String getCatalogUri() { } public Connection getConnection() { + boolean isValid = false; + + try { + isValid = conn.isValid(100); + } catch (SQLException e) { + e.printStackTrace(); + } catch (NullPointerException e) { + LOG.info("Conn abortion while checking isValid; Try to connect database"); + } + try { - boolean isValid = conn.isValid(100); if (!isValid) { CatalogUtil.closeQuietly(conn); conn = createConnection(conf); diff --git a/tajo-catalog/tajo-catalog-server/src/main/java/org/apache/tajo/catalog/store/MariaDBStore.java b/tajo-catalog/tajo-catalog-server/src/main/java/org/apache/tajo/catalog/store/MariaDBStore.java new file mode 100644 index 0000000000..ed3763296c --- /dev/null +++ b/tajo-catalog/tajo-catalog-server/src/main/java/org/apache/tajo/catalog/store/MariaDBStore.java @@ -0,0 +1,289 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * + */ +package org.apache.tajo.catalog.store; + +import org.apache.hadoop.conf.Configuration; +import org.apache.tajo.catalog.CatalogUtil; +import org.apache.tajo.catalog.exception.CatalogException; +import org.apache.tajo.exception.InternalException; + +import java.sql.*; +import java.util.HashMap; +import java.util.Map; + +public class MariaDBStore extends AbstractDBStore { + /** 2014-05-22: First versioning */ + private static final int MARIADB_CATALOG_STORE_VERSION = 2; + + private static final String CATALOG_DRIVER = "org.mariadb.jdbc.Driver"; + protected String getCatalogDriverName(){ + return CATALOG_DRIVER; + } + + public MariaDBStore(final Configuration conf) throws InternalException { + super(conf); + } + + @Override + public int getDriverVersion() { + return MARIADB_CATALOG_STORE_VERSION; + } + + protected Connection createConnection(Configuration conf) throws SQLException { + Connection con = DriverManager.getConnection(getCatalogUri(), this.connectionId, + this.connectionPassword); + //TODO con.setAutoCommit(false); + return con; + } + + @Override + public String readSchemaFile(String filename) throws CatalogException { + return super.readSchemaFile("mariadb/" + filename); + } + + // TODO - DDL and index statements should be renamed + @Override + protected void createBaseTable() throws CatalogException { + Statement stmt = null; + Connection conn = null; + + try { + conn = getConnection(); + stmt = conn.createStatement(); + + + // META + if (!baseTableMaps.get(TB_META)) { + String sql = super.readSchemaFile("common/meta.sql"); + + if (LOG.isDebugEnabled()) { + LOG.debug(sql.toString()); + } + + stmt.executeUpdate(sql.toString()); + LOG.info("Table '" + TB_META + " is created."); + baseTableMaps.put(TB_META, true); + } + + // TABLE SPACES + if (!baseTableMaps.get(TB_SPACES)) { + String sql = readSchemaFile("tablespaces.sql"); + + if (LOG.isDebugEnabled()) { + LOG.debug(sql); + } + + stmt.executeUpdate(sql); + + LOG.info("Table '" + TB_SPACES + "' is created."); + baseTableMaps.put(TB_SPACES, true); + } + + // DATABASES + if (!baseTableMaps.get(TB_DATABASES)) { + String sql = readSchemaFile("databases.sql"); + if (LOG.isDebugEnabled()) { + LOG.debug(sql); + } + LOG.info("Table '" + TB_DATABASES + "' is created."); + baseTableMaps.put(TB_DATABASES, true); + stmt.executeUpdate(sql); + } + + // TABLES + if (!baseTableMaps.get(TB_TABLES)) { + String sql = readSchemaFile("tables.sql"); + if (LOG.isDebugEnabled()) { + LOG.debug(sql); + } + stmt.executeUpdate(sql); + LOG.info("Table '" + TB_TABLES + "' is created."); + baseTableMaps.put(TB_TABLES, true); + } + + // COLUMNS + if (!baseTableMaps.get(TB_COLUMNS)) { + String sql = readSchemaFile("columns.sql"); + if (LOG.isDebugEnabled()) { + LOG.debug(sql); + } + + stmt.executeUpdate(sql.toString()); + LOG.info("Table '" + TB_COLUMNS + " is created."); + baseTableMaps.put(TB_COLUMNS, true); + } + + // OPTIONS + if (!baseTableMaps.get(TB_OPTIONS)) { + String sql = readSchemaFile("table_properties.sql"); + + if (LOG.isDebugEnabled()) { + LOG.debug(sql.toString()); + } + + stmt.executeUpdate(sql.toString()); + LOG.info("Table '" + TB_OPTIONS + " is created."); + baseTableMaps.put(TB_OPTIONS, true); + } + + // INDEXES + if (!baseTableMaps.get(TB_INDEXES)) { + String sql = readSchemaFile("indexes.sql"); + + if (LOG.isDebugEnabled()) { + LOG.debug(sql.toString()); + } + + stmt.executeUpdate(sql.toString()); + LOG.info("Table '" + TB_INDEXES + "' is created."); + baseTableMaps.put(TB_INDEXES, true); + } + + if (!baseTableMaps.get(TB_STATISTICS)) { + String sql = readSchemaFile("stats.sql"); + + if (LOG.isDebugEnabled()) { + LOG.debug(sql.toString()); + } + + stmt.executeUpdate(sql.toString()); + LOG.info("Table '" + TB_STATISTICS + "' is created."); + baseTableMaps.put(TB_STATISTICS, true); + } + + // PARTITION_METHODS + if (!baseTableMaps.get(TB_PARTITION_METHODS)) { + String sql = readSchemaFile("partition_methods.sql"); + + if (LOG.isDebugEnabled()) { + LOG.debug(sql); + } + + stmt.executeUpdate(sql); + LOG.info("Table '" + TB_PARTITION_METHODS + "' is created."); + baseTableMaps.put(TB_PARTITION_METHODS, true); + } + + // PARTITIONS + if (!baseTableMaps.get(TB_PARTTIONS)) { + String sql = readSchemaFile("partitions.sql"); + + if (LOG.isDebugEnabled()) { + LOG.debug(sql.toString()); + } + + stmt.executeUpdate(sql.toString()); + LOG.info("Table '" + TB_PARTTIONS + "' is created."); + baseTableMaps.put(TB_PARTTIONS, true); + } + + insertSchemaVersion(); + + } catch (SQLException se) { + throw new CatalogException("failed to create base tables for MariaDB catalog store", se); + } finally { + CatalogUtil.closeQuietly(stmt); + } + } + + @Override + protected void dropBaseTable() throws CatalogException { + Connection conn = null; + Statement stmt = null; + Map droppedTable = new HashMap(); + + try { + conn = getConnection(); + stmt = conn.createStatement(); + StringBuilder sql = new StringBuilder(); + + for(Map.Entry entry : baseTableMaps.entrySet()) { + if(entry.getValue() && !entry.getKey().equals(TB_TABLES)) { + sql.delete(0, sql.length()); + sql.append("DROP TABLE ").append(entry.getKey()); + stmt.addBatch(sql.toString()); + droppedTable.put(entry.getKey(), true); + } + } + if(baseTableMaps.get(TB_TABLES)) { + sql.delete(0, sql.length()); + sql.append("DROP TABLE ").append(TB_TABLES); + stmt.addBatch(sql.toString()); + droppedTable.put(TB_TABLES, true); + } + stmt.executeBatch(); + + for(String tableName : droppedTable.keySet()) { + LOG.info("Table '" + tableName + "' is dropped"); + } + } catch (SQLException se) { + throw new CatalogException(se); + } finally { + CatalogUtil.closeQuietly(stmt); + } + } + + @Override + protected boolean isInitialized() throws CatalogException { + Connection conn; + ResultSet res = null; + + try { + conn = getConnection(); + res = conn.getMetaData().getTables(null, null, null, + new String[]{"TABLE"}); + + baseTableMaps.put(TB_META, false); + baseTableMaps.put(TB_SPACES, false); + baseTableMaps.put(TB_DATABASES, false); + baseTableMaps.put(TB_TABLES, false); + baseTableMaps.put(TB_COLUMNS, false); + baseTableMaps.put(TB_OPTIONS, false); + baseTableMaps.put(TB_STATISTICS, false); + baseTableMaps.put(TB_INDEXES, false); + baseTableMaps.put(TB_PARTITION_METHODS, false); + baseTableMaps.put(TB_PARTTIONS, false); + + if (res.wasNull()) + return false; + + while (res.next()) { + // if my.cnf has lower_case_table_names = 1, + // TABLE_NAME returns lower case even it created by upper case. + baseTableMaps.put(res.getString("TABLE_NAME").toUpperCase(), true); + } + + for(Map.Entry entry : baseTableMaps.entrySet()) { + if (!entry.getValue()) { + return false; + } + } + + } catch(SQLException se) { + throw new CatalogException(se); + } finally { + CatalogUtil.closeQuietly(res); + } + + return true; + } +} diff --git a/tajo-catalog/tajo-catalog-server/src/main/resources/schemas/mariadb/columns.sql b/tajo-catalog/tajo-catalog-server/src/main/resources/schemas/mariadb/columns.sql new file mode 100644 index 0000000000..51e2a1b54c --- /dev/null +++ b/tajo-catalog/tajo-catalog-server/src/main/resources/schemas/mariadb/columns.sql @@ -0,0 +1,9 @@ +CREATE TABLE COLUMNS ( + TID INT NOT NULL, + COLUMN_NAME VARCHAR(255) NOT NULL, + ORDINAL_POSITION INT NOT NULL, + DATA_TYPE CHAR(16), + TYPE_LENGTH INTEGER, + PRIMARY KEY (TID, COLUMN_NAME), + FOREIGN KEY (TID) REFERENCES TABLES (TID) ON DELETE CASCADE +) \ No newline at end of file diff --git a/tajo-catalog/tajo-catalog-server/src/main/resources/schemas/mariadb/databases.sql b/tajo-catalog/tajo-catalog-server/src/main/resources/schemas/mariadb/databases.sql new file mode 100644 index 0000000000..e07e916998 --- /dev/null +++ b/tajo-catalog/tajo-catalog-server/src/main/resources/schemas/mariadb/databases.sql @@ -0,0 +1,7 @@ +CREATE TABLE DATABASES_ ( + DB_ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY, + DB_NAME VARCHAR(128) NOT NULL UNIQUE, + SPACE_ID INT NOT NULL, + FOREIGN KEY (SPACE_ID) REFERENCES TABLESPACES (SPACE_ID), + UNIQUE INDEX IDX_NAME (DB_NAME) +) \ No newline at end of file diff --git a/tajo-catalog/tajo-catalog-server/src/main/resources/schemas/mariadb/indexes.sql b/tajo-catalog/tajo-catalog-server/src/main/resources/schemas/mariadb/indexes.sql new file mode 100644 index 0000000000..62feb3630e --- /dev/null +++ b/tajo-catalog/tajo-catalog-server/src/main/resources/schemas/mariadb/indexes.sql @@ -0,0 +1,16 @@ +CREATE TABLE INDEXES ( + DB_ID INT NOT NULL, + TID INT NOT NULL, + INDEX_NAME VARCHAR(128) NOT NULL, + COLUMN_NAME VARCHAR(128) NOT NULL, + DATA_TYPE VARCHAR(128) NOT NULL, + INDEX_TYPE CHAR(32) NOT NULL, + IS_UNIQUE BOOLEAN NOT NULL, + IS_CLUSTERED BOOLEAN NOT NULL, + IS_ASCENDING BOOLEAN NOT NULL, + PRIMARY KEY (DB_ID, INDEX_NAME), + FOREIGN KEY (DB_ID) REFERENCES DATABASES_ (DB_ID) ON DELETE CASCADE, + FOREIGN KEY (TID) REFERENCES TABLES (TID) ON DELETE CASCADE, + UNIQUE INDEX IDX_DB_ID_NAME (DB_ID, INDEX_NAME), + INDEX IDX_TID_COLUMN_NAME (TID, COLUMN_NAME) +) \ No newline at end of file diff --git a/tajo-catalog/tajo-catalog-server/src/main/resources/schemas/mariadb/partition_methods.sql b/tajo-catalog/tajo-catalog-server/src/main/resources/schemas/mariadb/partition_methods.sql new file mode 100644 index 0000000000..060c4c8cb0 --- /dev/null +++ b/tajo-catalog/tajo-catalog-server/src/main/resources/schemas/mariadb/partition_methods.sql @@ -0,0 +1,7 @@ +CREATE TABLE PARTITION_METHODS ( + TID INT NOT NULL PRIMARY KEY, + PARTITION_TYPE VARCHAR(10) NOT NULL, + EXPRESSION VARCHAR(1024) NOT NULL, + EXPRESSION_SCHEMA VARCHAR(1024) NOT NULL, + FOREIGN KEY (TID) REFERENCES TABLES (TID) ON DELETE CASCADE +) \ No newline at end of file diff --git a/tajo-catalog/tajo-catalog-server/src/main/resources/schemas/mariadb/partitions.sql b/tajo-catalog/tajo-catalog-server/src/main/resources/schemas/mariadb/partitions.sql new file mode 100644 index 0000000000..428f5a4bbd --- /dev/null +++ b/tajo-catalog/tajo-catalog-server/src/main/resources/schemas/mariadb/partitions.sql @@ -0,0 +1,12 @@ +CREATE TABLE PARTITIONS ( + PID INT NOT NULL PRIMARY KEY, + TID INT NOT NULL, + PARTITION_NAME VARCHAR(128), + ORDINAL_POSITION INT NOT NULL, + PARTITION_VALUE VARCHAR(1024), + PATH VARCHAR(4096), + FOREIGN KEY (TID) REFERENCES TABLES (TID) ON DELETE CASCADE, + CONSTRAINT C_PARTITION_UNIQUE UNIQUE (TID, PARTITION_NAME), + INDEX IDX_TID (TID), + UNIQUE INDEX IDX_TID_NAME (TID, PARTITION_NAME) +) \ No newline at end of file diff --git a/tajo-catalog/tajo-catalog-server/src/main/resources/schemas/mariadb/stats.sql b/tajo-catalog/tajo-catalog-server/src/main/resources/schemas/mariadb/stats.sql new file mode 100644 index 0000000000..bba8ee7fb0 --- /dev/null +++ b/tajo-catalog/tajo-catalog-server/src/main/resources/schemas/mariadb/stats.sql @@ -0,0 +1,6 @@ +CREATE TABLE STATS ( + TID INT NOT NULL PRIMARY KEY, + NUM_ROWS BIGINT, + NUM_BYTES BIGINT, + FOREIGN KEY (TID) REFERENCES TABLES (TID) ON DELETE CASCADE +) \ No newline at end of file diff --git a/tajo-catalog/tajo-catalog-server/src/main/resources/schemas/mariadb/table_properties.sql b/tajo-catalog/tajo-catalog-server/src/main/resources/schemas/mariadb/table_properties.sql new file mode 100644 index 0000000000..78e281e5fa --- /dev/null +++ b/tajo-catalog/tajo-catalog-server/src/main/resources/schemas/mariadb/table_properties.sql @@ -0,0 +1,7 @@ +CREATE TABLE OPTIONS ( + TID INT NOT NULL, + KEY_ VARCHAR(255) NOT NULL, + VALUE_ VARCHAR(255) NOT NULL, + PRIMARY KEY (TID, KEY_), + FOREIGN KEY (TID) REFERENCES TABLES (TID) ON DELETE CASCADE +) \ No newline at end of file diff --git a/tajo-catalog/tajo-catalog-server/src/main/resources/schemas/mariadb/tables.sql b/tajo-catalog/tajo-catalog-server/src/main/resources/schemas/mariadb/tables.sql new file mode 100644 index 0000000000..e7297f7ff8 --- /dev/null +++ b/tajo-catalog/tajo-catalog-server/src/main/resources/schemas/mariadb/tables.sql @@ -0,0 +1,11 @@ +CREATE TABLE TABLES ( + TID INT NOT NULL AUTO_INCREMENT PRIMARY KEY, + DB_ID INT NOT NULL, + TABLE_NAME VARCHAR(128) NOT NULL, + TABLE_TYPE VARCHAR(128) NOT NULL, + PATH VARCHAR(4096), + STORE_TYPE CHAR(16), + FOREIGN KEY (DB_ID) REFERENCES DATABASES_ (DB_ID), + INDEX IDX_DB_ID (DB_ID), + UNIQUE INDEX IDX_TABLE_ID (DB_ID, TABLE_NAME) +) \ No newline at end of file diff --git a/tajo-catalog/tajo-catalog-server/src/main/resources/schemas/mariadb/tablespaces.sql b/tajo-catalog/tajo-catalog-server/src/main/resources/schemas/mariadb/tablespaces.sql new file mode 100644 index 0000000000..f2e2299c3a --- /dev/null +++ b/tajo-catalog/tajo-catalog-server/src/main/resources/schemas/mariadb/tablespaces.sql @@ -0,0 +1,7 @@ +CREATE TABLE TABLESPACES ( + SPACE_ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY, + SPACE_NAME VARCHAR(128) NOT NULL UNIQUE, + SPACE_HANDLER VARCHAR (1024) DEFAULT 'HDFS', + SPACE_URI VARCHAR (4096) NOT NULL, + UNIQUE INDEX IDX_NAME (SPACE_NAME) +) \ No newline at end of file diff --git a/tajo-catalog/tajo-catalog-server/src/test/java/org/apache/tajo/catalog/TestCatalog.java b/tajo-catalog/tajo-catalog-server/src/test/java/org/apache/tajo/catalog/TestCatalog.java index 32ea83b0d4..82df908f37 100644 --- a/tajo-catalog/tajo-catalog-server/src/test/java/org/apache/tajo/catalog/TestCatalog.java +++ b/tajo-catalog/tajo-catalog-server/src/test/java/org/apache/tajo/catalog/TestCatalog.java @@ -31,6 +31,7 @@ import org.apache.tajo.catalog.proto.CatalogProtos.StoreType; import org.apache.tajo.catalog.store.DerbyStore; import org.apache.tajo.catalog.store.MySQLStore; +import org.apache.tajo.catalog.store.MariaDBStore; import org.apache.tajo.common.TajoDataTypes; import org.apache.tajo.common.TajoDataTypes.Type; import org.apache.tajo.conf.TajoConf; @@ -85,8 +86,8 @@ public static void setUp() throws Exception { conf.set(CATALOG_URI, catalogURI); conf.setVar(TajoConf.ConfVars.CATALOG_ADDRESS, "127.0.0.1:0"); - // MySQLStore requires password - if (driverClass.equals(MySQLStore.class.getCanonicalName())) { + // MySQLStore/MariaDBStore requires password + if (driverClass.equals(MySQLStore.class.getCanonicalName()) || driverClass.equals(MariaDBStore.class.getCanonicalName())) { if (connectionId == null) { throw new CatalogException(String.format("%s driver requires %s", driverClass, CatalogConstants.CONNECTION_ID)); } diff --git a/tajo-docs/src/main/sphinx/configuration/catalog_configuration.rst b/tajo-docs/src/main/sphinx/configuration/catalog_configuration.rst index d6e81869a4..80902a5660 100644 --- a/tajo-docs/src/main/sphinx/configuration/catalog_configuration.rst +++ b/tajo-docs/src/main/sphinx/configuration/catalog_configuration.rst @@ -14,6 +14,8 @@ If you want to customize the catalog service, copy ``$TAJO_HOME/conf/catalog-sit +-----------------------------------+------------------------------------------------+ | tajo.catalog.store.MySQLStore | this storage class uses MySQL. | +-----------------------------------+------------------------------------------------+ +| tajo.catalog.store.MariaDBStore | this storage class uses MariaDB. | ++-----------------------------------+------------------------------------------------+ | tajo.catalog.store.MemStore | this is the in-memory storage. It is only used | | | in unit tests to shorten the duration of unit | | | tests. | @@ -63,11 +65,34 @@ Finally, you should add the following config to `conf/catalog-site.xml` : tajo.catalog.jdbc.connection.password - + tajo.catalog.jdbc.uri jdbc:mysql://:/?createDatabaseIfNotExist=true +========================= +MariaDBStore Configuration +========================= + +All configurations for using MariaDBStore is compatible with MySQLStore except following: + +.. code-block:: sh + + export TAJO_CLASSPATH=/usr/local/mysql/lib/mariadb-java-clinet-x.x.x.jar + +.. code-block:: xml + + + tajo.catalog.store.class + org.apache.tajo.catalog.store.MariaDBStore + + + tajo.catalog.jdbc.uri + jdbc:mariadb://:/?createDatabaseIfNotExist=true + + + + ---------------------------------- HCatalogStore Configuration @@ -105,4 +130,4 @@ Lastly, you should add the following config to ``conf/catalog-site.xml`` : tajo.catalog.store.class org.apache.tajo.catalog.store.HCatalogStore - \ No newline at end of file +