Skip to content

Commit 1e27e1a

Browse files
authored
Merge pull request #588 from alex268/resultset_fixes
Fixed ResultSetReader inconsistency
2 parents 4e71d05 + 25ce39c commit 1e27e1a

File tree

6 files changed

+260
-106
lines changed

6 files changed

+260
-106
lines changed

query/src/main/java/tech/ydb/query/tools/QueryReader.java

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ public ValueReader getColumn(String name) {
166166

167167
@Override
168168
public Type getColumnType(int index) {
169-
if (partIndex < 0) {
169+
if (partIndex < 0 || partIndex >= parts.length) {
170170
return null;
171171
}
172172
return parts[partIndex].getColumnType(index);
@@ -179,38 +179,37 @@ public int getRowCount() {
179179

180180
@Override
181181
public void setRowIndex(int index) {
182-
// TODO: Enable after JDBC fixing
183-
// if (index < 0 || index >= rowsCount) {
184-
// throw new IndexOutOfBoundsException(String.format("Index %s out of bounds for length %s",
185-
// index, rowsCount));
186-
// }
187-
// int currentIdx = index;
188-
int currentIdx = Math.max(0, index);
182+
if (index < 0) { // reset all
183+
partIndex = parts.length == 0 ? -1 : 0;
184+
for (ResultSetReader rs: parts) {
185+
rs.setRowIndex(-1);
186+
}
187+
return;
188+
}
189+
190+
int currentIdx = index;
191+
189192
partIndex = 0;
190193
while (partIndex < parts.length) {
191194
int readerRows = parts[partIndex].getRowCount();
192195
if (currentIdx < readerRows) {
193196
parts[partIndex].setRowIndex(currentIdx);
194-
break;
197+
for (int partStep = partIndex + 1; partStep < parts.length; partStep++) {
198+
parts[partStep].setRowIndex(-1);
199+
}
200+
return;
195201
}
196202
parts[partIndex].setRowIndex(readerRows);
197203
currentIdx -= readerRows;
198204
partIndex++;
199205
}
200206

201-
// TODO: remove after JDBC fixing
202-
if (partIndex >= parts.length) {
203-
partIndex = parts.length - 1;
204-
}
205-
206-
for (int partStep = partIndex + 1; partStep < parts.length; partStep++) {
207-
parts[partStep].setRowIndex(0);
208-
}
207+
partIndex = parts.length - 1;
209208
}
210209

211210
@Override
212211
public boolean next() {
213-
if (partIndex < 0) {
212+
if (partIndex < 0 || partIndex >= parts.length) {
214213
return false;
215214
}
216215
boolean res = parts[partIndex].next();

query/src/test/java/tech/ydb/query/tools/QueryReaderTest.java

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -132,26 +132,32 @@ public void compositeResultSetTest() {
132132
Assert.assertEquals(6, readAll(rsr, 0));
133133
Assert.assertEquals(0, readAll(rsr, 0));
134134

135-
rsr.setRowIndex(0);
135+
rsr.setRowIndex(-1);
136+
Assert.assertEquals(6, readAll(rsr, 0));
137+
Assert.assertEquals(0, readAll(rsr, 0));
138+
139+
rsr.setRowIndex(-100);
136140
Assert.assertEquals(6, readAll(rsr, 0));
137141
Assert.assertEquals(0, readAll(rsr, 0));
138142

143+
rsr.setRowIndex(0);
144+
Assert.assertEquals(5, readAll(rsr, 1));
145+
Assert.assertEquals(0, readAll(rsr, 0));
146+
139147
rsr.setRowIndex(3);
140-
Assert.assertEquals(3, readAll(rsr, 3));
148+
Assert.assertEquals(2, readAll(rsr, 4));
141149

142150
rsr.setRowIndex(5);
143-
Assert.assertEquals(1, readAll(rsr, 5));
151+
Assert.assertEquals(0, readAll(rsr, 0));
144152

145153
rsr.setRowIndex(-1);
146154
Assert.assertEquals(6, readAll(rsr, 0));
147155

148156
rsr.setRowIndex(6);
149157
Assert.assertEquals(0, readAll(rsr, 0));
150-
// IndexOutOfBoundsException ex1 = Assert.assertThrows(IndexOutOfBoundsException.class, () -> rsr.setRowIndex(6));
151-
// Assert.assertEquals("Index 6 out of bounds for length 6", ex1.getMessage());
152-
//
153-
// IndexOutOfBoundsException ex2 = Assert.assertThrows(IndexOutOfBoundsException.class, () -> rsr.setRowIndex(-1));
154-
// Assert.assertEquals("Index -1 out of bounds for length 6", ex2.getMessage());
158+
159+
rsr.setRowIndex(100);
160+
Assert.assertEquals(0, readAll(rsr, 0));
155161
}
156162

157163
private int readAll(ResultSetReader rsr, int startKey) {

table/src/main/java/tech/ydb/table/result/ResultSetReader.java

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,55 +8,96 @@
88
public interface ResultSetReader {
99

1010
/**
11-
* Returns {@code true} if the result was truncated, {@code false} otherwise.
11+
* Gets whether this result set was truncated.
12+
*
13+
* @return {@code true} if the result was truncated, {@code false} otherwise.
1214
*/
1315
boolean isTruncated();
1416

1517
/**
16-
* Returns number of columns.
18+
* Gets number of this result set columns
19+
*
20+
* @return the result set columns count
1721
*/
1822
int getColumnCount();
1923

2024
/**
21-
* Returns number of rows.
25+
* Gets number of this result set rows
26+
*
27+
* @return the result set rows count
2228
*/
2329
int getRowCount();
2430

2531
/**
26-
* Explicitly switch to a specific row.
32+
* Explicitly sets the reader on a specific row position.
33+
* <p>
34+
* Valid row indexes are in the range <code>0 &lt;= index &lt; getRowCount()</code>, where
35+
* {@code 0} is the first row and {@code getRowCount() - 1} is the last row.
36+
* Passing a negative value positions the reader <em>before</em> the first row, so that
37+
* a subsequent {@link #next()} call advances to the first row. Passing a value greater
38+
* than or equal to {@link #getRowCount()} positions the reader <em>after</em> the last
39+
* row, so that {@link #next()} will return {@code false}.
40+
*
41+
* @param index the desired row index, zero is the first row; see above for special values
2742
*/
2843
void setRowIndex(int index);
2944

3045
/**
31-
* Set iterator to the next table row.
46+
* Set reader to the next table row.
47+
* <p>
48+
* On success {@link #next()} will reset all column parsers to the values in next row. Column parsers
49+
* are invalid before the first {@link #next()} call.
3250
*
33-
* On success tryNextRow will reset all column parsers to the values in next row.
34-
* Column parsers are invalid before the first TryNextRow call.
51+
* @return returns {@code true} if the next row is available, {@code false} otherwise.
3552
*/
3653
boolean next();
3754

3855
/**
39-
* Returns column name by index.
56+
* Gets column name by index.
57+
*
58+
* @param index the column index, zero is the first column
59+
* @return the column name
60+
* @throws IllegalArgumentException if the index is out of range
61+
* (<code>index &lt; 0 || index &gt;= getColumnCount()</code>)
4062
*/
4163
String getColumnName(int index);
4264

4365
/**
44-
* Returns column index by name or {@code -1} if column with given name is not present.
66+
* Gets column type by index.
67+
*
68+
* @param index the column index, zero is the first column
69+
* @return the column type
70+
* @throws IllegalArgumentException if the index is out of range
71+
* (<code>index &lt; 0 || index &gt;= getColumnCount()</code>)
4572
*/
46-
int getColumnIndex(String name);
73+
Type getColumnType(int index);
4774

4875
/**
49-
* Returns value reader for column by index.
76+
* Gets column index by name or {@code -1} if column with given name is not present.
77+
*
78+
* @param name the column name
79+
* @return the column index
5080
*/
51-
ValueReader getColumn(int index);
81+
int getColumnIndex(String name);
5282

5383
/**
54-
* Returns value reader for column by name.
84+
* Gets the current row value reader by the column index
85+
*
86+
* @param index the column index, zero is the first column
87+
* @return the value reader
88+
* @throws IllegalArgumentException if the column index is out of range
89+
* (<code>index &lt; 0 || index &gt;= getColumnCount()</code>)
90+
* @throws IllegalStateException if the result set reading was not started or was already finished
5591
*/
56-
ValueReader getColumn(String name);
92+
ValueReader getColumn(int index);
5793

5894
/**
59-
* Returns column type by index (always create a new type instance)
95+
* Gets the current row value reader by the column name
96+
*
97+
* @param name the column name
98+
* @return the value reader
99+
* @throws IllegalArgumentException if column with given name is not present
100+
* @throws IllegalStateException if the result set reading was not started or was already finished
60101
*/
61-
Type getColumnType(int index);
102+
ValueReader getColumn(String name);
62103
}

table/src/main/java/tech/ydb/table/result/impl/ProtoResultSetReader.java

Lines changed: 48 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -15,75 +15,78 @@
1515
*/
1616
final class ProtoResultSetReader implements ResultSetReader {
1717

18-
private final ValueProtos.ResultSet resultSet;
19-
private final Map<String, Integer> columnIndexes; // TODO: use better data structure
20-
private final AbstractValueReader[] columnReaders;
18+
private final ValueProtos.ResultSet rs;
19+
private final Map<String, Integer> columnIndexes;
20+
private final AbstractValueReader[] readers;
2121

22-
private int rowIndex;
23-
private ValueProtos.Value currentRow;
22+
private int rowIndex = -1; // before first
23+
private ValueProtos.Value currentRow = null;
2424

2525
ProtoResultSetReader(ValueProtos.ResultSet resultSet) {
26-
this.resultSet = resultSet;
26+
this.rs = resultSet;
2727
this.columnIndexes = Maps.newHashMapWithExpectedSize(resultSet.getColumnsCount());
28-
this.columnReaders = new AbstractValueReader[resultSet.getColumnsCount()];
28+
this.readers = new AbstractValueReader[resultSet.getColumnsCount()];
2929

3030
for (int i = 0; i < resultSet.getColumnsCount(); i++) {
3131
ValueProtos.Column columnMeta = resultSet.getColumns(i);
3232
this.columnIndexes.put(columnMeta.getName(), i);
33-
this.columnReaders[i] = ProtoValueReaders.forTypeImpl(columnMeta.getType());
33+
this.readers[i] = ProtoValueReaders.forTypeImpl(columnMeta.getType());
3434
}
3535
}
3636

3737
@Override
3838
public boolean isTruncated() {
39-
return resultSet.getTruncated();
39+
return rs.getTruncated();
4040
}
4141

42-
/**
43-
* Returns number of columns.
44-
*/
4542
@Override
4643
public int getColumnCount() {
47-
return resultSet.getColumnsCount();
44+
return rs.getColumnsCount();
4845
}
4946

50-
/**
51-
* Returns number of rows.
52-
*/
5347
@Override
5448
public int getRowCount() {
55-
return resultSet.getRowsCount();
49+
return rs.getRowsCount();
5650
}
5751

5852
@Override
5953
public void setRowIndex(int index) {
60-
if (index < 0 || index >= resultSet.getRowsCount()) {
54+
if (index <= -1) {
55+
rowIndex = -1; // before first
6156
currentRow = null;
62-
} else {
63-
rowIndex = index;
64-
currentRow = resultSet.getRows(index);
57+
return;
6558
}
59+
60+
if (index >= rs.getRowsCount()) {
61+
rowIndex = rs.getRowsCount(); // after last
62+
currentRow = null;
63+
return;
64+
}
65+
66+
rowIndex = index;
67+
currentRow = rs.getRows(rowIndex);
6668
}
6769

68-
/**
69-
* Set iterator to the next table row.
70-
*
71-
* On success tryNextRow will reset all column parsers to the values in next row.
72-
* Column parsers are invalid before the first TryNextRow call.
73-
*/
7470
@Override
7571
public boolean next() {
76-
if (rowIndex >= resultSet.getRowsCount()) {
72+
rowIndex++;
73+
74+
if (rowIndex >= rs.getRowsCount()) {
75+
rowIndex = rs.getRowsCount(); // after last
7776
currentRow = null;
7877
return false;
7978
}
80-
currentRow = resultSet.getRows(rowIndex++);
79+
80+
currentRow = rs.getRows(rowIndex);
8181
return true;
8282
}
8383

8484
@Override
8585
public String getColumnName(int index) {
86-
return resultSet.getColumns(index).getName();
86+
if (index < 0 || index >= rs.getColumnsCount()) {
87+
throw new IllegalArgumentException("Column index: " + index + ", columns count: " + readers.length);
88+
}
89+
return rs.getColumns(index).getName();
8790
}
8891

8992
@Override
@@ -95,34 +98,36 @@ public int getColumnIndex(String name) {
9598
@Override
9699
public ValueReader getColumn(int index) {
97100
if (currentRow == null) {
98-
throw new IllegalStateException("empty result set or next() was never called");
101+
throw new IllegalStateException("ResultSetReader not positioned properly, perhaps you need to call next.");
99102
}
100-
AbstractValueReader reader = columnReaders[index];
103+
if (index < 0 || index >= readers.length) {
104+
throw new IllegalArgumentException("Column index: " + index + ", columns count: " + readers.length);
105+
}
106+
AbstractValueReader reader = readers[index];
101107
reader.setProtoValue(currentRow.getItems(index));
102108
return reader;
103109
}
104110

105111
@Override
106112
public ValueReader getColumn(String name) {
107-
int index = columnIndex(name);
113+
Integer index = columnIndexes.get(name);
114+
if (index == null) {
115+
throw new IllegalArgumentException("Unknown column '" + name + "'");
116+
}
108117
return getColumn(index);
109118
}
110119

111120
@Override
112121
public Type getColumnType(int index) {
113-
AbstractValueReader reader = columnReaders[index];
114-
return reader.getType();
115-
}
116-
117-
private int columnIndex(String name) {
118-
Integer index = columnIndexes.get(name);
119-
if (index == null) {
120-
throw new IllegalArgumentException("unknown column '" + name + "'");
122+
if (index < 0 || index >= readers.length) {
123+
throw new IllegalArgumentException("Column index: " + index + ", columns count: " + readers.length);
121124
}
122-
return index;
125+
AbstractValueReader reader = readers[index];
126+
return reader.getType();
123127
}
124128

129+
@Deprecated
125130
ValueProtos.ResultSet getResultSet() {
126-
return resultSet;
131+
return rs;
127132
}
128133
}

table/src/main/java/tech/ydb/table/result/impl/ProtoValueReaders.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public static ResultSetReader forResultSet(ValueProtos.ResultSet resultSet) {
2020
return new ProtoResultSetReader(resultSet);
2121
}
2222

23+
@Deprecated
2324
public static ResultSetReader forResultSets(Collection<ResultSetReader> resultSets) {
2425
// TODO: add lightweight implementation instead of proto joining
2526
Preconditions.checkArgument(!resultSets.isEmpty(), "Expect multiple result sets to join from");

0 commit comments

Comments
 (0)