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

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


The following commit(s) were added to refs/heads/master by this push:
     new 1f88ca83f0 PHOENIX-7309 Support specifying splits.txt file while 
creating a table (#1931)
1f88ca83f0 is described below

commit 1f88ca83f0efb576f01bdffb35becdc5ebccf58a
Author: Rushabh Shah <[email protected]>
AuthorDate: Tue Jul 23 09:29:03 2024 -0700

    PHOENIX-7309 Support specifying splits.txt file while creating a table 
(#1931)
---
 .../apache/phoenix/exception/SQLExceptionCode.java |   5 +-
 .../org/apache/phoenix/query/QueryConstants.java   |   1 +
 .../org/apache/phoenix/schema/MetaDataClient.java  |  54 +++++++++++
 .../org/apache/phoenix/end2end/CreateTableIT.java  | 108 +++++++++++++++++++++
 4 files changed, 167 insertions(+), 1 deletion(-)

diff --git 
a/phoenix-core-client/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java
 
b/phoenix-core-client/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java
index d36827306a..24abfc4bb4 100644
--- 
a/phoenix-core-client/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java
+++ 
b/phoenix-core-client/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java
@@ -485,7 +485,10 @@ public enum SQLExceptionCode {
             "Missing ENCODED_QUALIFIER."),
     EXECUTE_BATCH_FOR_STMT_WITH_RESULT_SET(1151, "XCL51", "A batch operation 
can't include a "
             + "statement that produces result sets.", 
Factory.BATCH_UPDATE_ERROR),
-
+    SPLITS_AND_SPLIT_FILE_EXISTS(1152, "XCL52", "Both splits and split file 
are passed"),
+    // 1153 code is taken by CANNOT_DROP_CDC_INDEX
+    SPLIT_FILE_DONT_EXIST(1154, "XCL54", "Either split file don't exist or is 
not a file"),
+    UNABLE_TO_OPEN_SPLIT_FILE(1155, "XCL55", "Exception occurred while opening 
splits file"),
 
     /**
      * Implementation defined class. Phoenix internal error. (errorcode 20, 
sqlstate INT).
diff --git 
a/phoenix-core-client/src/main/java/org/apache/phoenix/query/QueryConstants.java
 
b/phoenix-core-client/src/main/java/org/apache/phoenix/query/QueryConstants.java
index 39332d9e66..aac042b4e0 100644
--- 
a/phoenix-core-client/src/main/java/org/apache/phoenix/query/QueryConstants.java
+++ 
b/phoenix-core-client/src/main/java/org/apache/phoenix/query/QueryConstants.java
@@ -344,6 +344,7 @@ public interface QueryConstants {
     String CDC_CHANGE_IMAGE = "change_image";
     String CDC_UPSERT_EVENT_TYPE = "upsert";
     String CDC_DELETE_EVENT_TYPE = "delete";
+    String SPLITS_FILE = "SPLITS_FILE";
 
     /**
      * We mark counter values 0 to 10 as reserved. Value 0 is used by
diff --git 
a/phoenix-core-client/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
 
b/phoenix-core-client/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
index 672deb5397..fb7b3a9595 100644
--- 
a/phoenix-core-client/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
+++ 
b/phoenix-core-client/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
@@ -25,6 +25,7 @@ import static 
org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.TTL_NOT_DEFINED;
 import static 
org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.STREAMING_TOPIC_NAME;
 import static 
org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SYSTEM_TASK_TABLE;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.TTL;
+import static org.apache.phoenix.query.QueryConstants.SPLITS_FILE;
 import static org.apache.phoenix.query.QueryConstants.SYSTEM_SCHEMA_NAME;
 import static 
org.apache.phoenix.query.QueryServices.INDEX_CREATE_DEFAULT_STATE;
 import static org.apache.phoenix.schema.PTableType.CDC;
@@ -134,8 +135,13 @@ import static org.apache.phoenix.schema.PTableType.VIEW;
 import static org.apache.phoenix.schema.types.PDataType.FALSE_BYTES;
 import static org.apache.phoenix.schema.types.PDataType.TRUE_BYTES;
 
+import java.io.BufferedReader;
+import java.io.File;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.sql.Connection;
 import java.sql.Date;
 import java.sql.PreparedStatement;
@@ -1053,6 +1059,7 @@ public class MetaDataClient {
         Map<String,Object> commonFamilyProps = 
Maps.newHashMapWithExpectedSize(statement.getProps().size() + 1);
         populatePropertyMaps(statement.getProps(), tableProps, 
commonFamilyProps, statement.getTableType());
 
+        splits = processSplits(tableProps, splits);
         boolean isAppendOnlySchema = false;
         long updateCacheFrequency = (Long) 
ConnectionProperty.UPDATE_CACHE_FREQUENCY.getValue(
                 connection.getQueryServices().getProps().get(
@@ -1150,6 +1157,53 @@ public class MetaDataClient {
         return connection.getQueryServices().updateData(plan);
     }
 
+    /*
+      Create splits either from the provided splits or reading from 
SPLITS_FILE.
+     */
+    private byte[][] processSplits(Map<String, Object> tableProperties, 
byte[][] splits)
+            throws SQLException {
+        String splitFilesLocation = (String) tableProperties.get(SPLITS_FILE);
+        if (splitFilesLocation == null || splitFilesLocation.isEmpty()) {
+            splitFilesLocation = null;
+        }
+
+        // Both splits and split file location are not passed, so return empty 
split.
+        if (splits.length == 0 && splitFilesLocation == null) {
+            return splits;
+        }
+
+        // Both splits[] and splitFileLocation are provided. Throw an 
exception in this case.
+        if (splits.length != 0 && splitFilesLocation != null) {
+            throw new 
SQLExceptionInfo.Builder(SQLExceptionCode.SPLITS_AND_SPLIT_FILE_EXISTS)
+                    .build().buildException();
+        }
+
+        // This means we only have splits[] and no split file location is 
specified
+        if (splitFilesLocation == null) {
+            return splits;
+        }
+        // This means splits[] is empty and split file location is not null.
+        File splitFile = new File(splitFilesLocation);
+        // Check if file exists and is a file not a directory.
+        if (!splitFile.exists() || !splitFile.isFile()) {
+            throw new 
SQLExceptionInfo.Builder(SQLExceptionCode.SPLIT_FILE_DONT_EXIST)
+                    .build().buildException();
+        }
+        List<byte[]> splitsListFromFile = new ArrayList<>();
+        Path path = Paths.get(splitFilesLocation);
+        try (BufferedReader reader = Files.newBufferedReader(path)) {
+            String line;
+            while ((line = reader.readLine()) != null) {
+                splitsListFromFile.add(Bytes.toBytes(line));
+            }
+        } catch (IOException ioe) {
+            LOGGER.warn("Exception while reading splits file", ioe);
+            throw new 
SQLExceptionInfo.Builder(SQLExceptionCode.UNABLE_TO_OPEN_SPLIT_FILE)
+                    .build().buildException();
+        }
+        return splitsListFromFile.toArray(new 
byte[splitsListFromFile.size()][]);
+    }
+
     /**
      * Populate properties for the table and common properties for all column 
families of the table
      * @param statementProps Properties specified in SQL statement
diff --git 
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/CreateTableIT.java 
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/CreateTableIT.java
index 9d28654dca..879539473d 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/CreateTableIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/CreateTableIT.java
@@ -30,6 +30,9 @@ import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.junit.Assert.assertThrows;
 
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
 import java.io.IOException;
 import java.sql.Connection;
 import java.sql.DriverManager;
@@ -90,6 +93,7 @@ import org.apache.phoenix.util.QueryUtil;
 import org.apache.phoenix.util.ReadOnlyProps;
 import org.apache.phoenix.util.SchemaUtil;
 import org.apache.phoenix.util.TestUtil;
+import org.junit.Assert;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
@@ -129,6 +133,110 @@ public class CreateTableIT extends 
ParallelStatsDisabledIT {
         assertTrue(splits.size() > 0);
     }
 
+    @Test
+    public void testSplitsWithFile() throws Exception {
+        File splitFile = new File("splitFile.txt");
+        try {
+            try (BufferedWriter writer = new BufferedWriter(new 
FileWriter(splitFile))) {
+                writer.write("EA");
+                writer.newLine();
+                writer.write("EZ");
+            }
+            Properties props = new Properties();
+            Connection conn = DriverManager.getConnection(getUrl(), props);
+            String tableName = generateUniqueName();
+            conn.createStatement().execute("CREATE TABLE " + tableName
+                    + " (pk char(2) not null primary key) 
SPLITS_FILE='splitFile.txt'");
+            conn.close();
+            String query = "select * from  " + tableName;
+            conn = DriverManager.getConnection(getUrl(), props);
+            Statement statement = conn.createStatement();
+            statement.execute(query);
+            PhoenixStatement pstatement = 
statement.unwrap(PhoenixStatement.class);
+            List<KeyRange> splits = pstatement.getQueryPlan().getSplits();
+            // There will be 3 region splits: '' - EA, EA - EZ, EZ - ''
+            assertEquals(3, splits.size());
+        } finally {
+            // Delete split file.
+            splitFile.delete();
+        }
+    }
+
+    /**
+     * Pass the absolute path of the splits file while creating the table.
+     * @throws Exception
+     */
+    @Test
+    public void testSplitsWithAbsoluteFileName() throws Exception {
+        File splitFile = new File("splitFile.txt");
+        try {
+            try (BufferedWriter writer = new BufferedWriter(new 
FileWriter(splitFile))) {
+                writer.write("EA");
+                writer.newLine();
+                writer.write("EZ");
+            }
+            Properties props = new Properties();
+            Connection conn = DriverManager.getConnection(getUrl(), props);
+            String tableName = generateUniqueName();
+            String createTableSql = "CREATE TABLE " + tableName
+                    + " (pk char(2) not null primary key) SPLITS_FILE='" + 
splitFile.getAbsolutePath() + "'";
+            conn.createStatement().execute(createTableSql);
+            conn.close();
+            String query = "select * from  " + tableName;
+            conn = DriverManager.getConnection(getUrl(), props);
+            Statement statement = conn.createStatement();
+            statement.execute(query);
+            PhoenixStatement pstatement = 
statement.unwrap(PhoenixStatement.class);
+            List<KeyRange> splits = pstatement.getQueryPlan().getSplits();
+            // There will be 3 region splits: '' - EA, EA - EZ, EZ - ''
+            assertEquals(3, splits.size());
+        } finally {
+            // Delete split file.
+            splitFile.delete();
+        }
+    }
+
+    /**
+     * Test create table fails with an invalid file name.
+     * @throws Exception
+     */
+    @Test
+    public void testSplitsWithBadFileName() throws Exception {
+        Properties props = new Properties();
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String tableName = generateUniqueName();
+        try {
+            conn.createStatement().execute("CREATE TABLE " + tableName
+                    + " (pk char(2) not null primary key) 
SPLITS_FILE='bad-split-file.txt'");
+            Assert.fail("Shouldn't come here");
+        } catch (SQLException e) {
+            
assertEquals(SQLExceptionCode.SPLIT_FILE_DONT_EXIST.getErrorCode(), 
e.getErrorCode());
+        } finally {
+            conn.close();
+        }
+    }
+
+    /**
+     * Test create table fails when both split file and split points are 
provided.
+     * @throws Exception
+     */
+    @Test
+    public void testSplitsWithBothSplitPointsAndSplitFileProvided() throws 
Exception {
+        Properties props = new Properties();
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String tableName = generateUniqueName();
+        try {
+            conn.createStatement().execute("CREATE TABLE " + tableName
+                    + " (pk char(2) not null primary key) 
SPLITS_FILE='bad-split-file.txt'" +
+                    " SPLIT ON ('EA','EZ')" );
+            Assert.fail("Shouldn't come here");
+        } catch (SQLException e) {
+            
assertEquals(SQLExceptionCode.SPLITS_AND_SPLIT_FILE_EXISTS.getErrorCode(), 
e.getErrorCode());
+        } finally {
+            conn.close();
+        }
+    }
+
     @Test
     public void testCreateAlterTableWithDuplicateColumn() throws Exception {
         Properties props = new Properties();

Reply via email to