This is an automated email from the ASF dual-hosted git repository.

ayushtkn pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hive.git


The following commit(s) were added to refs/heads/master by this push:
     new 2b189ebcdad HIVE-26441: Expand GenericJdbcDatabaseAccessor unit test 
coverage (#6587)
2b189ebcdad is described below

commit 2b189ebcdadb3846e1580e1627f16152d5218f49
Author: Hemanth Umashankar 
<[email protected]>
AuthorDate: Wed Jul 29 00:02:59 2026 +0530

    HIVE-26441: Expand GenericJdbcDatabaseAccessor unit test coverage (#6587)
---
 .../dao/TestDatabaseAccessorByDatabaseType.java    | 537 +++++++++++++++++++++
 .../jdbc/dao/TestGenericJdbcDatabaseAccessor.java  | 317 ++++++++++++
 jdbc-handler/src/test/resources/test_script.sql    |   7 +
 3 files changed, 861 insertions(+)

diff --git 
a/jdbc-handler/src/test/java/org/apache/hive/storage/jdbc/dao/TestDatabaseAccessorByDatabaseType.java
 
b/jdbc-handler/src/test/java/org/apache/hive/storage/jdbc/dao/TestDatabaseAccessorByDatabaseType.java
new file mode 100644
index 00000000000..b1ded74b0ea
--- /dev/null
+++ 
b/jdbc-handler/src/test/java/org/apache/hive/storage/jdbc/dao/TestDatabaseAccessorByDatabaseType.java
@@ -0,0 +1,537 @@
+/*
+ * 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.hive.storage.jdbc.dao;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hive.serde.serdeConstants;
+import org.apache.hive.storage.jdbc.conf.DatabaseType;
+import org.apache.hive.storage.jdbc.conf.JdbcStorageConfig;
+import org.apache.hive.storage.jdbc.exception.HiveJdbcDatabaseAccessException;
+
+import org.junit.Test;
+import org.junit.experimental.runners.Enclosed;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Locale;
+import java.util.function.Function;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.equalToIgnoringCase;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@RunWith(Enclosed.class)
+public class TestDatabaseAccessorByDatabaseType {
+
+  private static final String BASE = "select * from test_strategy";
+  private static final String H2_DRIVER = "org.h2.Driver";
+  private static final String R = OracleDatabaseAccessor.ROW_NUM_COLUMN_NAME;
+
+  @RunWith(Parameterized.class)
+  public static class AddLimitToQueryWithLimit {
+    private final DatabaseType databaseType;
+    private final String expectedQuery;
+
+    public AddLimitToQueryWithLimit(DatabaseType databaseType, String 
expectedQuery) {
+      this.databaseType = databaseType;
+      this.expectedQuery = expectedQuery;
+    }
+
+    @Parameters(name = "{0}")
+    public static Collection<Object[]> data() {
+      return queryRows(database -> database.limit2);
+    }
+
+    @Test
+    public void testAddLimitToQuery_withLimit() {
+      assertThat(accessor(databaseType).addLimitToQuery(BASE, 2), 
is(equalTo(expectedQuery)));
+    }
+  }
+
+  @RunWith(Parameterized.class)
+  public static class AddLimitToQueryNoLimit {
+    private final DatabaseType databaseType;
+    private final String expectedQuery;
+
+    public AddLimitToQueryNoLimit(DatabaseType databaseType, String 
expectedQuery) {
+      this.databaseType = databaseType;
+      this.expectedQuery = expectedQuery;
+    }
+
+    @Parameters(name = "{0}")
+    public static Collection<Object[]> data() {
+      return queryRows(database -> database.noLimit);
+    }
+
+    @Test
+    public void testAddLimitToQuery_noLimit() {
+      assertThat(accessor(databaseType).addLimitToQuery(BASE, -1), 
is(equalTo(expectedQuery)));
+    }
+  }
+
+  @RunWith(Parameterized.class)
+  public static class AddLimitAndOffsetToQueryLimitAndOffset {
+    private final DatabaseType databaseType;
+    private final String expectedQuery;
+
+    public AddLimitAndOffsetToQueryLimitAndOffset(DatabaseType databaseType, 
String expectedQuery) {
+      this.databaseType = databaseType;
+      this.expectedQuery = expectedQuery;
+    }
+
+    @Parameters(name = "{0}")
+    public static Collection<Object[]> data() {
+      return queryRows(database -> database.limitAndOffset);
+    }
+
+    @Test
+    public void testAddLimitAndOffsetToQuery_limitAndOffset() {
+      assertThat(accessor(databaseType).addLimitAndOffsetToQuery(BASE, 2, 1), 
is(equalTo(expectedQuery)));
+    }
+  }
+
+  @RunWith(Parameterized.class)
+  public static class AddLimitAndOffsetToQueryOffsetOnly {
+    private final DatabaseType databaseType;
+    private final String expectedQuery;
+
+    public AddLimitAndOffsetToQueryOffsetOnly(DatabaseType databaseType, 
String expectedQuery) {
+      this.databaseType = databaseType;
+      this.expectedQuery = expectedQuery;
+    }
+
+    @Parameters(name = "{0}")
+    public static Collection<Object[]> data() {
+      return queryRows(database -> database.offsetOnly);
+    }
+
+    @Test
+    public void testAddLimitAndOffsetToQuery_offsetOnly() {
+      assertThat(accessor(databaseType).addLimitAndOffsetToQuery(BASE, -1, 2), 
is(equalTo(expectedQuery)));
+    }
+  }
+
+  @RunWith(Parameterized.class)
+  public static class AddLimitAndOffsetToQueryOffsetZero {
+    private final DatabaseType databaseType;
+    private final String expectedQuery;
+
+    public AddLimitAndOffsetToQueryOffsetZero(DatabaseType databaseType, 
String expectedQuery) {
+      this.databaseType = databaseType;
+      this.expectedQuery = expectedQuery;
+    }
+
+    @Parameters(name = "{0}")
+    public static Collection<Object[]> data() {
+      return queryRows(database -> database.offsetZero);
+    }
+
+    @Test
+    public void testAddLimitAndOffsetToQuery_offsetZero() {
+      assertThat(accessor(databaseType).addLimitAndOffsetToQuery(BASE, 2, 0), 
is(equalTo(expectedQuery)));
+    }
+  }
+
+  @RunWith(Parameterized.class)
+  public static class NeedColumnQuote {
+    private final DatabaseType databaseType;
+    private final boolean expectedNeedColumnQuote;
+
+    public NeedColumnQuote(DatabaseType databaseType, boolean 
expectedNeedColumnQuote) {
+      this.databaseType = databaseType;
+      this.expectedNeedColumnQuote = expectedNeedColumnQuote;
+    }
+
+    @Parameters(name = "{0}")
+    public static Collection<Object[]> data() {
+      return rowsFor(database -> database.needColumnQuote);
+    }
+
+    @Test
+    public void testNeedColumnQuote() {
+      assertThat(accessor(databaseType).needColumnQuote(), 
is(expectedNeedColumnQuote));
+    }
+  }
+
+  @RunWith(Parameterized.class)
+  public static class ConstructQuery {
+    private final DatabaseType databaseType;
+    private final String expectedQuery;
+
+    public ConstructQuery(DatabaseType databaseType, String expectedQuery) {
+      this.databaseType = databaseType;
+      this.expectedQuery = expectedQuery;
+    }
+
+    @Parameters(name = "{0}")
+    public static Collection<Object[]> data() {
+      return queryRows(database -> database.constructQuery);
+    }
+
+    @Test
+    public void testConstructQuery() {
+      assertThat(accessor(databaseType).constructQuery("t", new String[]{"a", 
"b", "c"}),
+          is(equalTo(expectedQuery)));
+    }
+  }
+
+  @RunWith(Parameterized.class)
+  public static class GetMetaDataQuery {
+    private final DatabaseType databaseType;
+    private final String expectedQuery;
+
+    public GetMetaDataQuery(DatabaseType databaseType, String expectedQuery) {
+      this.databaseType = databaseType;
+      this.expectedQuery = expectedQuery;
+    }
+
+    @Parameters(name = "{0}")
+    public static Collection<Object[]> data() {
+      return queryRows(database -> database.metaDataQuery);
+    }
+
+    @Test
+    public void testGetMetaDataQuery() {
+      assertThat(accessor(databaseType).getMetaDataQuery(BASE), 
is(equalTo(expectedQuery)));
+    }
+  }
+
+  @RunWith(Parameterized.class)
+  public static class AccessorType {
+    private final DatabaseType databaseType;
+
+    public AccessorType(DatabaseType databaseType) {
+      this.databaseType = databaseType;
+    }
+
+    @Parameters(name = "{0}")
+    public static Collection<Object[]> data() {
+      return rowsFor(database -> null);
+    }
+
+    @Test
+    public void testAccessorType() {
+      
assertThat(DatabaseAccessorFactory.getAccessor(buildConfiguration(databaseType)),
+          instanceOf(accessorCase(databaseType).expectedType));
+    }
+  }
+
+  @RunWith(Parameterized.class)
+  public static class FactoryMapping {
+    private final DatabaseType databaseType;
+    private final String driverClass;
+    private final Class<? extends DatabaseAccessor> expectedType;
+
+    public FactoryMapping(DatabaseType databaseType, String driverClass,
+        Class<? extends DatabaseAccessor> expectedType) {
+      this.databaseType = databaseType;
+      this.driverClass = driverClass;
+      this.expectedType = expectedType;
+    }
+
+    @Parameters(name = "{0}:{1}")
+    public static Collection<Object[]> data() {
+      return Arrays.asList(new Object[][]{
+          {DatabaseType.H2, "org.h2.Driver", 
GenericJdbcDatabaseAccessor.class},
+          {DatabaseType.METASTORE, "com.mysql.cj.jdbc.Driver", 
MySqlDatabaseAccessor.class},
+          {DatabaseType.METASTORE, "org.postgresql.Driver", 
PostgresDatabaseAccessor.class},
+          {DatabaseType.METASTORE, "oracle.jdbc.OracleDriver", 
OracleDatabaseAccessor.class},
+          {DatabaseType.METASTORE, 
"com.microsoft.sqlserver.jdbc.SQLServerDriver", MsSqlDatabaseAccessor.class},
+          {DatabaseType.METASTORE, "org.unknown.Driver", 
GenericJdbcDatabaseAccessor.class},
+      });
+    }
+
+    @Test
+    public void testFactoryMapping() {
+      Configuration config = new Configuration();
+      config.set(JdbcStorageConfig.DATABASE_TYPE.getPropertyName(), 
databaseType.name());
+      config.set(JdbcStorageConfig.JDBC_DRIVER_CLASS.getPropertyName(), 
driverClass);
+      assertThat(DatabaseAccessorFactory.getAccessor(config), 
instanceOf(expectedType));
+    }
+  }
+
+  public static class HiveColumnNames {
+    @Test
+    public void testGetColNamesFromRS_stripsQualifiedColumnNames() throws 
Exception {
+      ResultSet rs = mock(ResultSet.class);
+      ResultSetMetaData metadata = mock(ResultSetMetaData.class);
+      when(rs.getMetaData()).thenReturn(metadata);
+      when(metadata.getColumnCount()).thenReturn(3);
+      when(metadata.getColumnName(1)).thenReturn("test_strategy.strategy_id");
+      when(metadata.getColumnName(2)).thenReturn("name");
+      when(metadata.getColumnName(3)).thenReturn("db.schema.priority");
+
+      HiveDatabaseAccessor accessor = new HiveDatabaseAccessor();
+
+      assertThat(accessor.getColNamesFromRS(rs),
+          is(equalTo(Arrays.asList("strategy_id", "name", "priority"))));
+    }
+  }
+
+  @RunWith(Parameterized.class)
+  public static class H2CompatibleIntegration {
+    private final DatabaseType databaseType;
+
+    public H2CompatibleIntegration(DatabaseType databaseType) {
+      this.databaseType = databaseType;
+    }
+
+    @Parameters(name = "{0}")
+    public static Collection<Object[]> data() {
+      return Arrays.asList(new Object[][]{
+          {DatabaseType.MYSQL},
+          {DatabaseType.POSTGRES},
+          {DatabaseType.DB2},
+          {DatabaseType.DERBY},
+          {DatabaseType.HIVE},
+          {DatabaseType.JETHRO_DATA},
+      });
+    }
+
+    @Test
+    public void testGetRecordIterator_limitTwo() throws 
HiveJdbcDatabaseAccessException {
+      Configuration conf = buildIntegrationConfiguration(databaseType);
+      DatabaseAccessor accessor = DatabaseAccessorFactory.getAccessor(conf);
+      JdbcRecordIterator iterator = accessor.getRecordIterator(conf, null, 
null, null, 2, 0);
+
+      assertThat(iterator, is(notNullValue()));
+      int count = 0;
+      while (iterator.hasNext()) {
+        iterator.next();
+        count++;
+      }
+      assertThat(count, is(equalTo(2)));
+      iterator.close();
+    }
+
+    @Test
+    public void testGetColumnNames() throws HiveJdbcDatabaseAccessException {
+      Configuration conf = buildIntegrationConfiguration(databaseType);
+      DatabaseAccessor accessor = DatabaseAccessorFactory.getAccessor(conf);
+      List<String> columnNames = accessor.getColumnNames(conf);
+
+      assertThat(columnNames, is(notNullValue()));
+      assertThat(columnNames.size(), is(equalTo(7)));
+      assertThat(columnNames.get(0), is(equalToIgnoringCase("strategy_id")));
+    }
+
+    @Test
+    public void testGetBounds_minAndMax() throws 
HiveJdbcDatabaseAccessException {
+      Configuration conf = buildIntegrationConfiguration(databaseType);
+      DatabaseAccessor accessor = DatabaseAccessorFactory.getAccessor(conf);
+      Pair<String, String> bounds = accessor.getBounds(conf, 
accessor.getColumnNames(conf).get(0), true, true);
+
+      assertThat(bounds.getLeft(), is(equalTo("1")));
+      assertThat(bounds.getRight(), is(equalTo("5")));
+    }
+  }
+
+  private static final AccessorCase[] CASES = {
+      db(DatabaseType.MYSQL, MySqlDatabaseAccessor.class,
+          BASE + " LIMIT 2",
+          BASE,
+          BASE + " LIMIT 1,2",
+          BASE,
+          BASE + " LIMIT 2",
+          false,
+          "INSERT INTO t VALUES (?,?,?);",
+          BASE + " LIMIT 1"),
+      db(DatabaseType.POSTGRES, PostgresDatabaseAccessor.class,
+          BASE + " LIMIT 2",
+          BASE,
+          BASE + " LIMIT 2 OFFSET 1",
+          BASE,
+          BASE + " LIMIT 2",
+          true,
+          "INSERT INTO t VALUES (?,?,?);",
+          BASE + " LIMIT 1"),
+      db(DatabaseType.DB2, DB2DatabaseAccessor.class,
+          BASE + " LIMIT 2",
+          BASE,
+          BASE + " LIMIT 2 OFFSET 1",
+          BASE,
+          BASE + " LIMIT 2",
+          true,
+          "INSERT INTO t VALUES (?,?,?)",
+          BASE + " LIMIT 1"),
+      db(DatabaseType.DERBY, DerbyDatabaseAccessor.class,
+          BASE + " {LIMIT 2}",
+          BASE,
+          BASE + " {LIMIT 2 OFFSET 1}",
+          BASE + " {OFFSET 2}",
+          BASE + " {LIMIT 2}",
+          true,
+          "INSERT INTO t VALUES (?,?,?)",
+          BASE + " {LIMIT 1}"),
+      db(DatabaseType.MSSQL, MsSqlDatabaseAccessor.class,
+          BASE + " {LIMIT 2}",
+          BASE,
+          BASE + " ORDER BY 1 OFFSET 1 ROWS FETCH NEXT 2 ROWS ONLY",
+          BASE,
+          BASE + " {LIMIT 2}",
+          true,
+          "INSERT INTO t VALUES (?,?,?);",
+          BASE + " {LIMIT 1}"),
+      db(DatabaseType.ORACLE, OracleDatabaseAccessor.class,
+          "SELECT * FROM (" + BASE + ") WHERE ROWNUM <= 2",
+          BASE,
+          "SELECT * FROM (SELECT t.*, ROWNUM AS " + R + " FROM (" + BASE + ") 
t) WHERE "
+              + R + " >1 AND " + R + " <=3",
+          BASE,
+          "SELECT * FROM (" + BASE + ") WHERE ROWNUM <= 2",
+          true,
+          "INSERT INTO t VALUES (?,?,?)",
+          "SELECT * FROM (" + BASE + ") WHERE ROWNUM <= 1"),
+      db(DatabaseType.HIVE, HiveDatabaseAccessor.class,
+          BASE + " LIMIT 2",
+          BASE,
+          BASE + " LIMIT 2 OFFSET 1",
+          BASE,
+          BASE + " LIMIT 2",
+          true,
+          "INSERT INTO t VALUES (?,?,?);",
+          BASE + " LIMIT 0"),
+      db(DatabaseType.JETHRO_DATA, JethroDatabaseAccessor.class,
+          "Select * from (" + BASE + ") as \"tmp\" limit 2",
+          "Select * from (" + BASE + ") as \"tmp\" limit -1",
+          BASE + " LIMIT 1,2",
+          BASE + " LIMIT 2,-1",
+          "Select * from (" + BASE + ") as \"tmp\" limit 2",
+          true,
+          "INSERT INTO t VALUES (?,?,?);",
+          "Select * from (" + BASE + ") as \"tmp\" limit 0")
+  };
+
+  private static Collection<Object[]> rowsFor(Function<AccessorCase, Object> 
expectedValue) {
+    Object[][] rows = new Object[CASES.length][];
+    for (int i = 0; i < CASES.length; i++) {
+      AccessorCase database = CASES[i];
+      Object expected = expectedValue.apply(database);
+      rows[i] = expected == null
+          ? new Object[]{database.databaseType}
+          : new Object[]{database.databaseType, expected};
+    }
+    return Arrays.asList(rows);
+  }
+
+  private static Collection<Object[]> queryRows(Function<AccessorCase, String> 
expectedQuery) {
+    return rowsFor(database -> expectedQuery.apply(database));
+  }
+
+  private static AccessorCase db(
+      DatabaseType databaseType,
+      Class<? extends GenericJdbcDatabaseAccessor> expectedType,
+      String limit2,
+      String noLimit,
+      String limitAndOffset,
+      String offsetOnly,
+      String offsetZero,
+      boolean needColumnQuote,
+      String constructQuery,
+      String metaDataQuery) {
+    return new AccessorCase(databaseType, expectedType, limit2, noLimit, 
limitAndOffset,
+        offsetOnly, offsetZero, needColumnQuote, constructQuery, 
metaDataQuery);
+  }
+
+  private static AccessorCase accessorCase(DatabaseType databaseType) {
+    for (AccessorCase accessorCase : CASES) {
+      if (accessorCase.databaseType == databaseType) {
+        return accessorCase;
+      }
+    }
+    throw new IllegalArgumentException("Unexpected database type: " + 
databaseType);
+  }
+
+  private static GenericJdbcDatabaseAccessor accessor(DatabaseType 
databaseType) {
+    DatabaseAccessor accessor = 
DatabaseAccessorFactory.getAccessor(buildConfiguration(databaseType));
+    assertThat(accessor, instanceOf(accessorCase(databaseType).expectedType));
+    return (GenericJdbcDatabaseAccessor) accessor;
+  }
+
+  private static Configuration buildConfiguration(DatabaseType databaseType) {
+    Configuration config = new Configuration();
+    config.set(JdbcStorageConfig.DATABASE_TYPE.getPropertyName(), 
databaseType.name());
+    config.set(JdbcStorageConfig.JDBC_DRIVER_CLASS.getPropertyName(), 
H2_DRIVER);
+    return config;
+  }
+
+  private static Configuration buildIntegrationConfiguration(DatabaseType 
databaseType) {
+    String scriptPath =
+        
TestDatabaseAccessorByDatabaseType.class.getClassLoader().getResource("test_script.sql").getPath();
+    Configuration config = new Configuration();
+    config.set(JdbcStorageConfig.DATABASE_TYPE.getPropertyName(), 
databaseType.name());
+    config.set(JdbcStorageConfig.JDBC_DRIVER_CLASS.getPropertyName(), 
H2_DRIVER);
+    config.set(JdbcStorageConfig.JDBC_URL.getPropertyName(),
+        "jdbc:h2:mem:test_dbtype_integration_" + 
databaseType.name().toLowerCase(Locale.ROOT)
+            + ";MODE=MySQL;INIT=runscript from '" + scriptPath + "'");
+    config.set(JdbcStorageConfig.QUERY.getPropertyName(), BASE);
+    config.set(serdeConstants.LIST_COLUMNS,
+        
"strategy_id,name,referrer,landing,priority,implementation,last_modified");
+    config.set(serdeConstants.LIST_COLUMN_TYPES, 
"int,string,string,string,int,string,timestamp");
+    return config;
+  }
+
+  private static class AccessorCase {
+    private final DatabaseType databaseType;
+    private final Class<? extends GenericJdbcDatabaseAccessor> expectedType;
+    private final String limit2;
+    private final String noLimit;
+    private final String limitAndOffset;
+    private final String offsetOnly;
+    private final String offsetZero;
+    private final boolean needColumnQuote;
+    private final String constructQuery;
+    private final String metaDataQuery;
+
+    private AccessorCase(
+        DatabaseType databaseType,
+        Class<? extends GenericJdbcDatabaseAccessor> expectedType,
+        String limit2,
+        String noLimit,
+        String limitAndOffset,
+        String offsetOnly,
+        String offsetZero,
+        boolean needColumnQuote,
+        String constructQuery,
+        String metaDataQuery) {
+      this.databaseType = databaseType;
+      this.expectedType = expectedType;
+      this.limit2 = limit2;
+      this.noLimit = noLimit;
+      this.limitAndOffset = limitAndOffset;
+      this.offsetOnly = offsetOnly;
+      this.offsetZero = offsetZero;
+      this.needColumnQuote = needColumnQuote;
+      this.constructQuery = constructQuery;
+      this.metaDataQuery = metaDataQuery;
+    }
+  }
+
+}
diff --git 
a/jdbc-handler/src/test/java/org/apache/hive/storage/jdbc/dao/TestGenericJdbcDatabaseAccessor.java
 
b/jdbc-handler/src/test/java/org/apache/hive/storage/jdbc/dao/TestGenericJdbcDatabaseAccessor.java
index 98888ecd045..93e471bc7f8 100644
--- 
a/jdbc-handler/src/test/java/org/apache/hive/storage/jdbc/dao/TestGenericJdbcDatabaseAccessor.java
+++ 
b/jdbc-handler/src/test/java/org/apache/hive/storage/jdbc/dao/TestGenericJdbcDatabaseAccessor.java
@@ -14,16 +14,24 @@
  */
 package org.apache.hive.storage.jdbc.dao;
 
+import org.apache.commons.lang3.tuple.Pair;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.hive.serde.serdeConstants;
 import org.apache.hadoop.hive.serde2.typeinfo.TypeInfo;
 import org.apache.hadoop.hive.serde2.typeinfo.TypeInfoFactory;
+import org.apache.hadoop.mapreduce.RecordWriter;
+import org.apache.hadoop.mapreduce.TaskAttemptContext;
+import org.apache.hive.storage.jdbc.DBRecordWritable;
 import org.apache.hive.storage.jdbc.conf.JdbcStorageConfig;
 import org.apache.hive.storage.jdbc.exception.HiveJdbcDatabaseAccessException;
 
 import org.junit.Assert;
 import org.junit.Test;
 
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.Statement;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -31,11 +39,16 @@
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.equalToIgnoringCase;
 import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
 import static org.hamcrest.Matchers.notNullValue;
 import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 public class TestGenericJdbcDatabaseAccessor {
 
+  private static final String BOUNDARY_SQL = "select * from test_strategy";
+
   @Test
   public void testGetColumnNames_starQuery() throws 
HiveJdbcDatabaseAccessException {
     Configuration conf = buildConfiguration();
@@ -212,6 +225,29 @@ public void testGetRecordIterator_offsets() throws 
HiveJdbcDatabaseAccessExcepti
   }
 
 
+  @Test
+  public void testGetRecordIterator_limitOffsetLastPartition() throws 
HiveJdbcDatabaseAccessException {
+    Configuration conf = buildConfiguration();
+    DatabaseAccessor accessor = DatabaseAccessorFactory.getAccessor(conf);
+    JdbcRecordIterator iterator = accessor.getRecordIterator(conf, null, null, 
null, 2, 4);
+
+    assertThat(iterator, is(notNullValue()));
+
+    int count = 0;
+    while (iterator.hasNext()) {
+      Map<String, Object> record = iterator.next();
+      count++;
+
+      assertThat(record, is(notNullValue()));
+      assertThat(record.size(), is(equalTo(7)));
+      assertThat(record.get("strategy_id"), is(equalTo(5)));
+    }
+
+    assertThat(count, is(equalTo(1)));
+    iterator.close();
+  }
+
+
   @Test
   public void testGetRecordIterator_emptyResultSet() throws 
HiveJdbcDatabaseAccessException {
     Configuration conf = buildConfiguration();
@@ -225,6 +261,91 @@ public void testGetRecordIterator_emptyResultSet() throws 
HiveJdbcDatabaseAccess
   }
 
 
+  @Test
+  public void testGetRecordIterator_partitionFirst() throws 
HiveJdbcDatabaseAccessException {
+    Configuration conf = buildConfiguration();
+    DatabaseAccessor accessor = DatabaseAccessorFactory.getAccessor(conf);
+    JdbcRecordIterator iterator =
+        accessor.getRecordIterator(conf, getStrategyIdColumnName(conf, 
accessor), null, "3", -1, 0);
+
+    assertThat(iterator, is(notNullValue()));
+
+    int count = 0;
+    while (iterator.hasNext()) {
+      Map<String, Object> record = iterator.next();
+      count++;
+
+      assertThat(record, is(notNullValue()));
+      assertThat(record.size(), is(equalTo(7)));
+      assertThat(record.get("strategy_id"), is(equalTo(count)));
+    }
+
+    assertThat(count, is(equalTo(2)));
+    iterator.close();
+  }
+
+
+  @Test
+  public void testGetRecordIterator_partitionMiddle() throws 
HiveJdbcDatabaseAccessException {
+    Configuration conf = buildConfiguration();
+    DatabaseAccessor accessor = DatabaseAccessorFactory.getAccessor(conf);
+    JdbcRecordIterator iterator =
+        accessor.getRecordIterator(conf, getStrategyIdColumnName(conf, 
accessor), "3", "5", -1, 0);
+
+    assertThat(iterator, is(notNullValue()));
+
+    int count = 0;
+    while (iterator.hasNext()) {
+      Map<String, Object> record = iterator.next();
+      count++;
+
+      assertThat(record, is(notNullValue()));
+      assertThat(record.size(), is(equalTo(7)));
+      assertThat(record.get("strategy_id"), is(equalTo(count + 2)));
+    }
+
+    assertThat(count, is(equalTo(2)));
+    iterator.close();
+  }
+
+
+  @Test
+  public void testGetRecordIterator_partitionLast() throws 
HiveJdbcDatabaseAccessException {
+    Configuration conf = buildConfiguration();
+    DatabaseAccessor accessor = DatabaseAccessorFactory.getAccessor(conf);
+    JdbcRecordIterator iterator =
+        accessor.getRecordIterator(conf, getStrategyIdColumnName(conf, 
accessor), "5", null, -1, 0);
+
+    assertThat(iterator, is(notNullValue()));
+
+    int count = 0;
+    while (iterator.hasNext()) {
+      Map<String, Object> record = iterator.next();
+      count++;
+
+      assertThat(record, is(notNullValue()));
+      assertThat(record.size(), is(equalTo(7)));
+      assertThat(record.get("strategy_id"), is(equalTo(5)));
+    }
+
+    assertThat(count, is(equalTo(1)));
+    iterator.close();
+  }
+
+
+  @Test
+  public void testGetRecordIterator_partitionEmptyResultSet() throws 
HiveJdbcDatabaseAccessException {
+    Configuration conf = buildConfiguration();
+    DatabaseAccessor accessor = DatabaseAccessorFactory.getAccessor(conf);
+    JdbcRecordIterator iterator =
+        accessor.getRecordIterator(conf, getStrategyIdColumnName(conf, 
accessor), "6", "7", -1, 0);
+
+    assertThat(iterator, is(notNullValue()));
+    assertThat(iterator.hasNext(), is(false));
+    iterator.close();
+  }
+
+
   @Test
   public void testGetRecordIterator_largeOffset() throws 
HiveJdbcDatabaseAccessException {
     Configuration conf = buildConfiguration();
@@ -246,6 +367,202 @@ public void testGetRecordIterator_invalidQuery() throws 
HiveJdbcDatabaseAccessEx
       JdbcRecordIterator iterator = accessor.getRecordIterator(conf, null, 
null, null, 0, 2);
   }
 
+  @Test
+  public void testGetBounds_minAndMax() throws HiveJdbcDatabaseAccessException 
{
+    Configuration conf = buildConfiguration();
+    DatabaseAccessor accessor = DatabaseAccessorFactory.getAccessor(conf);
+    Pair<String, String> bounds = accessor.getBounds(conf, 
getStrategyIdColumnName(conf, accessor), true, true);
+
+    assertThat(bounds.getLeft(), is(equalTo("1")));
+    assertThat(bounds.getRight(), is(equalTo("5")));
+  }
+
+  @Test
+  public void testGetBounds_minOnly() throws HiveJdbcDatabaseAccessException {
+    Configuration conf = buildConfiguration();
+    DatabaseAccessor accessor = DatabaseAccessorFactory.getAccessor(conf);
+    Pair<String, String> bounds = accessor.getBounds(conf, 
getStrategyIdColumnName(conf, accessor), true, false);
+
+    assertThat(bounds.getLeft(), is(equalTo("1")));
+    assertThat(bounds.getRight(), is(nullValue()));
+  }
+
+  @Test
+  public void testGetBounds_maxOnly() throws HiveJdbcDatabaseAccessException {
+    Configuration conf = buildConfiguration();
+    DatabaseAccessor accessor = DatabaseAccessorFactory.getAccessor(conf);
+    Pair<String, String> bounds = accessor.getBounds(conf, 
getStrategyIdColumnName(conf, accessor), false, true);
+
+    assertThat(bounds.getLeft(), is(nullValue()));
+    assertThat(bounds.getRight(), is(equalTo("5")));
+  }
+
+  @Test
+  public void testAddBoundaryToQuery_bothBounds() {
+    GenericJdbcDatabaseAccessor accessor = new GenericJdbcDatabaseAccessor();
+    String result = accessor.addBoundaryToQuery(null, BOUNDARY_SQL, 
"strategy_id", "3", "5");
+
+    assertThat(result, is(equalTo(
+        "SELECT * FROM (" + BOUNDARY_SQL + ") tmptable WHERE \"strategy_id\" 
>= 3 AND \"strategy_id\" < 5")));
+  }
+
+  @Test
+  public void testAddBoundaryToQuery_upperOnlyIncludesNulls() {
+    GenericJdbcDatabaseAccessor accessor = new GenericJdbcDatabaseAccessor();
+    String result = accessor.addBoundaryToQuery(null, BOUNDARY_SQL, 
"strategy_id", null, "5");
+
+    assertThat(result, is(equalTo(
+        "SELECT * FROM (" + BOUNDARY_SQL + ") tmptable WHERE \"strategy_id\" < 
5 OR \"strategy_id\" IS NULL")));
+  }
+
+  @Test
+  public void testAddBoundaryToQuery_lowerOnly() {
+    GenericJdbcDatabaseAccessor accessor = new GenericJdbcDatabaseAccessor();
+    String result = accessor.addBoundaryToQuery(null, BOUNDARY_SQL, 
"strategy_id", "3", null);
+
+    assertThat(result, is(equalTo(
+        "SELECT * FROM (" + BOUNDARY_SQL + ") tmptable WHERE \"strategy_id\" 
>= 3")));
+  }
+
+  @Test
+  public void testAddBoundaryToQuery_rewritesNamedTable() {
+    GenericJdbcDatabaseAccessor accessor = new GenericJdbcDatabaseAccessor();
+    String result = accessor.addBoundaryToQuery("test_strategy",
+        "select * from test_strategy where priority > 0", "strategy_id", "3", 
"5");
+
+    assertThat(result, is(equalTo(
+        "select * from  (SELECT * FROM test_strategy WHERE \"strategy_id\" >= 
3 AND \"strategy_id\" < 5)"
+            + " tmptable  where priority > 0")));
+  }
+
+  @Test
+  public void testAddBoundaryToQuery_mysqlAccessorDoesNotQuoteColumn() {
+    MySqlDatabaseAccessor accessor = new MySqlDatabaseAccessor();
+    String result = accessor.addBoundaryToQuery(null, BOUNDARY_SQL, 
"strategy_id", "3", "5");
+
+    assertThat(result, is(equalTo(
+        "SELECT * FROM (" + BOUNDARY_SQL + ") tmptable WHERE strategy_id >= 3 
AND strategy_id < 5")));
+  }
+
+  @Test
+  public void testNeedColumnQuote_genericAccessor() {
+    Configuration conf = buildConfiguration();
+    DatabaseAccessor accessor = DatabaseAccessorFactory.getAccessor(conf);
+
+    assertThat(accessor.needColumnQuote(), is(true));
+  }
+
+  @Test
+  public void testNeedColumnQuote_mysqlAccessor() {
+    Configuration conf = buildConfiguration();
+    conf.set(JdbcStorageConfig.DATABASE_TYPE.getPropertyName(), "MYSQL");
+    DatabaseAccessor accessor = DatabaseAccessorFactory.getAccessor(conf);
+
+    assertThat(accessor.needColumnQuote(), is(false));
+  }
+
+  @Test
+  public void testClose_afterInitializedAccessor() throws Exception {
+    Configuration conf = buildConfiguration();
+    DatabaseAccessor accessor = DatabaseAccessorFactory.getAccessor(conf);
+
+    assertThat(accessor.getColumnNames(conf), is(notNullValue()));
+    accessor.close();
+    accessor.close();
+  }
+
+  @Test
+  public void testGetRecordWriter_writesRows() throws Exception {
+    Configuration conf = buildConfiguration();
+    conf.set(JdbcStorageConfig.JDBC_URL.getPropertyName(),
+        conf.get(JdbcStorageConfig.JDBC_URL.getPropertyName())
+            .replace("jdbc:h2:mem:test;MODE=MySQL;INIT",
+                "jdbc:h2:mem:test_writer;MODE=MySQL;DB_CLOSE_DELAY=-1;INIT"));
+    conf.set(JdbcStorageConfig.TABLE.getPropertyName(), "test_writer");
+    conf.set(serdeConstants.LIST_COLUMNS, "id,name");
+    conf.set(serdeConstants.LIST_COLUMN_TYPES, "int,string");
+
+    TaskAttemptContext context = mock(TaskAttemptContext.class);
+    when(context.getConfiguration()).thenReturn(conf);
+
+    DatabaseAccessor accessor = DatabaseAccessorFactory.getAccessor(conf);
+    @SuppressWarnings("rawtypes")
+    RecordWriter writer = accessor.getRecordWriter(context);
+    DBRecordWritable record = new DBRecordWritable(2);
+    record.set(0, 10);
+    record.set(1, "written");
+
+    writer.write(record, null);
+    writer.close(context);
+
+    String readBackUrl = 
conf.get(JdbcStorageConfig.JDBC_URL.getPropertyName()).replaceAll(";INIT=.*", 
"");
+    try (Connection conn = DriverManager.getConnection(readBackUrl);
+         Statement statement = conn.createStatement()) {
+      try (ResultSet rs = statement.executeQuery("select id, name from 
test_writer")) {
+        assertThat(rs.next(), is(true));
+        assertThat(rs.getInt("id"), is(equalTo(10)));
+        assertThat(rs.getString("name"), is(equalTo("written")));
+        assertThat(rs.next(), is(false));
+      } finally {
+        statement.execute("SHUTDOWN");
+      }
+    }
+  }
+
+  @Test
+  public void testGetRecordWriter_writesMultipleRows() throws Exception {
+    Configuration conf = buildConfiguration();
+    conf.set(JdbcStorageConfig.JDBC_URL.getPropertyName(),
+        conf.get(JdbcStorageConfig.JDBC_URL.getPropertyName())
+            .replace("jdbc:h2:mem:test;MODE=MySQL;INIT",
+                
"jdbc:h2:mem:test_writer_multi;MODE=MySQL;DB_CLOSE_DELAY=-1;INIT"));
+    conf.set(JdbcStorageConfig.TABLE.getPropertyName(), "test_writer");
+    conf.set(serdeConstants.LIST_COLUMNS, "id,name");
+    conf.set(serdeConstants.LIST_COLUMN_TYPES, "int,string");
+
+    TaskAttemptContext context = mock(TaskAttemptContext.class);
+    when(context.getConfiguration()).thenReturn(conf);
+
+    DatabaseAccessor accessor = DatabaseAccessorFactory.getAccessor(conf);
+    @SuppressWarnings("rawtypes")
+    RecordWriter writer = accessor.getRecordWriter(context);
+
+    DBRecordWritable firstRecord = new DBRecordWritable(2);
+    firstRecord.set(0, 11);
+    firstRecord.set(1, "first");
+    writer.write(firstRecord, null);
+
+    DBRecordWritable secondRecord = new DBRecordWritable(2);
+    secondRecord.set(0, 12);
+    secondRecord.set(1, "second");
+    writer.write(secondRecord, null);
+
+    writer.close(context);
+
+    String readBackUrl = 
conf.get(JdbcStorageConfig.JDBC_URL.getPropertyName()).replaceAll(";INIT=.*", 
"");
+    try (Connection conn = DriverManager.getConnection(readBackUrl);
+         Statement statement = conn.createStatement()) {
+      try (ResultSet rs = statement.executeQuery("select id, name from 
test_writer order by id")) {
+        assertThat(rs.next(), is(true));
+        assertThat(rs.getInt("id"), is(equalTo(11)));
+        assertThat(rs.getString("name"), is(equalTo("first")));
+
+        assertThat(rs.next(), is(true));
+        assertThat(rs.getInt("id"), is(equalTo(12)));
+        assertThat(rs.getString("name"), is(equalTo("second")));
+
+        assertThat(rs.next(), is(false));
+      } finally {
+        statement.execute("SHUTDOWN");
+      }
+    }
+  }
+
+  private String getStrategyIdColumnName(Configuration conf, DatabaseAccessor 
accessor)
+      throws HiveJdbcDatabaseAccessException {
+    return accessor.getColumnNames(conf).get(0);
+  }
+
 
   private Configuration buildConfiguration() {
     String scriptPath =
diff --git a/jdbc-handler/src/test/resources/test_script.sql 
b/jdbc-handler/src/test/resources/test_script.sql
index 29a2db7402f..60e44574958 100644
--- a/jdbc-handler/src/test/resources/test_script.sql
+++ b/jdbc-handler/src/test/resources/test_script.sql
@@ -1,4 +1,5 @@
 DROP TABLE IF EXISTS test_strategy;
+DROP TABLE IF EXISTS test_writer;
 
 CREATE TABLE IF NOT EXISTS test_strategy (
   strategy_id int(11) NOT NULL,
@@ -18,6 +19,12 @@ INSERT INTO test_strategy (strategy_id, name, referrer, 
landing, priority, imple
 INSERT INTO test_strategy (strategy_id, name, referrer, landing, priority, 
implementation, last_modified) VALUES (4,'S4','ddd','jkl',980,NULL,'2012-05-08 
15:01:15');
 INSERT INTO test_strategy (strategy_id, name, referrer, landing, priority, 
implementation, last_modified) VALUES (5,'S5','eee',NULL,NULL,NULL,'2012-05-08 
15:01:15');
 
+CREATE TABLE IF NOT EXISTS test_writer (
+  id int(11) NOT NULL,
+  name varchar(50) DEFAULT NULL,
+  PRIMARY KEY (id)
+);
+
 CREATE TABLE IF NOT EXISTS all_types_table (
     char_col CHAR,
     char_with_len_col CHAR(20),

Reply via email to