From 8e1917b7d73779699d3c97fe53b6d3bb70ac1df3 Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 8 Nov 2016 17:56:52 -0500 Subject: [PATCH] Metadata Storage extension for Microsoft SqlServer (sqlserver-metadata-storage) (#3421) --- .../extensions-contrib/sqlserver.md | 37 +++ docs/content/development/extensions.md | 13 +- .../sqlserver-metadata-storage/pom.xml | 68 +++++ .../storage/sqlserver/SQLServerConnector.java | 283 ++++++++++++++++++ .../SQLServerMetadataStorageModule.java | 65 ++++ .../io.druid.initialization.DruidModule | 1 + .../CustomStatementRewriterTest.java | 160 ++++++++++ .../sqlserver/SQLServerConnectorTest.java | 68 +++++ 8 files changed, 689 insertions(+), 6 deletions(-) create mode 100644 docs/content/development/extensions-contrib/sqlserver.md create mode 100644 extensions-contrib/sqlserver-metadata-storage/pom.xml create mode 100644 extensions-contrib/sqlserver-metadata-storage/src/main/java/io/druid/metadata/storage/sqlserver/SQLServerConnector.java create mode 100644 extensions-contrib/sqlserver-metadata-storage/src/main/java/io/druid/metadata/storage/sqlserver/SQLServerMetadataStorageModule.java create mode 100644 extensions-contrib/sqlserver-metadata-storage/src/main/resources/META-INF/services/io.druid.initialization.DruidModule create mode 100644 extensions-contrib/sqlserver-metadata-storage/src/test/java/io/druid/metadata/storage/sqlserver/CustomStatementRewriterTest.java create mode 100644 extensions-contrib/sqlserver-metadata-storage/src/test/java/io/druid/metadata/storage/sqlserver/SQLServerConnectorTest.java diff --git a/docs/content/development/extensions-contrib/sqlserver.md b/docs/content/development/extensions-contrib/sqlserver.md new file mode 100644 index 000000000000..0ec3a271ba2d --- /dev/null +++ b/docs/content/development/extensions-contrib/sqlserver.md @@ -0,0 +1,37 @@ +--- +layout: doc_page +--- + +# Microsoft SQLServer + +Make sure to [include](../../operations/including-extensions.html) `sqlserver-metadata-storage` as an extension. + +## Setting up SQLServer + +1. Install Microsoft SQLServer + +2. Create a druid database and user + + Create the druid user + - Microsoft SQL Server Management Studio - Security - Logins - New Login... + - Create a druid user, enter `diurd` when prompted for the password. + + Create a druid database owned by the user we just created + - Databases - New Database + - Database Name: druid, Owner: druid + +3. Add the Microsoft JDBC library to the Druid classpath + - To ensure the com.microsoft.sqlserver.jdbc.SQLServerDriver class is loaded you will have to add the appropriate Microsoft JDBC library (sqljdbc*.jar) to the Druid classpath. + - For instance, if all jar files in your "druid/lib" directory are automatically added to your Druid classpath, then manually download the Microsoft JDBC drivers from ( https://www.microsoft.com/en-ca/download/details.aspx?id=11774) and drop it into my druid/lib directory. + +4. Configure your Druid metadata storage extension: + + Add the following parameters to your Druid configuration, replacing `` + with the location (host name and port) of the database. + + ```properties + druid.metadata.storage.type=sqlserver + druid.metadata.storage.connector.connectURI=jdbc:sqlserver://;databaseName=druid + druid.metadata.storage.connector.user=druid + druid.metadata.storage.connector.password=diurd + ``` diff --git a/docs/content/development/extensions.md b/docs/content/development/extensions.md index 4ca2b19c0c8c..23eba76e22f6 100644 --- a/docs/content/development/extensions.md +++ b/docs/content/development/extensions.md @@ -13,7 +13,7 @@ metadata store. Many clusters will also use additional extensions. ## Including extensions -Please see [here](../operations/including-extensions.html). +Please see [here](../operations/including-extensions.html). ## Core extensions @@ -39,12 +39,12 @@ Core extensions are maintained by Druid committers. # Community Extensions
-Community extensions are not maintained by Druid committers, although we accept patches from community members using these extensions. They may not have been as extensively tested as the core extensions. +Community extensions are not maintained by Druid committers, although we accept patches from community members using these extensions. They may not have been as extensively tested as the core extensions.
-A number of community members have contributed their own extensions to Druid that are not packaged with the default Druid tarball. +A number of community members have contributed their own extensions to Druid that are not packaged with the default Druid tarball. If you'd like to take on maintenance for a community extension, please post on [druid-development group](https://groups.google.com/forum/#!forum/druid-development) to let us know! - + All of these community extensions can be downloaded using *pull-deps* with the coordinate io.druid.extensions.contrib:EXTENSION_NAME:LATEST_DRUID_STABLE_VERSION. |Name|Description|Docs| @@ -58,13 +58,14 @@ All of these community extensions can be downloaded using *pull-deps* with the c |druid-parquet-extensions|Support for data in Apache Parquet data format. Requires druid-avro-extensions to be loaded.|[link](../development/extensions-contrib/parquet.html)| |druid-rabbitmq|RabbitMQ firehose.|[link](../development/extensions-contrib/rabbitmq.html)| |druid-rocketmq|RocketMQ firehose.|[link](../development/extensions-contrib/rocketmq.html)| +|sqlserver-metadata-storage|Microsoft SqlServer deep storage.|[link](../development/extensions-contrib/sqlserver.html)| |graphite-emitter|Graphite metrics emitter|[link](../development/extensions-contrib/graphite.html)| |statsd-emitter|StatsD metrics emitter|[link](../development/extensions-contrib/statsd.html)| ## Promoting Community Extension to Core Extension -Please [let us know](https://groups.google.com/forum/#!forum/druid-development) if you'd like an extension to be promoted to core. -If we see a community extension actively supported by the community, we can promote it to core based on community feedback. +Please [let us know](https://groups.google.com/forum/#!forum/druid-development) if you'd like an extension to be promoted to core. +If we see a community extension actively supported by the community, we can promote it to core based on community feedback. # Creating your own Extensions diff --git a/extensions-contrib/sqlserver-metadata-storage/pom.xml b/extensions-contrib/sqlserver-metadata-storage/pom.xml new file mode 100644 index 000000000000..81f32df4f220 --- /dev/null +++ b/extensions-contrib/sqlserver-metadata-storage/pom.xml @@ -0,0 +1,68 @@ + + + + 4.0.0 + + io.druid.extensions.contrib + sqlserver-metadata-storage + sqlserver-metadata-storage + sqlserver-metadata-storage + + + io.druid + druid + 0.9.2-SNAPSHOT + ../../pom.xml + + + + + + io.druid + druid-api + ${project.parent.version} + provided + + + io.druid + druid-common + ${project.parent.version} + provided + + + io.druid + druid-server + ${project.parent.version} + provided + + + + org.jdbi + jdbi + provided + + + + junit + junit + 4.10 + test + + + + diff --git a/extensions-contrib/sqlserver-metadata-storage/src/main/java/io/druid/metadata/storage/sqlserver/SQLServerConnector.java b/extensions-contrib/sqlserver-metadata-storage/src/main/java/io/druid/metadata/storage/sqlserver/SQLServerConnector.java new file mode 100644 index 000000000000..5ea0ca79f897 --- /dev/null +++ b/extensions-contrib/sqlserver-metadata-storage/src/main/java/io/druid/metadata/storage/sqlserver/SQLServerConnector.java @@ -0,0 +1,283 @@ + +/* + * Licensed to Metamarkets Group Inc. (Metamarkets) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. Metamarkets 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 io.druid.metadata.storage.sqlserver; + +import java.sql.SQLException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Pattern; + +import org.apache.commons.dbcp2.BasicDataSource; +import org.skife.jdbi.v2.Binding; +import org.skife.jdbi.v2.ColonPrefixNamedParamStatementRewriter; +import org.skife.jdbi.v2.DBI; +import org.skife.jdbi.v2.Handle; +import org.skife.jdbi.v2.StatementContext; +import org.skife.jdbi.v2.tweak.HandleCallback; +import org.skife.jdbi.v2.tweak.RewrittenStatement; +import org.skife.jdbi.v2.tweak.StatementRewriter; +import org.skife.jdbi.v2.util.StringMapper; + +import com.google.common.base.Supplier; +import com.google.inject.Inject; +import com.metamx.common.logger.Logger; + +import io.druid.metadata.MetadataStorageConnectorConfig; +import io.druid.metadata.MetadataStorageTablesConfig; +import io.druid.metadata.SQLMetadataConnector; + +@SuppressWarnings("nls") +public class SQLServerConnector extends SQLMetadataConnector +{ + private static final Logger log = new Logger(SQLServerConnector.class); + + /** + *

+ *

+ * + *
+   *
+   * Sql Server equivalent to the SERIAL_TYPE value in other SQLMetadataConnectors.
+   *
+   * SqlServer - PAYLOAD_TYPE = "VARBINARY(MAX)"
+   *     Variable-length binary data
+   *
+   * PostgreSQL - PAYLOAD_TYPE = "BYTEA"
+   *     variable-length binary string
+   *
+   * MySQL - PAYLOAD_TYPE = "LONGBLOB"
+   *     a binary large object that can hold a variable amount of data
+   *
+   * 
+ * + *
+ *

+ * + * @see MS + * SQL Server Numeric Types + * @see io.druid.metadata.storage.postgresql.PostgreSQLConnector + * @see io.druid.metadata.storage.mysql.MySQLConnector + * + */ + private static final String PAYLOAD_TYPE = "VARBINARY(MAX)"; + + /** + * + *

+ *

+ * + *
+   * Sql Server equivalent to the SERIAL_TYPE value in other SQLMetadataConnectors.
+   *
+   * SqlServer - SERIAL_TYPE = "[bigint] IDENTITY (1, 1)"
+   *     The bigint range is from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
+   *
+   * PostgreSQL - SERIAL_TYPE ="BIGSERIAL"
+   *     The BIGSERIAL range is from 1 to 9223372036854775807
+   *
+   * MySQL - SERIAL_TYPE = "BIGINT(20) AUTO_INCREMENT"
+   *     The BIGINT range is from -9223372036854775808 to 9223372036854775807
+   *     Also note that "SERIAL" is an alias for "BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE"
+   *
+   * 
+ * + *
+ *

+ * + * @see MS SQL Server Numeric Types + * @see io.druid.metadata.storage.postgresql.PostgreSQLConnector + * @see io.druid.metadata.storage.mysql.MySQLConnector + * + * + * + */ + private static final String SERIAL_TYPE = "[bigint] IDENTITY (1, 1)"; + + public static final int DEFAULT_STREAMING_RESULT_SIZE = 100; + + private final DBI dbi; + + /** + *

+ *

+ * + *
+   * Classify Transient Sql State Codes
+   * 
+ * + *
+ *

+ * + * @see Spring Framework SQLStateSQLExceptionTranslator + * @see java.sql.SQLException#getSQLState() + */ + private final Set TRANSIENT_SQL_CLASS_CODES = new HashSet<>(Arrays.asList( + "08", "53", "54", "57", "58", // Resource Failures + "JW", "JZ", "S1", // Transient Failures + "40" // Transaction Rollback + )); + + @Inject + public SQLServerConnector(Supplier config, Supplier dbTables) + { + super(config, dbTables); + + final BasicDataSource datasource = getDatasource(); + datasource.setDriverClassLoader(getClass().getClassLoader()); + datasource.setDriverClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); + + this.dbi = new DBI(datasource); + + this.dbi.setStatementRewriter(new CustomStatementRewriter()); + + log.info("Configured Sql Server as metadata storage"); + } + + public static class CustomStatementRewriter implements StatementRewriter + { + + private static final Pattern REWRITE_PATTERN1 = Pattern.compile("(?i)BOOLEAN NOT NULL DEFAULT FALSE"); + private static final Pattern REWRITE_PATTERN2 = Pattern.compile("(?i)BOOLEAN NOT NULL DEFAULT TRUE"); + private static final Pattern REWRITE_PATTERN3 = Pattern.compile("(?i)BOOLEAN DEFAULT FALSE"); + private static final Pattern REWRITE_PATTERN4 = Pattern.compile("(?i)BOOLEAN DEFAULT TRUE"); + private static final Pattern REWRITE_PATTERN5 = Pattern.compile("(?i)BOOLEAN"); + private static final Pattern REWRITE_PATTERN6 = Pattern.compile("(?i)TRUE"); + private static final Pattern REWRITE_PATTERN7 = Pattern.compile("(?i)FALSE"); + + private ColonPrefixNamedParamStatementRewriter colonPrefixNamedParamStatementRewriter = new ColonPrefixNamedParamStatementRewriter(); + + @Override + public RewrittenStatement rewrite(String sql, Binding params, StatementContext ctx) + { + + String currentSql = sql; + currentSql = REWRITE_PATTERN1.matcher(currentSql).replaceAll("BIT NOT NULL DEFAULT (0)"); + currentSql = REWRITE_PATTERN2.matcher(currentSql).replaceAll("BIT NOT NULL DEFAULT (1)"); + currentSql = REWRITE_PATTERN3.matcher(currentSql).replaceAll("BIT NOT NULL DEFAULT (0)"); + currentSql = REWRITE_PATTERN4.matcher(currentSql).replaceAll("BIT NOT NULL DEFAULT (1)"); + currentSql = REWRITE_PATTERN5.matcher(currentSql).replaceAll("BIT"); + currentSql = REWRITE_PATTERN6.matcher(currentSql).replaceAll("1"); + currentSql = REWRITE_PATTERN7.matcher(currentSql).replaceAll("0"); + + return (colonPrefixNamedParamStatementRewriter).rewrite(currentSql, params, ctx); + + } + } + + @Override + protected String getPayloadType() + { + return PAYLOAD_TYPE; + } + + @Override + protected String getSerialType() + { + return SERIAL_TYPE; + } + + @Override + protected int getStreamingFetchSize() + { + return DEFAULT_STREAMING_RESULT_SIZE; + } + + @Override + public boolean tableExists(final Handle handle, final String tableName) + { + return !handle.createQuery("SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = :tableName") + .bind("tableName", tableName) + .map(StringMapper.FIRST) + .list() + .isEmpty(); + } + + /** + * + * {@inheritDoc} + * + * @see http://stackoverflow.com/questions/1197733/does-sql-server-offer-anything-like-mysqls-on-duplicate-key-update + * + */ + @Override + public Void insertOrUpdate( + final String tableName, + final String keyColumn, + final String valueColumn, + final String key, + final byte[] value) throws Exception + { + return getDBI().withHandle( + new HandleCallback() + { + @Override + public Void withHandle(Handle handle) throws Exception + { + handle.createStatement(String.format( + "MERGE INTO %1$s WITH (UPDLOCK, HOLDLOCK) as target" + + " USING " + + " (:key, :value) as source (%2$s, %3$s)" + + " ON" + + " (target.%2$s = source.%2$s)" + + " WHEN MATCHED THEN UPDATE SET %3$s = :value" + + " WHEN NOT MATCHED THEN INSERT (%2$s, %3$s) VALUES (:key, :value)", + tableName, + keyColumn, + valueColumn)) + .bind("key", key) + .bind("value", value) + .execute(); + + return null; + } + }); + } + + @Override + public DBI getDBI() + { + return dbi; + } + + /** + * + * {@inheritDoc} + * + * @see java.sql.SQLException#getSQLState() + * + */ + @Override + protected boolean connectorIsTransientException(Throwable e) + { + if (e instanceof SQLException) { + final String sqlState = ((SQLException) e).getSQLState(); + if (sqlState == null) { + return false; + } + + final String sqlClassCode = sqlState.substring(0, 2); + if (TRANSIENT_SQL_CLASS_CODES.contains(sqlClassCode)) { + return true; + } + } + return false; + } +} diff --git a/extensions-contrib/sqlserver-metadata-storage/src/main/java/io/druid/metadata/storage/sqlserver/SQLServerMetadataStorageModule.java b/extensions-contrib/sqlserver-metadata-storage/src/main/java/io/druid/metadata/storage/sqlserver/SQLServerMetadataStorageModule.java new file mode 100644 index 000000000000..51d9cab41c7e --- /dev/null +++ b/extensions-contrib/sqlserver-metadata-storage/src/main/java/io/druid/metadata/storage/sqlserver/SQLServerMetadataStorageModule.java @@ -0,0 +1,65 @@ +/* + * Licensed to Metamarkets Group Inc. (Metamarkets) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. Metamarkets 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 io.druid.metadata.storage.sqlserver; + +import com.google.common.collect.ImmutableList; +import com.google.inject.Binder; +import com.google.inject.Key; +import io.druid.guice.LazySingleton; +import io.druid.guice.PolyBind; +import io.druid.guice.SQLMetadataStorageDruidModule; +import io.druid.initialization.DruidModule; +import io.druid.metadata.MetadataStorageConnector; +import io.druid.metadata.SQLMetadataConnector; + +import java.util.List; + +@SuppressWarnings("nls") +public class SQLServerMetadataStorageModule extends SQLMetadataStorageDruidModule implements DruidModule +{ + + public static final String TYPE = "sqlserver"; + + public SQLServerMetadataStorageModule() + { + super(TYPE); + } + + @Override + public List getJacksonModules() + { + return ImmutableList.of(); + } + + @Override + public void configure(Binder binder) + { + super.configure(binder); + + PolyBind.optionBinder(binder, Key.get(MetadataStorageConnector.class)) + .addBinding(TYPE) + .to(SQLServerConnector.class) + .in(LazySingleton.class); + + PolyBind.optionBinder(binder, Key.get(SQLMetadataConnector.class)) + .addBinding(TYPE) + .to(SQLServerConnector.class) + .in(LazySingleton.class); + } +} diff --git a/extensions-contrib/sqlserver-metadata-storage/src/main/resources/META-INF/services/io.druid.initialization.DruidModule b/extensions-contrib/sqlserver-metadata-storage/src/main/resources/META-INF/services/io.druid.initialization.DruidModule new file mode 100644 index 000000000000..3b2d6d4d271f --- /dev/null +++ b/extensions-contrib/sqlserver-metadata-storage/src/main/resources/META-INF/services/io.druid.initialization.DruidModule @@ -0,0 +1 @@ +io.druid.metadata.storage.sqlserver.SQLServerMetadataStorageModule diff --git a/extensions-contrib/sqlserver-metadata-storage/src/test/java/io/druid/metadata/storage/sqlserver/CustomStatementRewriterTest.java b/extensions-contrib/sqlserver-metadata-storage/src/test/java/io/druid/metadata/storage/sqlserver/CustomStatementRewriterTest.java new file mode 100644 index 000000000000..2bb511b2e6dd --- /dev/null +++ b/extensions-contrib/sqlserver-metadata-storage/src/test/java/io/druid/metadata/storage/sqlserver/CustomStatementRewriterTest.java @@ -0,0 +1,160 @@ +/* + * Licensed to Metamarkets Group Inc. (Metamarkets) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. Metamarkets 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 io.druid.metadata.storage.sqlserver; + +import org.junit.Before; +import org.junit.Test; +import org.skife.jdbi.v2.Binding; +import org.skife.jdbi.v2.StatementContext; +import org.skife.jdbi.v2.exceptions.UnableToCreateStatementException; +import org.skife.jdbi.v2.tweak.RewrittenStatement; + +import io.druid.metadata.storage.sqlserver.SQLServerConnector.CustomStatementRewriter; +import junit.framework.Assert; + +@SuppressWarnings("nls") +public class CustomStatementRewriterTest +{ + + private CustomStatementRewriter customStatementRewriter; + private Binding params; + private StatementContext ctx; + + @Before + public void setUp() + { + customStatementRewriter = new CustomStatementRewriter(); + + params = null; + ctx = null; + } + + private String rewrite(String sql) + { + RewrittenStatement rewrittenStatement = customStatementRewriter.rewrite(sql, params, ctx); + return rewrittenStatement.getSql(); + } + + @Test + public void testExactPatternReplacement() + { + + Assert.assertEquals("BIT NOT NULL DEFAULT (0)", rewrite("BOOLEAN NOT NULL DEFAULT FALSE")); + Assert.assertEquals("BIT NOT NULL DEFAULT (1)", rewrite("BOOLEAN NOT NULL DEFAULT TRUE")); + Assert.assertEquals("BIT NOT NULL DEFAULT (0)", rewrite("BOOLEAN DEFAULT FALSE")); + Assert.assertEquals("BIT NOT NULL DEFAULT (1)", rewrite("BOOLEAN DEFAULT TRUE")); + Assert.assertEquals("BIT", rewrite("BOOLEAN")); + Assert.assertEquals("1", rewrite("TRUE")); + Assert.assertEquals("0", rewrite("FALSE")); + } + + /** + * + * @see org.skife.jdbi.v2.TestColonStatementRewriter + * @see https://github.com/jdbi/jdbi/blob/master/src/test/java/org/skife/jdbi/v2/TestColonStatementRewriter.java + * + */ + @Test + public void testCustomStatementRewriter() + { + + Assert.assertEquals("select column# from table1 where id = ?", + rewrite("select column# from table1 where id = :id")); + + Assert.assertEquals("select * from table2\n where id = ?", rewrite("select * from table2\n where id = :id")); + + try { + rewrite("select * from table3 where id = :\u0091\u009c"); // Control codes + // - + // https://en.wikipedia.org/wiki/List_of_Unicode_characters + Assert.fail("Expected 'UnableToCreateStatementException'"); + } catch (UnableToCreateStatementException e) { + // + } + + } + + /** + * + * @see io.druid.metadata.SQLMetadataConnector#createTable(String, Iterable) + * + */ + @Test + public void testSQLMetadataConnectorCreateTable() + { + String sqlIn = "CREATE TABLE %1$s (\n" + + " id VARCHAR(255) NOT NULL,\n" + + " dataSource VARCHAR(255) NOT NULL,\n" + + " created_date VARCHAR(255) NOT NULL,\n" + + " start VARCHAR(255) NOT NULL,\n" + + " \"end\" VARCHAR(255) NOT NULL,\n" + + " partitioned BOOLEAN NOT NULL,\n" + + " version VARCHAR(255) NOT NULL,\n" + + " used BOOLEAN NOT NULL,\n" + + " payload %2$s NOT NULL,\n" + + " PRIMARY KEY (id)\n" + + ")"; + + String sqlOut = "CREATE TABLE %1$s (\n" + + " id VARCHAR(255) NOT NULL,\n" + + " dataSource VARCHAR(255) NOT NULL,\n" + + " created_date VARCHAR(255) NOT NULL,\n" + + " start VARCHAR(255) NOT NULL,\n" + + " \"end\" VARCHAR(255) NOT NULL,\n" + + " partitioned BIT NOT NULL,\n" + + " version VARCHAR(255) NOT NULL,\n" + + " used BIT NOT NULL,\n" + + " payload %2$s NOT NULL,\n" + + " PRIMARY KEY (id)\n" + + ")"; + + Assert.assertEquals(sqlOut, rewrite(sqlIn)); + + } + + /** + * + * @see io.druid.metadata.SQLMetadataStorageActionHandler#setStatus(String, + * boolean, Object) + * + */ + @Test + public void testSQLMetadataStorageActionHandlerSetStatus() + { + Assert.assertEquals("UPDATE %s SET active = ?, status_payload = ? WHERE id = ? AND active = 1", + rewrite("UPDATE %s SET active = :active, status_payload = :status_payload WHERE id = :id AND active = TRUE")); + + } + + /** + * + * @see io.druid.metadata.SQLMetadataStorageActionHandler#getInactiveStatusesSince(org.joda.time.DateTime) + * + */ + @Test + public void testSQLMetadataStorageActionHandlerGetInactiveStatusesSince() + { + Assert.assertEquals( + "SELECT id, status_payload FROM %s WHERE active = 0 AND created_date >= ? ORDER BY created_date DESC", + rewrite( + "SELECT id, status_payload FROM %s WHERE active = FALSE AND created_date >= :start ORDER BY created_date DESC")); + } + +} diff --git a/extensions-contrib/sqlserver-metadata-storage/src/test/java/io/druid/metadata/storage/sqlserver/SQLServerConnectorTest.java b/extensions-contrib/sqlserver-metadata-storage/src/test/java/io/druid/metadata/storage/sqlserver/SQLServerConnectorTest.java new file mode 100644 index 000000000000..7c90fd8440b4 --- /dev/null +++ b/extensions-contrib/sqlserver-metadata-storage/src/test/java/io/druid/metadata/storage/sqlserver/SQLServerConnectorTest.java @@ -0,0 +1,68 @@ +/* + * Licensed to Metamarkets Group Inc. (Metamarkets) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. Metamarkets 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 io.druid.metadata.storage.sqlserver; + +import java.sql.SQLException; + +import org.junit.Assert; +import org.junit.Test; + +import com.google.common.base.Suppliers; + +import io.druid.metadata.MetadataStorageConnectorConfig; +import io.druid.metadata.MetadataStorageTablesConfig; + +@SuppressWarnings("nls") +public class SQLServerConnectorTest +{ + + @Test + public void testIsTransientException() throws Exception + { + SQLServerConnector connector = new SQLServerConnector( + Suppliers.ofInstance(new MetadataStorageConnectorConfig()), + Suppliers.ofInstance( + new MetadataStorageTablesConfig( + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null + ) + ) + ); + + Assert.assertTrue(connector.isTransientException(new SQLException("Resource Failure!", "08DIE"))); + Assert.assertTrue(connector.isTransientException(new SQLException("Resource Failure as well!", "53RES"))); + Assert.assertTrue(connector.isTransientException(new SQLException("Transient Failures", "JW001"))); + Assert.assertTrue(connector.isTransientException(new SQLException("Transient Rollback", "40001"))); + + Assert.assertFalse(connector.isTransientException(new SQLException("SQLException with reason only"))); + Assert.assertFalse(connector.isTransientException(new SQLException())); + Assert.assertFalse(connector.isTransientException(new Exception("Exception with reason only"))); + Assert.assertFalse(connector.isTransientException(new Throwable("Throwable with reason only"))); + } + +}