forked from databricks/databricks-jdbc
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathColumnarRowView.java
More file actions
159 lines (140 loc) · 5.99 KB
/
ColumnarRowView.java
File metadata and controls
159 lines (140 loc) · 5.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package com.databricks.jdbc.api.impl;
import com.databricks.jdbc.exception.DatabricksSQLException;
import com.databricks.jdbc.model.client.thrift.generated.TColumn;
import com.databricks.jdbc.model.client.thrift.generated.TRowSet;
import com.databricks.jdbc.model.telemetry.enums.DatabricksDriverErrorCode;
import java.util.BitSet;
import java.util.List;
/**
* Memory-efficient columnar view that provides row-based access without materializing all rows.
* Instead of creating List<List<Object>>, this class provides direct access to columnar data on a
* per-row, per-column basis, significantly reducing memory allocations.
*/
public class ColumnarRowView {
private final List<TColumn> columns;
private final int rowCount;
private final ColumnAccessor[] columnAccessors;
public ColumnarRowView(TRowSet rowSet) throws DatabricksSQLException {
this.columns = rowSet != null ? rowSet.getColumns() : null;
if (columns == null || columns.isEmpty()) {
this.rowCount = 0;
this.columnAccessors = new ColumnAccessor[0];
} else {
this.rowCount = getRowCountFromFirstColumn();
this.columnAccessors = new ColumnAccessor[columns.size()];
for (int i = 0; i < columns.size(); i++) {
this.columnAccessors[i] = createColumnAccessor(columns.get(i));
}
}
}
/** Gets the number of rows in this view. */
public int getRowCount() {
return rowCount;
}
/** Gets the number of columns in this view. */
public int getColumnCount() {
return columns != null ? columns.size() : 0;
}
/** Gets the value at the specified row and column without materializing the entire row. */
public Object getValue(int rowIndex, int columnIndex) throws DatabricksSQLException {
if (rowIndex < 0 || rowIndex >= rowCount) {
throw new DatabricksSQLException(
"Row index out of bounds: " + rowIndex, DatabricksDriverErrorCode.INVALID_STATE);
}
if (columnIndex < 0 || columnIndex >= columnAccessors.length) {
throw new DatabricksSQLException(
"Column index out of bounds: " + columnIndex, DatabricksDriverErrorCode.INVALID_STATE);
}
return columnAccessors[columnIndex].getValue(rowIndex);
}
/**
* Creates a materialized row only when explicitly requested (for backward compatibility). This
* should be avoided in performance-critical paths.
*/
public Object[] materializeRow(int rowIndex) throws DatabricksSQLException {
if (rowIndex < 0 || rowIndex >= rowCount) {
throw new DatabricksSQLException(
"Row index out of bounds: " + rowIndex, DatabricksDriverErrorCode.INVALID_STATE);
}
Object[] row = new Object[columnAccessors.length];
for (int col = 0; col < columnAccessors.length; col++) {
row[col] = columnAccessors[col].getValue(rowIndex);
}
return row;
}
private int getRowCountFromFirstColumn() throws DatabricksSQLException {
if (columns.isEmpty()) {
return 0;
}
TColumn firstColumn = columns.get(0);
return getColumnSize(firstColumn);
}
private static int getColumnSize(TColumn column) throws DatabricksSQLException {
if (column.isSetBinaryVal()) return column.getBinaryVal().getValuesSize();
if (column.isSetBoolVal()) return column.getBoolVal().getValuesSize();
if (column.isSetByteVal()) return column.getByteVal().getValuesSize();
if (column.isSetDoubleVal()) return column.getDoubleVal().getValuesSize();
if (column.isSetI16Val()) return column.getI16Val().getValuesSize();
if (column.isSetI32Val()) return column.getI32Val().getValuesSize();
if (column.isSetI64Val()) return column.getI64Val().getValuesSize();
if (column.isSetStringVal()) return column.getStringVal().getValuesSize();
throw new DatabricksSQLException(
"Unsupported column type: " + column, DatabricksDriverErrorCode.UNSUPPORTED_OPERATION);
}
private static ColumnAccessor createColumnAccessor(TColumn column) throws DatabricksSQLException {
if (column.isSetBinaryVal()) {
return new TypedColumnAccessor<>(
column.getBinaryVal().getValues(), column.getBinaryVal().getNulls());
}
if (column.isSetBoolVal()) {
return new TypedColumnAccessor<>(
column.getBoolVal().getValues(), column.getBoolVal().getNulls());
}
if (column.isSetByteVal()) {
return new TypedColumnAccessor<>(
column.getByteVal().getValues(), column.getByteVal().getNulls());
}
if (column.isSetDoubleVal()) {
return new TypedColumnAccessor<>(
column.getDoubleVal().getValues(), column.getDoubleVal().getNulls());
}
if (column.isSetI16Val()) {
return new TypedColumnAccessor<>(
column.getI16Val().getValues(), column.getI16Val().getNulls());
}
if (column.isSetI32Val()) {
return new TypedColumnAccessor<>(
column.getI32Val().getValues(), column.getI32Val().getNulls());
}
if (column.isSetI64Val()) {
return new TypedColumnAccessor<>(
column.getI64Val().getValues(), column.getI64Val().getNulls());
}
if (column.isSetStringVal()) {
return new TypedColumnAccessor<>(
column.getStringVal().getValues(), column.getStringVal().getNulls());
}
throw new DatabricksSQLException(
"Unsupported column type: " + column, DatabricksDriverErrorCode.UNSUPPORTED_OPERATION);
}
/** Interface for accessing column values by index without materializing the entire column. */
private interface ColumnAccessor {
Object getValue(int rowIndex);
}
/** Memory-efficient column accessor that handles nulls and provides direct index-based access. */
private static class TypedColumnAccessor<T> implements ColumnAccessor {
private final List<T> values;
private final BitSet nullBits;
public TypedColumnAccessor(List<T> values, byte[] nulls) {
this.values = values;
this.nullBits = nulls != null ? BitSet.valueOf(nulls) : null;
}
@Override
public Object getValue(int rowIndex) {
if (nullBits != null && nullBits.get(rowIndex)) {
return null;
}
return values.get(rowIndex);
}
}
}