Skip to content

Commit 5dddb6e

Browse files
HIVE-1996: Beeline performance poor with drivers having slow DatabaseMetaData.getPrimaryKeys impl (#6023)
1 parent ac3ea05 commit 5dddb6e

File tree

4 files changed

+289
-63
lines changed

4 files changed

+289
-63
lines changed

beeline/src/java/org/apache/hive/beeline/Rows.java

Lines changed: 45 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,13 @@
2929
import java.sql.SQLException;
3030
import java.text.DecimalFormat;
3131
import java.text.NumberFormat;
32+
import java.util.ArrayList;
3233
import java.util.Arrays;
3334
import java.util.Base64;
35+
import java.util.HashMap;
3436
import java.util.Iterator;
37+
import java.util.List;
38+
import java.util.Map;
3539

3640
import org.apache.hadoop.hive.common.cli.EscapeCRLFHelper;
3741

@@ -42,7 +46,8 @@
4246
abstract class Rows implements Iterator {
4347
protected final BeeLine beeLine;
4448
final ResultSetMetaData rsMeta;
45-
final Boolean[] primaryKeys;
49+
final boolean[] primaryKeyColumns;
50+
boolean isPrimaryKeyColumnsInitialized;
4651
final NumberFormat numberFormat;
4752
private boolean convertBinaryArrayToString;
4853
private final String nullStr;
@@ -51,14 +56,15 @@ abstract class Rows implements Iterator {
5156
this.beeLine = beeLine;
5257
nullStr = beeLine.getOpts().getNullString();
5358
rsMeta = rs.getMetaData();
54-
int count = rsMeta.getColumnCount();
55-
primaryKeys = new Boolean[count];
5659
if (beeLine.getOpts().getNumberFormat().equals("default")) {
5760
numberFormat = null;
5861
} else {
5962
numberFormat = new DecimalFormat(beeLine.getOpts().getNumberFormat());
6063
}
6164
this.convertBinaryArrayToString = beeLine.getOpts().getConvertBinaryArrayToString();
65+
66+
int count = this.rsMeta.getColumnCount();
67+
primaryKeyColumns = new boolean[count];
6268
}
6369

6470
@Override
@@ -78,37 +84,47 @@ public void remove() {
7884
* JDBC driver property implements {@link ResultSetMetaData#getTableName} (many do not), it
7985
* is not reliable for all databases.
8086
*/
81-
boolean isPrimaryKey(int col) {
82-
if (primaryKeys[col] == null) {
83-
try {
84-
// this doesn't always work, since some JDBC drivers (e.g.,
85-
// Oracle's) return a blank string from getDbTableName.
86-
String table = rsMeta.getTableName(col + 1);
87-
String column = rsMeta.getColumnName(col + 1);
87+
boolean isPrimaryKeyCol(int col) {
88+
if (!isPrimaryKeyColumnsInitialized) {
89+
initializePrimaryKeyMetadata();
90+
}
91+
92+
return primaryKeyColumns[col];
93+
}
94+
95+
private void initializePrimaryKeyMetadata() {
96+
Map<String, List<String>> tablePrimaryKeys = new HashMap<>();
97+
98+
try {
99+
for (int i = 0; i < primaryKeyColumns.length; i++) {
100+
String table = rsMeta.getTableName(i + 1);
101+
String column = rsMeta.getColumnName(i + 1);
88102

89103
if (table == null || table.isEmpty() || column == null || column.isEmpty()) {
90-
primaryKeys[col] = Boolean.FALSE;
91-
} else {
92-
ResultSet pks = beeLine.getDatabaseConnection().getDatabaseMetaData().getPrimaryKeys(
93-
beeLine.getDatabaseConnection().getDatabaseMetaData().getConnection().getCatalog(), null, table);
104+
continue;
105+
}
106+
107+
if (!tablePrimaryKeys.containsKey(table)) {
108+
try (ResultSet pks = beeLine.getDatabaseConnection().getDatabaseMetaData().getPrimaryKeys(
109+
beeLine.getDatabaseConnection().getDatabaseMetaData().getConnection().getCatalog(), null, table)) {
110+
111+
List<String> pkNames = new ArrayList<>();
94112

95-
primaryKeys[col] = Boolean.FALSE;
96-
try {
97113
while (pks.next()) {
98-
if (column.equalsIgnoreCase(pks.getString("COLUMN_NAME"))) {
99-
primaryKeys[col] = Boolean.TRUE;
100-
break;
101-
}
114+
pkNames.add(pks.getString("COLUMN_NAME"));
102115
}
103-
} finally {
104-
pks.close();
116+
117+
tablePrimaryKeys.put(table, pkNames);
105118
}
106119
}
107-
} catch (SQLException sqle) {
108-
primaryKeys[col] = Boolean.FALSE;
120+
121+
primaryKeyColumns[i] = tablePrimaryKeys.get(table).contains(column);
109122
}
123+
} catch (SQLException e) {
124+
// Do nothing. We cannot decide if the given column is a primary key so we keep it as false
110125
}
111-
return primaryKeys[col].booleanValue();
126+
127+
isPrimaryKeyColumnsInitialized = true;
112128
}
113129

114130
class Row {
@@ -134,7 +150,7 @@ class Row {
134150
}
135151

136152
@Override
137-
public String toString(){
153+
public String toString() {
138154
return Arrays.asList(values).toString();
139155
}
140156

@@ -156,17 +172,17 @@ public String toString(){
156172
} catch (Throwable t) {
157173
}
158174

159-
for (int i = 0; i < size; i++) {
175+
for (int i = 0; i < size; i++) {
160176
Object o = rs.getObject(i + 1);
161177
String value = null;
162178

163179
if (o == null) {
164180
value = nullStr;
165181
} else if (o instanceof Number) {
166182
value = numberFormat != null ? numberFormat.format(o) :
167-
o instanceof BigDecimal ? ((BigDecimal)o).toPlainString() : o.toString();
183+
o instanceof BigDecimal ? ((BigDecimal) o).toPlainString() : o.toString();
168184
} else if (o instanceof byte[]) {
169-
value = convertBinaryArrayToString ? new String((byte[])o, StandardCharsets.UTF_8) : Base64.getEncoder().withoutPadding().encodeToString((byte[])o);
185+
value = convertBinaryArrayToString ? new String((byte[]) o, StandardCharsets.UTF_8) : Base64.getEncoder().withoutPadding().encodeToString((byte[]) o);
170186
} else {
171187
value = rs.getString(i + 1);
172188
}

beeline/src/java/org/apache/hive/beeline/TableOutputFormat.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,14 +122,14 @@ ColorBuffer getOutputString(Rows rows, Rows.Row row, String delim) {
122122

123123
if (row.isMeta) {
124124
v = beeLine.getColorBuffer().center(row.values[i], row.sizes[i]);
125-
if (beeLine.getOpts().getColor() && rows.isPrimaryKey(i)) {
125+
if (beeLine.getOpts().getColor() && rows.isPrimaryKeyCol(i)) {
126126
buf.cyan(v.getMono());
127127
} else {
128128
buf.bold(v.getMono());
129129
}
130130
} else {
131131
v = beeLine.getColorBuffer().pad(row.values[i], row.sizes[i]);
132-
if (beeLine.getOpts().getColor() && rows.isPrimaryKey(i)) {
132+
if (beeLine.getOpts().getColor() && rows.isPrimaryKeyCol(i)) {
133133
buf.cyan(v.getMono());
134134
} else {
135135
buf.append(v.getMono());
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.hive.beeline;
19+
20+
import java.io.PrintStream;
21+
import java.util.ArrayList;
22+
import java.util.List;
23+
24+
public class BeelineMock extends BeeLine {
25+
public BeelineMock() {
26+
this.allPrintedLines = new ArrayList<>();
27+
}
28+
29+
private String lastPrintedLine;
30+
private List<String> allPrintedLines;
31+
32+
@Override
33+
final void output(final ColorBuffer msg, boolean newline, PrintStream out) {
34+
lastPrintedLine = msg.getMono();
35+
allPrintedLines.add(msg.getColor());
36+
super.output(msg, newline, out);
37+
}
38+
39+
public String getLastPrintedLine() {
40+
return lastPrintedLine;
41+
}
42+
43+
public List<String> getAllPrintedLines() {
44+
return allPrintedLines;
45+
}
46+
}

0 commit comments

Comments
 (0)