Skip to content

Commit

Permalink
[YSQL] #955: Handle primary key as an index
Browse files Browse the repository at this point in the history
Summary: Primary key of a YSQL table is now handled as a table index so that it can be used in an index scan path. The current ybc_fdw scan path becomes a simple sequential scan.

Test Plan: Jenkins

Reviewers: neil, neha, mihnea

Reviewed By: mihnea

Subscribers: yql

Differential Revision: https://phabricator.dev.yugabyte.com/D6326
  • Loading branch information
robertpang committed Mar 31, 2019
1 parent 4b48e25 commit be0908b
Show file tree
Hide file tree
Showing 29 changed files with 850 additions and 610 deletions.
17 changes: 17 additions & 0 deletions java/yb-pgsql/src/test/java/org/yb/pgsql/BasePgSQLTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,23 @@ protected void assertRowSet(String stmt, Set<Row> expectedRows) throws SQLExcept
}
}

/*
* Returns whether or not this select statement uses index.
*/
protected boolean useIndex(String stmt, String index) throws SQLException {
try (Statement statement = connection.createStatement()) {
try (ResultSet rs = statement.executeQuery("EXPLAIN " + stmt)) {
assert(rs.getMetaData().getColumnCount() == 1); // Expecting one string column.
while (rs.next()) {
if (rs.getString(1).contains("Index Scan using " + index)) {
return true;
}
}
return false;
}
}
}

