| /* |
| * 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.phoenix.end2end; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertTrue; |
| |
| import java.sql.Connection; |
| import java.sql.Date; |
| import java.sql.DriverManager; |
| import java.sql.PreparedStatement; |
| import java.sql.ResultSet; |
| import java.sql.Statement; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Properties; |
| |
| import org.apache.hadoop.hbase.util.Base64; |
| import org.apache.hadoop.hbase.util.Pair; |
| import org.apache.phoenix.jdbc.PhoenixConnection; |
| import org.apache.phoenix.query.QueryServices; |
| import org.apache.phoenix.util.PhoenixRuntime; |
| import org.apache.phoenix.util.TestUtil; |
| import org.junit.Test; |
| |
| import com.google.common.collect.Lists; |
| |
| |
| public class QueryMoreIT extends ParallelStatsDisabledIT { |
| |
| private String dataTableName; |
| //queryAgainstTenantSpecificView = true, dataTableSalted = true |
| @Test |
| public void testQueryMore1() throws Exception { |
| testQueryMore(true, true); |
| } |
| |
| //queryAgainstTenantSpecificView = false, dataTableSalted = true |
| @Test |
| public void testQueryMore2() throws Exception { |
| testQueryMore(false, true); |
| } |
| |
| //queryAgainstTenantSpecificView = false, dataTableSalted = false |
| @Test |
| public void testQueryMore3() throws Exception { |
| testQueryMore(false, false); |
| } |
| |
| //queryAgainstTenantSpecificView = true, dataTableSalted = false |
| @Test |
| public void testQueryMore4() throws Exception { |
| testQueryMore(true, false); |
| } |
| |
| private void testQueryMore(boolean queryAgainstTenantSpecificView, boolean dataTableSalted) throws Exception { |
| String[] tenantIds = new String[] {"T1_" + generateUniqueName(), "T2_" + generateUniqueName(), "T3_" + generateUniqueName()}; |
| int numRowsPerTenant = 10; |
| String cursorTableName = generateUniqueName(); |
| String base_history_table = generateUniqueName(); |
| this.dataTableName = base_history_table + (dataTableSalted ? "_SALTED" : ""); |
| String cursorTableDDL = "CREATE TABLE IF NOT EXISTS " + |
| cursorTableName + " (\n" + |
| "TENANT_ID VARCHAR NOT NULL\n," + |
| "QUERY_ID VARCHAR(15) NOT NULL,\n" + |
| "CURSOR_ORDER BIGINT NOT NULL \n" + |
| "CONSTRAINT CURSOR_TABLE_PK PRIMARY KEY (TENANT_ID, QUERY_ID, CURSOR_ORDER)) "+ |
| "SALT_BUCKETS = 4, TTL=86400"; |
| String baseDataTableDDL = "CREATE TABLE IF NOT EXISTS " + |
| dataTableName + " (\n" + |
| "TENANT_ID VARCHAR NOT NULL,\n" + |
| "PARENT_ID CHAR(15) NOT NULL,\n" + |
| "CREATED_DATE DATE NOT NULL,\n" + |
| "ENTITY_HISTORY_ID CHAR(15) NOT NULL,\n" + |
| "DATA_TYPE VARCHAR,\n" + |
| "OLDVAL_STRING VARCHAR,\n" + |
| "NEWVAL_STRING VARCHAR\n" + |
| "CONSTRAINT PK PRIMARY KEY(TENANT_ID, PARENT_ID, CREATED_DATE DESC, ENTITY_HISTORY_ID)) " + |
| "VERSIONS = 1, MULTI_TENANT = true" + (dataTableSalted ? ", SALT_BUCKETS = 4" : ""); |
| |
| //create cursor and data tables. |
| Connection conn = DriverManager.getConnection(getUrl()); |
| conn.createStatement().execute(cursorTableDDL); |
| conn.createStatement().execute(baseDataTableDDL); |
| conn.close(); |
| |
| //upsert rows in the data table for all the tenantIds |
| Map<String, List<String>> historyIdsPerTenant = createHistoryTableRows(dataTableName, tenantIds, numRowsPerTenant); |
| |
| // assert query more for tenantId -> tenantIds[0] |
| String tenantId = tenantIds[0]; |
| String cursorQueryId = "00TcursrqueryId"; |
| String tableOrViewName = queryAgainstTenantSpecificView ? ("HISTORY_TABLE_" + tenantId) : dataTableName; |
| |
| assertEquals(numRowsPerTenant, upsertSelectRecordsInCursorTableForTenant(tableOrViewName, queryAgainstTenantSpecificView, tenantId, cursorQueryId, |
| cursorTableName)); |
| |
| /*// assert that the data inserted in cursor table matches the data in the data table for tenantId. |
| String selectDataTable = "SELECT TENANT_ID, PARENT_ID, CREATED_DATE, ENTITY_HISTORY_ID FROM BASE_HISTORY_TABLE WHERE TENANT_ID = ? "; |
| String selectCursorTable = "SELECT TENANT_ID, PARENT_ID, CREATED_DATE, ENTITY_HISTORY_ID FROM CURSOR_TABLE (PARENT_ID CHAR(15), CREATED_DATE DATE, ENTITY_HISTORY_ID CHAR(15)) WHERE TENANT_ID = ? "; |
| |
| PreparedStatement stmtData = DriverManager.getConnection(getUrl()).prepareStatement(selectDataTable); |
| stmtData.setString(1, tenantId); |
| ResultSet rsData = stmtData.executeQuery(); |
| |
| PreparedStatement stmtCursor = DriverManager.getConnection(getUrl()).prepareStatement(selectCursorTable); |
| stmtCursor.setString(1, tenantId); |
| ResultSet rsCursor = stmtCursor.executeQuery(); |
| |
| while(rsData.next() && rsCursor.next()) { |
| assertEquals(rsData.getString("TENANT_ID"), rsCursor.getString("TENANT_ID")); |
| assertEquals(rsData.getString("PARENT_ID"), rsCursor.getString("PARENT_ID")); |
| assertEquals(rsData.getDate("CREATED_DATE"), rsCursor.getDate("CREATED_DATE")); |
| assertEquals(rsData.getString("ENTITY_HISTORY_ID"), rsCursor.getString("ENTITY_HISTORY_ID")); |
| } |
| |
| */ |
| Connection conn2 = DriverManager.getConnection(getUrl()); |
| ResultSet rs = conn2.createStatement().executeQuery("SELECT count(*) from " + cursorTableName); |
| rs.next(); |
| assertEquals(numRowsPerTenant, rs.getInt(1)); |
| conn2.close(); |
| |
| int startOrder = 0; |
| int endOrder = 5; |
| int numRecordsThatShouldBeRetrieved = numRowsPerTenant/2; // we will test for two rounds of query more. |
| |
| // get first batch of cursor ids out of the cursor table. |
| String[] cursorIds = getRecordsOutofCursorTable(tableOrViewName, queryAgainstTenantSpecificView, tenantId, cursorQueryId, startOrder, endOrder, |
| cursorTableName); |
| assertEquals(numRecordsThatShouldBeRetrieved, cursorIds.length); |
| // now query and fetch first batch of records. |
| List<String> historyIds = doQueryMore(queryAgainstTenantSpecificView, tenantId, tableOrViewName, cursorIds); |
| // assert that history ids match for this tenant |
| assertEquals(historyIdsPerTenant.get(tenantId).subList(startOrder, endOrder), historyIds); |
| |
| // get the next batch of cursor ids out of the cursor table. |
| cursorIds = getRecordsOutofCursorTable(tableOrViewName, queryAgainstTenantSpecificView, tenantId, cursorQueryId, startOrder + numRecordsThatShouldBeRetrieved, endOrder + numRecordsThatShouldBeRetrieved, |
| cursorTableName); |
| assertEquals(numRecordsThatShouldBeRetrieved, cursorIds.length); |
| // now query and fetch the next batch of records. |
| historyIds = doQueryMore(queryAgainstTenantSpecificView, tenantId, tableOrViewName, cursorIds); |
| // assert that the history ids match for this tenant |
| assertEquals(historyIdsPerTenant.get(tenantId).subList(startOrder + numRecordsThatShouldBeRetrieved, endOrder+ numRecordsThatShouldBeRetrieved), historyIds); |
| |
| // get the next batch of cursor ids out of the cursor table. |
| cursorIds = getRecordsOutofCursorTable(tableOrViewName, queryAgainstTenantSpecificView, tenantId, cursorQueryId, startOrder + 2 * numRecordsThatShouldBeRetrieved, endOrder + 2 * numRecordsThatShouldBeRetrieved, |
| cursorTableName); |
| // assert that there are no more cursorids left for this tenant. |
| assertEquals(0, cursorIds.length); |
| } |
| |
| private Map<String, List<String>> createHistoryTableRows(String dataTableName, String[] tenantIds, int numRowsPerTenant) throws Exception { |
| String upsertDML = "UPSERT INTO " + dataTableName + " VALUES (?, ?, ?, ?, ?, ?, ?)"; |
| Connection conn = DriverManager.getConnection(getUrl()); |
| Map<String, List<String>> historyIdsForTenant = new HashMap<String, List<String>>(); |
| try { |
| PreparedStatement stmt = conn.prepareStatement(upsertDML); |
| for (int j = 0; j < tenantIds.length; j++) { |
| List<String> historyIds = new ArrayList<String>(); |
| for (int i = 0; i < numRowsPerTenant; i++) { |
| stmt.setString(1, tenantIds[j]); |
| String parentId = "parentId" + i; |
| stmt.setString(2, parentId); |
| stmt.setDate(3, new Date(100)); |
| String historyId = "historyId" + i; |
| stmt.setString(4, historyId); |
| stmt.setString(5, "datatype"); |
| stmt.setString(6, "oldval"); |
| stmt.setString(7, "newval"); |
| stmt.executeUpdate(); |
| historyIds.add(historyId); |
| } |
| historyIdsForTenant.put(tenantIds[j], historyIds); |
| } |
| conn.commit(); |
| return historyIdsForTenant; |
| } finally { |
| conn.close(); |
| } |
| } |
| |
| private int upsertSelectRecordsInCursorTableForTenant(String tableOrViewName, boolean queryAgainstTenantView, String tenantId, String cursorQueryId, |
| final String cursorTable) throws Exception { |
| String sequenceName = "\"" + tenantId + "_SEQ\""; |
| Connection conn = queryAgainstTenantView ? getTenantSpecificConnection(tenantId) : DriverManager.getConnection(getUrl()); |
| |
| // Create a sequence. This sequence is used to fill cursor_order column for each row inserted in the cursor table. |
| conn.createStatement().execute("CREATE SEQUENCE " + sequenceName + " CACHE " + Long.MAX_VALUE); |
| conn.setAutoCommit(true); |
| if (queryAgainstTenantView) { |
| createTenantSpecificViewIfNecessary(tableOrViewName, conn); |
| } |
| try { |
| String tenantIdFilter = queryAgainstTenantView ? "" : " WHERE TENANT_ID = ? "; |
| |
| // Using dynamic columns, we can use the same cursor table for storing primary keys for all the tables. |
| String upsertSelectDML = "UPSERT INTO " + cursorTable + " " + |
| "(TENANT_ID, QUERY_ID, CURSOR_ORDER, PARENT_ID CHAR(15), CREATED_DATE DATE, ENTITY_HISTORY_ID CHAR(15)) " + |
| "SELECT ?, ?, NEXT VALUE FOR " + sequenceName + ", PARENT_ID, CREATED_DATE, ENTITY_HISTORY_ID " + |
| " FROM " + tableOrViewName + tenantIdFilter; |
| |
| PreparedStatement stmt = conn.prepareStatement(upsertSelectDML); |
| stmt.setString(1, tenantId); |
| stmt.setString(2, cursorQueryId); |
| if (!queryAgainstTenantView) { |
| stmt.setString(3, tenantId); |
| } |
| int numRecords = stmt.executeUpdate(); |
| return numRecords; |
| } finally { |
| try { |
| conn.createStatement().execute("DROP SEQUENCE " + sequenceName); |
| } finally { |
| conn.close(); |
| } |
| } |
| } |
| |
| private Connection getTenantSpecificConnection(String tenantId) throws Exception { |
| Properties props = new Properties(); |
| props.setProperty(PhoenixRuntime.TENANT_ID_ATTRIB, tenantId); |
| return DriverManager.getConnection(getUrl(), props); |
| } |
| |
| private String createTenantSpecificViewIfNecessary(String tenantViewName, Connection tenantConn) throws Exception { |
| tenantConn.createStatement().execute("CREATE VIEW IF NOT EXISTS " + tenantViewName + " AS SELECT * FROM " + dataTableName); |
| return tenantViewName; |
| } |
| |
| private String[] getRecordsOutofCursorTable(String tableOrViewName, boolean queryAgainstTenantSpecificView, String tenantId, String cursorQueryId, |
| int startOrder, int endOrder, final String cursorTable) throws Exception { |
| Connection conn = DriverManager.getConnection(getUrl()); |
| List<String> pkIds = new ArrayList<String>(); |
| String cols = queryAgainstTenantSpecificView ? "PARENT_ID, CREATED_DATE, ENTITY_HISTORY_ID" : "TENANT_ID, PARENT_ID, CREATED_DATE, ENTITY_HISTORY_ID"; |
| String dynCols = queryAgainstTenantSpecificView ? "(PARENT_ID CHAR(15), CREATED_DATE DATE, ENTITY_HISTORY_ID CHAR(15))" : "(TENANT_ID CHAR(15), PARENT_ID CHAR(15), CREATED_DATE DATE, ENTITY_HISTORY_ID CHAR(15))"; |
| String selectCursorSql = "SELECT " + cols + " " + |
| "FROM " + cursorTable + " \n" + |
| dynCols + " \n" + |
| "WHERE TENANT_ID = ? AND \n" + |
| "QUERY_ID = ? AND \n" + |
| "CURSOR_ORDER > ? AND \n" + |
| "CURSOR_ORDER <= ?"; |
| |
| PreparedStatement stmt = conn.prepareStatement(selectCursorSql); |
| stmt.setString(1, tenantId); |
| stmt.setString(2, cursorQueryId); |
| stmt.setInt(3, startOrder); |
| stmt.setInt(4, endOrder); |
| |
| ResultSet rs = stmt.executeQuery(); |
| @SuppressWarnings("unchecked") |
| List<Pair<String, String>> columns = queryAgainstTenantSpecificView ? Lists.newArrayList(new Pair<String, String>(null, "PARENT_ID"), new Pair<String, String>(null, "CREATED_DATE"), new Pair<String, String>(null, "ENTITY_HISTORY_ID")) : Lists.newArrayList(new Pair<String, String>(null, "TENANT_ID"), new Pair<String, String>(null, "PARENT_ID"), new Pair<String, String>(null, "CREATED_DATE"), new Pair<String, String>(null, "ENTITY_HISTORY_ID")); |
| while(rs.next()) { |
| Object[] values = new Object[columns.size()]; |
| for (int i = 0; i < columns.size(); i++) { |
| values[i] = rs.getObject(i + 1); |
| } |
| conn = getTenantSpecificConnection(tenantId); |
| pkIds.add(Base64.encodeBytes(PhoenixRuntime.encodeColumnValues(conn, tableOrViewName.toUpperCase(), values, columns))); |
| } |
| return pkIds.toArray(new String[pkIds.size()]); |
| } |
| |
| private List<String> doQueryMore(boolean queryAgainstTenantView, String tenantId, String tenantViewName, String[] cursorIds) throws Exception { |
| Connection conn = queryAgainstTenantView ? getTenantSpecificConnection(tenantId) : DriverManager.getConnection(getUrl()); |
| String tableName = queryAgainstTenantView ? tenantViewName : dataTableName; |
| @SuppressWarnings("unchecked") |
| List<Pair<String, String>> columns = queryAgainstTenantView ? Lists.newArrayList(new Pair<String, String>(null, "PARENT_ID"), new Pair<String, String>(null, "CREATED_DATE"), new Pair<String, String>(null, "ENTITY_HISTORY_ID")) : Lists.newArrayList(new Pair<String, String>(null, "TENANT_ID"), new Pair<String, String>(null, "PARENT_ID"), new Pair<String, String>(null, "CREATED_DATE"), new Pair<String, String>(null, "ENTITY_HISTORY_ID")); |
| StringBuilder sb = new StringBuilder(); |
| String where = queryAgainstTenantView ? " WHERE (PARENT_ID, CREATED_DATE, ENTITY_HISTORY_ID) IN " : " WHERE (TENANT_ID, PARENT_ID, CREATED_DATE, ENTITY_HISTORY_ID) IN "; |
| sb.append("SELECT ENTITY_HISTORY_ID FROM " + tableName + where); |
| int numPkCols = columns.size(); |
| String query = addRvcInBinds(sb, cursorIds.length, numPkCols); |
| PreparedStatement stmt = conn.prepareStatement(query); |
| int bindCounter = 1; |
| for (int i = 0; i < cursorIds.length; i++) { |
| Object[] pkParts = PhoenixRuntime.decodeColumnValues(conn, tableName.toUpperCase(), Base64.decode(cursorIds[i]), columns); |
| for (int j = 0; j < pkParts.length; j++) { |
| stmt.setObject(bindCounter++, pkParts[j]); |
| } |
| } |
| ResultSet rs = stmt.executeQuery(); |
| List<String> historyIds = new ArrayList<String>(); |
| while(rs.next()) { |
| historyIds.add(rs.getString(1)); |
| } |
| return historyIds; |
| } |
| |
| private String addRvcInBinds(StringBuilder sb, int numRvcs, int numPkCols) { |
| sb.append("("); |
| for (int i = 0 ; i < numRvcs; i++) { |
| for (int j = 0; j < numPkCols; j++) { |
| if (j == 0) { |
| sb.append("("); |
| } |
| sb.append("?"); |
| if (j < numPkCols - 1) { |
| sb.append(","); |
| } else { |
| sb.append(")"); |
| } |
| } |
| if (i < numRvcs - 1) { |
| sb.append(","); |
| } |
| } |
| sb.append(")"); |
| return sb.toString(); |
| } |
| |
| @Test // see - https://issues.apache.org/jira/browse/PHOENIX-1696 |
| public void testSelectColumnMoreThanOnce() throws Exception { |
| Date date = new Date(System.currentTimeMillis()); |
| initEntityHistoryTableValues("abcd", getDefaultSplits("abcd"), date, 100l); |
| String query = "SELECT NEW_VALUE, NEW_VALUE FROM " + TestUtil.ENTITY_HISTORY_TABLE_NAME + " LIMIT 1"; |
| ResultSet rs = DriverManager.getConnection(getUrl()).createStatement().executeQuery(query); |
| assertTrue(rs.next()); |
| rs.getObject("NEW_VALUE"); |
| assertFalse(rs.next()); |
| } |
| |
| @SuppressWarnings("deprecation") |
| @Test |
| public void testNullBigDecimalWithScale() throws Exception { |
| final String table = generateUniqueName(); |
| final Connection conn = DriverManager.getConnection(getUrl()); |
| conn.setAutoCommit(true); |
| try (Statement stmt = conn.createStatement()) { |
| assertFalse(stmt.execute("CREATE TABLE IF NOT EXISTS " + table + " (\n" + |
| "PK VARCHAR(15) NOT NULL\n," + |
| "\"DEC\" DECIMAL,\n" + |
| "CONSTRAINT TABLE_PK PRIMARY KEY (PK))")); |
| } |
| |
| try (PreparedStatement stmt = conn.prepareStatement("UPSERT INTO " + table + " (PK, \"DEC\") VALUES(?, ?)")) { |
| stmt.setString(1, "key"); |
| stmt.setBigDecimal(2, null); |
| assertFalse(stmt.execute()); |
| assertEquals(1, stmt.getUpdateCount()); |
| } |
| |
| try (Statement stmt = conn.createStatement()) { |
| final ResultSet rs = stmt.executeQuery("SELECT * FROM " + table); |
| assertNotNull(rs); |
| assertTrue(rs.next()); |
| assertEquals("key", rs.getString(1)); |
| assertNull(rs.getBigDecimal(2)); |
| assertNull(rs.getBigDecimal(2, 10)); |
| } |
| } |
| |
| // FIXME: this repros PHOENIX-3382, but turned up two more issues: |
| // 1) PHOENIX-3383 Comparison between descending row keys used in RVC is reverse |
| // 2) PHOENIX-3384 Optimize RVC expressions for non leading row key columns |
| @Test |
| public void testRVCOnDescWithLeadingPKEquality() throws Exception { |
| final Connection conn = DriverManager.getConnection(getUrl()); |
| String fullTableName = generateUniqueName(); |
| try (Statement stmt = conn.createStatement()) { |
| stmt.execute("CREATE TABLE " + fullTableName + "(\n" + |
| " ORGANIZATION_ID CHAR(15) NOT NULL,\n" + |
| " SCORE DOUBLE NOT NULL,\n" + |
| " ENTITY_ID CHAR(15) NOT NULL\n" + |
| " CONSTRAINT PAGE_SNAPSHOT_PK PRIMARY KEY (\n" + |
| " ORGANIZATION_ID,\n" + |
| " SCORE DESC,\n" + |
| " ENTITY_ID DESC\n" + |
| " )\n" + |
| ") MULTI_TENANT=TRUE"); |
| } |
| |
| conn.createStatement().execute("UPSERT INTO " + fullTableName + " VALUES ('org1',3,'01')"); |
| conn.createStatement().execute("UPSERT INTO " + fullTableName + " VALUES ('org1',2,'04')"); |
| conn.createStatement().execute("UPSERT INTO " + fullTableName + " VALUES ('org1',2,'03')"); |
| conn.createStatement().execute("UPSERT INTO " + fullTableName + " VALUES ('org1',1,'02')"); |
| conn.commit(); |
| |
| // FIXME: PHOENIX-3383 |
| // This comparison is really backwards: it should be (score, entity_id) < (2, '04'), |
| // but because we're matching a descending key, our comparison has to be switched. |
| try (Statement stmt = conn.createStatement()) { |
| final ResultSet rs = stmt.executeQuery("SELECT entity_id, score\n" + |
| "FROM " + fullTableName + "\n" + |
| "WHERE organization_id = 'org1'\n" + |
| "AND (score, entity_id) > (2, '04')\n" + |
| "ORDER BY score DESC, entity_id DESC\n" + |
| "LIMIT 3"); |
| assertTrue(rs.next()); |
| assertEquals("03", rs.getString(1)); |
| assertEquals(2.0, rs.getDouble(2), 0.001); |
| assertTrue(rs.next()); |
| assertEquals("02", rs.getString(1)); |
| assertEquals(1.0, rs.getDouble(2), 0.001); |
| assertFalse(rs.next()); |
| } |
| // FIXME: PHOENIX-3384 |
| // It should not be necessary to specify organization_id in this query |
| try (Statement stmt = conn.createStatement()) { |
| final ResultSet rs = stmt.executeQuery("SELECT entity_id, score\n" + |
| "FROM " + fullTableName + "\n" + |
| "WHERE organization_id = 'org1'\n" + |
| "AND (organization_id, score, entity_id) > ('org1', 2, '04')\n" + |
| "ORDER BY score DESC, entity_id DESC\n" + |
| "LIMIT 3"); |
| assertTrue(rs.next()); |
| assertEquals("03", rs.getString(1)); |
| assertEquals(2.0, rs.getDouble(2), 0.001); |
| assertTrue(rs.next()); |
| assertEquals("02", rs.getString(1)); |
| assertEquals(1.0, rs.getDouble(2), 0.001); |
| assertFalse(rs.next()); |
| } |
| } |
| |
| @Test |
| public void testSingleDescPKColumnComparison() throws Exception { |
| final Connection conn = DriverManager.getConnection(getUrl()); |
| String fullTableName = generateUniqueName(); |
| try (Statement stmt = conn.createStatement()) { |
| stmt.execute("CREATE TABLE " + fullTableName + "(\n" + |
| " ORGANIZATION_ID CHAR(15) NOT NULL,\n" + |
| " SCORE DOUBLE NOT NULL,\n" + |
| " ENTITY_ID CHAR(15) NOT NULL\n" + |
| " CONSTRAINT PAGE_SNAPSHOT_PK PRIMARY KEY (\n" + |
| " ORGANIZATION_ID,\n" + |
| " SCORE DESC,\n" + |
| " ENTITY_ID DESC\n" + |
| " )\n" + |
| ") MULTI_TENANT=TRUE"); |
| } |
| |
| conn.createStatement().execute("UPSERT INTO " + fullTableName + " VALUES ('org1',3,'01')"); |
| conn.createStatement().execute("UPSERT INTO " + fullTableName + " VALUES ('org1',2,'04')"); |
| conn.createStatement().execute("UPSERT INTO " + fullTableName + " VALUES ('org1',2,'03')"); |
| conn.createStatement().execute("UPSERT INTO " + fullTableName + " VALUES ('org1',1,'02')"); |
| conn.commit(); |
| |
| try (Statement stmt = conn.createStatement()) { |
| // Even though SCORE is descending, the > comparison makes sense logically |
| // and doesn't have to be reversed as RVC expression comparisons do (which |
| // is really just a bug - see PHOENIX-3383). |
| final ResultSet rs = stmt.executeQuery("SELECT entity_id, score\n" + |
| "FROM " + fullTableName + "\n" + |
| "WHERE organization_id = 'org1'\n" + |
| "AND score > 2.0\n" + |
| "ORDER BY score DESC\n" + |
| "LIMIT 3"); |
| assertTrue(rs.next()); |
| assertEquals("01", rs.getString(1)); |
| assertEquals(3.0, rs.getDouble(2), 0.001); |
| assertFalse(rs.next()); |
| } |
| } |
| |
| @Test |
| public void testMutationBatch() throws Exception { |
| Properties connectionProperties = new Properties(); |
| connectionProperties.setProperty(QueryServices.MUTATE_BATCH_SIZE_BYTES_ATTRIB, "1024"); |
| PhoenixConnection connection = (PhoenixConnection) DriverManager.getConnection(getUrl(), connectionProperties); |
| String fullTableName = generateUniqueName(); |
| try (Statement stmt = connection.createStatement()) { |
| stmt.execute("CREATE TABLE " + fullTableName + "(\n" + |
| " ORGANIZATION_ID CHAR(15) NOT NULL,\n" + |
| " SCORE DOUBLE NOT NULL,\n" + |
| " ENTITY_ID CHAR(15) NOT NULL\n" + |
| " CONSTRAINT PAGE_SNAPSHOT_PK PRIMARY KEY (\n" + |
| " ORGANIZATION_ID,\n" + |
| " SCORE DESC,\n" + |
| " ENTITY_ID DESC\n" + |
| " )\n" + |
| ") MULTI_TENANT=TRUE"); |
| } |
| PreparedStatement stmt = connection.prepareStatement("upsert into " + fullTableName + |
| " (organization_id, entity_id, score) values (?,?,?)"); |
| try { |
| for (int i = 0; i < 4; i++) { |
| stmt.setString(1, "AAAA" + i); |
| stmt.setString(2, "BBBB" + i); |
| stmt.setInt(3, 1); |
| stmt.execute(); |
| } |
| connection.commit(); |
| } catch (IllegalArgumentException expected) {} |
| |
| assertEquals(2L, connection.getMutationState().getBatchCount()); |
| } |
| } |