/*
* Returns whether or not this select statement requires filtering by Postgres (i.e. not all
* conditions can be pushed down to YugaByte).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,23 +106,24 @@ public void testJdbcPrepareExecute() throws Exception {
}

// Test bind variable pushdown:
// Equality on hash key -- expect no postgres filtering, everything should be pushed to YB.
// Equality on hash key -- expect index is used with index condition.
String query = "EXPLAIN SELECT * FROM test WHERE h = ?";
try (PreparedStatement sel = connection.prepareStatement(query)) {
sel.setLong(1, 2);
ResultSet rs = sel.executeQuery();
List<Row> rows = getRowList(rs);
assertFalse(rows.toString().contains("Filter: "));
assertTrue(rows.toString().contains("Index Cond: "));
}

// Test bind variable pushdown:
// Inequality on hash key -- expect postgres filtering.
// Inequality on hash key -- expect index is used also with index condition. We do not support
// hash inequality in DocDB yet and the filtering is done inside the YB's index access method.
query = "EXPLAIN SELECT * FROM test WHERE h > ?";
try (PreparedStatement sel = connection.prepareStatement(query)) {
sel.setLong(1, 2);
ResultSet rs = sel.executeQuery();
List<Row> rows = getRowList(rs);
assertTrue(rows.toString().contains("Filter: "));
assertTrue(rows.toString().contains("Index Cond: "));
}
}

Expand Down
19 changes: 10 additions & 9 deletions java/yb-pgsql/src/test/java/org/yb/pgsql/TestPgSelect.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,14 @@ public class TestPgSelect extends BasePgSQLTest {
@Test
public void testWhereClause() throws Exception {
List<Row> allRows = setupSimpleTable("test_where");
final String PRIMARY_KEY = "test_where_pkey";
try (Statement statement = connection.createStatement()) {
// Test no where clause -- select all rows.
String query = "SELECT * FROM test_where";
try (ResultSet rs = statement.executeQuery(query)) {
assertEquals(allRows, getSortedRowList(rs));
}
assertFalse(needsPgFiltering(query));
assertFalse(useIndex(query, PRIMARY_KEY));

// Test fixed hash key.
query = "SELECT * FROM test_where WHERE h = 2";
Expand All @@ -52,7 +53,7 @@ public void testWhereClause() throws Exception {
assertEquals(10, expectedRows.size());
assertEquals(expectedRows, getSortedRowList(rs));
}
assertFalse(needsPgFiltering(query));
assertTrue(useIndex(query, PRIMARY_KEY));

// Test fixed primary key.
query = "SELECT * FROM test_where WHERE h = 2 AND r = 3.5";
Expand All @@ -64,7 +65,7 @@ public void testWhereClause() throws Exception {
assertEquals(1, expectedRows.size());
assertEquals(expectedRows, getSortedRowList(rs));
}
assertFalse(needsPgFiltering(query));
assertTrue(useIndex(query, PRIMARY_KEY));

// Test fixed range key without fixed hash key.
query = "SELECT * FROM test_where WHERE r = 6.5";
Expand All @@ -75,7 +76,7 @@ public void testWhereClause() throws Exception {
assertEquals(10, expectedRows.size());
assertEquals(expectedRows, getSortedRowList(rs));
}
assertTrue(needsPgFiltering(query));
assertTrue(useIndex(query, PRIMARY_KEY));

// Test range scan.
query = "SELECT * FROM test_where WHERE h = 2 AND r >= 3.5 AND r < 8.5";
Expand All @@ -88,7 +89,7 @@ public void testWhereClause() throws Exception {
assertEquals(5, expectedRows.size());
assertEquals(expectedRows, getSortedRowList(rs));
}
assertTrue(needsPgFiltering(query));
assertTrue(useIndex(query, PRIMARY_KEY));

// Test conditions on regular (non-primary-key) columns.
query = "SELECT * FROM test_where WHERE vi < 14 AND vs != 'v09'";
Expand All @@ -101,10 +102,10 @@ public void testWhereClause() throws Exception {
assertEquals(13, expectedRows.size());
assertEquals(expectedRows, getSortedRowList(rs));
}
assertTrue(needsPgFiltering(query));
assertFalse(useIndex(query, PRIMARY_KEY));

// Test other WHERE operators (IN, OR, LIKE).
query = "SELECT * FROM test_where WHERE h IN (2,3) OR vs LIKE 'v_2'";
query = "SELECT * FROM test_where WHERE h = 2 OR h = 3 OR vs LIKE 'v_2'";
try (ResultSet rs = statement.executeQuery(query)) {
List<Row> expectedRows = allRows.stream()
.filter(row -> row.getLong(0).equals(2L) ||
Expand All @@ -115,7 +116,7 @@ public void testWhereClause() throws Exception {
assertEquals(28, expectedRows.size());
assertEquals(expectedRows, getSortedRowList(rs));
}
assertTrue(needsPgFiltering(query));
assertFalse(useIndex(query, PRIMARY_KEY));
}
}

Expand Down Expand Up @@ -228,7 +229,7 @@ public void testJoins() throws Exception {

// Test join with WHERE clause.
joinStmt = "SELECT a.h, a.r, a.v as av, b.v as bv FROM t1 a LEFT JOIN t2 b " +
"ON (a.h = b.h and a.r = b.r) WHERE a.h = 1 AND a.r IN (2.5, 3.5)";
"ON (a.h = b.h and a.r = b.r) WHERE a.h = 1 AND (a.r = 2.5 OR a.r = 3.5)";
try (ResultSet rs = statement.executeQuery(joinStmt)) {
assertNextRow(rs, 1L, 2.5D, "abc", "foo");
assertNextRow(rs, 1L, 3.5D, "def", null);
Expand Down
12 changes: 11 additions & 1 deletion src/postgres/src/backend/access/index/indexam.c
Original file line number Diff line number Diff line change
Expand Up @@ -633,9 +633,19 @@ index_getnext_tid(IndexScanDesc scan, ScanDirection direction)
HeapTuple
index_fetch_heap(IndexScanDesc scan)
{
/* For YugaByte index, we need to select from the base table using ybctid */
/*
* For YugaByte secondary indexes, we need to select from the base table using
* ybctid. For primary keys, the row is already prepared in "xs_hitup" that can
* be returned directly.
*/
if (IsYugaByteEnabled())
{
if (scan->indexRelation->rd_index->indisprimary)
{
Assert(scan->xs_hitup != 0);
return scan->xs_hitup;
}

return YBCFetchTuple(scan->heapRelation, scan->xs_ctup.t_ybctid);
}

Expand Down
Loading

0 comments on commit be0908b

Please sign in to comment.